pepr 0.36.0 → 0.37.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.
Files changed (96) hide show
  1. package/dist/cli/init/index.d.ts.map +1 -1
  2. package/dist/cli/init/templates.d.ts +3 -1
  3. package/dist/cli/init/templates.d.ts.map +1 -1
  4. package/dist/cli/init/utils.d.ts.map +1 -1
  5. package/dist/cli/init/walkthrough.d.ts +10 -3
  6. package/dist/cli/init/walkthrough.d.ts.map +1 -1
  7. package/dist/cli.js +253 -31
  8. package/dist/controller.js +138 -1
  9. package/dist/lib/adjudicators.d.ts +63 -0
  10. package/dist/lib/adjudicators.d.ts.map +1 -0
  11. package/dist/lib/adjudicators.test.d.ts +2 -0
  12. package/dist/lib/adjudicators.test.d.ts.map +1 -0
  13. package/dist/lib/assets/loader.d.ts.map +1 -1
  14. package/dist/lib/assets/pods.d.ts +1 -0
  15. package/dist/lib/assets/pods.d.ts.map +1 -1
  16. package/dist/lib/capability.d.ts +1 -0
  17. package/dist/lib/capability.d.ts.map +1 -1
  18. package/dist/lib/capability.test.d.ts +2 -0
  19. package/dist/lib/capability.test.d.ts.map +1 -0
  20. package/dist/lib/controller/index.d.ts.map +1 -1
  21. package/dist/lib/controller/store.d.ts +4 -0
  22. package/dist/lib/controller/store.d.ts.map +1 -1
  23. package/dist/lib/controller/store.test.d.ts +2 -0
  24. package/dist/lib/controller/store.test.d.ts.map +1 -0
  25. package/dist/lib/filter.d.ts +2 -3
  26. package/dist/lib/filter.d.ts.map +1 -1
  27. package/dist/lib/filter.test.d.ts +2 -1
  28. package/dist/lib/filter.test.d.ts.map +1 -1
  29. package/dist/lib/finalizer.d.ts +6 -0
  30. package/dist/lib/finalizer.d.ts.map +1 -0
  31. package/dist/lib/finalizer.test.d.ts +2 -0
  32. package/dist/lib/finalizer.test.d.ts.map +1 -0
  33. package/dist/lib/helpers.d.ts +2 -2
  34. package/dist/lib/helpers.d.ts.map +1 -1
  35. package/dist/lib/helpers.test.d.ts +1 -1
  36. package/dist/lib/helpers.test.d.ts.map +1 -1
  37. package/dist/lib/k8s.d.ts.map +1 -1
  38. package/dist/lib/module.d.ts +2 -1
  39. package/dist/lib/module.d.ts.map +1 -1
  40. package/dist/lib/mutate-processor.d.ts +2 -1
  41. package/dist/lib/mutate-processor.d.ts.map +1 -1
  42. package/dist/lib/mutate-request.d.ts +1 -2
  43. package/dist/lib/mutate-request.d.ts.map +1 -1
  44. package/dist/lib/schedule.d.ts +1 -2
  45. package/dist/lib/schedule.d.ts.map +1 -1
  46. package/dist/lib/storage.d.ts.map +1 -1
  47. package/dist/lib/types.d.ts +115 -6
  48. package/dist/lib/types.d.ts.map +1 -1
  49. package/dist/lib/validate-processor.d.ts +4 -2
  50. package/dist/lib/validate-processor.d.ts.map +1 -1
  51. package/dist/lib/validate-request.d.ts +1 -1
  52. package/dist/lib/validate-request.d.ts.map +1 -1
  53. package/dist/lib/watch-processor.d.ts +1 -1
  54. package/dist/lib/watch-processor.d.ts.map +1 -1
  55. package/dist/lib.js +383 -204
  56. package/dist/lib.js.map +4 -4
  57. package/package.json +9 -7
  58. package/src/cli/build.ts +3 -3
  59. package/src/cli/init/index.ts +20 -11
  60. package/src/cli/init/templates.ts +1 -1
  61. package/src/cli/init/utils.test.ts +11 -20
  62. package/src/cli/init/utils.ts +5 -0
  63. package/src/cli/init/walkthrough.test.ts +92 -11
  64. package/src/cli/init/walkthrough.ts +71 -16
  65. package/src/cli/monitor.ts +1 -1
  66. package/src/cli.ts +4 -2
  67. package/src/fixtures/data/create-pod.json +1 -1
  68. package/src/fixtures/data/delete-pod.json +1 -1
  69. package/src/lib/adjudicators.test.ts +1232 -0
  70. package/src/lib/adjudicators.ts +235 -0
  71. package/src/lib/assets/index.ts +1 -1
  72. package/src/lib/assets/loader.ts +1 -0
  73. package/src/lib/assets/webhooks.ts +1 -1
  74. package/src/lib/capability.test.ts +655 -0
  75. package/src/lib/capability.ts +104 -11
  76. package/src/lib/controller/index.ts +7 -4
  77. package/src/lib/controller/store.test.ts +131 -0
  78. package/src/lib/controller/store.ts +43 -5
  79. package/src/lib/filter.test.ts +194 -8
  80. package/src/lib/filter.ts +46 -107
  81. package/src/lib/finalizer.test.ts +236 -0
  82. package/src/lib/finalizer.ts +63 -0
  83. package/src/lib/helpers.test.ts +329 -69
  84. package/src/lib/helpers.ts +141 -100
  85. package/src/lib/k8s.ts +4 -0
  86. package/src/lib/module.ts +3 -3
  87. package/src/lib/mutate-processor.ts +5 -4
  88. package/src/lib/mutate-request.test.ts +1 -2
  89. package/src/lib/mutate-request.ts +1 -3
  90. package/src/lib/schedule.ts +1 -1
  91. package/src/lib/storage.ts +5 -6
  92. package/src/lib/types.ts +151 -5
  93. package/src/lib/validate-processor.ts +5 -2
  94. package/src/lib/validate-request.test.ts +1 -4
  95. package/src/lib/validate-request.ts +1 -1
  96. package/src/lib/watch-processor.ts +19 -5
package/dist/lib.js CHANGED
@@ -31,27 +31,27 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var lib_exports = {};
32
32
  __export(lib_exports, {
33
33
  Capability: () => Capability,
34
- K8s: () => import_kubernetes_fluent_client7.K8s,
34
+ K8s: () => import_kubernetes_fluent_client8.K8s,
35
35
  Log: () => logger_default,
36
36
  PeprModule: () => PeprModule,
37
37
  PeprMutateRequest: () => PeprMutateRequest,
38
38
  PeprUtils: () => utils_exports,
39
39
  PeprValidateRequest: () => PeprValidateRequest,
40
40
  R: () => R,
41
- RegisterKind: () => import_kubernetes_fluent_client7.RegisterKind,
42
- a: () => import_kubernetes_fluent_client7.kind,
43
- fetch: () => import_kubernetes_fluent_client7.fetch,
44
- fetchStatus: () => import_kubernetes_fluent_client7.fetchStatus,
45
- kind: () => import_kubernetes_fluent_client7.kind,
41
+ RegisterKind: () => import_kubernetes_fluent_client8.RegisterKind,
42
+ a: () => import_kubernetes_fluent_client8.kind,
43
+ fetch: () => import_kubernetes_fluent_client8.fetch,
44
+ fetchStatus: () => import_kubernetes_fluent_client8.fetchStatus,
45
+ kind: () => import_kubernetes_fluent_client8.kind,
46
46
  sdk: () => sdk_exports
47
47
  });
48
48
  module.exports = __toCommonJS(lib_exports);
49
- var import_kubernetes_fluent_client7 = require("kubernetes-fluent-client");
49
+ var import_kubernetes_fluent_client8 = require("kubernetes-fluent-client");
50
50
  var R = __toESM(require("ramda"));
51
51
 
52
52
  // src/lib/capability.ts
53
- var import_kubernetes_fluent_client6 = require("kubernetes-fluent-client");
54
- var import_ramda6 = require("ramda");
53
+ var import_kubernetes_fluent_client7 = require("kubernetes-fluent-client");
54
+ var import_ramda7 = require("ramda");
55
55
 
56
56
  // src/lib/logger.ts
57
57
  var import_pino = require("pino");
