pepr 0.46.2-nightly.1 → 0.46.2-nightly.2
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/dist/cli.js +79 -23
- package/dist/controller.js +1 -1
- package/dist/lib.js +124 -38
- package/dist/lib.js.map +2 -2
- package/dist/src/cli/init/templates.d.ts +1 -0
- package/dist/src/cli/init/templates.d.ts.map +1 -1
- package/dist/src/lib/assets/assets.d.ts.map +1 -1
- package/dist/src/lib/assets/defaultTestObjects.d.ts.map +1 -1
- package/dist/src/lib/assets/deploy.d.ts.map +1 -1
- package/dist/src/lib/assets/index.d.ts.map +1 -1
- package/dist/src/lib/assets/pods.d.ts.map +1 -1
- package/dist/src/lib/assets/webhooks.d.ts.map +1 -1
- package/dist/src/lib/assets/yaml/generateAllYaml.d.ts.map +1 -1
- package/dist/src/lib/controller/index.d.ts.map +1 -1
- package/dist/src/lib/core/capability.d.ts.map +1 -1
- package/dist/src/lib/core/module.d.ts.map +1 -1
- package/dist/src/lib/core/storage.d.ts.map +1 -1
- package/dist/src/lib/deploymentChecks.d.ts.map +1 -1
- package/dist/src/lib/filter/adjudicators/admissionRequest.d.ts.map +1 -1
- package/dist/src/lib/filter/adjudicators/binding.d.ts.map +1 -1
- package/dist/src/lib/filter/adjudicators/kubernetesObject.d.ts.map +1 -1
- package/dist/src/lib/filter/adjudicators/mismatch.d.ts.map +1 -1
- package/dist/src/lib/filter/adjudicators/postCollection.d.ts.map +1 -1
- package/dist/src/lib/filter/filter.d.ts.map +1 -1
- package/dist/src/lib/helpers.d.ts.map +1 -1
- package/dist/src/lib/included-files.d.ts.map +1 -1
- package/dist/src/lib/processors/decode-utils.d.ts.map +1 -1
- package/dist/src/lib/processors/mutate-processor.d.ts.map +1 -1
- package/dist/src/lib/processors/validate-processor.d.ts.map +1 -1
- package/dist/src/lib/processors/watch-processor.d.ts.map +1 -1
- package/dist/src/lib/telemetry/metrics.d.ts.map +1 -1
- package/dist/src/lib/telemetry/webhookTimeouts.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/lib/assets/assets.ts +46 -11
- package/src/lib/assets/defaultTestObjects.ts +13 -2
- package/src/lib/assets/deploy.ts +25 -5
- package/src/lib/assets/index.ts +8 -2
- package/src/lib/assets/pods.ts +5 -1
- package/src/lib/assets/webhooks.ts +12 -3
- package/src/lib/assets/yaml/generateAllYaml.ts +12 -2
- package/src/lib/controller/index.ts +9 -3
- package/src/lib/core/capability.ts +32 -8
- package/src/lib/core/module.ts +5 -1
- package/src/lib/core/storage.ts +3 -1
- package/src/lib/deploymentChecks.ts +3 -1
- package/src/lib/filter/adjudicators/admissionRequest.ts +4 -1
- package/src/lib/filter/adjudicators/binding.ts +17 -4
- package/src/lib/filter/adjudicators/kubernetesObject.ts +4 -2
- package/src/lib/filter/adjudicators/mismatch.ts +25 -6
- package/src/lib/filter/adjudicators/postCollection.ts +15 -3
- package/src/lib/filter/filter.ts +63 -15
- package/src/lib/helpers.ts +36 -10
- package/src/lib/included-files.ts +5 -1
- package/src/lib/processors/decode-utils.ts +4 -1
- package/src/lib/processors/mutate-processor.ts +4 -1
- package/src/lib/processors/validate-processor.ts +4 -1
- package/src/lib/processors/watch-processor.ts +49 -19
- package/src/lib/telemetry/metrics.ts +6 -2
- package/src/lib/telemetry/webhookTimeouts.ts +4 -1
- package/src/templates/.prettierrc.json +3 -2
- package/src/templates/capabilities/hello-pepr.ts +2 -8
- package/src/lib/.prettierrc +0 -14
|
@@ -206,7 +206,16 @@ export class Capability implements CapabilityExport {
|
|
|
206
206
|
|
|
207
207
|
const bindings = this.#bindings;
|
|
208
208
|
const prefix = `${this.#name}: ${model.name}`;
|
|
209
|
-
const commonChain = {
|
|
209
|
+
const commonChain = {
|
|
210
|
+
WithLabel,
|
|
211
|
+
WithAnnotation,
|
|
212
|
+
WithDeletionTimestamp,
|
|
213
|
+
Mutate,
|
|
214
|
+
Validate,
|
|
215
|
+
Watch,
|
|
216
|
+
Reconcile,
|
|
217
|
+
Alias,
|
|
218
|
+
};
|
|
210
219
|
|
|
211
220
|
type CommonChainType = typeof commonChain;
|
|
212
221
|
type ExtendedCommonChainType = CommonChainType & {
|
|
@@ -240,7 +249,9 @@ export class Capability implements CapabilityExport {
|
|
|
240
249
|
...binding,
|
|
241
250
|
isValidate: true,
|
|
242
251
|
validateCallback: async (req, logger = aliasLogger) => {
|
|
243
|
-
Log.info(
|
|
252
|
+
Log.info(
|
|
253
|
+
`Executing validate action with alias: ${binding.alias || "no alias provided"}`,
|
|
254
|
+
);
|
|
244
255
|
return await validateCallback(req, logger);
|
|
245
256
|
},
|
|
246
257
|
});
|
|
@@ -262,7 +273,9 @@ export class Capability implements CapabilityExport {
|
|
|
262
273
|
...binding,
|
|
263
274
|
isMutate: true,
|
|
264
275
|
mutateCallback: async (req, logger = aliasLogger) => {
|
|
265
|
-
Log.info(
|
|
276
|
+
Log.info(
|
|
277
|
+
`Executing mutation action with alias: ${binding.alias || "no alias provided"}`,
|
|
278
|
+
);
|
|
266
279
|
await mutateCallback(req, logger);
|
|
267
280
|
},
|
|
268
281
|
});
|
|
@@ -277,7 +290,9 @@ export class Capability implements CapabilityExport {
|
|
|
277
290
|
log("Watch Action", watchCallback.toString());
|
|
278
291
|
|
|
279
292
|
// Create the child logger and cast it to the expected type
|
|
280
|
-
const aliasLogger = Log.child({
|
|
293
|
+
const aliasLogger = Log.child({
|
|
294
|
+
alias: binding.alias || "no alias provided",
|
|
295
|
+
}) as typeof Log;
|
|
281
296
|
|
|
282
297
|
// Push the binding to the list of bindings for this capability as a new BindingAction
|
|
283
298
|
// with the callback function to preserve
|
|
@@ -298,7 +313,9 @@ export class Capability implements CapabilityExport {
|
|
|
298
313
|
log("Reconcile Action", reconcileCallback.toString());
|
|
299
314
|
|
|
300
315
|
// Create the child logger and cast it to the expected type
|
|
301
|
-
const aliasLogger = Log.child({
|
|
316
|
+
const aliasLogger = Log.child({
|
|
317
|
+
alias: binding.alias || "no alias provided",
|
|
318
|
+
}) as typeof Log;
|
|
302
319
|
|
|
303
320
|
// Push the binding to the list of bindings for this capability as a new BindingAction
|
|
304
321
|
// with the callback function to preserve
|
|
@@ -307,7 +324,9 @@ export class Capability implements CapabilityExport {
|
|
|
307
324
|
isWatch: true,
|
|
308
325
|
isQueue: true,
|
|
309
326
|
watchCallback: async (update, phase, logger = aliasLogger) => {
|
|
310
|
-
Log.info(
|
|
327
|
+
Log.info(
|
|
328
|
+
`Executing reconcile action with alias: ${binding.alias || "no alias provided"}`,
|
|
329
|
+
);
|
|
311
330
|
await reconcileCallback(update, phase, logger);
|
|
312
331
|
},
|
|
313
332
|
});
|
|
@@ -340,8 +359,13 @@ export class Capability implements CapabilityExport {
|
|
|
340
359
|
isWatch: true,
|
|
341
360
|
isFinalize: true,
|
|
342
361
|
event: Event.UPDATE,
|
|
343
|
-
finalizeCallback: async (
|
|
344
|
-
|
|
362
|
+
finalizeCallback: async (
|
|
363
|
+
update: InstanceType<T>,
|
|
364
|
+
logger = aliasLogger,
|
|
365
|
+
): Promise<boolean | void> => {
|
|
366
|
+
Log.info(
|
|
367
|
+
`Executing finalize action with alias: ${binding.alias || "no alias provided"}`,
|
|
368
|
+
);
|
|
345
369
|
return await finalizeCallback(update, logger);
|
|
346
370
|
},
|
|
347
371
|
};
|
package/src/lib/core/module.ts
CHANGED
|
@@ -21,7 +21,11 @@ export class PeprModule {
|
|
|
21
21
|
* @param capabilities The capabilities to be loaded into the Pepr runtime
|
|
22
22
|
* @param opts Options for the Pepr runtime
|
|
23
23
|
*/
|
|
24
|
-
constructor(
|
|
24
|
+
constructor(
|
|
25
|
+
{ description, pepr }: PackageJSON,
|
|
26
|
+
capabilities: Capability[] = [],
|
|
27
|
+
opts: PeprModuleOptions = {},
|
|
28
|
+
) {
|
|
25
29
|
const config: ModuleConfig = clone(pepr);
|
|
26
30
|
config.description = description;
|
|
27
31
|
|
package/src/lib/core/storage.ts
CHANGED
|
@@ -169,7 +169,9 @@ export class Storage implements PeprStore {
|
|
|
169
169
|
// If promise has not resolved before MAX_WAIT_TIME reject
|
|
170
170
|
record.timeout = setTimeout(() => {
|
|
171
171
|
record.unsubscribe!();
|
|
172
|
-
return reject(
|
|
172
|
+
return reject(
|
|
173
|
+
`MAX_WAIT_TIME elapsed: Key ${key} still seen after ${MAX_WAIT_TIME / 1000}s`,
|
|
174
|
+
);
|
|
173
175
|
}, MAX_WAIT_TIME);
|
|
174
176
|
|
|
175
177
|
record.unsubscribe = this.subscribe(data => {
|
|
@@ -29,7 +29,9 @@ export async function checkDeploymentStatus(namespace: string): Promise<boolean>
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// wait for all deployments in the pepr-system namespace to be ready
|
|
32
|
-
export async function namespaceDeploymentsReady(
|
|
32
|
+
export async function namespaceDeploymentsReady(
|
|
33
|
+
namespace: string = "pepr-system",
|
|
34
|
+
): Promise<true | undefined> {
|
|
33
35
|
Log.info(`Checking ${namespace} deployments status...`);
|
|
34
36
|
let ready = false;
|
|
35
37
|
while (!ready) {
|
|
@@ -22,4 +22,7 @@ export const declaredKind = pipe(
|
|
|
22
22
|
(request: AdmissionRequest<KubernetesObject>): string => request?.kind?.kind,
|
|
23
23
|
defaultTo(""),
|
|
24
24
|
);
|
|
25
|
-
export const declaredUid = pipe(
|
|
25
|
+
export const declaredUid = pipe(
|
|
26
|
+
(request: AdmissionRequest<KubernetesObject>): string => request?.uid,
|
|
27
|
+
defaultTo(""),
|
|
28
|
+
);
|
|
@@ -27,13 +27,22 @@ export const definesNameRegex = pipe(definedNameRegex, equals(""), not);
|
|
|
27
27
|
export const definedNamespaces = pipe(binding => binding?.filters?.namespaces, defaultTo([]));
|
|
28
28
|
export const definesNamespaces = pipe(definedNamespaces, equals([]), not);
|
|
29
29
|
|
|
30
|
-
export const definedNamespaceRegexes = pipe(
|
|
30
|
+
export const definedNamespaceRegexes = pipe(
|
|
31
|
+
binding => binding?.filters?.regexNamespaces,
|
|
32
|
+
defaultTo([]),
|
|
33
|
+
);
|
|
31
34
|
export const definesNamespaceRegexes = pipe(definedNamespaceRegexes, equals([]), not);
|
|
32
35
|
|
|
33
|
-
export const definedAnnotations = pipe(
|
|
36
|
+
export const definedAnnotations = pipe(
|
|
37
|
+
(binding: Partial<Binding>) => binding?.filters?.annotations,
|
|
38
|
+
defaultTo({}),
|
|
39
|
+
);
|
|
34
40
|
export const definesAnnotations = pipe(definedAnnotations, equals({}), not);
|
|
35
41
|
|
|
36
|
-
export const definedLabels = pipe(
|
|
42
|
+
export const definedLabels = pipe(
|
|
43
|
+
(binding: Partial<Binding>) => binding?.filters?.labels,
|
|
44
|
+
defaultTo({}),
|
|
45
|
+
);
|
|
37
46
|
export const definesLabels = pipe(definedLabels, equals({}), not);
|
|
38
47
|
|
|
39
48
|
export const definedEvent = (binding: Binding): Event => {
|
|
@@ -82,4 +91,8 @@ export const definedCallback = (binding: Partial<Binding>): DefinedCallbackRetur
|
|
|
82
91
|
binding.isValidate ? binding.validateCallback :
|
|
83
92
|
null;
|
|
84
93
|
};
|
|
85
|
-
export const definedCallbackName = pipe(
|
|
94
|
+
export const definedCallbackName = pipe(
|
|
95
|
+
definedCallback,
|
|
96
|
+
defaultTo({ name: "" }),
|
|
97
|
+
callback => callback.name,
|
|
98
|
+
);
|
|
@@ -15,7 +15,8 @@ export const carriedKind = pipe(
|
|
|
15
15
|
defaultTo("not set"),
|
|
16
16
|
);
|
|
17
17
|
export const carriedVersion = pipe(
|
|
18
|
-
(kubernetesObject: KubernetesObject): string | undefined =>
|
|
18
|
+
(kubernetesObject: KubernetesObject): string | undefined =>
|
|
19
|
+
kubernetesObject?.metadata?.resourceVersion,
|
|
19
20
|
defaultTo("not set"),
|
|
20
21
|
);
|
|
21
22
|
export const carriedName = pipe(
|
|
@@ -40,7 +41,8 @@ export const carriedAnnotations = pipe(
|
|
|
40
41
|
export const carriesAnnotations = pipe(carriedAnnotations, equals({}), not);
|
|
41
42
|
|
|
42
43
|
export const carriedLabels = pipe(
|
|
43
|
-
(kubernetesObject: KubernetesObject): { [key: string]: string } | undefined =>
|
|
44
|
+
(kubernetesObject: KubernetesObject): { [key: string]: string } | undefined =>
|
|
45
|
+
kubernetesObject?.metadata?.labels,
|
|
44
46
|
defaultTo({}),
|
|
45
47
|
);
|
|
46
48
|
export const carriesLabels = pipe(carriedLabels, equals({}), not);
|
|
@@ -32,7 +32,12 @@ import {
|
|
|
32
32
|
carriedNamespace,
|
|
33
33
|
missingDeletionTimestamp,
|
|
34
34
|
} from "./kubernetesObject";
|
|
35
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
declaredOperation,
|
|
37
|
+
declaredGroup,
|
|
38
|
+
declaredVersion,
|
|
39
|
+
declaredKind,
|
|
40
|
+
} from "./admissionRequest";
|
|
36
41
|
import { Event, Operation } from "../../enums";
|
|
37
42
|
import { AdmissionRequest } from "../../common-types";
|
|
38
43
|
|
|
@@ -48,12 +53,20 @@ export const mismatchedName = allPass([
|
|
|
48
53
|
|
|
49
54
|
export const mismatchedNameRegex = allPass([
|
|
50
55
|
pipe(nthArg(0), definesNameRegex),
|
|
51
|
-
pipe(
|
|
56
|
+
pipe(
|
|
57
|
+
(binding, kubernetesObject) =>
|
|
58
|
+
new RegExp(definedNameRegex(binding)).test(carriedName(kubernetesObject)),
|
|
59
|
+
not,
|
|
60
|
+
),
|
|
52
61
|
]);
|
|
53
62
|
|
|
54
63
|
export const mismatchedNamespace = allPass([
|
|
55
64
|
pipe(nthArg(0), definesNamespaces),
|
|
56
|
-
pipe(
|
|
65
|
+
pipe(
|
|
66
|
+
(binding, kubernetesObject) =>
|
|
67
|
+
definedNamespaces(binding).includes(carriedNamespace(kubernetesObject)),
|
|
68
|
+
not,
|
|
69
|
+
),
|
|
57
70
|
]);
|
|
58
71
|
|
|
59
72
|
export const mismatchedNamespaceRegex = allPass([
|
|
@@ -95,12 +108,16 @@ export const metasMismatch = pipe(
|
|
|
95
108
|
|
|
96
109
|
export const mismatchedAnnotations = allPass([
|
|
97
110
|
pipe(nthArg(0), definesAnnotations),
|
|
98
|
-
pipe((binding, kubernetesObject) =>
|
|
111
|
+
pipe((binding, kubernetesObject) =>
|
|
112
|
+
metasMismatch(definedAnnotations(binding), carriedAnnotations(kubernetesObject)),
|
|
113
|
+
),
|
|
99
114
|
]);
|
|
100
115
|
|
|
101
116
|
export const mismatchedLabels = allPass([
|
|
102
117
|
pipe(nthArg(0), definesLabels),
|
|
103
|
-
pipe((binding, kubernetesObject) =>
|
|
118
|
+
pipe((binding, kubernetesObject) =>
|
|
119
|
+
metasMismatch(definedLabels(binding), carriedLabels(kubernetesObject)),
|
|
120
|
+
),
|
|
104
121
|
]);
|
|
105
122
|
|
|
106
123
|
export const mismatchedEvent = pipe(
|
|
@@ -126,5 +143,7 @@ export const mismatchedKind = allPass([
|
|
|
126
143
|
export const operationMatchesEvent = anyPass([
|
|
127
144
|
pipe(nthArg(1), equals(Event.ANY)),
|
|
128
145
|
pipe((operation: Operation, event: Event): boolean => operation.valueOf() === event.valueOf()),
|
|
129
|
-
pipe((operation: Operation, event: Event): boolean =>
|
|
146
|
+
pipe((operation: Operation, event: Event): boolean =>
|
|
147
|
+
operation ? event.includes(operation) : false,
|
|
148
|
+
),
|
|
130
149
|
]);
|
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
|
|
4
4
|
import { __, allPass, curry, difference, equals, gt, length, not, nthArg, pipe } from "ramda";
|
|
5
5
|
import { KubernetesObject } from "kubernetes-fluent-client";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
definedKind,
|
|
8
|
+
definedNamespaces,
|
|
9
|
+
definesDelete,
|
|
10
|
+
definesDeletionTimestamp,
|
|
11
|
+
definesNamespaces,
|
|
12
|
+
} from "./binding";
|
|
7
13
|
import { carriedNamespace, carriesNamespace } from "./kubernetesObject";
|
|
8
14
|
|
|
9
15
|
/*
|
|
@@ -14,7 +20,10 @@ import { carriedNamespace, carriesNamespace } from "./kubernetesObject";
|
|
|
14
20
|
*/
|
|
15
21
|
|
|
16
22
|
export const bindsToKind = curry(
|
|
17
|
-
allPass([
|
|
23
|
+
allPass([
|
|
24
|
+
pipe(nthArg(0), definedKind, equals(""), not),
|
|
25
|
+
pipe((binding, kind) => definedKind(binding) === kind),
|
|
26
|
+
]),
|
|
18
27
|
);
|
|
19
28
|
export const bindsToNamespace = curry(pipe(bindsToKind(__, "Namespace")));
|
|
20
29
|
export const misboundNamespace = allPass([bindsToNamespace, definesNamespaces]);
|
|
@@ -74,4 +83,7 @@ export const unbindableNamespaces = allPass([
|
|
|
74
83
|
),
|
|
75
84
|
]);
|
|
76
85
|
|
|
77
|
-
export const misboundDeleteWithDeletionTimestamp = allPass([
|
|
86
|
+
export const misboundDeleteWithDeletionTimestamp = allPass([
|
|
87
|
+
definesDelete,
|
|
88
|
+
definesDeletionTimestamp,
|
|
89
|
+
]);
|
package/src/lib/filter/filter.ts
CHANGED
|
@@ -12,7 +12,12 @@ import {
|
|
|
12
12
|
unbindableNamespaces,
|
|
13
13
|
uncarryableNamespace,
|
|
14
14
|
} from "./adjudicators/postCollection";
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
declaredOperation,
|
|
17
|
+
declaredGroup,
|
|
18
|
+
declaredVersion,
|
|
19
|
+
declaredKind,
|
|
20
|
+
} from "./adjudicators/admissionRequest";
|
|
16
21
|
import {
|
|
17
22
|
definedEvent,
|
|
18
23
|
definedName,
|
|
@@ -25,7 +30,12 @@ import {
|
|
|
25
30
|
definedNamespaceRegexes,
|
|
26
31
|
definedNameRegex,
|
|
27
32
|
} from "./adjudicators/binding";
|
|
28
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
carriedName,
|
|
35
|
+
carriedNamespace,
|
|
36
|
+
carriedLabels,
|
|
37
|
+
carriedAnnotations,
|
|
38
|
+
} from "./adjudicators/kubernetesObject";
|
|
29
39
|
import {
|
|
30
40
|
mismatchedDeletionTimestamp,
|
|
31
41
|
mismatchedEvent,
|
|
@@ -137,49 +147,72 @@ export function adjudicateMisboundNamespace(binding: Binding): AdjudicationResul
|
|
|
137
147
|
return misboundNamespace(binding) ? "Cannot use namespace filter on a namespace object." : null;
|
|
138
148
|
}
|
|
139
149
|
|
|
140
|
-
export function adjudicateMisboundDeleteWithDeletionTimestamp(
|
|
150
|
+
export function adjudicateMisboundDeleteWithDeletionTimestamp(
|
|
151
|
+
binding: Binding,
|
|
152
|
+
): AdjudicationResult {
|
|
141
153
|
return misboundDeleteWithDeletionTimestamp(binding)
|
|
142
154
|
? "Cannot use deletionTimestamp filter on a DELETE operation."
|
|
143
155
|
: null;
|
|
144
156
|
}
|
|
145
157
|
|
|
146
|
-
export function adjudicateMismatchedDeletionTimestamp(
|
|
158
|
+
export function adjudicateMismatchedDeletionTimestamp(
|
|
159
|
+
binding: Binding,
|
|
160
|
+
obj: KubernetesObject,
|
|
161
|
+
): AdjudicationResult {
|
|
147
162
|
return mismatchedDeletionTimestamp(binding, obj)
|
|
148
163
|
? "Binding defines deletionTimestamp but Object does not carry it."
|
|
149
164
|
: null;
|
|
150
165
|
}
|
|
151
166
|
|
|
152
|
-
export function adjudicateMismatchedEvent(
|
|
167
|
+
export function adjudicateMismatchedEvent(
|
|
168
|
+
binding: Binding,
|
|
169
|
+
req: AdmissionRequest,
|
|
170
|
+
): AdjudicationResult {
|
|
153
171
|
return mismatchedEvent(binding, req)
|
|
154
172
|
? `Binding defines event '${definedEvent(binding)}' but Request declares '${declaredOperation(req)}'.`
|
|
155
173
|
: null;
|
|
156
174
|
}
|
|
157
175
|
|
|
158
|
-
export function adjudicateMismatchedName(
|
|
176
|
+
export function adjudicateMismatchedName(
|
|
177
|
+
binding: Binding,
|
|
178
|
+
obj: KubernetesObject,
|
|
179
|
+
): AdjudicationResult {
|
|
159
180
|
return mismatchedName(binding, obj)
|
|
160
181
|
? `Binding defines name '${definedName(binding)}' but Object carries '${carriedName(obj)}'.`
|
|
161
182
|
: null;
|
|
162
183
|
}
|
|
163
184
|
|
|
164
|
-
export function adjudicateMismatchedGroup(
|
|
185
|
+
export function adjudicateMismatchedGroup(
|
|
186
|
+
binding: Binding,
|
|
187
|
+
req: AdmissionRequest,
|
|
188
|
+
): AdjudicationResult {
|
|
165
189
|
return mismatchedGroup(binding, req)
|
|
166
190
|
? `Binding defines group '${definedGroup(binding)}' but Request declares '${declaredGroup(req)}'.`
|
|
167
191
|
: null;
|
|
168
192
|
}
|
|
169
193
|
|
|
170
|
-
export function adjudicateMismatchedVersion(
|
|
194
|
+
export function adjudicateMismatchedVersion(
|
|
195
|
+
binding: Binding,
|
|
196
|
+
req: AdmissionRequest,
|
|
197
|
+
): AdjudicationResult {
|
|
171
198
|
return mismatchedVersion(binding, req)
|
|
172
199
|
? `Binding defines version '${definedVersion(binding)}' but Request declares '${declaredVersion(req)}'.`
|
|
173
200
|
: null;
|
|
174
201
|
}
|
|
175
202
|
|
|
176
|
-
export function adjudicateMismatchedKind(
|
|
203
|
+
export function adjudicateMismatchedKind(
|
|
204
|
+
binding: Binding,
|
|
205
|
+
req: AdmissionRequest,
|
|
206
|
+
): AdjudicationResult {
|
|
177
207
|
return mismatchedKind(binding, req)
|
|
178
208
|
? `Binding defines kind '${definedKind(binding)}' but Request declares '${declaredKind(req)}'.`
|
|
179
209
|
: null;
|
|
180
210
|
}
|
|
181
211
|
|
|
182
|
-
export function adjudicateUnbindableNamespaces(
|
|
212
|
+
export function adjudicateUnbindableNamespaces(
|
|
213
|
+
capabilityNamespaces: string[],
|
|
214
|
+
binding: Binding,
|
|
215
|
+
): AdjudicationResult {
|
|
183
216
|
return unbindableNamespaces(capabilityNamespaces, binding)
|
|
184
217
|
? `Binding defines namespaces ${JSON.stringify(definedNamespaces(binding))} but namespaces allowed by Capability are '${JSON.stringify(capabilityNamespaces)}'.`
|
|
185
218
|
: null;
|
|
@@ -194,31 +227,46 @@ export function adjudicateUncarryableNamespace(
|
|
|
194
227
|
: null;
|
|
195
228
|
}
|
|
196
229
|
|
|
197
|
-
export function adjudicateMismatchedNamespace(
|
|
230
|
+
export function adjudicateMismatchedNamespace(
|
|
231
|
+
binding: Binding,
|
|
232
|
+
obj: KubernetesObject,
|
|
233
|
+
): AdjudicationResult {
|
|
198
234
|
return mismatchedNamespace(binding, obj)
|
|
199
235
|
? `Binding defines namespaces '${JSON.stringify(definedNamespaces(binding))}' but Object carries '${carriedNamespace(obj)}'.`
|
|
200
236
|
: null;
|
|
201
237
|
}
|
|
202
238
|
|
|
203
|
-
export function adjudicateMismatchedLabels(
|
|
239
|
+
export function adjudicateMismatchedLabels(
|
|
240
|
+
binding: Binding,
|
|
241
|
+
obj: KubernetesObject,
|
|
242
|
+
): AdjudicationResult {
|
|
204
243
|
return mismatchedLabels(binding, obj)
|
|
205
244
|
? `Binding defines labels '${JSON.stringify(definedLabels(binding))}' but Object carries '${JSON.stringify(carriedLabels(obj))}'.`
|
|
206
245
|
: null;
|
|
207
246
|
}
|
|
208
247
|
|
|
209
|
-
export function adjudicateMismatchedAnnotations(
|
|
248
|
+
export function adjudicateMismatchedAnnotations(
|
|
249
|
+
binding: Binding,
|
|
250
|
+
obj: KubernetesObject,
|
|
251
|
+
): AdjudicationResult {
|
|
210
252
|
return mismatchedAnnotations(binding, obj)
|
|
211
253
|
? `Binding defines annotations '${JSON.stringify(definedAnnotations(binding))}' but Object carries '${JSON.stringify(carriedAnnotations(obj))}'.`
|
|
212
254
|
: null;
|
|
213
255
|
}
|
|
214
256
|
|
|
215
|
-
export function adjudicateMismatchedNamespaceRegex(
|
|
257
|
+
export function adjudicateMismatchedNamespaceRegex(
|
|
258
|
+
binding: Binding,
|
|
259
|
+
obj: KubernetesObject,
|
|
260
|
+
): AdjudicationResult {
|
|
216
261
|
return mismatchedNamespaceRegex(binding, obj)
|
|
217
262
|
? `Binding defines namespace regexes '${JSON.stringify(definedNamespaceRegexes(binding))}' but Object carries '${carriedNamespace(obj)}'.`
|
|
218
263
|
: null;
|
|
219
264
|
}
|
|
220
265
|
|
|
221
|
-
export function adjudicateMismatchedNameRegex(
|
|
266
|
+
export function adjudicateMismatchedNameRegex(
|
|
267
|
+
binding: Binding,
|
|
268
|
+
obj: KubernetesObject,
|
|
269
|
+
): AdjudicationResult {
|
|
222
270
|
return mismatchedNameRegex(binding, obj)
|
|
223
271
|
? `Binding defines name regex '${definedNameRegex(binding)}' but Object carries '${carriedName(obj)}'.`
|
|
224
272
|
: null;
|
package/src/lib/helpers.ts
CHANGED
|
@@ -15,7 +15,9 @@ export function validateCapabilityNames(capabilities: CapabilityExport[] | undef
|
|
|
15
15
|
if (capabilities && capabilities.length > 0) {
|
|
16
16
|
for (let i = 0; i < capabilities.length; i++) {
|
|
17
17
|
if (capabilities[i].name !== sanitizeResourceName(capabilities[i].name)) {
|
|
18
|
-
throw new ValidationError(
|
|
18
|
+
throw new ValidationError(
|
|
19
|
+
`Capability name is not a valid Kubernetes resource name: ${capabilities[i].name}`,
|
|
20
|
+
);
|
|
19
21
|
}
|
|
20
22
|
}
|
|
21
23
|
}
|
|
@@ -88,15 +90,23 @@ export function hasAnyOverlap<T>(array1: T[], array2: T[]): boolean {
|
|
|
88
90
|
return array1.some(element => array2.includes(element));
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
export function ignoredNamespaceConflict(
|
|
93
|
+
export function ignoredNamespaceConflict(
|
|
94
|
+
ignoreNamespaces: string[],
|
|
95
|
+
bindingNamespaces: string[],
|
|
96
|
+
): boolean {
|
|
92
97
|
return hasAnyOverlap(bindingNamespaces, ignoreNamespaces);
|
|
93
98
|
}
|
|
94
99
|
|
|
95
|
-
export function bindingAndCapabilityNSConflict(
|
|
100
|
+
export function bindingAndCapabilityNSConflict(
|
|
101
|
+
bindingNamespaces: string[],
|
|
102
|
+
capabilityNamespaces: string[],
|
|
103
|
+
): boolean {
|
|
96
104
|
if (!capabilityNamespaces) {
|
|
97
105
|
return false;
|
|
98
106
|
}
|
|
99
|
-
return
|
|
107
|
+
return (
|
|
108
|
+
capabilityNamespaces.length !== 0 && !hasEveryOverlap(bindingNamespaces, capabilityNamespaces)
|
|
109
|
+
);
|
|
100
110
|
}
|
|
101
111
|
|
|
102
112
|
export function generateWatchNamespaceError(
|
|
@@ -125,9 +135,14 @@ export function generateWatchNamespaceError(
|
|
|
125
135
|
}
|
|
126
136
|
|
|
127
137
|
// namespaceComplianceValidator ensures that capability bindings respect ignored and capability namespaces
|
|
128
|
-
export function namespaceComplianceValidator(
|
|
138
|
+
export function namespaceComplianceValidator(
|
|
139
|
+
capability: CapabilityExport,
|
|
140
|
+
ignoredNamespaces?: string[],
|
|
141
|
+
): void {
|
|
129
142
|
const { namespaces: capabilityNamespaces, bindings, name } = capability;
|
|
130
|
-
const bindingNamespaces: string[] = bindings.flatMap(
|
|
143
|
+
const bindingNamespaces: string[] = bindings.flatMap(
|
|
144
|
+
(binding: Binding) => binding.filters.namespaces,
|
|
145
|
+
);
|
|
131
146
|
const bindingRegexNamespaces: string[] = bindings.flatMap(
|
|
132
147
|
(binding: Binding) => binding.filters.regexNamespaces || [],
|
|
133
148
|
);
|
|
@@ -153,12 +168,18 @@ const matchRegexToCapababilityNamespace = (
|
|
|
153
168
|
bindingRegexNamespaces: string[],
|
|
154
169
|
capabilityNamespaces: string[] | undefined,
|
|
155
170
|
): void => {
|
|
156
|
-
if (
|
|
171
|
+
if (
|
|
172
|
+
bindingRegexNamespaces.length > 0 &&
|
|
173
|
+
capabilityNamespaces &&
|
|
174
|
+
capabilityNamespaces.length > 0
|
|
175
|
+
) {
|
|
157
176
|
for (const regexNamespace of bindingRegexNamespaces) {
|
|
158
177
|
let matches = false;
|
|
159
178
|
matches =
|
|
160
179
|
regexNamespace !== "" &&
|
|
161
|
-
capabilityNamespaces.some(capabilityNamespace =>
|
|
180
|
+
capabilityNamespaces.some(capabilityNamespace =>
|
|
181
|
+
matchesRegex(regexNamespace, capabilityNamespace),
|
|
182
|
+
);
|
|
162
183
|
if (!matches) {
|
|
163
184
|
throw new Error(
|
|
164
185
|
`Ignoring Watch Callback: Object namespace does not match any capability namespace with regex ${regexNamespace}.`,
|
|
@@ -168,10 +189,15 @@ const matchRegexToCapababilityNamespace = (
|
|
|
168
189
|
}
|
|
169
190
|
};
|
|
170
191
|
|
|
171
|
-
const checkRegexNamespaces = (
|
|
192
|
+
const checkRegexNamespaces = (
|
|
193
|
+
bindingRegexNamespaces: string[],
|
|
194
|
+
ignoredNamespaces: string[] | undefined,
|
|
195
|
+
): void => {
|
|
172
196
|
if (bindingRegexNamespaces.length > 0 && ignoredNamespaces && ignoredNamespaces.length > 0) {
|
|
173
197
|
for (const regexNamespace of bindingRegexNamespaces) {
|
|
174
|
-
const matchedNS = ignoredNamespaces.find(ignoredNS =>
|
|
198
|
+
const matchedNS = ignoredNamespaces.find(ignoredNS =>
|
|
199
|
+
matchesRegex(regexNamespace, ignoredNS),
|
|
200
|
+
);
|
|
175
201
|
if (matchedNS) {
|
|
176
202
|
throw new Error(
|
|
177
203
|
`Ignoring Watch Callback: Regex namespace: ${regexNamespace}, is an ignored namespace: ${matchedNS}.`,
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
import { promises as fs } from "fs";
|
|
5
5
|
|
|
6
|
-
export async function createDockerfile(
|
|
6
|
+
export async function createDockerfile(
|
|
7
|
+
version: string,
|
|
8
|
+
description: string,
|
|
9
|
+
includedFiles: string[],
|
|
10
|
+
): Promise<void> {
|
|
7
11
|
const file = `
|
|
8
12
|
# Use an official Node.js runtime as the base image
|
|
9
13
|
FROM ghcr.io/defenseunicorns/pepr/controller:v${version}
|
|
@@ -18,7 +18,10 @@ export function decodeData(wrapped: PeprMutateRequest<KubernetesObject>): {
|
|
|
18
18
|
return { skipped, wrapped };
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export function reencodeData(
|
|
21
|
+
export function reencodeData(
|
|
22
|
+
wrapped: PeprMutateRequest<KubernetesObject>,
|
|
23
|
+
skipped: string[],
|
|
24
|
+
): KubernetesObject {
|
|
22
25
|
const transformed = clone(wrapped.Raw);
|
|
23
26
|
|
|
24
27
|
const isSecret = wrapped.Request.kind.version === "v1" && wrapped.Request.kind.kind === "Secret";
|
|
@@ -195,7 +195,10 @@ export async function mutateProcessor(
|
|
|
195
195
|
return { ...response, allowed: true };
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
-
export function updateResponsePatchAndWarnings(
|
|
198
|
+
export function updateResponsePatchAndWarnings(
|
|
199
|
+
patches: Operation[],
|
|
200
|
+
response: MutateResponse,
|
|
201
|
+
): void {
|
|
199
202
|
// Only add the patch if there are patches to apply
|
|
200
203
|
if (patches.length > 0) {
|
|
201
204
|
response.patchType = "JSONPatch";
|
|
@@ -41,7 +41,10 @@ export async function processRequest(
|
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
Log.info(
|
|
44
|
+
Log.info(
|
|
45
|
+
actionMetadata,
|
|
46
|
+
`Validation action complete (${label}): ${callbackResp.allowed ? "allowed" : "denied"}`,
|
|
47
|
+
);
|
|
45
48
|
return valResp;
|
|
46
49
|
} catch (e) {
|
|
47
50
|
// If any validation throws an error, note the failure in the Response
|