pepr 0.46.0 → 0.46.1-nightly.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/cli/init/index.d.ts.map +1 -1
  2. package/dist/cli/init/templates.d.ts +2 -3
  3. package/dist/cli/init/templates.d.ts.map +1 -1
  4. package/dist/cli.js +36 -23
  5. package/dist/controller.js +1 -1
  6. package/dist/lib/assets/defaultTestObjects.d.ts +21 -0
  7. package/dist/lib/assets/defaultTestObjects.d.ts.map +1 -0
  8. package/dist/lib/filter/adjudicators/admissionRequest.d.ts +9 -0
  9. package/dist/lib/filter/adjudicators/admissionRequest.d.ts.map +1 -0
  10. package/dist/lib/filter/adjudicators/binding.d.ts +31 -0
  11. package/dist/lib/filter/adjudicators/binding.d.ts.map +1 -0
  12. package/dist/lib/filter/adjudicators/kubernetesObject.d.ts +22 -0
  13. package/dist/lib/filter/adjudicators/kubernetesObject.d.ts.map +1 -0
  14. package/dist/lib/filter/adjudicators/mismatch.d.ts +16 -0
  15. package/dist/lib/filter/adjudicators/mismatch.d.ts.map +1 -0
  16. package/dist/lib/filter/adjudicators/postCollection.d.ts +10 -0
  17. package/dist/lib/filter/adjudicators/postCollection.d.ts.map +1 -0
  18. package/dist/lib/filter/filter.d.ts.map +1 -1
  19. package/dist/lib.js +196 -152
  20. package/dist/lib.js.map +4 -4
  21. package/package.json +5 -6
  22. package/src/cli/init/index.ts +38 -18
  23. package/src/cli/init/templates.ts +5 -7
  24. package/src/lib/assets/defaultTestObjects.ts +137 -0
  25. package/src/lib/filter/adjudicators/admissionRequest.ts +25 -0
  26. package/src/lib/filter/adjudicators/binding.ts +85 -0
  27. package/src/lib/filter/adjudicators/kubernetesObject.ts +90 -0
  28. package/src/lib/filter/adjudicators/mismatch.ts +129 -0
  29. package/src/lib/filter/adjudicators/postCollection.ts +77 -0
  30. package/src/lib/filter/filter.ts +22 -24
  31. package/src/lib/processors/watch-processor.ts +1 -1
  32. package/dist/lib/filter/adjudicators/adjudicators.d.ts +0 -74
  33. package/dist/lib/filter/adjudicators/adjudicators.d.ts.map +0 -1
  34. package/src/lib/filter/adjudicators/adjudicators.ts +0 -334
package/package.json CHANGED
@@ -15,7 +15,7 @@
15
15
  "!src/**/*.test.ts",
16
16
  "!dist/**/*.test.d.ts*"
17
17
  ],
18
- "version": "0.46.0",
18
+ "version": "0.46.1-nightly.1",
19
19
  "main": "dist/lib.js",
20
20
  "types": "dist/lib.d.ts",
21
21
  "scripts": {
@@ -52,7 +52,7 @@
52
52
  "follow-redirects": "1.15.9",
53
53
  "http-status-codes": "^2.3.0",
54
54
  "json-pointer": "^0.6.2",
55
- "kubernetes-fluent-client": "3.4.0",
55
+ "kubernetes-fluent-client": "3.4.2",
56
56
  "pino": "9.6.0",
57
57
  "pino-pretty": "13.0.0",
58
58
  "prom-client": "15.1.3",
@@ -76,12 +76,11 @@
76
76
  "jest": "29.7.0",
77
77
  "js-yaml": "^4.1.0",
78
78
  "shellcheck": "^3.0.0",
79
- "ts-jest": "29.2.5",
79
+ "ts-jest": "29.2.6",
80
80
  "undici": "^7.0.1"
81
81
  },
82
82
  "overrides": {
83
- "glob": "^9.0.0",
84
- "jsonpath-plus": "^10.3.0"
83
+ "glob": "^9.0.0"
85
84
  },
86
85
  "peerDependencies": {
87
86
  "@types/prompts": "2.4.9",
@@ -96,4 +95,4 @@
96
95
  "typescript": "5.7.3",
97
96
  "uuid": "11.0.5"
98
97
  }
