pepr 0.40.0 → 0.42.0
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.
- package/README.md +11 -5
- package/dist/cli/build.d.ts.map +1 -1
- package/dist/cli/deploy.d.ts.map +1 -1
- package/dist/cli/init/templates.d.ts +2 -2
- package/dist/cli.js +228 -179
- package/dist/controller.js +52 -27
- package/dist/lib/assets/index.d.ts.map +1 -1
- package/dist/lib/capability.d.ts.map +1 -1
- package/dist/lib/controller/index.d.ts.map +1 -1
- package/dist/lib/controller/index.util.d.ts +10 -0
- package/dist/lib/controller/index.util.d.ts.map +1 -0
- package/dist/lib/controller/store.d.ts +1 -1
- package/dist/lib/deploymentChecks.d.ts +3 -0
- package/dist/lib/deploymentChecks.d.ts.map +1 -0
- package/dist/lib/enums.d.ts +5 -5
- package/dist/lib/enums.d.ts.map +1 -1
- package/dist/lib/filesystemService.d.ts +2 -0
- package/dist/lib/filesystemService.d.ts.map +1 -0
- package/dist/lib/filter/adjudicators/adjudicators.d.ts +73 -0
- package/dist/lib/filter/adjudicators/adjudicators.d.ts.map +1 -0
- package/dist/lib/filter/adjudicators/defaultTestObjects.d.ts +7 -0
- package/dist/lib/filter/adjudicators/defaultTestObjects.d.ts.map +1 -0
- package/dist/lib/helpers.d.ts +1 -4
- package/dist/lib/helpers.d.ts.map +1 -1
- package/dist/lib/schedule.d.ts.map +1 -1
- package/dist/lib/storage.d.ts +1 -1
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/{logger.d.ts → telemetry/logger.d.ts} +1 -1
- package/dist/lib/telemetry/logger.d.ts.map +1 -0
- package/dist/lib/{metrics.d.ts → telemetry/metrics.d.ts} +3 -1
- package/dist/lib/telemetry/metrics.d.ts.map +1 -0
- package/dist/lib/types.d.ts +10 -9
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib.d.ts +1 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +151 -126
- package/dist/lib.js.map +4 -4
- package/dist/sdk/sdk.d.ts +3 -4
- package/dist/sdk/sdk.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/cli/build.ts +2 -1
- package/src/cli/deploy.ts +2 -1
- package/src/cli/init/templates.ts +1 -1
- package/src/lib/assets/deploy.ts +1 -1
- package/src/lib/assets/destroy.ts +1 -1
- package/src/lib/assets/index.ts +102 -81
- package/src/lib/assets/webhooks.ts +2 -2
- package/src/lib/capability.ts +8 -9
- package/src/lib/controller/index.ts +32 -62
- package/src/lib/controller/index.util.ts +47 -0
- package/src/lib/controller/store.ts +2 -2
- package/src/lib/controller/storeCache.ts +1 -1
- package/src/lib/deploymentChecks.ts +43 -0
- package/src/lib/enums.ts +5 -5
- package/src/lib/filesystemService.ts +16 -0
- package/src/lib/filter/{adjudicators.ts → adjudicators/adjudicators.ts} +67 -35
- package/src/lib/filter/adjudicators/defaultTestObjects.ts +46 -0
- package/src/lib/filter/filter.ts +1 -1
- package/src/lib/finalizer.ts +1 -1
- package/src/lib/helpers.ts +31 -88
- package/src/lib/mutate-processor.ts +1 -1
- package/src/lib/queue.ts +1 -1
- package/src/lib/schedule.ts +8 -8
- package/src/lib/storage.ts +17 -17
- package/src/lib/{logger.ts → telemetry/logger.ts} +1 -1
- package/src/lib/{metrics.ts → telemetry/metrics.ts} +18 -17
- package/src/lib/types.ts +12 -9
- package/src/lib/utils.ts +1 -1
- package/src/lib/validate-processor.ts +1 -1
- package/src/lib/watch-processor.ts +8 -8
- package/src/lib.ts +1 -1
- package/src/runtime/controller.ts +1 -1
- package/src/sdk/sdk.ts +6 -9
- package/src/templates/capabilities/hello-pepr.ts +19 -9
- package/dist/lib/filter/adjudicators.d.ts +0 -69
- package/dist/lib/filter/adjudicators.d.ts.map +0 -1
- package/dist/lib/logger.d.ts.map +0 -1
- package/dist/lib/metrics.d.ts.map +0 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// check to see if all replicas are ready for all deployments in the pepr-system namespace
|
|
2
|
+
|
|
3
|
+
import { K8s, kind } from "kubernetes-fluent-client";
|
|
4
|
+
import Log from "./telemetry/logger";
|
|
5
|
+
|
|
6
|
+
// returns true if all deployments are ready, false otherwise
|
|
7
|
+
export async function checkDeploymentStatus(namespace: string) {
|
|
8
|
+
const deployments = await K8s(kind.Deployment).InNamespace(namespace).Get();
|
|
9
|
+
let status = false;
|
|
10
|
+
let readyCount = 0;
|
|
11
|
+
|
|
12
|
+
for (const deployment of deployments.items) {
|
|
13
|
+
const readyReplicas = deployment.status?.readyReplicas ? deployment.status?.readyReplicas : 0;
|
|
14
|
+
if (deployment.status?.readyReplicas !== deployment.spec?.replicas) {
|
|
15
|
+
Log.info(
|
|
16
|
+
`Waiting for deployment ${deployment.metadata?.name} rollout to finish: ${readyReplicas} of ${deployment.spec?.replicas} replicas are available`,
|
|
17
|
+
);
|
|
18
|
+
} else {
|
|
19
|
+
Log.info(
|
|
20
|
+
`Deployment ${deployment.metadata?.name} rolled out: ${readyReplicas} of ${deployment.spec?.replicas} replicas are available`,
|
|
21
|
+
);
|
|
22
|
+
readyCount++;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (readyCount === deployments.items.length) {
|
|
26
|
+
status = true;
|
|
27
|
+
}
|
|
28
|
+
return status;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// wait for all deployments in the pepr-system namespace to be ready
|
|
32
|
+
export async function namespaceDeploymentsReady(namespace: string = "pepr-system") {
|
|
33
|
+
Log.info(`Checking ${namespace} deployments status...`);
|
|
34
|
+
let ready = false;
|
|
35
|
+
while (!ready) {
|
|
36
|
+
ready = await checkDeploymentStatus(namespace);
|
|
37
|
+
if (ready) {
|
|
38
|
+
return ready;
|
|
39
|
+
}
|
|
40
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
41
|
+
}
|
|
42
|
+
Log.info(`All ${namespace} deployments are ready`);
|
|
43
|
+
}
|
package/src/lib/enums.ts
CHANGED
|
@@ -13,9 +13,9 @@ export enum Operation {
|
|
|
13
13
|
* The type of Kubernetes mutating webhook event that the action is registered for.
|
|
14
14
|
*/
|
|
15
15
|
export enum Event {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
CREATE = "CREATE",
|
|
17
|
+
UPDATE = "UPDATE",
|
|
18
|
+
DELETE = "DELETE",
|
|
19
|
+
CREATE_OR_UPDATE = "CREATEORUPDATE",
|
|
20
|
+
ANY = "*",
|
|
21
21
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
|
+
|
|
4
|
+
import { promises } from "fs";
|
|
5
|
+
|
|
6
|
+
export async function createDirectoryIfNotExists(path: string) {
|
|
7
|
+
try {
|
|
8
|
+
await promises.access(path);
|
|
9
|
+
} catch (error) {
|
|
10
|
+
if (error.code === "ENOENT") {
|
|
11
|
+
await promises.mkdir(path, { recursive: true });
|
|
12
|
+
} else {
|
|
13
|
+
throw error;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
3
|
|
|
4
|
-
import { Event, Operation } from "
|
|
5
|
-
import { AdmissionRequest } from "../../
|
|
4
|
+
import { Event, Operation } from "../../enums";
|
|
5
|
+
import { AdmissionRequest, Binding } from "../../types";
|
|
6
6
|
import {
|
|
7
7
|
__,
|
|
8
8
|
allPass,
|
|
@@ -58,33 +58,60 @@ export const carriesDeletionTimestamp = pipe(
|
|
|
58
58
|
);
|
|
59
59
|
export const missingDeletionTimestamp = complement(carriesDeletionTimestamp);
|
|
60
60
|
|
|
61
|
-
export const carriedKind = pipe(
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
export const carriedKind = pipe(
|
|
62
|
+
(kubernetesObject: KubernetesObject): string | undefined => kubernetesObject?.kind,
|
|
63
|
+
defaultTo("not set"),
|
|
64
|
+
);
|
|
65
|
+
export const carriedVersion = pipe(
|
|
66
|
+
(kubernetesObject: KubernetesObject): string | undefined => kubernetesObject?.metadata?.resourceVersion,
|
|
67
|
+
defaultTo("not set"),
|
|
68
|
+
);
|
|
69
|
+
export const carriedName = pipe(
|
|
70
|
+
(kubernetesObject: KubernetesObject): string | undefined => kubernetesObject?.metadata?.name,
|
|
71
|
+
defaultTo(""),
|
|
72
|
+
);
|
|
64
73
|
export const carriesName = pipe(carriedName, equals(""), not);
|
|
65
74
|
export const missingName = complement(carriesName);
|
|
66
75
|
|
|
67
|
-
export const carriedNamespace = pipe(
|
|
76
|
+
export const carriedNamespace = pipe(
|
|
77
|
+
(kubernetesObject: KubernetesObject): string | undefined => kubernetesObject?.metadata?.namespace,
|
|
78
|
+
defaultTo(""),
|
|
79
|
+
);
|
|
68
80
|
export const carriesNamespace = pipe(carriedNamespace, equals(""), not);
|
|
69
81
|
|
|
70
|
-
export const carriedAnnotations = pipe(
|
|
82
|
+
export const carriedAnnotations = pipe(
|
|
83
|
+
(kubernetesObject: KubernetesObject): { [key: string]: string } | undefined =>
|
|
84
|
+
kubernetesObject?.metadata?.annotations,
|
|
85
|
+
defaultTo({}),
|
|
86
|
+
);
|
|
71
87
|
export const carriesAnnotations = pipe(carriedAnnotations, equals({}), not);
|
|
72
88
|
|
|
73
|
-
export const carriedLabels = pipe(
|
|
89
|
+
export const carriedLabels = pipe(
|
|
90
|
+
(kubernetesObject: KubernetesObject): { [key: string]: string } | undefined => kubernetesObject?.metadata?.labels,
|
|
91
|
+
defaultTo({}),
|
|
92
|
+
);
|
|
74
93
|
export const carriesLabels = pipe(carriedLabels, equals({}), not);
|
|
75
94
|
|
|
76
95
|
/*
|
|
77
96
|
Binding collectors
|
|
78
97
|
*/
|
|
79
98
|
|
|
80
|
-
export const definesDeletionTimestamp = pipe(
|
|
99
|
+
export const definesDeletionTimestamp = pipe(
|
|
100
|
+
(binding: Binding): boolean => binding?.filters?.deletionTimestamp ?? false,
|
|
101
|
+
defaultTo(false),
|
|
102
|
+
);
|
|
81
103
|
export const ignoresDeletionTimestamp = complement(definesDeletionTimestamp);
|
|
82
104
|
|
|
83
|
-
export const definedName = pipe(binding =>
|
|
105
|
+
export const definedName = pipe((binding: Binding): string => {
|
|
106
|
+
return binding.filters.name;
|
|
107
|
+
}, defaultTo(""));
|
|
84
108
|
export const definesName = pipe(definedName, equals(""), not);
|
|
85
109
|
export const ignoresName = complement(definesName);
|
|
86
110
|
|
|
87
|
-
export const definedNameRegex = pipe(
|
|
111
|
+
export const definedNameRegex = pipe(
|
|
112
|
+
(binding: Partial<Binding>): string | undefined => binding.filters?.regexName,
|
|
113
|
+
defaultTo(""),
|
|
114
|
+
);
|
|
88
115
|
export const definesNameRegex = pipe(definedNameRegex, equals(""), not);
|
|
89
116
|
|
|
90
117
|
export const definedNamespaces = pipe(binding => binding?.filters?.namespaces, defaultTo([]));
|
|
@@ -93,45 +120,49 @@ export const definesNamespaces = pipe(definedNamespaces, equals([]), not);
|
|
|
93
120
|
export const definedNamespaceRegexes = pipe(binding => binding?.filters?.regexNamespaces, defaultTo([]));
|
|
94
121
|
export const definesNamespaceRegexes = pipe(definedNamespaceRegexes, equals([]), not);
|
|
95
122
|
|
|
96
|
-
export const definedAnnotations = pipe(binding => binding?.filters?.annotations, defaultTo({}));
|
|
123
|
+
export const definedAnnotations = pipe((binding: Partial<Binding>) => binding?.filters?.annotations, defaultTo({}));
|
|
97
124
|
export const definesAnnotations = pipe(definedAnnotations, equals({}), not);
|
|
98
125
|
|
|
99
|
-
export const definedLabels = pipe(binding => binding?.filters?.labels, defaultTo({}));
|
|
126
|
+
export const definedLabels = pipe((binding: Partial<Binding>) => binding?.filters?.labels, defaultTo({}));
|
|
100
127
|
export const definesLabels = pipe(definedLabels, equals({}), not);
|
|
101
128
|
|
|
102
|
-
export const definedEvent =
|
|
103
|
-
|
|
129
|
+
export const definedEvent = (binding: Binding): Event => {
|
|
130
|
+
return binding.event;
|
|
131
|
+
};
|
|
104
132
|
|
|
105
|
-
export const
|
|
133
|
+
export const definesDelete = pipe(definedEvent, equals(Event.DELETE));
|
|
134
|
+
|
|
135
|
+
export const definedGroup = pipe((binding): string => binding?.kind?.group, defaultTo(""));
|
|
106
136
|
export const definesGroup = pipe(definedGroup, equals(""), not);
|
|
107
137
|
|
|
108
|
-
export const definedVersion = pipe(
|
|
138
|
+
export const definedVersion = pipe(
|
|
139
|
+
(binding: Partial<Binding>): string | undefined => binding?.kind?.version,
|
|
140
|
+
defaultTo(""),
|
|
141
|
+
);
|
|
109
142
|
export const definesVersion = pipe(definedVersion, equals(""), not);
|
|
110
143
|
|
|
111
|
-
export const definedKind = pipe(binding => binding?.kind?.kind, defaultTo(""));
|
|
144
|
+
export const definedKind = pipe((binding): string => binding?.kind?.kind, defaultTo(""));
|
|
112
145
|
export const definesKind = pipe(definedKind, equals(""), not);
|
|
113
146
|
|
|
114
|
-
export const definedCategory =
|
|
147
|
+
export const definedCategory = (binding: Partial<Binding>) => {
|
|
148
|
+
// Ordering matters, finalize is a "watch"
|
|
115
149
|
// prettier-ignore
|
|
116
|
-
return
|
|
117
|
-
binding.isFinalize ? "Finalize" :
|
|
150
|
+
return binding.isFinalize ? "Finalize" :
|
|
118
151
|
binding.isWatch ? "Watch" :
|
|
119
152
|
binding.isMutate ? "Mutate" :
|
|
120
153
|
binding.isValidate ? "Validate" :
|
|
121
|
-
""
|
|
122
|
-
|
|
123
|
-
});
|
|
154
|
+
"";
|
|
155
|
+
};
|
|
124
156
|
|
|
125
|
-
export const definedCallback =
|
|
157
|
+
export const definedCallback = (binding: Partial<Binding>) => {
|
|
158
|
+
// Ordering matters, finalize is a "watch"
|
|
126
159
|
// prettier-ignore
|
|
127
|
-
return
|
|
128
|
-
binding.isFinalize ? binding.finalizeCallback :
|
|
160
|
+
return binding.isFinalize ? binding.finalizeCallback :
|
|
129
161
|
binding.isWatch ? binding.watchCallback :
|
|
130
162
|
binding.isMutate ? binding.mutateCallback :
|
|
131
|
-
binding.isValidate ? binding.validateCallback:
|
|
132
|
-
null
|
|
133
|
-
|
|
134
|
-
});
|
|
163
|
+
binding.isValidate ? binding.validateCallback :
|
|
164
|
+
null;
|
|
165
|
+
};
|
|
135
166
|
export const definedCallbackName = pipe(definedCallback, defaultTo({ name: "" }), callback => callback.name);
|
|
136
167
|
|
|
137
168
|
/*
|
|
@@ -245,13 +276,14 @@ export const unbindableNamespaces = allPass([
|
|
|
245
276
|
export const misboundDeleteWithDeletionTimestamp = allPass([definesDelete, definesDeletionTimestamp]);
|
|
246
277
|
|
|
247
278
|
export const operationMatchesEvent = anyPass([
|
|
248
|
-
pipe(nthArg(1), equals(Event.
|
|
249
|
-
pipe((operation, event) => operation === event),
|
|
250
|
-
pipe((operation, event) => (operation ? event.includes(operation) : false)),
|
|
279
|
+
pipe(nthArg(1), equals(Event.ANY)),
|
|
280
|
+
pipe((operation: Operation, event: Event): boolean => operation.valueOf() === event.valueOf()),
|
|
281
|
+
pipe((operation: Operation, event: Event): boolean => (operation ? event.includes(operation) : false)),
|
|
251
282
|
]);
|
|
252
283
|
|
|
253
284
|
export const mismatchedEvent = pipe(
|
|
254
|
-
(binding, request) =>
|
|
285
|
+
(binding: Binding, request: AdmissionRequest): boolean =>
|
|
286
|
+
operationMatchesEvent(declaredOperation(request), definedEvent(binding)),
|
|
255
287
|
not,
|
|
256
288
|
);
|
|
257
289
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { GroupVersionKind, kind, KubernetesObject } from "kubernetes-fluent-client";
|
|
2
|
+
import { Event, Operation } from "../../enums";
|
|
3
|
+
import { AdmissionRequest, Binding, Filters } from "../../types";
|
|
4
|
+
|
|
5
|
+
export const defaultFilters: Filters = {
|
|
6
|
+
annotations: {},
|
|
7
|
+
deletionTimestamp: false,
|
|
8
|
+
labels: {},
|
|
9
|
+
name: "",
|
|
10
|
+
namespaces: [],
|
|
11
|
+
regexName: "^default$",
|
|
12
|
+
regexNamespaces: [] as string[],
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const defaultGroupVersionKind: GroupVersionKind = {
|
|
16
|
+
kind: "some-kind",
|
|
17
|
+
group: "some-group",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const defaultBinding: Binding = {
|
|
21
|
+
event: Event.ANY,
|
|
22
|
+
filters: defaultFilters,
|
|
23
|
+
kind: defaultGroupVersionKind,
|
|
24
|
+
model: kind.Pod,
|
|
25
|
+
isFinalize: false,
|
|
26
|
+
isMutate: false,
|
|
27
|
+
isQueue: false,
|
|
28
|
+
isValidate: false,
|
|
29
|
+
isWatch: false,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const defaultAdmissionRequest: AdmissionRequest = {
|
|
33
|
+
uid: "some-uid",
|
|
34
|
+
kind: { kind: "a-kind", group: "a-group" },
|
|
35
|
+
resource: { group: "some-group", version: "some-version", resource: "some-resource" },
|
|
36
|
+
operation: Operation.CONNECT,
|
|
37
|
+
name: "some-name",
|
|
38
|
+
userInfo: {},
|
|
39
|
+
object: {},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const defaultKubernetesObject: KubernetesObject = {
|
|
43
|
+
apiVersion: "some-version",
|
|
44
|
+
kind: "some-kind",
|
|
45
|
+
metadata: { name: "some-name" },
|
|
46
|
+
};
|
package/src/lib/filter/filter.ts
CHANGED
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
missingCarriableNamespace,
|
|
39
39
|
unbindableNamespaces,
|
|
40
40
|
uncarryableNamespace,
|
|
41
|
-
} from "./adjudicators";
|
|
41
|
+
} from "./adjudicators/adjudicators";
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* shouldSkipRequest determines if a request should be skipped based on the binding filters.
|
package/src/lib/finalizer.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
3
|
|
|
4
4
|
import { K8s, KubernetesObject, RegisterKind } from "kubernetes-fluent-client";
|
|
5
|
-
import Log from "./logger";
|
|
5
|
+
import Log from "./telemetry/logger";
|
|
6
6
|
import { Binding, DeepPartial } from "./types";
|
|
7
7
|
import { Operation } from "./enums";
|
|
8
8
|
import { PeprMutateRequest } from "./mutate-request";
|
package/src/lib/helpers.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import Log from "./logger";
|
|
4
|
+
import { KubernetesObject } from "kubernetes-fluent-client";
|
|
5
|
+
import Log from "./telemetry/logger";
|
|
7
6
|
import { Binding, CapabilityExport } from "./types";
|
|
8
7
|
import { sanitizeResourceName } from "../sdk/sdk";
|
|
9
8
|
import {
|
|
@@ -29,16 +28,10 @@ import {
|
|
|
29
28
|
missingCarriableNamespace,
|
|
30
29
|
unbindableNamespaces,
|
|
31
30
|
uncarryableNamespace,
|
|
32
|
-
} from "./filter/adjudicators";
|
|
31
|
+
} from "./filter/adjudicators/adjudicators";
|
|
33
32
|
|
|
34
33
|
export function matchesRegex(pattern: string, testString: string): boolean {
|
|
35
|
-
|
|
36
|
-
if (!pattern) {
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const regex = new RegExp(pattern);
|
|
41
|
-
return regex.test(testString);
|
|
34
|
+
return new RegExp(pattern).test(testString);
|
|
42
35
|
}
|
|
43
36
|
|
|
44
37
|
export class ValidationError extends Error {}
|
|
@@ -73,8 +66,8 @@ export type RBACMap = {
|
|
|
73
66
|
* Decide to run callback after the event comes back from API Server
|
|
74
67
|
**/
|
|
75
68
|
export function filterNoMatchReason(
|
|
76
|
-
binding:
|
|
77
|
-
|
|
69
|
+
binding: Binding,
|
|
70
|
+
kubernetesObject: Partial<KubernetesObject>,
|
|
78
71
|
capabilityNamespaces: string[],
|
|
79
72
|
ignoredNamespaces?: string[],
|
|
80
73
|
): string {
|
|
@@ -82,30 +75,30 @@ export function filterNoMatchReason(
|
|
|
82
75
|
|
|
83
76
|
// prettier-ignore
|
|
84
77
|
return (
|
|
85
|
-
mismatchedDeletionTimestamp(binding,
|
|
78
|
+
mismatchedDeletionTimestamp(binding, kubernetesObject) ?
|
|
86
79
|
`${prefix} Binding defines deletionTimestamp but Object does not carry it.` :
|
|
87
80
|
|
|
88
|
-
mismatchedName(binding,
|
|
89
|
-
`${prefix} Binding defines name '${definedName(binding)}' but Object carries '${carriedName(
|
|
81
|
+
mismatchedName(binding, kubernetesObject) ?
|
|
82
|
+
`${prefix} Binding defines name '${definedName(binding)}' but Object carries '${carriedName(kubernetesObject)}'.` :
|
|
90
83
|
|
|
91
84
|
misboundNamespace(binding) ?
|
|
92
85
|
`${prefix} Cannot use namespace filter on a namespace object.` :
|
|
93
86
|
|
|
94
|
-
mismatchedLabels(binding,
|
|
87
|
+
mismatchedLabels(binding, kubernetesObject) ?
|
|
95
88
|
(
|
|
96
89
|
`${prefix} Binding defines labels '${JSON.stringify(definedLabels(binding))}' ` +
|
|
97
|
-
`but Object carries '${JSON.stringify(carriedLabels(
|
|
90
|
+
`but Object carries '${JSON.stringify(carriedLabels(kubernetesObject))}'.`
|
|
98
91
|
) :
|
|
99
92
|
|
|
100
|
-
mismatchedAnnotations(binding,
|
|
93
|
+
mismatchedAnnotations(binding, kubernetesObject) ?
|
|
101
94
|
(
|
|
102
95
|
`${prefix} Binding defines annotations '${JSON.stringify(definedAnnotations(binding))}' ` +
|
|
103
|
-
`but Object carries '${JSON.stringify(carriedAnnotations(
|
|
96
|
+
`but Object carries '${JSON.stringify(carriedAnnotations(kubernetesObject))}'.`
|
|
104
97
|
) :
|
|
105
98
|
|
|
106
|
-
uncarryableNamespace(capabilityNamespaces,
|
|
99
|
+
uncarryableNamespace(capabilityNamespaces, kubernetesObject) ?
|
|
107
100
|
(
|
|
108
|
-
`${prefix} Object carries namespace '${carriedNamespace(
|
|
101
|
+
`${prefix} Object carries namespace '${carriedNamespace(kubernetesObject)}' ` +
|
|
109
102
|
`but namespaces allowed by Capability are '${JSON.stringify(capabilityNamespaces)}'.`
|
|
110
103
|
) :
|
|
111
104
|
|
|
@@ -115,32 +108,32 @@ export function filterNoMatchReason(
|
|
|
115
108
|
`but namespaces allowed by Capability are '${JSON.stringify(capabilityNamespaces)}'.`
|
|
116
109
|
) :
|
|
117
110
|
|
|
118
|
-
mismatchedNamespace(binding,
|
|
111
|
+
mismatchedNamespace(binding, kubernetesObject) ?
|
|
119
112
|
(
|
|
120
113
|
`${prefix} Binding defines namespaces '${JSON.stringify(definedNamespaces(binding))}' ` +
|
|
121
|
-
`but Object carries '${carriedNamespace(
|
|
114
|
+
`but Object carries '${carriedNamespace(kubernetesObject)}'.`
|
|
122
115
|
) :
|
|
123
116
|
|
|
124
|
-
mismatchedNamespaceRegex(binding,
|
|
117
|
+
mismatchedNamespaceRegex(binding, kubernetesObject) ?
|
|
125
118
|
(
|
|
126
119
|
`${prefix} Binding defines namespace regexes ` +
|
|
127
120
|
`'${JSON.stringify(definedNamespaceRegexes(binding))}' ` +
|
|
128
|
-
`but Object carries '${carriedNamespace(
|
|
121
|
+
`but Object carries '${carriedNamespace(kubernetesObject)}'.`
|
|
129
122
|
) :
|
|
130
123
|
|
|
131
|
-
mismatchedNameRegex(binding,
|
|
124
|
+
mismatchedNameRegex(binding, kubernetesObject) ?
|
|
132
125
|
(
|
|
133
126
|
`${prefix} Binding defines name regex '${definedNameRegex(binding)}' ` +
|
|
134
|
-
`but Object carries '${carriedName(
|
|
127
|
+
`but Object carries '${carriedName(kubernetesObject)}'.`
|
|
135
128
|
) :
|
|
136
129
|
|
|
137
|
-
carriesIgnoredNamespace(ignoredNamespaces,
|
|
130
|
+
carriesIgnoredNamespace(ignoredNamespaces, kubernetesObject) ?
|
|
138
131
|
(
|
|
139
|
-
`${prefix} Object carries namespace '${carriedNamespace(
|
|
132
|
+
`${prefix} Object carries namespace '${carriedNamespace(kubernetesObject)}' ` +
|
|
140
133
|
`but ignored namespaces include '${JSON.stringify(ignoredNamespaces)}'.`
|
|
141
134
|
) :
|
|
142
135
|
|
|
143
|
-
missingCarriableNamespace(capabilityNamespaces,
|
|
136
|
+
missingCarriableNamespace(capabilityNamespaces, kubernetesObject) ?
|
|
144
137
|
(
|
|
145
138
|
`${prefix} Object does not carry a namespace ` +
|
|
146
139
|
`but namespaces allowed by Capability are '${JSON.stringify(capabilityNamespaces)}'.`
|
|
@@ -191,18 +184,6 @@ export function createRBACMap(capabilities: CapabilityExport[]): RBACMap {
|
|
|
191
184
|
}, {});
|
|
192
185
|
}
|
|
193
186
|
|
|
194
|
-
export async function createDirectoryIfNotExists(path: string) {
|
|
195
|
-
try {
|
|
196
|
-
await fs.access(path);
|
|
197
|
-
} catch (error) {
|
|
198
|
-
if (error.code === "ENOENT") {
|
|
199
|
-
await fs.mkdir(path, { recursive: true });
|
|
200
|
-
} else {
|
|
201
|
-
throw error;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
187
|
export function hasEveryOverlap<T>(array1: T[], array2: T[]): boolean {
|
|
207
188
|
if (!Array.isArray(array1) || !Array.isArray(array2)) {
|
|
208
189
|
return false;
|
|
@@ -255,11 +236,13 @@ export function generateWatchNamespaceError(
|
|
|
255
236
|
return err.replace(/\.([^ ])/g, ". $1");
|
|
256
237
|
}
|
|
257
238
|
|
|
258
|
-
// namespaceComplianceValidator ensures that capability
|
|
239
|
+
// namespaceComplianceValidator ensures that capability bindings respect ignored and capability namespaces
|
|
259
240
|
export function namespaceComplianceValidator(capability: CapabilityExport, ignoredNamespaces?: string[]) {
|
|
260
241
|
const { namespaces: capabilityNamespaces, bindings, name } = capability;
|
|
261
|
-
const bindingNamespaces = bindings.flatMap((binding: Binding) => binding.filters.namespaces);
|
|
262
|
-
const bindingRegexNamespaces = bindings.flatMap(
|
|
242
|
+
const bindingNamespaces: string[] = bindings.flatMap((binding: Binding) => binding.filters.namespaces);
|
|
243
|
+
const bindingRegexNamespaces: string[] = bindings.flatMap(
|
|
244
|
+
(binding: Binding) => binding.filters.regexNamespaces || [],
|
|
245
|
+
);
|
|
263
246
|
|
|
264
247
|
const namespaceError = generateWatchNamespaceError(
|
|
265
248
|
ignoredNamespaces ? ignoredNamespaces : [],
|
|
@@ -310,46 +293,6 @@ export function namespaceComplianceValidator(capability: CapabilityExport, ignor
|
|
|
310
293
|
}
|
|
311
294
|
}
|
|
312
295
|
|
|
313
|
-
// check to see if all replicas are ready for all deployments in the pepr-system namespace
|
|
314
|
-
// returns true if all deployments are ready, false otherwise
|
|
315
|
-
export async function checkDeploymentStatus(namespace: string) {
|
|
316
|
-
const deployments = await K8s(kind.Deployment).InNamespace(namespace).Get();
|
|
317
|
-
let status = false;
|
|
318
|
-
let readyCount = 0;
|
|
319
|
-
|
|
320
|
-
for (const deployment of deployments.items) {
|
|
321
|
-
const readyReplicas = deployment.status?.readyReplicas ? deployment.status?.readyReplicas : 0;
|
|
322
|
-
if (deployment.status?.readyReplicas !== deployment.spec?.replicas) {
|
|
323
|
-
Log.info(
|
|
324
|
-
`Waiting for deployment ${deployment.metadata?.name} rollout to finish: ${readyReplicas} of ${deployment.spec?.replicas} replicas are available`,
|
|
325
|
-
);
|
|
326
|
-
} else {
|
|
327
|
-
Log.info(
|
|
328
|
-
`Deployment ${deployment.metadata?.name} rolled out: ${readyReplicas} of ${deployment.spec?.replicas} replicas are available`,
|
|
329
|
-
);
|
|
330
|
-
readyCount++;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
if (readyCount === deployments.items.length) {
|
|
334
|
-
status = true;
|
|
335
|
-
}
|
|
336
|
-
return status;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// wait for all deployments in the pepr-system namespace to be ready
|
|
340
|
-
export async function namespaceDeploymentsReady(namespace: string = "pepr-system") {
|
|
341
|
-
Log.info(`Checking ${namespace} deployments status...`);
|
|
342
|
-
let ready = false;
|
|
343
|
-
while (!ready) {
|
|
344
|
-
ready = await checkDeploymentStatus(namespace);
|
|
345
|
-
if (ready) {
|
|
346
|
-
return ready;
|
|
347
|
-
}
|
|
348
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
349
|
-
}
|
|
350
|
-
Log.info(`All ${namespace} deployments are ready`);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
296
|
// check if secret is over the size limit
|
|
354
297
|
export function secretOverLimit(str: string): boolean {
|
|
355
298
|
const encoder = new TextEncoder();
|
|
@@ -374,7 +317,7 @@ export const parseTimeout = (value: string, previous: unknown): number => {
|
|
|
374
317
|
};
|
|
375
318
|
|
|
376
319
|
// Remove leading whitespace while keeping format of file
|
|
377
|
-
export function dedent(file: string) {
|
|
320
|
+
export function dedent(file: string): string {
|
|
378
321
|
// Check if the first line is empty and remove it
|
|
379
322
|
const lines = file.split("\n");
|
|
380
323
|
if (lines[0].trim() === "") {
|
|
@@ -391,7 +334,7 @@ export function dedent(file: string) {
|
|
|
391
334
|
return file;
|
|
392
335
|
}
|
|
393
336
|
|
|
394
|
-
export function replaceString(str: string, stringA: string, stringB: string) {
|
|
337
|
+
export function replaceString(str: string, stringA: string, stringB: string): string {
|
|
395
338
|
// eslint-disable-next-line no-useless-escape
|
|
396
339
|
const escapedStringA = stringA.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
397
340
|
const regExp = new RegExp(escapedStringA, "g");
|
|
@@ -9,7 +9,7 @@ import { Errors } from "./errors";
|
|
|
9
9
|
import { shouldSkipRequest } from "./filter/filter";
|
|
10
10
|
import { MutateResponse } from "./k8s";
|
|
11
11
|
import { AdmissionRequest } from "./types";
|
|
12
|
-
import Log from "./logger";
|
|
12
|
+
import Log from "./telemetry/logger";
|
|
13
13
|
import { ModuleConfig } from "./module";
|
|
14
14
|
import { PeprMutateRequest } from "./mutate-request";
|
|
15
15
|
import { base64Encode, convertFromBase64Map, convertToBase64Map } from "./utils";
|
package/src/lib/queue.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { KubernetesObject } from "@kubernetes/client-node";
|
|
4
4
|
import { WatchPhase } from "kubernetes-fluent-client/dist/fluent/types";
|
|
5
5
|
import { randomBytes } from "node:crypto";
|
|
6
|
-
import Log from "./logger";
|
|
6
|
+
import Log from "./telemetry/logger";
|
|
7
7
|
|
|
8
8
|
type WatchCallback = (obj: KubernetesObject, phase: WatchPhase) => Promise<void>;
|
|
9
9
|
|
package/src/lib/schedule.ts
CHANGED
|
@@ -57,11 +57,11 @@ export class OnSchedule implements Schedule {
|
|
|
57
57
|
this.startTime = schedule?.startTime;
|
|
58
58
|
this.completions = schedule?.completions;
|
|
59
59
|
}
|
|
60
|
-
setStore(store: PeprStore) {
|
|
60
|
+
setStore(store: PeprStore): void {
|
|
61
61
|
this.store = store;
|
|
62
62
|
this.startInterval();
|
|
63
63
|
}
|
|
64
|
-
startInterval() {
|
|
64
|
+
startInterval(): void {
|
|
65
65
|
this.checkStore();
|
|
66
66
|
this.getDuration();
|
|
67
67
|
this.setupInterval();
|
|
@@ -70,7 +70,7 @@ export class OnSchedule implements Schedule {
|
|
|
70
70
|
* Checks the store for this schedule and sets the values if it exists
|
|
71
71
|
* @returns
|
|
72
72
|
*/
|
|
73
|
-
checkStore() {
|
|
73
|
+
checkStore(): void {
|
|
74
74
|
const result = this.store && this.store.getItem(this.name);
|
|
75
75
|
if (result) {
|
|
76
76
|
const storedSchedule = JSON.parse(result);
|
|
@@ -84,7 +84,7 @@ export class OnSchedule implements Schedule {
|
|
|
84
84
|
* Saves the schedule to the store
|
|
85
85
|
* @returns
|
|
86
86
|
*/
|
|
87
|
-
saveToStore() {
|
|
87
|
+
saveToStore(): void {
|
|
88
88
|
const schedule = {
|
|
89
89
|
completions: this.completions,
|
|
90
90
|
startTime: this.startTime,
|
|
@@ -97,7 +97,7 @@ export class OnSchedule implements Schedule {
|
|
|
97
97
|
/**
|
|
98
98
|
* Gets the durations in milliseconds
|
|
99
99
|
*/
|
|
100
|
-
getDuration() {
|
|
100
|
+
getDuration(): void {
|
|
101
101
|
switch (this.unit) {
|
|
102
102
|
case "seconds":
|
|
103
103
|
if (this.every < 10) throw new Error("10 Seconds in the smallest interval allowed");
|
|
@@ -119,7 +119,7 @@ export class OnSchedule implements Schedule {
|
|
|
119
119
|
/**
|
|
120
120
|
* Sets up the interval
|
|
121
121
|
*/
|
|
122
|
-
setupInterval() {
|
|
122
|
+
setupInterval(): void {
|
|
123
123
|
const now = new Date();
|
|
124
124
|
let delay: number | undefined;
|
|
125
125
|
|
|
@@ -146,7 +146,7 @@ export class OnSchedule implements Schedule {
|
|
|
146
146
|
/**
|
|
147
147
|
* Starts the interval
|
|
148
148
|
*/
|
|
149
|
-
start() {
|
|
149
|
+
start(): void {
|
|
150
150
|
this.intervalId = setInterval(() => {
|
|
151
151
|
if (this.completions === 0) {
|
|
152
152
|
this.stop();
|
|
@@ -165,7 +165,7 @@ export class OnSchedule implements Schedule {
|
|
|
165
165
|
/**
|
|
166
166
|
* Stops the interval
|
|
167
167
|
*/
|
|
168
|
-
stop() {
|
|
168
|
+
stop(): void {
|
|
169
169
|
if (this.intervalId) {
|
|
170
170
|
clearInterval(this.intervalId);
|
|
171
171
|
this.intervalId = null;
|