@@ -74,7 +74,7 @@ if (process.env.LOG_LEVEL) {
74
74
  var logger_default = Log;
75
75
 
76
76
  // src/lib/module.ts
77
- var import_ramda4 = require("ramda");
77
+ var import_ramda5 = require("ramda");
78
78
 
79
79
  // src/lib/controller/index.ts
80
80
  var import_express = __toESM(require("express"));
@@ -224,90 +224,151 @@ function ValidateError(error = "") {
224
224
  }
225
225
  }
226
226
 
227
- // src/lib/k8s.ts
228
- var import_kubernetes_fluent_client = require("kubernetes-fluent-client");
229
- var PeprStore = class extends import_kubernetes_fluent_client.GenericKind {
230
- };
231
- var peprStoreGVK = {
232
- kind: "PeprStore",
233
- version: "v1",
234
- group: "pepr.dev"
235
- };
236
- (0, import_kubernetes_fluent_client.RegisterKind)(PeprStore, peprStoreGVK);
227
+ // src/lib/adjudicators.ts
228
+ var import_ramda = require("ramda");
229
+ var declaredOperation = (0, import_ramda.pipe)((request) => request?.operation, (0, import_ramda.defaultTo)(""));
230
+ var declaredGroup = (0, import_ramda.pipe)((request) => request?.kind?.group, (0, import_ramda.defaultTo)(""));
231
+ var declaredVersion = (0, import_ramda.pipe)((request) => request?.kind?.version, (0, import_ramda.defaultTo)(""));
232
+ var declaredKind = (0, import_ramda.pipe)((request) => request?.kind?.kind, (0, import_ramda.defaultTo)(""));
233
+ var declaredUid = (0, import_ramda.pipe)((request) => request?.uid, (0, import_ramda.defaultTo)(""));
234
+ var carriesDeletionTimestamp = (0, import_ramda.pipe)((obj) => !!obj.metadata?.deletionTimestamp, (0, import_ramda.defaultTo)(false));
235
+ var missingDeletionTimestamp = (0, import_ramda.complement)(carriesDeletionTimestamp);
236
+ var carriedName = (0, import_ramda.pipe)((obj) => obj?.metadata?.name, (0, import_ramda.defaultTo)(""));
237
+ var carriesName = (0, import_ramda.pipe)(carriedName, (0, import_ramda.equals)(""), import_ramda.not);
238
+ var missingName = (0, import_ramda.complement)(carriesName);
239
+ var carriedNamespace = (0, import_ramda.pipe)((obj) => obj?.metadata?.namespace, (0, import_ramda.defaultTo)(""));
240
+ var carriesNamespace = (0, import_ramda.pipe)(carriedNamespace, (0, import_ramda.equals)(""), import_ramda.not);
241
+ var carriedAnnotations = (0, import_ramda.pipe)((obj) => obj?.metadata?.annotations, (0, import_ramda.defaultTo)({}));
242
+ var carriesAnnotations = (0, import_ramda.pipe)(carriedAnnotations, (0, import_ramda.equals)({}), import_ramda.not);
243
+ var carriedLabels = (0, import_ramda.pipe)((obj) => obj?.metadata?.labels, (0, import_ramda.defaultTo)({}));
244
+ var carriesLabels = (0, import_ramda.pipe)(carriedLabels, (0, import_ramda.equals)({}), import_ramda.not);
245
+ var definesDeletionTimestamp = (0, import_ramda.pipe)((binding) => binding?.filters?.deletionTimestamp, (0, import_ramda.defaultTo)(false));
246
+ var ignoresDeletionTimestamp = (0, import_ramda.complement)(definesDeletionTimestamp);
247
+ var definedName = (0, import_ramda.pipe)((binding) => binding?.filters?.name, (0, import_ramda.defaultTo)(""));
248
+ var definesName = (0, import_ramda.pipe)(definedName, (0, import_ramda.equals)(""), import_ramda.not);
249
+ var ignoresName = (0, import_ramda.complement)(definesName);
250
+ var definedNameRegex = (0, import_ramda.pipe)((binding) => binding?.filters?.regexName, (0, import_ramda.defaultTo)(""));
251
+ var definesNameRegex = (0, import_ramda.pipe)(definedNameRegex, (0, import_ramda.equals)(""), import_ramda.not);
252
+ var definedNamespaces = (0, import_ramda.pipe)((binding) => binding?.filters?.namespaces, (0, import_ramda.defaultTo)([]));
253
+ var definesNamespaces = (0, import_ramda.pipe)(definedNamespaces, (0, import_ramda.equals)([]), import_ramda.not);
254
+ var definedNamespaceRegexes = (0, import_ramda.pipe)((binding) => binding?.filters?.regexNamespaces, (0, import_ramda.defaultTo)([]));
255
+ var definesNamespaceRegexes = (0, import_ramda.pipe)(definedNamespaceRegexes, (0, import_ramda.equals)([]), import_ramda.not);
256
+ var definedAnnotations = (0, import_ramda.pipe)((binding) => binding?.filters?.annotations, (0, import_ramda.defaultTo)({}));
257
+ var definesAnnotations = (0, import_ramda.pipe)(definedAnnotations, (0, import_ramda.equals)({}), import_ramda.not);
258
+ var definedLabels = (0, import_ramda.pipe)((binding) => binding?.filters?.labels, (0, import_ramda.defaultTo)({}));
259
+ var definesLabels = (0, import_ramda.pipe)(definedLabels, (0, import_ramda.equals)({}), import_ramda.not);
260
+ var definedEvent = (0, import_ramda.pipe)((binding) => binding?.event, (0, import_ramda.defaultTo)(""));
261
+ var definesDelete = (0, import_ramda.pipe)(definedEvent, (0, import_ramda.equals)("DELETE" /* DELETE */));
262
+ var definedGroup = (0, import_ramda.pipe)((binding) => binding?.kind?.group, (0, import_ramda.defaultTo)(""));
263
+ var definesGroup = (0, import_ramda.pipe)(definedGroup, (0, import_ramda.equals)(""), import_ramda.not);
264
+ var definedVersion = (0, import_ramda.pipe)((binding) => binding?.kind?.version, (0, import_ramda.defaultTo)(""));
265
+ var definesVersion = (0, import_ramda.pipe)(definedVersion, (0, import_ramda.equals)(""), import_ramda.not);
266
+ var definedKind = (0, import_ramda.pipe)((binding) => binding?.kind?.kind, (0, import_ramda.defaultTo)(""));
267
+ var definesKind = (0, import_ramda.pipe)(definedKind, (0, import_ramda.equals)(""), import_ramda.not);
268
+ var definedCategory = (0, import_ramda.pipe)((binding) => {
269
+ return binding.isFinalize ? "Finalize" : binding.isWatch ? "Watch" : binding.isMutate ? "Mutate" : binding.isValidate ? "Validate" : "";
270
+ });
271
+ var definedCallback = (0, import_ramda.pipe)((binding) => {
272
+ return binding.isFinalize ? binding.finalizeCallback : binding.isWatch ? binding.watchCallback : binding.isMutate ? binding.mutateCallback : binding.isValidate ? binding.validateCallback : null;
273
+ });
274
+ var definedCallbackName = (0, import_ramda.pipe)(definedCallback, (0, import_ramda.defaultTo)({ name: "" }), (cb) => cb.name);
275
+ var mismatchedDeletionTimestamp = (0, import_ramda.allPass)([
276
+ (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), definesDeletionTimestamp),
277
+ (0, import_ramda.pipe)((0, import_ramda.nthArg)(1), missingDeletionTimestamp)
278
+ ]);
279
+ var mismatchedName = (0, import_ramda.allPass)([
280
+ (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), definesName),
281
+ (0, import_ramda.pipe)((bnd, obj) => definedName(bnd) !== carriedName(obj))
282
+ ]);
283
+ var mismatchedNameRegex = (0, import_ramda.allPass)([
284
+ (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), definesNameRegex),
285
+ (0, import_ramda.pipe)((bnd, obj) => new RegExp(definedNameRegex(bnd)).test(carriedName(obj)), import_ramda.not)
286
+ ]);
287
+ var bindsToKind = (0, import_ramda.curry)(
288
+ (0, import_ramda.allPass)([(0, import_ramda.pipe)((0, import_ramda.nthArg)(0), definedKind, (0, import_ramda.equals)(""), import_ramda.not), (0, import_ramda.pipe)((bnd, knd) => definedKind(bnd) === knd)])
289
+ );
290
+ var bindsToNamespace = (0, import_ramda.curry)((0, import_ramda.pipe)(bindsToKind(import_ramda.__, "Namespace")));
291
+ var misboundNamespace = (0, import_ramda.allPass)([bindsToNamespace, definesNamespaces]);
292
+ var mismatchedNamespace = (0, import_ramda.allPass)([
293
+ (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), definesNamespaces),
294
+ (0, import_ramda.pipe)((bnd, obj) => definedNamespaces(bnd).includes(carriedNamespace(obj)), import_ramda.not)
295
+ ]);
296
+ var mismatchedNamespaceRegex = (0, import_ramda.allPass)([
297
+ (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), definesNamespaceRegexes),
298
+ (0, import_ramda.pipe)(
299
+ (bnd, obj) => (0, import_ramda.pipe)(
300
+ (0, import_ramda.any)((rex) => new RegExp(rex).test(carriedNamespace(obj))),
301
+ import_ramda.not
302
+ )(definedNamespaceRegexes(bnd))
303
+ )
304
+ ]);
305
+ var metasMismatch = (0, import_ramda.pipe)(
306
+ (defined, carried) => {
307
+ const result = { defined, carried, unalike: {} };
308
+ result.unalike = Object.entries(result.defined).map(([key, val]) => {
309
+ const keyMissing = !Object.hasOwn(result.carried, key);
310
+ const noValue = !val;
311
+ const valMissing = !result.carried[key];
312
+ return keyMissing ? { [key]: val } : noValue ? {} : valMissing ? { [key]: val } : {};
313
+ }).reduce((acc, cur) => ({ ...acc, ...cur }), {});
314
+ return result.unalike;
315
+ },
316
+ (unalike) => Object.keys(unalike).length > 0
317
+ );
318
+ var mismatchedAnnotations = (0, import_ramda.allPass)([
319
+ (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), definesAnnotations),
320
+ (0, import_ramda.pipe)((bnd, obj) => metasMismatch(definedAnnotations(bnd), carriedAnnotations(obj)))
321
+ ]);
322
+ var mismatchedLabels = (0, import_ramda.allPass)([
323
+ (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), definesLabels),
324
+ (0, import_ramda.pipe)((bnd, obj) => metasMismatch(definedLabels(bnd), carriedLabels(obj)))
325
+ ]);
326
+ var uncarryableNamespace = (0, import_ramda.allPass)([
327
+ (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), import_ramda.length, (0, import_ramda.gt)(import_ramda.__, 0)),
328
+ (0, import_ramda.pipe)((0, import_ramda.nthArg)(1), carriesNamespace),
329
+ (0, import_ramda.pipe)((nss, obj) => nss.includes(carriedNamespace(obj)), import_ramda.not)
330
+ ]);
331
+ var carriesIgnoredNamespace = (0, import_ramda.allPass)([
332
+ (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), import_ramda.length, (0, import_ramda.gt)(import_ramda.__, 0)),
333
+ (0, import_ramda.pipe)((0, import_ramda.nthArg)(1), carriesNamespace),
334
+ (0, import_ramda.pipe)((nss, obj) => nss.includes(carriedNamespace(obj)))
335
+ ]);
336
+ var unbindableNamespaces = (0, import_ramda.allPass)([
337
+ (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), import_ramda.length, (0, import_ramda.gt)(import_ramda.__, 0)),
338
+ (0, import_ramda.pipe)((0, import_ramda.nthArg)(1), definesNamespaces),
339
+ (0, import_ramda.pipe)((nss, bnd) => (0, import_ramda.difference)(definedNamespaces(bnd), nss), import_ramda.length, (0, import_ramda.equals)(0), import_ramda.not)
340
+ ]);
341
+ var misboundDeleteWithDeletionTimestamp = (0, import_ramda.allPass)([definesDelete, definesDeletionTimestamp]);
342
+ var operationMatchesEvent = (0, import_ramda.anyPass)([
343
+ (0, import_ramda.pipe)((0, import_ramda.nthArg)(1), (0, import_ramda.equals)("*" /* Any */)),
344
+ (0, import_ramda.pipe)((op, evt) => op === evt),
345
+ (0, import_ramda.pipe)((op, evt) => op ? evt.includes(op) : false)
346
+ ]);
347
+ var mismatchedEvent = (0, import_ramda.pipe)(
348
+ (binding, request) => operationMatchesEvent(declaredOperation(request), definedEvent(binding)),
349
+ import_ramda.not
350
+ );
351
+ var mismatchedGroup = (0, import_ramda.allPass)([
352
+ (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), definesGroup),
353
+ (0, import_ramda.pipe)((binding, request) => definedGroup(binding) !== declaredGroup(request))
354
+ ]);
355
+ var mismatchedVersion = (0, import_ramda.allPass)([
356
+ (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), definesVersion),
357
+ (0, import_ramda.pipe)((binding, request) => definedVersion(binding) !== declaredVersion(request))
358
+ ]);
359
+ var mismatchedKind = (0, import_ramda.allPass)([
360
+ (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), definesKind),
361
+ (0, import_ramda.pipe)((binding, request) => definedKind(binding) !== declaredKind(request))
362
+ ]);
237
363
 