99
- }
98
+ }
@@ -9,10 +9,11 @@ import { RootCmd } from "../root";
9
9
  import {
10
10
  codeSettings,
11
11
  eslint,
12
- genPeprTS,
12
+ peprTSTemplate,
13
13
  genPkgJSON,
14
14
  gitignore,
15
15
  helloPepr,
16
+ peprPackageJSON,
16
17
  prettier,
17
18
  readme,
18
19
  samplesYaml,
@@ -50,29 +51,15 @@ export default function (program: RootCmd): void {
50
51
  .action(async opts => {
51
52
  const dirName = sanitizeName(response.name);
52
53
  const packageJSON = genPkgJSON(response, pkgOverride);
53
- const peprTS = genPeprTS();
54
54
 
55
- const confirmed = await confirm(dirName, packageJSON, peprTS.path, opts.confirm);
55
+ const confirmed = await confirm(dirName, packageJSON, peprTSTemplate.path, opts.confirm);
56
56
 
57
57
  if (confirmed) {
58
58
  console.log("Creating new Pepr module...");
59
59
 
60
60
  try {
61
- await createDir(dirName);
62
- await createDir(resolve(dirName, ".vscode"));
63
- await createDir(resolve(dirName, "capabilities"));
64
-
65
- await write(resolve(dirName, gitignore.path), gitignore.data);
66
- await write(resolve(dirName, eslint.path), eslint.data);
67
- await write(resolve(dirName, prettier.path), prettier.data);
68
- await write(resolve(dirName, packageJSON.path), packageJSON.data);
69
- await write(resolve(dirName, readme.path), readme.data);
70
- await write(resolve(dirName, tsConfig.path), tsConfig.data);
71
- await write(resolve(dirName, peprTS.path), peprTS.data);
72
- await write(resolve(dirName, ".vscode", snippet.path), snippet.data);
73
- await write(resolve(dirName, ".vscode", codeSettings.path), codeSettings.data);
74
- await write(resolve(dirName, "capabilities", samplesYaml.path), samplesYaml.data);
75
- await write(resolve(dirName, "capabilities", helloPepr.path), helloPepr.data);
61
+ await setupProjectStructure(dirName);
62
+ await createProjectFiles(dirName, packageJSON);
76
63
 
77
64
  if (!opts.skipPostInit) {
78
65
  doPostInitActions(dirName);
@@ -90,6 +77,39 @@ export default function (program: RootCmd): void {
90
77
  });
91
78
  }
92
79
 
80
+ async function setupProjectStructure(dirName: string): Promise<void> {
81
+ await createDir(dirName);
82
+ await createDir(resolve(dirName, ".vscode"));
83
+ await createDir(resolve(dirName, "capabilities"));
84
+ }
85
+
86
+ async function createProjectFiles(dirName: string, packageJSON: peprPackageJSON): Promise<void> {
87
+ const files = [
88
+ { path: gitignore.path, data: gitignore.data },
89
+ { path: eslint.path, data: eslint.data },
90
+ { path: prettier.path, data: prettier.data },
91
+ { path: packageJSON.path, data: packageJSON.data },
92
+ { path: readme.path, data: readme.data },
93
+ { path: tsConfig.path, data: tsConfig.data },
94
+ { path: peprTSTemplate.path, data: peprTSTemplate.data },
95
+ ];
96
+
97
+ const nestedFiles = [
98
+ { dir: ".vscode", path: snippet.path, data: snippet.data },
99
+ { dir: ".vscode", path: codeSettings.path, data: codeSettings.data },
100
+ { dir: "capabilities", path: samplesYaml.path, data: samplesYaml.data },
101
+ { dir: "capabilities", path: helloPepr.path, data: helloPepr.data },
102
+ ];
103
+
104
+ for (const file of files) {
105
+ await write(resolve(dirName, file.path), file.data);
106
+ }
107
+
108
+ for (const file of nestedFiles) {
109
+ await write(resolve(dirName, file.dir, file.path), file.data);
110
+ }
111
+ }
112
+
93
113
  const doPostInitActions = (dirName: string): void => {
94
114
  // run npm install from the new directory
95
115
  process.chdir(dirName);
@@ -20,7 +20,7 @@ import { sanitizeName } from "./utils";
20
20
 
21
21
  export const { dependencies, devDependencies, peerDependencies, scripts, version } = packageJSON;
22
22
 
23
- type peprPackageJSON = {
23
+ export type peprPackageJSON = {
24
24
  data: {
25
25
  name: string;
26
26
  version: string;
@@ -101,12 +101,10 @@ export function genPkgJSON(opts: InitOptions, pgkVerOverride?: string): peprPack
101
101
  };
102
102
  }
103
103
 
104
- export function genPeprTS(): { path: string; data: string } {
105
- return {
106
- path: "pepr.ts",
107
- data: peprTS,
108
- };
109
- }
104
+ export const peprTSTemplate = {
105
+ path: "pepr.ts",
106
+ data: peprTS,
107
+ };
110
108
 
111
109
  export const readme = {
112
110
  path: "README.md",
@@ -0,0 +1,137 @@
1
+ import { GenericClass } from "kubernetes-fluent-client";
2
+ import { Event } from "../enums";
3
+ import { Binding, CapabilityExport } from "../types";
4
+ import { defaultFilters } from "../filter/adjudicators/defaultTestObjects";
5
+ import { V1PolicyRule as PolicyRule } from "@kubernetes/client-node";
6
+
7
+ export const createMockRbacRule = (
8
+ apiGroups: string[] = ["pepr.dev"],
9
+ resources: string[] = ["peprstores"],
10
+ verbs: string[] = ["create", "get", "patch", "watch"],
11
+ ): PolicyRule => ({
12
+ apiGroups,
13
+ resources,
14
+ verbs,
15
+ });
16
+
17
+ export const createMockBinding = (
18
+ kindDetails: { group?: string; version?: string; kind?: string; plural?: string } = {},
19
+ options: { isWatch?: boolean; event?: Event; isFinalize?: boolean } = {},
20
+ ): Binding => {
21
+ const { group = "pepr.dev", version = "v1", kind = "peprstore", plural = "peprstores" } = kindDetails;
22
+
23
+ const { isWatch = false, event = Event.CREATE, isFinalize } = options;
24
+
25
+ return {
26
+ kind: { group, version, kind, plural },
27
+ isWatch,
28
+ ...(isFinalize !== undefined && { isFinalize }),
29
+ event,
30
+ model: {} as GenericClass,
31
+ filters: { ...defaultFilters, regexName: "" },
32
+ };
33
+ };
34
+
35
+ export const createMockCapability = (
36
+ rbacRules = [createMockRbacRule()],
37
+ bindings = [createMockBinding()],
38
+ ): CapabilityExport => ({
39
+ name: "",
40
+ hasSchedule: false,
41
+ description: "",
42
+ rbac: rbacRules,
43
+ bindings,
44
+ });
45
+
46
+ export const mockCapabilities: CapabilityExport[] = [
47
+ createMockCapability(),
48
+ createMockCapability(
49
+ [createMockRbacRule(["apiextensions.k8s.io"], ["customresourcedefinitions"], ["patch", "create"])],
50
+ [
51
+ createMockBinding(
52
+ {
53
+ group: "apiextensions.k8s.io",
54
+ version: "v1",
55
+ kind: "customresourcedefinition",
56
+ plural: "customresourcedefinitions",
57
+ },
58
+ { isWatch: false, event: Event.CREATE, isFinalize: false },
59
+ ),
60
+ ],
61
+ ),
62
+ createMockCapability(
63
+ [createMockRbacRule([""], ["namespaces"], ["watch"])],
64
+ [
65
+ createMockBinding(
66
+ { group: "", version: "v1", kind: "namespace", plural: "namespaces" },
67
+ { isWatch: true, event: Event.CREATE, isFinalize: false },
68
+ ),
69
+ ],
70
+ ),
71
+ createMockCapability(
72
+ [createMockRbacRule([""], ["configmaps"], ["watch"])],
73
+ [
74
+ createMockBinding(
75
+ { group: "", version: "v1", kind: "configmap", plural: "configmaps" },
76
+ { isWatch: true, event: Event.CREATE, isFinalize: false },
77
+ ),
78
+ ],
79
+ ),
80
+ ];
81
+
82
+ export const capabilityWithFinalize: CapabilityExport[] = [
83
+ createMockCapability(
84
+ [createMockRbacRule(["pepr.dev"], ["peprstores"], ["patch"])],
85
+ [
86
+ createMockBinding(
87
+ { group: "pepr.dev", version: "v1", kind: "peprstore", plural: "peprstores" },
88
+ { isWatch: false, event: Event.CREATE, isFinalize: true },
89
+ ),
90
+ ],
91
+ ),
92
+ ];
93
+
94
+ export const capabilityWithDuplicates: CapabilityExport[] = [
95
+ createMockCapability(
96
+ [createMockRbacRule(["pepr.dev"], ["peprstores"], ["create", "get"])],
97
+ [
98
+ createMockBinding(
99
+ { group: "pepr.dev", version: "v1", kind: "peprlog", plural: "peprlogs" },
100
+ { isWatch: false, event: Event.CREATE },
101
+ ),
102
+ ],
103
+ ),
104
+ createMockCapability(
105
+ [createMockRbacRule(["pepr.dev"], ["peprstores"], ["get", "patch"])],
106
+ [
107
+ createMockBinding(
108
+ { group: "pepr.dev", version: "v1", kind: "peprlog", plural: "peprlogs" },
109
+ { isWatch: false, event: Event.CREATE },
110
+ ),
111
+ ],
112
+ ),
113
+ ];
114
+
115
+ export const capabilityWithShortKey: CapabilityExport[] = [
116
+ createMockCapability(
117
+ [createMockRbacRule([""], ["nodes"], ["get"])],
118
+ [
119
+ createMockBinding(
120
+ { group: "", version: "v1", kind: "node", plural: "nodes" },
121
+ { isWatch: false, event: Event.CREATE },
122
+ ),
123
+ ],
124
+ ),
125
+ ];
126
+
127
+ export const capabilityWithLongKey: CapabilityExport[] = [
128
+ createMockCapability(
129
+ [createMockRbacRule(["apps"], ["deployments"], ["create"])],
130
+ [
131
+ createMockBinding(
132
+ { group: "apps", version: "v1", kind: "deployment", plural: "deployments" },
133
+ { isWatch: false, event: Event.CREATE },
134
+ ),
135
+ ],
136
+ ),
137
+ ];
@@ -0,0 +1,25 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { Operation } from "../../enums";
5
+ import { AdmissionRequest } from "../../types";
6
+ import { defaultTo, pipe } from "ramda";
7
+ import { KubernetesObject } from "kubernetes-fluent-client";
8
+
9
+ export const declaredOperation = pipe(
10
+ (request: AdmissionRequest<KubernetesObject>): Operation => request?.operation,
11
+ defaultTo(""),
12
+ );
13
+ export const declaredGroup = pipe(
14
+ (request: AdmissionRequest<KubernetesObject>): string => request?.kind?.group,
15
+ defaultTo(""),
16
+ );
17
+ export const declaredVersion = pipe(
18
+ (request: AdmissionRequest<KubernetesObject>): string | undefined => request?.kind?.version,
19
+ defaultTo(""),
20
+ );
21
+ export const declaredKind = pipe(
22
+ (request: AdmissionRequest<KubernetesObject>): string => request?.kind?.kind,
23
+ defaultTo(""),
24
+ );
25
+ export const declaredUid = pipe((request: AdmissionRequest<KubernetesObject>): string => request?.uid, defaultTo(""));
@@ -0,0 +1,85 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { Event } from "../../enums";
5
+ import { Binding, FinalizeAction, WatchLogAction, MutateAction, ValidateAction } from "../../types";
6
+ import { complement, defaultTo, equals, not, pipe } from "ramda";
7
+ import { GenericClass } from "kubernetes-fluent-client";
8
+
9
+ export const definesDeletionTimestamp = pipe(
10
+ (binding: Binding): boolean => binding?.filters?.deletionTimestamp ?? false,
11
+ defaultTo(false),
12
+ );
13
+ export const ignoresDeletionTimestamp = complement(definesDeletionTimestamp);
14
+
15
+ export const definedName = pipe((binding: Binding): string => {
16
+ return binding.filters.name;
17
+ }, defaultTo(""));
18
+ export const definesName = pipe(definedName, equals(""), not);
19
+ export const ignoresName = complement(definesName);
20
+
21
+ export const definedNameRegex = pipe(
22
+ (binding: Partial<Binding>): string | undefined => binding.filters?.regexName,
23
+ defaultTo(""),
24
+ );
25
+ export const definesNameRegex = pipe(definedNameRegex, equals(""), not);
26
+
27
+ export const definedNamespaces = pipe(binding => binding?.filters?.namespaces, defaultTo([]));
28
+ export const definesNamespaces = pipe(definedNamespaces, equals([]), not);
29
+
30
+ export const definedNamespaceRegexes = pipe(binding => binding?.filters?.regexNamespaces, defaultTo([]));
31
+ export const definesNamespaceRegexes = pipe(definedNamespaceRegexes, equals([]), not);
32
+
33
+ export const definedAnnotations = pipe((binding: Partial<Binding>) => binding?.filters?.annotations, defaultTo({}));
34
+ export const definesAnnotations = pipe(definedAnnotations, equals({}), not);
35
+
36
+ export const definedLabels = pipe((binding: Partial<Binding>) => binding?.filters?.labels, defaultTo({}));
37
+ export const definesLabels = pipe(definedLabels, equals({}), not);
38
+
39
+ export const definedEvent = (binding: Binding): Event => {
40
+ return binding.event;
41
+ };
42
+
43
+ export const definesDelete = pipe(definedEvent, equals(Event.DELETE));
44
+
45
+ export const definedGroup = pipe((binding): string => binding?.kind?.group, defaultTo(""));
46
+ export const definesGroup = pipe(definedGroup, equals(""), not);
47
+
48
+ export const definedVersion = pipe(
49
+ (binding: Partial<Binding>): string | undefined => binding?.kind?.version,
50
+ defaultTo(""),
51
+ );
52
+ export const definesVersion = pipe(definedVersion, equals(""), not);
53
+
54
+ export const definedKind = pipe((binding): string => binding?.kind?.kind, defaultTo(""));
55
+ export const definesKind = pipe(definedKind, equals(""), not);
56
+
57
+ export const definedCategory = (binding: Partial<Binding>): string => {
58
+ // Ordering matters, finalize is a "watch"
59
+ const categories: { [key: string]: boolean | undefined } = {
60
+ Finalize: binding.isFinalize,
61
+ Watch: binding.isWatch,
62
+ Mutate: binding.isMutate,
63
+ Validate: binding.isValidate,
64
+ };
65
+
66
+ return Object.keys(categories).find(key => categories[key]) || "";
67
+ };
68
+ export type DefinedCallbackReturnType =
69
+ | FinalizeAction<GenericClass, InstanceType<GenericClass>>
70
+ | WatchLogAction<GenericClass, InstanceType<GenericClass>>
71
+ | MutateAction<GenericClass, InstanceType<GenericClass>>
72
+ | ValidateAction<GenericClass, InstanceType<GenericClass>>
73
+ | null
74
+ | undefined;
75
+
76
+ export const definedCallback = (binding: Partial<Binding>): DefinedCallbackReturnType => {
77
+ // Ordering matters, finalize is a "watch"
78
+ // prettier-ignore
79
+ return binding.isFinalize ? binding.finalizeCallback :
80
+ binding.isWatch ? binding.watchCallback :
81
+ binding.isMutate ? binding.mutateCallback :
82
+ binding.isValidate ? binding.validateCallback :
83
+ null;
84
+ };
85
+ export const definedCallbackName = pipe(definedCallback, defaultTo({ name: "" }), callback => callback.name);
@@ -0,0 +1,90 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { __, allPass, complement, defaultTo, equals, length, gt, not, nthArg, pipe } from "ramda";
5
+ import { KubernetesObject } from "kubernetes-fluent-client";
6
+
7
+ export const carriesDeletionTimestamp = pipe(
8
+ kubernetesObject => !!kubernetesObject.metadata?.deletionTimestamp,
9
+ defaultTo(false),
10
+ );
11
+ export const missingDeletionTimestamp = complement(carriesDeletionTimestamp);
12
+
13
+ export const carriedKind = pipe(
14
+ (kubernetesObject: KubernetesObject): string | undefined => kubernetesObject?.kind,
15
+ defaultTo("not set"),
16
+ );
17
+ export const carriedVersion = pipe(
18
+ (kubernetesObject: KubernetesObject): string | undefined => kubernetesObject?.metadata?.resourceVersion,
19
+ defaultTo("not set"),
20
+ );
21
+ export const carriedName = pipe(
22
+ (kubernetesObject: KubernetesObject): string | undefined => kubernetesObject?.metadata?.name,
23
+ defaultTo(""),
24
+ );
25
+ export const carriesName = pipe(carriedName, equals(""), not);
26
+ export const missingName = complement(carriesName);
27
+
28
+ export const carriedNamespace = pipe(
29
+ (kubernetesObject: KubernetesObject): string | undefined => kubernetesObject?.metadata?.namespace,
30
+ defaultTo(""),
31
+ );
32
+
33
+ export const carriesNamespace = pipe(carriedNamespace, equals(""), not);
34
+
35
+ export const carriedAnnotations = pipe(
36
+ (kubernetesObject: KubernetesObject): { [key: string]: string } | undefined =>
37
+ kubernetesObject?.metadata?.annotations,
38
+ defaultTo({}),
39
+ );
40
+ export const carriesAnnotations = pipe(carriedAnnotations, equals({}), not);
41
+
42
+ export const carriedLabels = pipe(
43
+ (kubernetesObject: KubernetesObject): { [key: string]: string } | undefined => kubernetesObject?.metadata?.labels,
44
+ defaultTo({}),
45
+ );
46
+ export const carriesLabels = pipe(carriedLabels, equals({}), not);
47
+
48
+ /*
49
+ * If the object does not have a namespace, and it is not a namespace,
50
+ * then we must return false because it cannot be uncarryable
51
+ */
52
+ export const uncarryableNamespace = allPass([
53
+ pipe(nthArg(0), length, gt(__, 0)),
54
+ pipe((namespaceSelector, kubernetesObject) => {
55
+ if (kubernetesObject?.kind === "Namespace") {
56
+ return namespaceSelector.includes(kubernetesObject?.metadata?.name);
57
+ }
58
+ if (carriesNamespace(kubernetesObject)) {
59
+ return namespaceSelector.includes(carriedNamespace(kubernetesObject));
60
+ }
61
+ return true;
62
+ }, not),
63
+ ]);
64
+
65
+ export const missingCarriableNamespace = allPass([
66
+ pipe(nthArg(0), length, gt(__, 0)),
67
+ pipe((namespaceSelector: string[], kubernetesObject: KubernetesObject): boolean =>
68
+ kubernetesObject.kind === "Namespace"
69
+ ? !namespaceSelector.includes(kubernetesObject.metadata!.name!)
70
+ : !carriesNamespace(kubernetesObject),
71
+ ),
72
+ ]);
73
+
74
+ /*
75
+ * If the object does not have a namespace, and it is not a namespace,
76
+ * then we must return false because it cannot be ignored
77
+ */
78
+ export const carriesIgnoredNamespace = allPass([
79
+ pipe(nthArg(0), length, gt(__, 0)),
80
+ pipe((namespaceSelector, kubernetesObject) => {
81
+ if (kubernetesObject?.kind === "Namespace") {
82
+ return namespaceSelector.includes(kubernetesObject?.metadata?.name);
83
+ }
84
+ if (carriesNamespace(kubernetesObject)) {
85
+ return namespaceSelector.includes(carriedNamespace(kubernetesObject));
86
+ }
87
+
88
+ return false;
89
+ }),
90
+ ]);
@@ -0,0 +1,129 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { AdmissionRequest, Binding } from "../../types";
5
+ import { allPass, any, anyPass, equals, not, nthArg, pipe } from "ramda";
6
+ import {
7
+ definedAnnotations,
8
+ definedEvent,
9
+ definedGroup,
10
+ definedKind,
11
+ definedLabels,
12
+ definedName,
13
+ definedNameRegex,
14
+ definedNamespaceRegexes,
15
+ definedNamespaces,
16
+ definedVersion,
17
+ definesAnnotations,
18
+ definesDeletionTimestamp,
19
+ definesGroup,
20
+ definesKind,
21
+ definesLabels,
22
+ definesName,
23
+ definesNameRegex,
24
+ definesNamespaceRegexes,
25
+ definesNamespaces,
26
+ definesVersion,
27
+ } from "./binding";
28
+ import {
29
+ carriedAnnotations,
30
+ carriedLabels,
31
+ carriedName,
32
+ carriedNamespace,
33
+ missingDeletionTimestamp,
34
+ } from "./kubernetesObject";
35
+ import { declaredOperation, declaredGroup, declaredVersion, declaredKind } from "./admissionRequest";
36
+ import { Event, Operation } from "../../enums";
37
+
38
+ export const mismatchedDeletionTimestamp = allPass([
39
+ pipe(nthArg(0), definesDeletionTimestamp),
40
+ pipe(nthArg(1), missingDeletionTimestamp),
41
+ ]);
42
+
43
+ export const mismatchedName = allPass([
44
+ pipe(nthArg(0), definesName),
45
+ pipe((binding, kubernetesObject) => definedName(binding) !== carriedName(kubernetesObject)),
46
+ ]);
47
+
48
+ export const mismatchedNameRegex = allPass([
49
+ pipe(nthArg(0), definesNameRegex),
50
+ pipe((binding, kubernetesObject) => new RegExp(definedNameRegex(binding)).test(carriedName(kubernetesObject)), not),
51
+ ]);
52
+
53
+ export const mismatchedNamespace = allPass([
54
+ pipe(nthArg(0), definesNamespaces),
55
+ pipe((binding, kubernetesObject) => definedNamespaces(binding).includes(carriedNamespace(kubernetesObject)), not),
56
+ ]);
57
+
58
+ export const mismatchedNamespaceRegex = allPass([
59
+ pipe(nthArg(0), definesNamespaceRegexes),
60
+ pipe((binding, kubernetesObject) =>
61
+ pipe(
62
+ any((regEx: string) => new RegExp(regEx).test(carriedNamespace(kubernetesObject))),
63
+ not,
64
+ )(definedNamespaceRegexes(binding)),
65
+ ),
66
+ ]);
67
+
68
+ export const metasMismatch = pipe(
69
+ (defined, carried) => {
70
+ const result = { defined, carried, unalike: {} };
71
+
72
+ result.unalike = Object.entries(result.defined)
73
+ .map(([key, value]) => {
74
+ const keyMissing = !Object.hasOwn(result.carried, key);
75
+ const noValue = !value;
76
+ const valMissing = !result.carried[key];
77
+ const valDiffers = result.carried[key] !== result.defined[key];
78
+
79
+ // prettier-ignore
80
+ return (
81
+ keyMissing ? { [key]: value } :
82
+ noValue ? {} :
83
+ valMissing ? { [key]: value } :
84
+ valDiffers ? { [key]: value } :
85
+ {}
86
+ )
87
+ })
88
+ .reduce((acc, cur) => ({ ...acc, ...cur }), {});
89
+
90
+ return result.unalike;
91
+ },
92
+ unalike => Object.keys(unalike).length > 0,
93
+ );
94
+
95
+ export const mismatchedAnnotations = allPass([
96
+ pipe(nthArg(0), definesAnnotations),
97
+ pipe((binding, kubernetesObject) => metasMismatch(definedAnnotations(binding), carriedAnnotations(kubernetesObject))),
98
+ ]);
99
+
100
+ export const mismatchedLabels = allPass([
101
+ pipe(nthArg(0), definesLabels),
102
+ pipe((binding, kubernetesObject) => metasMismatch(definedLabels(binding), carriedLabels(kubernetesObject))),
103
+ ]);
104
+
105
+ export const mismatchedEvent = pipe(
106
+ (binding: Binding, request: AdmissionRequest): boolean =>
107
+ operationMatchesEvent(declaredOperation(request), definedEvent(binding)),
108
+ not,
109
+ );
110
+
111
+ export const mismatchedGroup = allPass([
112
+ pipe(nthArg(0), definesGroup),
113
+ pipe((binding, request) => definedGroup(binding) !== declaredGroup(request)),
114
+ ]);
115
+
116
+ export const mismatchedVersion = allPass([
117
+ pipe(nthArg(0), definesVersion),
118
+ pipe((binding, request) => definedVersion(binding) !== declaredVersion(request)),
119
+ ]);
120
+
121
+ export const mismatchedKind = allPass([
122
+ pipe(nthArg(0), definesKind),
123
+ pipe((binding, request) => definedKind(binding) !== declaredKind(request)),
124
+ ]);
125
+ export const operationMatchesEvent = anyPass([
126
+ pipe(nthArg(1), equals(Event.ANY)),
127
+ pipe((operation: Operation, event: Event): boolean => operation.valueOf() === event.valueOf()),
128
+ pipe((operation: Operation, event: Event): boolean => (operation ? event.includes(operation) : false)),
129
+ ]);