From 27ff9c39bf10e72943db4c811ff2c5cbafe7c3d0 Mon Sep 17 00:00:00 2001 From: Case Wylie Date: Wed, 6 Nov 2024 08:50:36 -0500 Subject: [PATCH] chore: starting point Signed-off-by: Case Wylie --- src/lib/filter/adjudicators.test.ts | 48 +++++++++---------- src/lib/filter/adjudicators.ts | 27 ++++++----- src/lib/filter/filtersWithLogs.ts | 4 +- src/lib/filter/logMessages.ts | 61 ++++++++++++++++++------ src/lib/filter/shouldSkipRequest.test.ts | 7 +-- 5 files changed, 90 insertions(+), 57 deletions(-) diff --git a/src/lib/filter/adjudicators.test.ts b/src/lib/filter/adjudicators.test.ts index 9fa21f9a..fb4f52bb 100644 --- a/src/lib/filter/adjudicators.test.ts +++ b/src/lib/filter/adjudicators.test.ts @@ -178,9 +178,9 @@ describe("carriedName", () => { [{ metadata: { name: null } }, ""], [{ metadata: { name: "name" } }, "name"], ])("given %j, returns '%s'", (given, expected) => { - const binding = given as DeepPartial; + const kubernetesObject = given as DeepPartial; - const result = sut.carriedName(binding); + const result = sut.carriedName(kubernetesObject); expect(result).toBe(expected); }); @@ -194,9 +194,9 @@ describe("carriesName", () => { [{ metadata: { name: null } }, false], [{ metadata: { name: "name" } }, true], ])("given %j, returns %s", (given, expected) => { - const binding = given as DeepPartial; + const kubernetesObject = given as DeepPartial; - const result = sut.carriesName(binding); + const result = sut.carriesName(kubernetesObject); expect(result).toBe(expected); }); @@ -210,9 +210,9 @@ describe("missingName", () => { [{ metadata: { name: null } }, true], [{ metadata: { name: "name" } }, false], ])("given %j, returns %s", (given, expected) => { - const binding = given as DeepPartial; + const kubernetesObject = given as DeepPartial; - const result = sut.missingName(binding); + const result = sut.missingName(kubernetesObject); expect(result).toBe(expected); }); @@ -377,9 +377,9 @@ describe("carriedNamespace", () => { [{ metadata: { namespace: "" } }, ""], [{ metadata: { namespace: "namespace" } }, "namespace"], ])("given %j, returns %j", (given, expected) => { - const binding = given as DeepPartial; + const object = given as DeepPartial; - const result = sut.carriedNamespace(binding); + const result = sut.carriedNamespace(object); expect(result).toEqual(expected); }); @@ -394,9 +394,9 @@ describe("carriesNamespace", () => { [{ metadata: { namespace: "" } }, false], [{ metadata: { namespace: "namespace" } }, true], ])("given %j, returns %s", (given, expected) => { - const binding = given as DeepPartial; + const object = given as DeepPartial; - const result = sut.carriesNamespace(binding); + const result = sut.carriesNamespace(object); expect(result).toBe(expected); }); @@ -412,7 +412,7 @@ describe("mismatchedNamespace", () => { [{ filters: { namespaces: ["namespace"] } }, { metadata: { namespace: "namespace" } }, false], ])("given binding %j and object %j, returns %s", (bnd, obj, expected) => { const binding = bnd as DeepPartial; - const object = obj as DeepPartial; + const object = obj as DeepPartial; const result = sut.mismatchedNamespace(binding, object); @@ -443,7 +443,7 @@ describe("mismatchedNamespaceRegex", () => { ], ])("given binding %j and object %j, returns %s", (bnd, obj, expected) => { const binding = bnd as DeepPartial; - const object = obj as DeepPartial; + const object = obj as DeepPartial; const result = sut.mismatchedNamespaceRegex(binding, object); @@ -513,9 +513,9 @@ describe("carriedAnnotations", () => { [{ metadata: { annotations: { annotation: "" } } }, { annotation: "" }], [{ metadata: { annotations: { anno: "tation" } } }, { anno: "tation" }], ])("given %j, returns %j", (given, expected) => { - const binding = given as DeepPartial; + const object = given as DeepPartial; - const result = sut.carriedAnnotations(binding); + const result = sut.carriedAnnotations(object); expect(result).toEqual(expected); }); @@ -531,9 +531,9 @@ describe("carriesAnnotations", () => { [{ metadata: { annotations: { annotation: "" } } }, true], [{ metadata: { annotations: { anno: "tation" } } }, true], ])("given %j, returns %s", (given, expected) => { - const binding = given as DeepPartial; + const object = given as DeepPartial; - const result = sut.carriesAnnotations(binding); + const result = sut.carriesAnnotations(object); expect(result).toBe(expected); }); @@ -591,7 +591,7 @@ describe("mismatchedAnnotations", () => { ], ])("given binding %j and object %j, returns %s", (bnd, obj, expected) => { const binding = bnd as DeepPartial; - const object = obj as DeepPartial; + const object = obj as DeepPartial; const result = sut.mismatchedAnnotations(binding, object); @@ -645,9 +645,9 @@ describe("carriedLabels", () => { [{ metadata: { labels: { label: "" } } }, { label: "" }], [{ metadata: { labels: { lab: "el" } } }, { lab: "el" }], ])("given %j, returns %j", (given, expected) => { - const binding = given as DeepPartial; + const obj = given as DeepPartial; - const result = sut.carriedLabels(binding); + const result = sut.carriedLabels(obj); expect(result).toEqual(expected); }); @@ -663,9 +663,9 @@ describe("carriesLabels", () => { [{ metadata: { labels: { label: "" } } }, true], [{ metadata: { labels: { lab: "el" } } }, true], ])("given %j, returns %s", (given, expected) => { - const binding = given as DeepPartial; + const obj = given as DeepPartial; - const result = sut.carriesLabels(binding); + const result = sut.carriesLabels(obj); expect(result).toBe(expected); }); @@ -691,7 +691,7 @@ describe("mismatchedLabels", () => { [{ filters: { labels: { l: "a", b: "le" } } }, { metadata: { labels: { l: "a", b: "le" } } }, false], ])("given binding %j and object %j, returns %s", (bnd, obj, expected) => { const binding = bnd as DeepPartial; - const object = obj as DeepPartial; + const object = obj as DeepPartial; const result = sut.mismatchedLabels(binding, object); @@ -720,7 +720,7 @@ describe("uncarryableNamespace", () => { [["name", "space"], { metadata: { namespace: "name" } }, false], [["name", "space"], { metadata: { namespace: "space" } }, false], ])("given capabilityNamespaces %j and object %j, returns %s", (nss, obj, expected) => { - const object = obj as DeepPartial; + const object = obj as DeepPartial; const result = sut.uncarryableNamespace(nss, object); @@ -749,7 +749,7 @@ describe("carriesIgnoredNamespace", () => { [["ign", "ored"], { metadata: { namespace: "ored" } }, true], [["ign", "ored"], { metadata: { namespace: "namespace" } }, false], ])("given capabilityNamespaces %j and object %j, returns %s", (nss, obj, expected) => { - const object = obj as DeepPartial; + const object = obj as DeepPartial; const result = sut.carriesIgnoredNamespace(nss, object); diff --git a/src/lib/filter/adjudicators.ts b/src/lib/filter/adjudicators.ts index f8cb6cd5..336b4512 100644 --- a/src/lib/filter/adjudicators.ts +++ b/src/lib/filter/adjudicators.ts @@ -18,7 +18,8 @@ import { nthArg, pipe, } from "ramda"; - +import { AdmissionRequest } from "../types"; +import { KubernetesObject } from "kubernetes-fluent-client"; /* Naming scheme: - AdmissionRequest - "declares" / "neglects" @@ -29,34 +30,34 @@ import { /* AdmissionRequest collectors */ -export const declaredOperation = pipe(request => request?.operation, defaultTo("")); -export const declaredGroup = pipe(request => request?.kind?.group, defaultTo("")); -export const declaredVersion = pipe(request => request?.kind?.version, defaultTo("")); -export const declaredKind = pipe(request => request?.kind?.kind, defaultTo("")); -export const declaredUid = pipe(request => request?.uid, defaultTo("")); +export const declaredOperation = pipe((request: AdmissionRequest) => request?.operation, defaultTo("")); +export const declaredGroup = pipe((request: AdmissionRequest) => request?.kind?.group, defaultTo("")); +export const declaredVersion = pipe((request: AdmissionRequest) => request?.kind?.version, defaultTo("")); +export const declaredKind = pipe((request: AdmissionRequest) => request?.kind?.kind, defaultTo("")); +export const declaredUid = pipe((request: AdmissionRequest) => request?.uid, defaultTo("")); /* KuberneteskubernetesObjectect collectors */ export const carriesDeletionTimestamp = pipe( - kubernetesObject => !!kubernetesObject.metadata?.deletionTimestamp, + (kubernetesObject: KubernetesObject) => !!kubernetesObject.metadata?.deletionTimestamp, defaultTo(false), ); export const missingDeletionTimestamp = complement(carriesDeletionTimestamp); -export const carriedKind = pipe(kubernetesObject => kubernetesObject?.metadata?.kind, defaultTo("not set")); -export const carriedVersion = pipe(kubernetesObject => kubernetesObject?.metadata?.version, defaultTo("not set")); -export const carriedName = pipe(kubernetesObject => kubernetesObject?.metadata?.name, defaultTo("")); +export const carriedKind = pipe((kubernetesObject: KubernetesObject) => kubernetesObject?.kind, defaultTo("not set")); +export const carriedAPIVersion = pipe((kubernetesObject: KubernetesObject) => kubernetesObject?.apiVersion, defaultTo("not set")); +export const carriedName = pipe((kubernetesObject: KubernetesObject) => kubernetesObject?.metadata?.name, defaultTo("")); export const carriesName = pipe(carriedName, equals(""), not); export const missingName = complement(carriesName); -export const carriedNamespace = pipe(kubernetesObject => kubernetesObject?.metadata?.namespace, defaultTo("")); +export const carriedNamespace = pipe((kubernetesObject: KubernetesObject) => kubernetesObject?.metadata?.namespace, defaultTo("")); export const carriesNamespace = pipe(carriedNamespace, equals(""), not); -export const carriedAnnotations = pipe(kubernetesObject => kubernetesObject?.metadata?.annotations, defaultTo({})); +export const carriedAnnotations = pipe((kubernetesObject: KubernetesObject) => kubernetesObject?.metadata?.annotations, defaultTo({})); export const carriesAnnotations = pipe(carriedAnnotations, equals({}), not); -export const carriedLabels = pipe(kubernetesObject => kubernetesObject?.metadata?.labels, defaultTo({})); +export const carriedLabels = pipe((kubernetesObject: KubernetesObject) => kubernetesObject?.metadata?.labels, defaultTo({})); export const carriesLabels = pipe(carriedLabels, equals({}), not); /* diff --git a/src/lib/filter/filtersWithLogs.ts b/src/lib/filter/filtersWithLogs.ts index 138c8074..cabd5dc2 100644 --- a/src/lib/filter/filtersWithLogs.ts +++ b/src/lib/filter/filtersWithLogs.ts @@ -96,7 +96,7 @@ export const carriesIgnoredNamespaceFilter = createFilter( data => data.ignoredNamespaces, data => getAdmissionRequest(data), (ignoreArray, kubernetesObject) => carriesIgnoredNamespace(ignoreArray, kubernetesObject), - (ignoreArray, kubernetesObject) => commonLogMessage("ignored namespaces", kubernetesObject, ignoreArray), + (ignoreArray, kubernetesObject) => commonLogMessage("ignored namespaces", ignoreArray, kubernetesObject), ); export const unbindableNamespacesFilter = createFilter( @@ -142,5 +142,5 @@ export const uncarryableNamespaceFilter = createFilter( data => getAdmissionRequest(data), (capabilityNamespaces, kubernetesObject) => uncarryableNamespace(capabilityNamespaces, kubernetesObject), (capabilityNamespaces, kubernetesObject) => - commonLogMessage("namespace array", kubernetesObject, capabilityNamespaces), + commonLogMessage("uncarryable namespace", capabilityNamespaces, kubernetesObject ), ); diff --git a/src/lib/filter/logMessages.ts b/src/lib/filter/logMessages.ts index d2f2096a..22b86862 100644 --- a/src/lib/filter/logMessages.ts +++ b/src/lib/filter/logMessages.ts @@ -1,10 +1,14 @@ -import { FilterInput } from "../types"; +import { KubernetesObject } from "kubernetes-fluent-client"; +import { AdmissionRequest, FilterInput } from "../types"; import { carriedKind, carriedName, carriedNamespace, - carriedVersion, + carriedAPIVersion, + declaredGroup, + declaredVersion, definedAnnotations, + declaredKind, definedGroup, definedKind, definedLabels, @@ -16,13 +20,14 @@ import { const prefix = "Ignoring Admission Callback:"; +const ignoredNamespacesKubernetesObjectCases = ["ignored namespaces"]; +const capabilityNamespacesKubernetesObjectCases = ["uncarryable namespace"]; const bindingKubernetesObjectCases = [ "annotations", "deletionTimestamp", "labels", "name regex", "name", - "namespace array", "namespace regexes", "namespaces", ]; @@ -33,44 +38,70 @@ export const commonLogMessage = (subject: string, filterInput: FilterInput, filt return getBindingKubernetesObjectMessage(subject, filterInput, filterCriteria); } else if (bindingAdmissionRequestCases.includes(subject)) { return getBindingAdmissionRequestMessage(subject, filterInput, filterCriteria); - } else if (subject === "ignored namespaces") { - return `${prefix} Object carries namespace '${carriedNamespace(filterInput)}' but ${subject} include '${JSON.stringify(filterCriteria)}'.`; + } else if (capabilityNamespacesKubernetesObjectCases.includes(subject)){ + return getCapabilityNamespacesKubernetesObjectMessage(subject, filterInput, filterCriteria); + } else if (ignoredNamespacesKubernetesObjectCases.includes(subject)) { + return getIgnoredNamespacesKubernetesObjectMessage(subject, filterInput, filterCriteria); } else { return getUndefinedLoggingConditionMessage(subject, filterInput, filterCriteria); } }; const getBindingAdmissionRequestMessage = (subject: string, filterInput: FilterInput, filterCriteria?: FilterInput) => { + const admissionFilterCriteria = filterCriteria as AdmissionRequest; switch (subject) { case "group": - return `${prefix} Binding defines ${subject} '${definedGroup(filterInput)}' but Request declares '${carriedName(filterCriteria)}'.`; + return `${prefix} Binding defines ${subject} '${definedGroup(filterInput)}' but Request declares '${declaredGroup(admissionFilterCriteria)}'.`; case "event": return `${prefix} Binding defines ${subject} '${definedKind(filterInput)}' but Request does not declare it.`; case "version": - return `${prefix} Binding defines ${subject} '${definedVersion(filterInput)}' but Request declares '${carriedVersion(filterCriteria)}'.`; + return `${prefix} Binding defines ${subject} '${definedVersion(filterInput)}' but Request declares '${declaredVersion(admissionFilterCriteria)}'.`; case "kind": - return `${prefix} Binding defines ${subject} '${definedKind(filterInput)}' but Request declares '${carriedKind(filterCriteria)}'.`; + return `${prefix} Binding defines ${subject} '${definedKind(filterInput)}' but Request declares '${declaredKind(admissionFilterCriteria)}'.`; default: return getUndefinedLoggingConditionMessage(subject, filterInput, filterCriteria); } }; +const getCapabilityNamespacesKubernetesObjectMessage = (subject: string, filterInput: FilterInput, filterCriteria?: FilterInput) => { + const capabilityNamespacesFilterInput = filterInput as string[]; + const kubernetesObjectFilterCriteria = filterCriteria as KubernetesObject; + switch(subject){ + case "uncarryable namespace": + return `${prefix} Object carries namespace '${carriedNamespace(kubernetesObjectFilterCriteria)}' but namespaces allowed by Capability are '${JSON.stringify(capabilityNamespacesFilterInput)}'.`; + default: + return getUndefinedLoggingConditionMessage(subject, filterInput, filterCriteria); + } +} +const getIgnoredNamespacesKubernetesObjectMessage = (subject: string, filterInput: FilterInput, filterCriteria?: FilterInput) => { + const ingoredNSFilterInput = filterInput as string[]; + const kubernetesObjectFilterCriteria = filterCriteria as KubernetesObject; + switch(subject){ + case "ignored namespaces": + return `${prefix} Object carries namespace '${carriedNamespace(kubernetesObjectFilterCriteria)}' but ${subject} include '${JSON.stringify(ingoredNSFilterInput)}'.`; + default: + return getUndefinedLoggingConditionMessage(subject, filterInput, filterCriteria); + } +} + const getBindingKubernetesObjectMessage = (subject: string, filterInput: FilterInput, filterCriteria?: FilterInput) => { + const kubernetesObjectFilterCriteria = filterCriteria as KubernetesObject; + switch (subject) { case "namespaces": - return `${prefix} Binding defines ${subject} '${definedNamespaces(filterInput)}' but Object carries '${carriedNamespace(filterCriteria)}'.`; + return `${prefix} Binding defines ${subject} '${definedNamespaces(filterInput)}' but Object carries '${carriedNamespace(kubernetesObjectFilterCriteria)}'.`; case "annotations": - return `${prefix} Binding defines ${subject} '${definedAnnotations(filterInput)}' but Object carries '${carriedName(filterCriteria)}'.`; + return `${prefix} Binding defines ${subject} '${definedAnnotations(filterInput)}' but Object carries '${carriedName(kubernetesObjectFilterCriteria)}'.`; case "labels": - return `${prefix} Binding defines ${subject} '${definedLabels(filterInput)}' but Object carries '${carriedName(filterCriteria)}'.`; + return `${prefix} Binding defines ${subject} '${definedLabels(filterInput)}' but Object carries '${carriedName(kubernetesObjectFilterCriteria)}'.`; case "name": - return `${prefix} Binding defines ${subject} '${definedName(filterInput)}' but Object carries '${carriedName(filterCriteria)}'.`; + return `${prefix} Binding defines ${subject} '${definedName(filterInput)}' but Object carries '${carriedName(kubernetesObjectFilterCriteria)}'.`; case "namespace array": - return `${prefix} Object carries namespace '${carriedNamespace(filterInput)}' but namespaces allowed by Capability are '${JSON.stringify(filterCriteria)}'.`; + return `${prefix} Object carries namespace '${carriedNamespace(kubernetesObjectFilterCriteria)}' but namespaces allowed by Capability are '${JSON.stringify(definedNamespaces(filterInput))}'.`; case "name regex": - return `${prefix} Binding defines ${subject} '${definedNameRegex(filterInput)}' but Object carries '${carriedName(filterCriteria)}'.`; + return `${prefix} Binding defines ${subject} '${definedNameRegex(filterInput)}' but Object carries '${carriedName(kubernetesObjectFilterCriteria)}'.`; case "namespace regexes": - return `${prefix} Binding defines ${subject} '${definedNameRegex(filterInput)}' but Object carries '${carriedName(filterCriteria)}'.`; + return `${prefix} Binding defines ${subject} '${definedNameRegex(filterInput)}' but Object carries '${carriedName(kubernetesObjectFilterCriteria)}'.`; case "deletionTimestamp": return getDeletionTimestampLogMessage(filterInput, filterCriteria); default: diff --git a/src/lib/filter/shouldSkipRequest.test.ts b/src/lib/filter/shouldSkipRequest.test.ts index 429d21f3..d7fb8591 100644 --- a/src/lib/filter/shouldSkipRequest.test.ts +++ b/src/lib/filter/shouldSkipRequest.test.ts @@ -269,9 +269,10 @@ it("should reject when kind does not match", () => { callback, }; const pod = CreatePod(); - + const actualOutput = shouldSkipRequest(binding, pod, []) + console.log(actualOutput) expect(shouldSkipRequest(binding, pod, [])).toMatch( - /Ignoring Admission Callback: Binding defines kind '.+' but Request declares 'not set'./, + /Ignoring Admission Callback: Binding defines kind '.+' but Request declares '.+'./, ); }); @@ -290,7 +291,7 @@ it("should reject when group does not match", () => { const pod = CreatePod(); expect(shouldSkipRequest(binding, pod, [])).toMatch( - /Ignoring Admission Callback: Binding defines group '.+' but Request declares '.+'./, + /Ignoring Admission Callback: Binding defines group '.+' but Request declares ''./, ); });