238
364
  // src/lib/filter.ts
239
- function shouldSkipRequest(binding, req, capabilityNamespaces) {
240
- const { group, kind: kind4, version } = binding.kind || {};
241
- const { namespaces, labels, annotations, name } = binding.filters || {};
242
- const operation = req.operation.toUpperCase();
243
- const uid = req.uid;
244
- const srcObject = operation === "DELETE" /* DELETE */ ? req.oldObject : req.object;
245
- const { metadata } = srcObject || {};
246
- const combinedNamespaces = [...namespaces, ...capabilityNamespaces];
247
- if (binding.event.includes("DELETE" /* Delete */) && binding.filters?.deletionTimestamp) {
248
- return true;
249
- }
250
- if (binding.filters?.deletionTimestamp && !req.object.metadata?.deletionTimestamp) {
251
- return true;
252
- }
253
- if (!binding.event.includes(operation) && !binding.event.includes("*" /* Any */)) {
254
- return true;
255
- }
256
- if (name && name !== req.name) {
257
- return true;
258
- }
259
- if (kind4 !== req.kind.kind) {
260
- return true;
261
- }
262
- if (group && group !== req.kind.group) {
263
- return true;
264
- }
265
- if (version && version !== req.kind.version) {
266
- return true;
267
- }
268
- if (combinedNamespaces.length && !combinedNamespaces.includes(req.namespace || "") || !namespaces.includes(req.namespace || "") && capabilityNamespaces.length !== 0 && namespaces.length !== 0) {
269
- let type = "";
270
- let label = "";
271
- if (binding.isMutate) {
272
- type = "Mutate";
273
- label = binding.mutateCallback.name;
274
- } else if (binding.isValidate) {
275
- type = "Validate";
276
- label = binding.validateCallback.name;
277
- } else if (binding.isWatch) {
278
- type = "Watch";
279
- label = binding.watchCallback.name;
280
- }
281
- logger_default.debug({ uid }, `${type} binding (${label}) does not match request namespace "${req.namespace}"`);
282
- return true;
283
- }
284
- for (const [key, value] of Object.entries(labels)) {
285
- const testKey = metadata?.labels?.[key];
286
- if (!testKey) {
287
- logger_default.debug({ uid }, `Label ${key} does not exist`);
288
- return true;
289
- }
290
- if (value && testKey !== value) {
291
- logger_default.debug({ uid }, `${testKey} does not match ${value}`);
292
- return true;
293
- }
294
- }
295
- for (const [key, value] of Object.entries(annotations)) {
296
- const testKey = metadata?.annotations?.[key];
297
- if (!testKey) {
298
- logger_default.debug({ uid }, `Annotation ${key} does not exist`);
299
- return true;
300
- }
301
- if (value && testKey !== value) {
302
- logger_default.debug({ uid }, `${testKey} does not match ${value}`);
303
- return true;
304
- }
305
- }
306
- return false;
365
+ function shouldSkipRequest(binding, req, capabilityNamespaces, ignoredNamespaces) {
366
+ const obj = req.operation === "DELETE" /* DELETE */ ? req.oldObject : req.object;
367
+ return misboundDeleteWithDeletionTimestamp(binding) ? true : mismatchedDeletionTimestamp(binding, obj) ? true : mismatchedEvent(binding, req) ? true : mismatchedName(binding, obj) ? true : mismatchedGroup(binding, req) ? true : mismatchedVersion(binding, req) ? true : mismatchedKind(binding, req) ? true : unbindableNamespaces(capabilityNamespaces, binding) ? true : uncarryableNamespace(capabilityNamespaces, obj) ? true : mismatchedNamespace(binding, obj) ? true : mismatchedLabels(binding, obj) ? true : mismatchedAnnotations(binding, obj) ? true : mismatchedNamespaceRegex(binding, obj) ? true : mismatchedNameRegex(binding, obj) ? true : carriesIgnoredNamespace(ignoredNamespaces, obj) ? true : false;
307
368
  }
308
369
 
309
370
  // src/lib/mutate-request.ts
310
- var import_ramda = require("ramda");
371
+ var import_ramda2 = require("ramda");
311
372
  var PeprMutateRequest = class {
312
373
  Raw;
313
374
  #input;
@@ -342,9 +403,9 @@ var PeprMutateRequest = class {
342
403
  constructor(input) {
343
404
  this.#input = input;
344
405
  if (input.operation.toUpperCase() === "DELETE" /* DELETE */) {
345
- this.Raw = (0, import_ramda.clone)(input.oldObject);
406
+ this.Raw = (0, import_ramda2.clone)(input.oldObject);
346
407
  } else {
347
- this.Raw = (0, import_ramda.clone)(input.object);
408
+ this.Raw = (0, import_ramda2.clone)(input.object);
348
409
  }
349
410
  if (!this.Raw) {
350
411
  throw new Error("unable to load the request object into PeprRequest.RawP");
@@ -356,7 +417,7 @@ var PeprMutateRequest = class {
356
417
  * @param obj - The object to merge with the current resource.
357
418
  */
358
419
  Merge = (obj) => {
359
- this.Raw = (0, import_ramda.mergeDeepRight)(this.Raw, obj);
420
+ this.Raw = (0, import_ramda2.mergeDeepRight)(this.Raw, obj);
360
421
  };
361
422
  /**
362
423
  * Updates a label on the Kubernetes resource.
@@ -489,7 +550,7 @@ async function mutateProcessor(config, capabilities, req, reqMetadata) {
489
550
  if (!action.mutateCallback) {
490
551
  continue;
491
552
  }
492
- if (shouldSkipRequest(action, req, namespaces)) {
553
+ if (shouldSkipRequest(action, req, namespaces, config?.alwaysIgnore?.namespaces)) {
493
554
  continue;
494
555
  }
495
556
  const label = action.mutateCallback.name;
@@ -562,7 +623,7 @@ async function mutateProcessor(config, capabilities, req, reqMetadata) {
562
623
  }
563
624
 
564
625
  // src/lib/validate-request.ts
565
- var import_ramda2 = require("ramda");
626
+ var import_ramda3 = require("ramda");
566
627
  var PeprValidateRequest = class {
567
628
  Raw;
568
629
  #input;
@@ -587,9 +648,9 @@ var PeprValidateRequest = class {
587
648
  constructor(input) {
588
649
  this.#input = input;
589
650
  if (input.operation.toUpperCase() === "DELETE" /* DELETE */) {
590
- this.Raw = (0, import_ramda2.clone)(input.oldObject);
651
+ this.Raw = (0, import_ramda3.clone)(input.oldObject);
591
652
  } else {
592
- this.Raw = (0, import_ramda2.clone)(input.object);
653
+ this.Raw = (0, import_ramda3.clone)(input.object);
593
654
  }
594
655
  if (!this.Raw) {
595
656
  throw new Error("unable to load the request object into PeprRequest.Raw");
@@ -640,7 +701,7 @@ var PeprValidateRequest = class {
640
701
  };
641
702
 
642
703
  // src/lib/validate-processor.ts
643
- async function validateProcessor(capabilities, req, reqMetadata) {
704
+ async function validateProcessor(config, capabilities, req, reqMetadata) {
644
705
  const wrapped = new PeprValidateRequest(req);
645
706
  const response = [];
646
707
  const isSecret = req.kind.version == "v1" && req.kind.kind == "Secret";
@@ -659,7 +720,7 @@ async function validateProcessor(capabilities, req, reqMetadata) {
659
720
  allowed: true
660
721
  // Assume it's allowed until a validation check fails
661
722
  };
662
- if (shouldSkipRequest(action, req, namespaces)) {
723
+ if (shouldSkipRequest(action, req, namespaces, config?.alwaysIgnore?.namespaces)) {
663
724
  continue;
664
725
  }
665
726
  const label = action.validateCallback.name;
@@ -691,7 +752,21 @@ async function validateProcessor(capabilities, req, reqMetadata) {
691
752
 
692
753
  // src/lib/controller/store.ts
693
754
  var import_kubernetes_fluent_client2 = require("kubernetes-fluent-client");
694
- var import_ramda3 = require("ramda");
755
+ var import_ramda4 = require("ramda");
756
+
757
+ // src/lib/k8s.ts
758
+ var import_kubernetes_fluent_client = require("kubernetes-fluent-client");
759
+ var PeprStore = class extends import_kubernetes_fluent_client.GenericKind {
760
+ };
761
+ var peprStoreGVK = {
762
+ kind: "PeprStore",
763
+ version: "v1",
764
+ group: "pepr.dev"
765
+ };
766
+ (0, import_kubernetes_fluent_client.RegisterKind)(PeprStore, peprStoreGVK);
767
+
768
+ // src/lib/controller/store.ts
769
+ var redactedValue = "**redacted**";
695
770
  var namespace = "pepr-system";
696
771
  var debounceBackoff = 5e3;
697
772
  var PeprControllerStore = class {
@@ -728,7 +803,7 @@ var PeprControllerStore = class {
728
803
  watcher.start().catch((e) => logger_default.error(e, "Error starting Pepr store watch"));
729
804
  };
730
805
  #migrateAndSetupWatch = async (store) => {
731
- logger_default.debug(store, "Pepr Store migration");
806
+ logger_default.debug(redactedStore(store), "Pepr Store migration");
732
807
  const data = store.data || {};
733
808
  const migrateCache = {};
734
809
  const flushCache = async () => {
@@ -738,7 +813,9 @@ var PeprControllerStore = class {
738
813
  delete migrateCache[idx];
739
814
  }
740
815
  try {
741
- await (0, import_kubernetes_fluent_client2.K8s)(PeprStore, { namespace, name: this.#name }).Patch(payload);
816
+ if (payload.length > 0) {
817
+ await (0, import_kubernetes_fluent_client2.K8s)(PeprStore, { namespace, name: this.#name }).Patch(payload);
818
+ }
742
819
  } catch (err) {
743
820
  logger_default.error(err, "Pepr store update failure");
744
821
  if (err.status === 422) {
@@ -774,7 +851,7 @@ var PeprControllerStore = class {
774
851
  for (const name of Object.keys(this.#stores)) {
775
852
  const offset = `${name}-`.length;
776
853
  for (const key of Object.keys(data)) {
777
- if ((0, import_ramda3.startsWith)(name, key) && !(0, import_ramda3.startsWith)(`${name}-v2`, key)) {
854
+ if ((0, import_ramda4.startsWith)(name, key) && !(0, import_ramda4.startsWith)(`${name}-v2`, key)) {
778
855
  fillCache(name, "remove", [key.slice(offset)], data[key]);
779
856
  fillCache(name, "add", [key.slice(offset)], data[key]);
780
857
  }
@@ -784,14 +861,14 @@ var PeprControllerStore = class {
784
861
  this.#setupWatch();
785
862
  };
786
863
  #receive = (store) => {
787
- logger_default.debug(store, "Pepr Store update");
864
+ logger_default.debug(redactedStore(store), "Pepr Store update");
788
865
  const debounced = () => {
789
866
  const data = store.data || {};
790
867
  for (const name of Object.keys(this.#stores)) {
791
868
  const offset = `${name}-`.length;
792
869
  const filtered = {};
793
870
  for (const key of Object.keys(data)) {
794
- if ((0, import_ramda3.startsWith)(name, key)) {
871
+ if ((0, import_ramda4.startsWith)(name, key)) {
795
872
  filtered[key.slice(offset)] = data[key];
796
873
  }
797
874
  }
@@ -835,7 +912,9 @@ var PeprControllerStore = class {
835
912
  delete sendCache[idx];
836
913
  }
837
914
  try {
838
- await (0, import_kubernetes_fluent_client2.K8s)(PeprStore, { namespace, name: this.#name }).Patch(payload);
915
+ if (payload.length > 0) {
916
+ await (0, import_kubernetes_fluent_client2.K8s)(PeprStore, { namespace, name: this.#name }).Patch(payload);
917
+ }
839
918
  } catch (err) {
840
919
  logger_default.error(err, "Pepr store update failure");
841
920
  if (err.status === 422) {
@@ -852,7 +931,7 @@ var PeprControllerStore = class {
852
931
  };
853
932
  setInterval(() => {
854
933
  if (Object.keys(sendCache).length > 0) {
855
- logger_default.debug(sendCache, "Sending updates to Pepr store");
934
+ logger_default.debug(redactedPatch(sendCache), "Sending updates to Pepr store");
856
935
  void flushCache();
857
936
  }
858
937
  }, debounceBackoff);
@@ -878,8 +957,38 @@ var PeprControllerStore = class {
878
957
  }
879
958
  };
880
959
  };
960
+ function redactedStore(store) {
961
+ const redacted = process.env.PEPR_STORE_REDACT_VALUES === "true";
962
+ return {
963
+ ...store,
964
+ data: Object.keys(store.data).reduce((acc, key) => {
965
+ acc[key] = redacted ? redactedValue : store.data[key];
966
+ return acc;
967
+ }, {})
968
+ };
969
+ }
970
+ function redactedPatch(patch = {}) {
971
+ const redacted = process.env.PEPR_STORE_REDACT_VALUES === "true";
972
+ if (!redacted) {
973
+ return patch;
974
+ }
975
+ const redactedCache = {};
976
+ Object.keys(patch).forEach((key) => {
977
+ const operation = patch[key];
978
+ const redactedKey = key.includes(":") ? key.substring(0, key.lastIndexOf(":")) + ":**redacted**" : key;
979
+ const redactedOperation = {
980
+ ...operation,
981
+ ..."value" in operation ? { value: "**redacted**" } : {}
982
+ };
983
+ redactedCache[redactedKey] = redactedOperation;
984
+ });
985
+ return redactedCache;
986
+ }
881
987
 
882
988
  // src/lib/controller/index.ts
989
+ if (!process.env.PEPR_NODE_WARNINGS) {
990
+ process.removeAllListeners("warning");
991
+ }
883
992
  var Controller = class _Controller {
884
993
  // Track whether the server is running
885
994
  #running = false;
@@ -939,7 +1048,7 @@ var Controller = class _Controller {
939
1048
  });
940
1049
  server.on("error", (e) => {
941
1050
  if (e.code === "EADDRINUSE") {
942
- logger_default.warn(
1051
+ logger_default.info(
943
1052
  `Address in use, retrying in 2 seconds. If this persists, ensure ${port} is not in use, e.g. "lsof -i :${port}"`
944
1053
  );
945
1054
  setTimeout(() => {
@@ -978,7 +1087,7 @@ var Controller = class _Controller {
978
1087
  const { token } = req.params;
979
1088
  if (token !== this.#token) {
980
1089
  const err = `Unauthorized: invalid token '${token.replace(/[^\w]/g, "_")}'`;
981
- logger_default.warn(err);
1090
+ logger_default.info(err);
982
1091
  res.status(401).send(err);
983
1092
  this.#metricsCollector.alert();
984
1093
  return;
@@ -1025,7 +1134,7 @@ var Controller = class _Controller {
1025
1134
  if (admissionKind === "Mutate") {
1026
1135
  response = await mutateProcessor(this.#config, this.#capabilities, request, reqMetadata);
1027
1136
  } else {
1028
- response = await validateProcessor(this.#capabilities, request, reqMetadata);
1137
+ response = await validateProcessor(this.#config, this.#capabilities, request, reqMetadata);
1029
1138
  }
1030
1139
  const responseList = Array.isArray(response) ? response : [response];
1031
1140
  responseList.map((res2) => {
@@ -1086,7 +1195,7 @@ var Controller = class _Controller {
1086
1195
  status: res.statusCode,
1087
1196
  duration: `${elapsedTime} ms`
1088
1197
  };
1089
- res.statusCode >= 300 ? logger_default.warn(message) : logger_default.info(message);
1198
+ logger_default.info(message);
1090
1199
  });
1091
1200
  next();
1092
1201
  }
@@ -1107,8 +1216,8 @@ var Controller = class _Controller {
1107
1216
  };
1108
1217
 
1109
1218
  // src/lib/watch-processor.ts
1110
- var import_kubernetes_fluent_client5 = require("kubernetes-fluent-client");
1111
- var import_types2 = require("kubernetes-fluent-client/dist/fluent/types");
1219
+ var import_kubernetes_fluent_client6 = require("kubernetes-fluent-client");
1220
+ var import_types6 = require("kubernetes-fluent-client/dist/fluent/types");
1112
1221
 
1113
1222
  // src/lib/helpers.ts
1114
1223
  var import_kubernetes_fluent_client4 = require("kubernetes-fluent-client");
@@ -1179,59 +1288,51 @@ function sanitizeResourceName(name) {
1179
1288
  }
1180
1289
 
1181
1290
  // src/lib/helpers.ts
1182
- function checkOverlap(bindingFilters, objectFilters) {
1183
- if (Object.keys(bindingFilters).length === 0) {
1184
- return true;
1185
- }
1186
- let matchCount = 0;
1187
- for (const key in bindingFilters) {
1188
- if (Object.prototype.hasOwnProperty.call(objectFilters, key)) {
1189
- const val1 = bindingFilters[key];
1190
- const val2 = objectFilters[key];
1191
- if (val1 === "" && key in objectFilters) {
1192
- matchCount++;
1193
- } else if (val1 !== "" && val1 === val2) {
1194
- matchCount++;
1195
- }
1196
- }
1197
- }
1198
- return matchCount === Object.keys(bindingFilters).length;
1291
+ function filterNoMatchReason(binding, obj, capabilityNamespaces, ignoredNamespaces) {
1292
+ const prefix = "Ignoring Watch Callback:";
1293
+ return mismatchedDeletionTimestamp(binding, obj) ? `${prefix} Binding defines deletionTimestamp but Object does not carry it.` : mismatchedName(binding, obj) ? `${prefix} Binding defines name '${definedName(binding)}' but Object carries '${carriedName(obj)}'.` : misboundNamespace(binding) ? `${prefix} Cannot use namespace filter on a namespace object.` : mismatchedLabels(binding, obj) ? `${prefix} Binding defines labels '${JSON.stringify(definedLabels(binding))}' but Object carries '${JSON.stringify(carriedLabels(obj))}'.` : mismatchedAnnotations(binding, obj) ? `${prefix} Binding defines annotations '${JSON.stringify(definedAnnotations(binding))}' but Object carries '${JSON.stringify(carriedAnnotations(obj))}'.` : uncarryableNamespace(capabilityNamespaces, obj) ? `${prefix} Object carries namespace '${carriedNamespace(obj)}' but namespaces allowed by Capability are '${JSON.stringify(capabilityNamespaces)}'.` : unbindableNamespaces(capabilityNamespaces, binding) ? `${prefix} Binding defines namespaces ${JSON.stringify(definedNamespaces(binding))} but namespaces allowed by Capability are '${JSON.stringify(capabilityNamespaces)}'.` : mismatchedNamespace(binding, obj) ? `${prefix} Binding defines namespaces '${JSON.stringify(definedNamespaces(binding))}' but Object carries '${carriedNamespace(obj)}'.` : mismatchedNamespaceRegex(binding, obj) ? `${prefix} Binding defines namespace regexes '${JSON.stringify(definedNamespaceRegexes(binding))}' but Object carries '${carriedNamespace(obj)}'.` : mismatchedNameRegex(binding, obj) ? `${prefix} Binding defines name regex '${definedNameRegex(binding)}' but Object carries '${carriedName(obj)}'.` : carriesIgnoredNamespace(ignoredNamespaces, obj) ? `${prefix} Object carries namespace '${carriedNamespace(obj)}' but ignored namespaces include '${JSON.stringify(ignoredNamespaces)}'.` : "";
1199
1294
  }
1200
- function filterNoMatchReason(binding, obj, capabilityNamespaces) {
1201
- if (binding.filters?.deletionTimestamp && !obj.metadata?.deletionTimestamp) {
1202
- return `Ignoring Watch Callback: Object does not have a deletion timestamp.`;
1203
- }
1204
- if (binding.kind && binding.kind.kind === "Namespace" && binding.filters && binding.filters.namespaces.length !== 0) {
1205
- return `Ignoring Watch Callback: Cannot use a namespace filter in a namespace object.`;
1206
- }
1207
- if (typeof obj === "object" && obj !== null && "metadata" in obj && obj.metadata !== void 0 && binding.filters) {
1208
- if (obj.metadata.labels && !checkOverlap(binding.filters.labels, obj.metadata.labels)) {
1209
- return `Ignoring Watch Callback: No overlap between binding and object labels. Binding labels ${JSON.stringify(
1210
- binding.filters.labels
1211
- )}, Object Labels ${JSON.stringify(obj.metadata.labels)}.`;
1212
- }
1213
- if (obj.metadata.annotations && !checkOverlap(binding.filters.annotations, obj.metadata.annotations)) {
1214
- return `Ignoring Watch Callback: No overlap between binding and object annotations. Binding annotations ${JSON.stringify(
1215
- binding.filters.annotations
1216
- )}, Object annotations ${JSON.stringify(obj.metadata.annotations)}.`;
1217
- }
1295
+
1296
+ // src/lib/finalizer.ts
1297
+ var import_kubernetes_fluent_client5 = require("kubernetes-fluent-client");
1298
+ function addFinalizer(request) {
1299
+ if (request.Request.operation === "DELETE" /* DELETE */) {
1300
+ return;
1218
1301
  }
1219
- if (Array.isArray(capabilityNamespaces) && capabilityNamespaces.length > 0 && obj.metadata && obj.metadata.namespace && !capabilityNamespaces.includes(obj.metadata.namespace)) {
1220
- return `Ignoring Watch Callback: Object is not in the capability namespace. Capability namespaces: ${capabilityNamespaces.join(
1221
- ", "
1222
- )}, Object namespace: ${obj.metadata.namespace}.`;
1302
+ if (request.Request.operation === "UPDATE" /* UPDATE */ && request.Raw.metadata?.deletionTimestamp) {
1303
+ return;
1223
1304
  }
1224
- if (Array.isArray(capabilityNamespaces) && capabilityNamespaces.length > 0 && binding.filters && Array.isArray(binding.filters.namespaces) && binding.filters.namespaces.length > 0 && !binding.filters.namespaces.every((ns) => capabilityNamespaces.includes(ns))) {
1225
- return `Ignoring Watch Callback: Binding namespace is not part of capability namespaces. Capability namespaces: ${capabilityNamespaces.join(
1226
- ", "
1227
- )}, Binding namespaces: ${binding.filters.namespaces.join(", ")}.`;
1305
+ const peprFinal = "pepr.dev/finalizer";
1306
+ const finalizers = request.Raw.metadata?.finalizers || [];
1307
+ if (!finalizers.includes(peprFinal)) {
1308
+ finalizers.push(peprFinal);
1228
1309
  }
1229
- if (binding.filters && Array.isArray(binding.filters.namespaces) && binding.filters.namespaces.length > 0 && obj.metadata && obj.metadata.namespace && !binding.filters.namespaces.includes(obj.metadata.namespace)) {
1230
- return `Ignoring Watch Callback: Binding namespace and object namespace are not the same. Binding namespaces: ${binding.filters.namespaces.join(
1231
- ", "
1232
- )}, Object namespace: ${obj.metadata.namespace}.`;
1310
+ request.Merge({ metadata: { finalizers } });
1311
+ }
1312
+ async function removeFinalizer(binding, obj) {
1313
+ const peprFinal = "pepr.dev/finalizer";
1314
+ const meta = obj.metadata;
1315
+ const resource = `${meta.namespace || "ClusterScoped"}/${meta.name}`;
1316
+ logger_default.debug({ obj }, `Removing finalizer '${peprFinal}' from '${resource}'`);
1317
+ const { model, kind: kind4 } = binding;
1318
+ try {
1319
+ (0, import_kubernetes_fluent_client5.RegisterKind)(model, kind4);
1320
+ } catch (e) {
1321
+ const expected = e.message === `GVK ${model.name} already registered`;
1322
+ if (!expected) {
1323
+ logger_default.error({ model, kind: kind4, error: e }, `Error registering "${kind4}" during finalization.`);
1324
+ return;
1325
+ }
1233
1326
  }
1234
- return "";
1327
+ const finalizers = meta.finalizers?.filter((f) => f !== peprFinal) || [];
1328
+ obj = await (0, import_kubernetes_fluent_client5.K8s)(model, meta).Patch([
1329
+ {
1330
+ op: "replace",
1331
+ path: `/metadata/finalizers`,
1332
+ value: finalizers
1333
+ }
1334
+ ]);
1335
+ logger_default.debug({ obj }, `Removed finalizer '${peprFinal}' from '${resource}'`);
1235
1336
  }
1236
1337
 
1237
1338
  // src/lib/queue.ts
@@ -1354,26 +1455,37 @@ var watchCfg = {
1354
1455
  relistIntervalSec: process.env.PEPR_RELIST_INTERVAL_SECONDS ? parseInt(process.env.PEPR_RELIST_INTERVAL_SECONDS, 10) : 600
1355
1456
  };
1356
1457
  var eventToPhaseMap = {
1357
- ["CREATE" /* Create */]: [import_types2.WatchPhase.Added],
1358
- ["UPDATE" /* Update */]: [import_types2.WatchPhase.Modified],
1359
- ["CREATEORUPDATE" /* CreateOrUpdate */]: [import_types2.WatchPhase.Added, import_types2.WatchPhase.Modified],
1360
- ["DELETE" /* Delete */]: [import_types2.WatchPhase.Deleted],
1361
- ["*" /* Any */]: [import_types2.WatchPhase.Added, import_types2.WatchPhase.Modified, import_types2.WatchPhase.Deleted]
1458
+ ["CREATE" /* Create */]: [import_types6.WatchPhase.Added],
1459
+ ["UPDATE" /* Update */]: [import_types6.WatchPhase.Modified],
1460
+ ["CREATEORUPDATE" /* CreateOrUpdate */]: [import_types6.WatchPhase.Added, import_types6.WatchPhase.Modified],
1461
+ ["DELETE" /* Delete */]: [import_types6.WatchPhase.Deleted],
1462
+ ["*" /* Any */]: [import_types6.WatchPhase.Added, import_types6.WatchPhase.Modified, import_types6.WatchPhase.Deleted]
1362
1463
  };
1363
- function setupWatch(capabilities) {
1464
+ function setupWatch(capabilities, ignoredNamespaces) {
1364
1465
  capabilities.map(
1365
- (capability) => capability.bindings.filter((binding) => binding.isWatch).forEach((bindingElement) => runBinding(bindingElement, capability.namespaces))
1466
+ (capability) => capability.bindings.filter((binding) => binding.isWatch).forEach((bindingElement) => runBinding(bindingElement, capability.namespaces, ignoredNamespaces))
1366
1467
  );
1367
1468
  }
1368
- async function runBinding(binding, capabilityNamespaces) {
1469
+ async function runBinding(binding, capabilityNamespaces, ignoredNamespaces) {
1369
1470
  const phaseMatch = eventToPhaseMap[binding.event] || eventToPhaseMap["*" /* Any */];
1370
1471
  logger_default.debug({ watchCfg }, "Effective WatchConfig");
1371
1472
  const watchCallback = async (obj, phase) => {
1372
1473
  if (phaseMatch.includes(phase)) {
1373
1474
  try {
1374
- const filterMatch = filterNoMatchReason(binding, obj, capabilityNamespaces);
1475
+ const filterMatch = filterNoMatchReason(binding, obj, capabilityNamespaces, ignoredNamespaces);
1375
1476
  if (filterMatch === "") {
1376
- await binding.watchCallback?.(obj, phase);
1477
+ if (binding.isFinalize) {
1478
+ if (!obj.metadata?.deletionTimestamp) {
1479
+ return;
1480
+ }
1481
+ try {
1482
+ await binding.finalizeCallback?.(obj);
1483
+ } finally {
1484
+ await removeFinalizer(binding, obj);
1485
+ }
1486
+ } else {
1487
+ await binding.watchCallback?.(obj, phase);
1488
+ }
1377
1489
  } else {
1378
1490
  logger_default.debug(filterMatch);
1379
1491
  }
@@ -1382,7 +1494,7 @@ async function runBinding(binding, capabilityNamespaces) {
1382
1494
  }
1383
1495
  }
1384
1496
  };
1385
- const watcher = (0, import_kubernetes_fluent_client5.K8s)(binding.model, binding.filters).Watch(async (obj, phase) => {
1497
+ const watcher = (0, import_kubernetes_fluent_client6.K8s)(binding.model, binding.filters).Watch(async (obj, phase) => {
1386
1498
  logger_default.debug(obj, `Watch event ${phase} received`);
1387
1499
  if (binding.isQueue) {
1388
1500
  const queue = getOrCreateQueue(obj);
@@ -1391,30 +1503,30 @@ async function runBinding(binding, capabilityNamespaces) {
1391
1503
  await watchCallback(obj, phase);
1392
1504
  }
1393
1505
  }, watchCfg);
1394
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.GIVE_UP, (err) => {
1506
+ watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.GIVE_UP, (err) => {
1395
1507
  logger_default.error(err, "Watch failed after 5 attempts, giving up");
1396
1508
  process.exit(1);
1397
1509
  });
1398
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.CONNECT, (url) => logEvent(import_kubernetes_fluent_client5.WatchEvent.CONNECT, url));
1399
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.DATA_ERROR, (err) => logEvent(import_kubernetes_fluent_client5.WatchEvent.DATA_ERROR, err.message));
1510
+ watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.CONNECT, (url) => logEvent(import_kubernetes_fluent_client6.WatchEvent.CONNECT, url));
1511
+ watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.DATA_ERROR, (err) => logEvent(import_kubernetes_fluent_client6.WatchEvent.DATA_ERROR, err.message));
1400
1512
  watcher.events.on(
1401
- import_kubernetes_fluent_client5.WatchEvent.RECONNECT,
1402
- (retryCount) => logEvent(import_kubernetes_fluent_client5.WatchEvent.RECONNECT, `Reconnecting after ${retryCount} attempt${retryCount === 1 ? "" : "s"}`)
1513
+ import_kubernetes_fluent_client6.WatchEvent.RECONNECT,
1514
+ (retryCount) => logEvent(import_kubernetes_fluent_client6.WatchEvent.RECONNECT, `Reconnecting after ${retryCount} attempt${retryCount === 1 ? "" : "s"}`)
1403
1515
  );
1404
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.RECONNECT_PENDING, () => logEvent(import_kubernetes_fluent_client5.WatchEvent.RECONNECT_PENDING));
1405
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.GIVE_UP, (err) => logEvent(import_kubernetes_fluent_client5.WatchEvent.GIVE_UP, err.message));
1406
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.ABORT, (err) => logEvent(import_kubernetes_fluent_client5.WatchEvent.ABORT, err.message));
1407
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.OLD_RESOURCE_VERSION, (err) => logEvent(import_kubernetes_fluent_client5.WatchEvent.OLD_RESOURCE_VERSION, err));
1408
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.NETWORK_ERROR, (err) => logEvent(import_kubernetes_fluent_client5.WatchEvent.NETWORK_ERROR, err.message));
1409
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.LIST_ERROR, (err) => logEvent(import_kubernetes_fluent_client5.WatchEvent.LIST_ERROR, err.message));
1410
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.LIST, (list) => logEvent(import_kubernetes_fluent_client5.WatchEvent.LIST, JSON.stringify(list, void 0, 2)));
1411
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.CACHE_MISS, (windowName) => {
1516
+ watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.RECONNECT_PENDING, () => logEvent(import_kubernetes_fluent_client6.WatchEvent.RECONNECT_PENDING));
1517
+ watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.GIVE_UP, (err) => logEvent(import_kubernetes_fluent_client6.WatchEvent.GIVE_UP, err.message));
1518
+ watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.ABORT, (err) => logEvent(import_kubernetes_fluent_client6.WatchEvent.ABORT, err.message));
1519
+ watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.OLD_RESOURCE_VERSION, (err) => logEvent(import_kubernetes_fluent_client6.WatchEvent.OLD_RESOURCE_VERSION, err));
1520
+ watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.NETWORK_ERROR, (err) => logEvent(import_kubernetes_fluent_client6.WatchEvent.NETWORK_ERROR, err.message));
1521
+ watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.LIST_ERROR, (err) => logEvent(import_kubernetes_fluent_client6.WatchEvent.LIST_ERROR, err.message));
1522
+ watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.LIST, (list) => logEvent(import_kubernetes_fluent_client6.WatchEvent.LIST, JSON.stringify(list, void 0, 2)));
1523
+ watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.CACHE_MISS, (windowName) => {
1412
1524
  metricsCollector.incCacheMiss(windowName);
1413
1525
  });
1414
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.INIT_CACHE_MISS, (windowName) => {
1526
+ watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.INIT_CACHE_MISS, (windowName) => {
1415
1527
  metricsCollector.initCacheMissWindow(windowName);
1416
1528
  });
1417
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.INC_RESYNC_FAILURE_COUNT, (retryCount) => {
1529
+ watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.INC_RESYNC_FAILURE_COUNT, (retryCount) => {
1418
1530
  metricsCollector.incRetryCount(retryCount);
1419
1531
  });
1420
1532
  try {
@@ -1447,7 +1559,7 @@ var PeprModule = class {
1447
1559
  * @param opts Options for the Pepr runtime
1448
1560
  */
1449
1561
  constructor({ description, pepr }, capabilities = [], opts = {}) {
1450
- const config = (0, import_ramda4.clone)(pepr);
1562
+ const config = (0, import_ramda5.clone)(pepr);
1451
1563
  config.description = description;
1452
1564
  ValidateError(config.onError);
1453
1565
  if (isBuildMode()) {
@@ -1470,7 +1582,7 @@ var PeprModule = class {
1470
1582
  this.#controller = new Controller(config, capabilities, opts.beforeHook, opts.afterHook, () => {
1471
1583
  if (isWatchMode() || isDevMode()) {
1472
1584
  try {
1473
- setupWatch(capabilities);
1585
+ setupWatch(capabilities, pepr?.alwaysIgnore?.namespaces);
1474
1586
  } catch (e) {
1475
1587
  logger_default.error(e, "Error setting up watch");
1476
1588
  process.exit(1);
@@ -1494,7 +1606,7 @@ var PeprModule = class {
1494
1606
  };
1495
1607
 
1496
1608
  // src/lib/storage.ts
1497
- var import_ramda5 = require("ramda");
1609
+ var import_ramda6 = require("ramda");
1498
1610
  var import_json_pointer = __toESM(require("json-pointer"));
1499
1611
  var MAX_WAIT_TIME = 15e3;
1500
1612
  var STORE_VERSION_PREFIX = "v2";
@@ -1514,11 +1626,10 @@ var Storage = class {
1514
1626
  this.#send = send;
1515
1627
  };
1516
1628
  receive = (data) => {
1517
- logger_default.debug(data, `Pepr store data received`);
1518
1629
  this.#store = data || {};
1519
1630
  this.#onReady();
1520
1631
  for (const idx in this.#subscribers) {
1521
- this.#subscribers[idx]((0, import_ramda5.clone)(this.#store));
1632
+ this.#subscribers[idx]((0, import_ramda6.clone)(this.#store));
1522
1633
  }
1523
1634
  };
1524
1635
  getItem = (key) => {
@@ -1529,7 +1640,7 @@ var Storage = class {
1529
1640
  return null;
1530
1641
  };
1531
1642
  clear = () => {
1532
- this.#dispatchUpdate(
1643
+ Object.keys(this.#store).length > 0 && this.#dispatchUpdate(
1533
1644
  "remove",
1534
1645
  Object.keys(this.#store).map((key) => import_json_pointer.default.escape(key))
1535
1646
  );
@@ -1602,7 +1713,7 @@ var Storage = class {
1602
1713
  };
1603
1714
  #onReady = () => {
1604
1715
  for (const handler of this.#readyHandlers) {
1605
- handler((0, import_ramda5.clone)(this.#store));
1716
+ handler((0, import_ramda6.clone)(this.#store));
1606
1717
  }
1607
1718
  this.#onReady = () => {
1608
1719
  };
@@ -1782,6 +1893,9 @@ var Capability = class {
1782
1893
  });
1783
1894
  }
1784
1895
  };
1896
+ getScheduleStore() {
1897
+ return this.#scheduleStore;
1898
+ }
1785
1899
  /**
1786
1900
  * Store is a key-value data store that can be used to persist data that should be shared
1787
1901
  * between requests. Each capability has its own store, and the data is persisted in Kubernetes
@@ -1876,7 +1990,7 @@ var Capability = class {
1876
1990
  * @returns
1877
1991
  */
1878
1992
  When = (model, kind4) => {
1879
- const matchedKind = (0, import_kubernetes_fluent_client6.modelToGroupVersionKind)(model.name);
1993
+ const matchedKind = (0, import_kubernetes_fluent_client7.modelToGroupVersionKind)(model.name);
1880
1994
  if (!matchedKind && !kind4) {
1881
1995
  throw new Error(`Kind not specified for ${model.name}`);
1882
1996
  }
@@ -1888,6 +2002,8 @@ var Capability = class {
1888
2002
  filters: {
1889
2003
  name: "",
1890
2004
  namespaces: [],
2005
+ regexNamespaces: [],
2006
+ regexName: "",
1891
2007
  labels: {},
1892
2008
  annotations: {},
1893
2009
  deletionTimestamp: false
@@ -1895,10 +2011,10 @@ var Capability = class {
1895
2011
  };
1896
2012
  const bindings = this.#bindings;
1897
2013
  const prefix = `${this.#name}: ${model.name}`;
1898
- const commonChain = { WithLabel, WithAnnotation, WithDeletionTimestamp, Mutate, Validate, Watch, Reconcile };
2014
+ const commonChain = { WithLabel, WithAnnotation, WithDeletionTimestamp, Mutate, Validate, Watch, Reconcile, Alias };
1899
2015
  const isNotEmpty = (value) => Object.keys(value).length > 0;
1900
2016
  const log = (message, cbString) => {
1901
- const filteredObj = (0, import_ramda6.pickBy)(isNotEmpty, binding.filters);
2017
+ const filteredObj = (0, import_ramda7.pickBy)(isNotEmpty, binding.filters);
1902
2018
  logger_default.info(`${message} configured for ${binding.event}`, prefix);
1903
2019
  logger_default.info(filteredObj, prefix);
1904
2020
  logger_default.debug(cbString, prefix);
@@ -1906,10 +2022,14 @@ var Capability = class {
1906
2022
  function Validate(validateCallback) {
1907
2023
  if (registerAdmission) {
1908
2024
  log("Validate Action", validateCallback.toString());
2025
+ const aliasLogger = logger_default.child({ alias: binding.alias || "no alias provided" });
1909
2026
  bindings.push({
1910
2027
  ...binding,
1911
2028
  isValidate: true,
1912
- validateCallback
2029
+ validateCallback: async (req, logger = aliasLogger) => {
2030
+ logger_default.info(`Executing validate action with alias: ${binding.alias || "no alias provided"}`);
2031
+ return await validateCallback(req, logger);
2032
+ }
1913
2033
  });
1914
2034
  }
1915
2035
  return { Watch, Reconcile };
@@ -1917,10 +2037,14 @@ var Capability = class {
1917
2037
  function Mutate(mutateCallback) {
1918
2038
  if (registerAdmission) {
1919
2039
  log("Mutate Action", mutateCallback.toString());
2040
+ const aliasLogger = logger_default.child({ alias: binding.alias || "no alias provided" });
1920
2041
  bindings.push({
1921
2042
  ...binding,
1922
2043
  isMutate: true,
1923
- mutateCallback
2044
+ mutateCallback: async (req, logger = aliasLogger) => {
2045
+ logger_default.info(`Executing mutation action with alias: ${binding.alias || "no alias provided"}`);
2046
+ await mutateCallback(req, logger);
2047
+ }
1924
2048
  });
1925
2049
  }
1926
2050
  return { Watch, Validate, Reconcile };
@@ -1928,34 +2052,81 @@ var Capability = class {
1928
2052
  function Watch(watchCallback) {
1929
2053
  if (registerWatch) {
1930
2054
  log("Watch Action", watchCallback.toString());
2055
+ const aliasLogger = logger_default.child({ alias: binding.alias || "no alias provided" });
1931
2056
  bindings.push({
1932
2057
  ...binding,
1933
2058
  isWatch: true,
1934
- watchCallback
2059
+ watchCallback: async (update, phase, logger = aliasLogger) => {
2060
+ logger_default.info(`Executing watch action with alias: ${binding.alias || "no alias provided"}`);
2061
+ await watchCallback(update, phase, logger);
2062
+ }
1935
2063
  });
1936
2064
  }
2065
+ return { Finalize };
1937
2066
  }
1938
- function Reconcile(watchCallback) {
2067
+ function Reconcile(reconcileCallback) {
1939
2068
  if (registerWatch) {
1940
- log("Reconcile Action", watchCallback.toString());
2069
+ log("Reconcile Action", reconcileCallback.toString());
2070
+ const aliasLogger = logger_default.child({ alias: binding.alias || "no alias provided" });
1941
2071
  bindings.push({
1942
2072
  ...binding,
1943
2073
  isWatch: true,
1944
2074
  isQueue: true,
1945
- watchCallback
2075
+ watchCallback: async (update, phase, logger = aliasLogger) => {
2076
+ logger_default.info(`Executing reconcile action with alias: ${binding.alias || "no alias provided"}`);
2077
+ await reconcileCallback(update, phase, logger);
2078
+ }
1946
2079
  });
1947
2080
  }
2081
+ return { Finalize };
2082
+ }
2083
+ function Finalize(finalizeCallback) {
2084
+ log("Finalize Action", finalizeCallback.toString());
2085
+ const aliasLogger = logger_default.child({ alias: binding.alias || "no alias provided" });
2086
+ if (registerAdmission) {
2087
+ const mutateBinding = {
2088
+ ...binding,
2089
+ isMutate: true,
2090
+ isFinalize: true,
2091
+ event: "*" /* Any */,
2092
+ mutateCallback: addFinalizer
2093
+ };
2094
+ bindings.push(mutateBinding);
2095
+ }
2096
+ if (registerWatch) {
2097
+ const watchBinding = {
2098
+ ...binding,
2099
+ isWatch: true,
2100
+ isFinalize: true,
2101
+ event: "UPDATE" /* Update */,
2102
+ finalizeCallback: async (update, logger = aliasLogger) => {
2103
+ logger_default.info(`Executing finalize action with alias: ${binding.alias || "no alias provided"}`);
2104
+ await finalizeCallback(update, logger);
2105
+ }
2106
+ };
2107
+ bindings.push(watchBinding);
2108
+ }
1948
2109
  }
1949
2110
  function InNamespace(...namespaces) {
1950
2111
  logger_default.debug(`Add namespaces filter ${namespaces}`, prefix);
1951
2112
  binding.filters.namespaces.push(...namespaces);
1952
- return { ...commonChain, WithName };
2113
+ return { ...commonChain, WithName, WithNameRegex };
2114
+ }
2115
+ function InNamespaceRegex(...namespaces) {
2116
+ logger_default.debug(`Add regex namespaces filter ${namespaces}`, prefix);
2117
+ binding.filters.regexNamespaces.push(...namespaces.map((regex) => regex.source));
2118
+ return { ...commonChain, WithName, WithNameRegex };
1953
2119
  }
1954
2120
  function WithDeletionTimestamp() {
1955
2121
  logger_default.debug("Add deletionTimestamp filter");
1956
2122
  binding.filters.deletionTimestamp = true;
1957
2123
  return commonChain;
1958
2124
  }
2125
+ function WithNameRegex(regexName) {
2126
+ logger_default.debug(`Add regex name filter ${regexName}`, prefix);
2127
+ binding.filters.regexName = regexName.source;
2128
+ return commonChain;
2129
+ }
1959
2130
  function WithName(name) {
1960
2131
  logger_default.debug(`Add name filter ${name}`, prefix);
1961
2132
  binding.filters.name = name;
@@ -1971,13 +2142,21 @@ var Capability = class {
1971
2142
  binding.filters.annotations[key] = value;
1972
2143
  return commonChain;
1973
2144
  }
2145
+ function Alias(alias) {
2146
+ logger_default.debug(`Adding prefix alias ${alias}`, prefix);
2147
+ binding.alias = alias;
2148
+ return commonChain;
2149
+ }
1974
2150
  function bindEvent(event) {
1975
2151
  binding.event = event;
1976
2152
  return {
1977
2153
  ...commonChain,
1978
2154
  InNamespace,
2155
+ InNamespaceRegex,
1979
2156
  WithName,
1980
- WithDeletionTimestamp
2157
+ WithNameRegex,
2158
+ WithDeletionTimestamp,
2159
+ Alias
1981
2160
  };
1982
2161
  }
1983
2162
  return {