pepr 0.31.1 → 0.32.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/package.json +6 -6
  2. package/src/lib/assets/helm.ts +18 -0
  3. package/src/lib/assets/index.ts +3 -1
  4. package/src/lib/assets/yaml.ts +35 -0
  5. package/src/lib/controller/index.ts +3 -3
  6. package/src/lib/controller/store.ts +7 -3
  7. package/src/lib/mutate-processor.ts +17 -6
  8. package/src/runtime/controller.ts +2 -2
  9. package/dist/cli.d.ts +0 -3
  10. package/dist/cli.d.ts.map +0 -1
  11. package/dist/cli.js +0 -2786
  12. package/dist/controller.js +0 -164
  13. package/dist/lib/assets/deploy.d.ts +0 -3
  14. package/dist/lib/assets/deploy.d.ts.map +0 -1
  15. package/dist/lib/assets/destroy.d.ts +0 -2
  16. package/dist/lib/assets/destroy.d.ts.map +0 -1
  17. package/dist/lib/assets/helm.d.ts +0 -5
  18. package/dist/lib/assets/helm.d.ts.map +0 -1
  19. package/dist/lib/assets/index.d.ts +0 -24
  20. package/dist/lib/assets/index.d.ts.map +0 -1
  21. package/dist/lib/assets/loader.d.ts +0 -8
  22. package/dist/lib/assets/loader.d.ts.map +0 -1
  23. package/dist/lib/assets/networking.d.ts +0 -7
  24. package/dist/lib/assets/networking.d.ts.map +0 -1
  25. package/dist/lib/assets/pods.d.ts +0 -126
  26. package/dist/lib/assets/pods.d.ts.map +0 -1
  27. package/dist/lib/assets/rbac.d.ts +0 -14
  28. package/dist/lib/assets/rbac.d.ts.map +0 -1
  29. package/dist/lib/assets/store.d.ts +0 -7
  30. package/dist/lib/assets/store.d.ts.map +0 -1
  31. package/dist/lib/assets/webhooks.d.ts +0 -6
  32. package/dist/lib/assets/webhooks.d.ts.map +0 -1
  33. package/dist/lib/assets/yaml.d.ts +0 -5
  34. package/dist/lib/assets/yaml.d.ts.map +0 -1
  35. package/dist/lib/capability.d.ts +0 -66
  36. package/dist/lib/capability.d.ts.map +0 -1
  37. package/dist/lib/controller/index.d.ts +0 -10
  38. package/dist/lib/controller/index.d.ts.map +0 -1
  39. package/dist/lib/controller/store.d.ts +0 -7
  40. package/dist/lib/controller/store.d.ts.map +0 -1
  41. package/dist/lib/errors.d.ts +0 -12
  42. package/dist/lib/errors.d.ts.map +0 -1
  43. package/dist/lib/filter.d.ts +0 -11
  44. package/dist/lib/filter.d.ts.map +0 -1
  45. package/dist/lib/helpers.d.ts +0 -34
  46. package/dist/lib/helpers.d.ts.map +0 -1
  47. package/dist/lib/included-files.d.ts +0 -2
  48. package/dist/lib/included-files.d.ts.map +0 -1
  49. package/dist/lib/k8s.d.ts +0 -132
  50. package/dist/lib/k8s.d.ts.map +0 -1
  51. package/dist/lib/logger.d.ts +0 -3
  52. package/dist/lib/logger.d.ts.map +0 -1
  53. package/dist/lib/metrics.d.ts +0 -39
  54. package/dist/lib/metrics.d.ts.map +0 -1
  55. package/dist/lib/module.d.ts +0 -62
  56. package/dist/lib/module.d.ts.map +0 -1
  57. package/dist/lib/mutate-processor.d.ts +0 -5
  58. package/dist/lib/mutate-processor.d.ts.map +0 -1
  59. package/dist/lib/mutate-request.d.ts +0 -79
  60. package/dist/lib/mutate-request.d.ts.map +0 -1
  61. package/dist/lib/queue.d.ts +0 -19
  62. package/dist/lib/queue.d.ts.map +0 -1
  63. package/dist/lib/schedule.d.ts +0 -76
  64. package/dist/lib/schedule.d.ts.map +0 -1
  65. package/dist/lib/storage.d.ts +0 -83
  66. package/dist/lib/storage.d.ts.map +0 -1
  67. package/dist/lib/tls.d.ts +0 -18
  68. package/dist/lib/tls.d.ts.map +0 -1
  69. package/dist/lib/types.d.ts +0 -192
  70. package/dist/lib/types.d.ts.map +0 -1
  71. package/dist/lib/utils.d.ts +0 -23
  72. package/dist/lib/utils.d.ts.map +0 -1
  73. package/dist/lib/validate-processor.d.ts +0 -4
  74. package/dist/lib/validate-processor.d.ts.map +0 -1
  75. package/dist/lib/validate-request.d.ts +0 -55
  76. package/dist/lib/validate-request.d.ts.map +0 -1
  77. package/dist/lib/watch-processor.d.ts +0 -10
  78. package/dist/lib/watch-processor.d.ts.map +0 -1
  79. package/dist/lib.d.ts +0 -11
  80. package/dist/lib.d.ts.map +0 -1
  81. package/dist/lib.js +0 -1794
  82. package/dist/lib.js.map +0 -7
  83. package/dist/runtime/controller.d.ts +0 -3
  84. package/dist/runtime/controller.d.ts.map +0 -1
  85. package/dist/sdk/sdk.d.ts +0 -38
  86. package/dist/sdk/sdk.d.ts.map +0 -1
package/dist/lib.js DELETED
@@ -1,1794 +0,0 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
-
30
- // src/lib.ts
31
- var lib_exports = {};
32
- __export(lib_exports, {
33
- Capability: () => Capability,
34
- K8s: () => import_kubernetes_fluent_client7.K8s,
35
- Log: () => logger_default,
36
- PeprModule: () => PeprModule,
37
- PeprMutateRequest: () => PeprMutateRequest,
38
- PeprUtils: () => utils_exports,
39
- PeprValidateRequest: () => PeprValidateRequest,
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,
46
- sdk: () => sdk_exports
47
- });
48
- module.exports = __toCommonJS(lib_exports);
49
- var import_kubernetes_fluent_client7 = require("kubernetes-fluent-client");
50
- var R = __toESM(require("ramda"));
51
-
52
- // src/lib/capability.ts
53
- var import_kubernetes_fluent_client6 = require("kubernetes-fluent-client");
54
- var import_ramda6 = require("ramda");
55
-
56
- // src/lib/logger.ts
57
- var import_pino = require("pino");
58
- var isPrettyLog = process.env.PEPR_PRETTY_LOGS === "true";
59
- var pretty = {
60
- target: "pino-pretty",
61
- options: {
62
- colorize: true
63
- }
64
- };
65
- var transport = isPrettyLog ? pretty : void 0;
66
- var pinoTimeFunction = process.env.PINO_TIME_STAMP === "iso" ? () => import_pino.stdTimeFunctions.isoTime() : () => import_pino.stdTimeFunctions.epochTime();
67
- var Log = (0, import_pino.pino)({
68
- transport,
69
- timestamp: pinoTimeFunction
70
- });
71
- if (process.env.LOG_LEVEL) {
72
- Log.level = process.env.LOG_LEVEL;
73
- }
74
- var logger_default = Log;
75
-
76
- // src/lib/module.ts
77
- var import_ramda4 = require("ramda");
78
-
79
- // src/lib/controller/index.ts
80
- var import_express = __toESM(require("express"));
81
- var import_fs = __toESM(require("fs"));
82
- var import_https = __toESM(require("https"));
83
-
84
- // src/lib/metrics.ts
85
- var import_perf_hooks = require("perf_hooks");
86
- var import_prom_client = __toESM(require("prom-client"));
87
- var loggingPrefix = "MetricsCollector";
88
- var MetricsCollector = class {
89
- #registry;
90
- #counters = /* @__PURE__ */ new Map();
91
- #summaries = /* @__PURE__ */ new Map();
92
- #prefix;
93
- #metricNames = {
94
- errors: "errors",
95
- alerts: "alerts",
96
- mutate: "Mutate",
97
- validate: "Validate"
98
- };
99
- /**
100
- * Creates a MetricsCollector instance with prefixed metrics.
101
- * @param [prefix='pepr'] - The prefix for the metric names.
102
- */
103
- constructor(prefix = "pepr") {
104
- this.#registry = new import_prom_client.Registry();
105
- this.#prefix = prefix;
106
- this.addCounter(this.#metricNames.errors, "Mutation/Validate errors encountered");
107
- this.addCounter(this.#metricNames.alerts, "Mutation/Validate bad api token received");
108
- this.addSummary(this.#metricNames.mutate, "Mutation operation summary");
109
- this.addSummary(this.#metricNames.validate, "Validation operation summary");
110
- }
111
- #getMetricName = (name) => `${this.#prefix}_${name}`;
112
- #addMetric = (collection, MetricType, name, help) => {
113
- if (collection.has(this.#getMetricName(name))) {
114
- logger_default.debug(`Metric for ${name} already exists`, loggingPrefix);
115
- return;
116
- }
117
- const metric = new MetricType({
118
- name: this.#getMetricName(name),
119
- help,
120
- registers: [this.#registry]
121
- });
122
- collection.set(this.#getMetricName(name), metric);
123
- };
124
- addCounter = (name, help) => {
125
- this.#addMetric(this.#counters, import_prom_client.default.Counter, name, help);
126
- };
127
- addSummary = (name, help) => {
128
- this.#addMetric(this.#summaries, import_prom_client.default.Summary, name, help);
129
- };
130
- incCounter = (name) => {
131
- this.#counters.get(this.#getMetricName(name))?.inc();
132
- };
133
- /**
134
- * Increments the error counter.
135
- */
136
- error = () => this.incCounter(this.#metricNames.errors);
137
- /**
138
- * Increments the alerts counter.
139
- */
140
- alert = () => this.incCounter(this.#metricNames.alerts);
141
- /**
142
- * Observes the duration since the provided start time and updates the summary.
143
- * @param startTime - The start time.
144
- * @param name - The metrics summary to increment.
145
- */
146
- observeEnd = (startTime, name = this.#metricNames.mutate) => {
147
- this.#summaries.get(this.#getMetricName(name))?.observe(import_perf_hooks.performance.now() - startTime);
148
- };
149
- /**
150
- * Fetches the current metrics from the registry.
151
- * @returns The metrics.
152
- */
153
- getMetrics = () => this.#registry.metrics();
154
- /**
155
- * Returns the current timestamp from performance.now() method. Useful for start timing an operation.
156
- * @returns The timestamp.
157
- */
158
- static observeStart() {
159
- return import_perf_hooks.performance.now();
160
- }
161
- };
162
-
163
- // src/lib/mutate-processor.ts
164
- var import_fast_json_patch = __toESM(require("fast-json-patch"));
165
-
166
- // src/lib/errors.ts
167
- var Errors = {
168
- audit: "audit",
169
- ignore: "ignore",
170
- reject: "reject"
171
- };
172
- var ErrorList = Object.values(Errors);
173
- function ValidateError(error = "") {
174
- if (!ErrorList.includes(error)) {
175
- throw new Error(`Invalid error: ${error}. Must be one of: ${ErrorList.join(", ")}`);
176
- }
177
- }
178
-
179
- // src/lib/k8s.ts
180
- var import_kubernetes_fluent_client = require("kubernetes-fluent-client");
181
- var PeprStore = class extends import_kubernetes_fluent_client.GenericKind {
182
- };
183
- var peprStoreGVK = {
184
- kind: "PeprStore",
185
- version: "v1",
186
- group: "pepr.dev"
187
- };
188
- (0, import_kubernetes_fluent_client.RegisterKind)(PeprStore, peprStoreGVK);
189
-
190
- // src/lib/filter.ts
191
- function shouldSkipRequest(binding, req, capabilityNamespaces) {
192
- const { group, kind: kind4, version } = binding.kind || {};
193
- const { namespaces, labels, annotations, name } = binding.filters || {};
194
- const operation = req.operation.toUpperCase();
195
- const uid = req.uid;
196
- const srcObject = operation === "DELETE" /* DELETE */ ? req.oldObject : req.object;
197
- const { metadata } = srcObject || {};
198
- const combinedNamespaces = [...namespaces, ...capabilityNamespaces];
199
- if (!binding.event.includes(operation) && !binding.event.includes("*" /* Any */)) {
200
- return true;
201
- }
202
- if (name && name !== req.name) {
203
- return true;
204
- }
205
- if (kind4 !== req.kind.kind) {
206
- return true;
207
- }
208
- if (group && group !== req.kind.group) {
209
- return true;
210
- }
211
- if (version && version !== req.kind.version) {
212
- return true;
213
- }
214
- if (combinedNamespaces.length && !combinedNamespaces.includes(req.namespace || "") || !namespaces.includes(req.namespace || "") && capabilityNamespaces.length !== 0 && namespaces.length !== 0) {
215
- let type = "";
216
- let label = "";
217
- if (binding.isMutate) {
218
- type = "Mutate";
219
- label = binding.mutateCallback.name;
220
- } else if (binding.isValidate) {
221
- type = "Validate";
222
- label = binding.validateCallback.name;
223
- } else if (binding.isWatch) {
224
- type = "Watch";
225
- label = binding.watchCallback.name;
226
- }
227
- logger_default.debug({ uid }, `${type} binding (${label}) does not match request namespace "${req.namespace}"`);
228
- return true;
229
- }
230
- for (const [key, value] of Object.entries(labels)) {
231
- const testKey = metadata?.labels?.[key];
232
- if (!testKey) {
233
- logger_default.debug({ uid }, `Label ${key} does not exist`);
234
- return true;
235
- }
236
- if (value && testKey !== value) {
237
- logger_default.debug({ uid }, `${testKey} does not match ${value}`);
238
- return true;
239
- }
240
- }
241
- for (const [key, value] of Object.entries(annotations)) {
242
- const testKey = metadata?.annotations?.[key];
243
- if (!testKey) {
244
- logger_default.debug({ uid }, `Annotation ${key} does not exist`);
245
- return true;
246
- }
247
- if (value && testKey !== value) {
248
- logger_default.debug({ uid }, `${testKey} does not match ${value}`);
249
- return true;
250
- }
251
- }
252
- return false;
253
- }
254
-
255
- // src/lib/mutate-request.ts
256
- var import_ramda = require("ramda");
257
- var PeprMutateRequest = class {
258
- Raw;
259
- #input;
260
- get PermitSideEffects() {
261
- return !this.#input.dryRun;
262
- }
263
- /**
264
- * Indicates whether the request is a dry run.
265
- * @returns true if the request is a dry run, false otherwise.
266
- */
267
- get IsDryRun() {
268
- return this.#input.dryRun;
269
- }
270
- /**
271
- * Provides access to the old resource in the request if available.
272
- * @returns The old Kubernetes resource object or null if not available.
273
- */
274
- get OldResource() {
275
- return this.#input.oldObject;
276
- }
277
- /**
278
- * Provides access to the request object.
279
- * @returns The request object containing the Kubernetes resource.
280
- */
281
- get Request() {
282
- return this.#input;
283
- }
284
- /**
285
- * Creates a new instance of the action class.
286
- * @param input - The request object containing the Kubernetes resource to modify.
287
- */
288
- constructor(input) {
289
- this.#input = input;
290
- if (input.operation.toUpperCase() === "DELETE" /* DELETE */) {
291
- this.Raw = (0, import_ramda.clone)(input.oldObject);
292
- } else {
293
- this.Raw = (0, import_ramda.clone)(input.object);
294
- }
295
- if (!this.Raw) {
296
- throw new Error("unable to load the request object into PeprRequest.RawP");
297
- }
298
- }
299
- /**
300
- * Deep merges the provided object with the current resource.
301
- *
302
- * @param obj - The object to merge with the current resource.
303
- */
304
- Merge = (obj) => {
305
- this.Raw = (0, import_ramda.mergeDeepRight)(this.Raw, obj);
306
- };
307
- /**
308
- * Updates a label on the Kubernetes resource.
309
- * @param key - The key of the label to update.
310
- * @param value - The value of the label.
311
- * @returns The current action instance for method chaining.
312
- */
313
- SetLabel = (key, value) => {
314
- const ref = this.Raw;
315
- ref.metadata = ref.metadata ?? {};
316
- ref.metadata.labels = ref.metadata.labels ?? {};
317
- ref.metadata.labels[key] = value;
318
- return this;
319
- };
320
- /**
321
- * Updates an annotation on the Kubernetes resource.
322
- * @param key - The key of the annotation to update.
323
- * @param value - The value of the annotation.
324
- * @returns The current action instance for method chaining.
325
- */
326
- SetAnnotation = (key, value) => {
327
- const ref = this.Raw;
328
- ref.metadata = ref.metadata ?? {};
329
- ref.metadata.annotations = ref.metadata.annotations ?? {};
330
- ref.metadata.annotations[key] = value;
331
- return this;
332
- };
333
- /**
334
- * Removes a label from the Kubernetes resource.
335
- * @param key - The key of the label to remove.
336
- * @returns The current Action instance for method chaining.
337
- */
338
- RemoveLabel = (key) => {
339
- if (this.Raw.metadata?.labels?.[key]) {
340
- delete this.Raw.metadata.labels[key];
341
- }
342
- return this;
343
- };
344
- /**
345
- * Removes an annotation from the Kubernetes resource.
346
- * @param key - The key of the annotation to remove.
347
- * @returns The current Action instance for method chaining.
348
- */
349
- RemoveAnnotation = (key) => {
350
- if (this.Raw.metadata?.annotations?.[key]) {
351
- delete this.Raw.metadata.annotations[key];
352
- }
353
- return this;
354
- };
355
- /**
356
- * Check if a label exists on the Kubernetes resource.
357
- *
358
- * @param key the label key to check
359
- * @returns
360
- */
361
- HasLabel = (key) => {
362
- return this.Raw.metadata?.labels?.[key] !== void 0;
363
- };
364
- /**
365
- * Check if an annotation exists on the Kubernetes resource.
366
- *
367
- * @param key the annotation key to check
368
- * @returns
369
- */
370
- HasAnnotation = (key) => {
371
- return this.Raw.metadata?.annotations?.[key] !== void 0;
372
- };
373
- };
374
-
375
- // src/lib/utils.ts
376
- var utils_exports = {};
377
- __export(utils_exports, {
378
- base64Decode: () => base64Decode,
379
- base64Encode: () => base64Encode,
380
- convertFromBase64Map: () => convertFromBase64Map,
381
- convertToBase64Map: () => convertToBase64Map,
382
- isAscii: () => isAscii
383
- });
384
- var isAscii = /^[\s\x20-\x7E]*$/;
385
- function convertToBase64Map(obj, skip) {
386
- obj.data = obj.data ?? {};
387
- for (const key in obj.data) {
388
- const value = obj.data[key];
389
- obj.data[key] = skip.includes(key) ? value : base64Encode(value);
390
- }
391
- }
392
- function convertFromBase64Map(obj) {
393
- const skip = [];
394
- obj.data = obj.data ?? {};
395
- for (const key in obj.data) {
396
- if (obj.data[key] == void 0) {
397
- obj.data[key] = "";
398
- } else {
399
- const decoded = base64Decode(obj.data[key]);
400
- if (isAscii.test(decoded)) {
401
- obj.data[key] = decoded;
402
- } else {
403
- skip.push(key);
404
- }
405
- }
406
- }
407
- logger_default.debug(`Non-ascii data detected in keys: ${skip}, skipping automatic base64 decoding`);
408
- return skip;
409
- }
410
- function base64Decode(data) {
411
- return Buffer.from(data, "base64").toString("utf-8");
412
- }
413
- function base64Encode(data) {
414
- return Buffer.from(data).toString("base64");
415
- }
416
-
417
- // src/lib/mutate-processor.ts
418
- async function mutateProcessor(config, capabilities, req, reqMetadata) {
419
- const wrapped = new PeprMutateRequest(req);
420
- const response = {
421
- uid: req.uid,
422
- warnings: [],
423
- allowed: false
424
- };
425
- let matchedAction = false;
426
- let skipDecode = [];
427
- const isSecret = req.kind.version == "v1" && req.kind.kind == "Secret";
428
- if (isSecret) {
429
- skipDecode = convertFromBase64Map(wrapped.Raw);
430
- }
431
- logger_default.info(reqMetadata, `Processing request`);
432
- for (const { name, bindings, namespaces } of capabilities) {
433
- const actionMetadata = { ...reqMetadata, name };
434
- for (const action of bindings) {
435
- if (!action.mutateCallback) {
436
- continue;
437
- }
438
- if (shouldSkipRequest(action, req, namespaces)) {
439
- continue;
440
- }
441
- const label = action.mutateCallback.name;
442
- logger_default.info(actionMetadata, `Processing mutation action (${label})`);
443
- matchedAction = true;
444
- const updateStatus = (status) => {
445
- if (req.operation == "DELETE") {
446
- return;
447
- }
448
- const identifier = `${config.uuid}.pepr.dev/${name}`;
449
- wrapped.Raw.metadata = wrapped.Raw.metadata || {};
450
- wrapped.Raw.metadata.annotations = wrapped.Raw.metadata.annotations || {};
451
- wrapped.Raw.metadata.annotations[identifier] = status;
452
- };
453
- updateStatus("started");
454
- try {
455
- await action.mutateCallback(wrapped);
456
- logger_default.info(actionMetadata, `Mutation action succeeded (${label})`);
457
- updateStatus("succeeded");
458
- } catch (e) {
459
- logger_default.warn(actionMetadata, `Action failed: ${e}`);
460
- updateStatus("warning");
461
- response.warnings = response.warnings || [];
462
- response.warnings.push(`Action failed: ${e}`);
463
- switch (config.onError) {
464
- case Errors.reject:
465
- logger_default.error(actionMetadata, `Action failed: ${e}`);
466
- response.result = "Pepr module configured to reject on error";
467
- return response;
468
- case Errors.audit:
469
- response.auditAnnotations = response.auditAnnotations || {};
470
- response.auditAnnotations[Date.now()] = e;
471
- break;
472
- }
473
- }
474
- }
475
- }
476
- response.allowed = true;
477
- if (!matchedAction) {
478
- logger_default.info(reqMetadata, `No matching actions found`);
479
- return response;
480
- }
481
- if (req.operation == "DELETE") {
482
- return response;
483
- }
484
- const transformed = wrapped.Raw;
485
- if (isSecret) {
486
- convertToBase64Map(transformed, skipDecode);
487
- }
488
- const patches = import_fast_json_patch.default.compare(req.object, transformed);
489
- if (patches.length > 0) {
490
- response.patchType = "JSONPatch";
491
- response.patch = base64Encode(JSON.stringify(patches));
492
- }
493
- if (response.warnings && response.warnings.length < 1) {
494
- delete response.warnings;
495
- }
496
- logger_default.debug({ ...reqMetadata, patches }, `Patches generated`);
497
- return response;
498
- }
499
-
500
- // src/lib/validate-request.ts
501
- var import_ramda2 = require("ramda");
502
- var PeprValidateRequest = class {
503
- Raw;
504
- #input;
505
- /**
506
- * Provides access to the old resource in the request if available.
507
- * @returns The old Kubernetes resource object or null if not available.
508
- */
509
- get OldResource() {
510
- return this.#input.oldObject;
511
- }
512
- /**
513
- * Provides access to the request object.
514
- * @returns The request object containing the Kubernetes resource.
515
- */
516
- get Request() {
517
- return this.#input;
518
- }
519
- /**
520
- * Creates a new instance of the Action class.
521
- * @param input - The request object containing the Kubernetes resource to modify.
522
- */
523
- constructor(input) {
524
- this.#input = input;
525
- if (input.operation.toUpperCase() === "DELETE" /* DELETE */) {
526
- this.Raw = (0, import_ramda2.clone)(input.oldObject);
527
- } else {
528
- this.Raw = (0, import_ramda2.clone)(input.object);
529
- }
530
- if (!this.Raw) {
531
- throw new Error("unable to load the request object into PeprRequest.Raw");
532
- }
533
- }
534
- /**
535
- * Check if a label exists on the Kubernetes resource.
536
- *
537
- * @param key the label key to check
538
- * @returns
539
- */
540
- HasLabel = (key) => {
541
- return this.Raw.metadata?.labels?.[key] !== void 0;
542
- };
543
- /**
544
- * Check if an annotation exists on the Kubernetes resource.
545
- *
546
- * @param key the annotation key to check
547
- * @returns
548
- */
549
- HasAnnotation = (key) => {
550
- return this.Raw.metadata?.annotations?.[key] !== void 0;
551
- };
552
- /**
553
- * Create a validation response that allows the request.
554
- *
555
- * @returns The validation response.
556
- */
557
- Approve = () => {
558
- return {
559
- allowed: true
560
- };
561
- };
562
- /**
563
- * Create a validation response that denies the request.
564
- *
565
- * @param statusMessage Optional status message to return to the user.
566
- * @param statusCode Optional status code to return to the user.
567
- * @returns The validation response.
568
- */
569
- Deny = (statusMessage, statusCode) => {
570
- return {
571
- allowed: false,
572
- statusCode,
573
- statusMessage
574
- };
575
- };
576
- };
577
-
578
- // src/lib/validate-processor.ts
579
- async function validateProcessor(capabilities, req, reqMetadata) {
580
- const wrapped = new PeprValidateRequest(req);
581
- const response = [];
582
- const isSecret = req.kind.version == "v1" && req.kind.kind == "Secret";
583
- if (isSecret) {
584
- convertFromBase64Map(wrapped.Raw);
585
- }
586
- logger_default.info(reqMetadata, `Processing validation request`);
587
- for (const { name, bindings, namespaces } of capabilities) {
588
- const actionMetadata = { ...reqMetadata, name };
589
- for (const action of bindings) {
590
- if (!action.validateCallback) {
591
- continue;
592
- }
593
- const localResponse = {
594
- uid: req.uid,
595
- allowed: true
596
- // Assume it's allowed until a validation check fails
597
- };
598
- if (shouldSkipRequest(action, req, namespaces)) {
599
- continue;
600
- }
601
- const label = action.validateCallback.name;
602
- logger_default.info(actionMetadata, `Processing validation action (${label})`);
603
- try {
604
- const resp = await action.validateCallback(wrapped);
605
- localResponse.allowed = resp.allowed;
606
- if (resp.statusCode || resp.statusMessage) {
607
- localResponse.status = {
608
- code: resp.statusCode || 400,
609
- message: resp.statusMessage || `Validation failed for ${name}`
610
- };
611
- }
612
- logger_default.info(actionMetadata, `Validation action complete (${label}): ${resp.allowed ? "allowed" : "denied"}`);
613
- } catch (e) {
614
- logger_default.error(actionMetadata, `Action failed: ${JSON.stringify(e)}`);
615
- localResponse.allowed = false;
616
- localResponse.status = {
617
- code: 500,
618
- message: `Action failed with error: ${JSON.stringify(e)}`
619
- };
620
- return [localResponse];
621
- }
622
- response.push(localResponse);
623
- }
624
- }
625
- return response;
626
- }
627
-
628
- // src/lib/controller/store.ts
629
- var import_kubernetes_fluent_client2 = require("kubernetes-fluent-client");
630
- var import_ramda3 = require("ramda");
631
- var namespace = "pepr-system";
632
- var debounceBackoff = 5e3;
633
- var PeprControllerStore = class {
634
- #name;
635
- #stores = {};
636
- #sendDebounce;
637
- #onReady;
638
- constructor(capabilities, name, onReady) {
639
- this.#onReady = onReady;
640
- this.#name = name;
641
- if (name.includes("schedule")) {
642
- for (const { name: name2, registerScheduleStore, hasSchedule } of capabilities) {
643
- if (hasSchedule !== true) {
644
- continue;
645
- }
646
- const { scheduleStore } = registerScheduleStore();
647
- scheduleStore.registerSender(this.#send(name2));
648
- this.#stores[name2] = scheduleStore;
649
- }
650
- } else {
651
- for (const { name: name2, registerStore } of capabilities) {
652
- const { store } = registerStore();
653
- store.registerSender(this.#send(name2));
654
- this.#stores[name2] = store;
655
- }
656
- }
657
- setTimeout(
658
- () => (0, import_kubernetes_fluent_client2.K8s)(PeprStore).InNamespace(namespace).Get(this.#name).then(this.#setupWatch).catch(this.#createStoreResource),
659
- Math.random() * 3e3
660
- );
661
- }
662
- #setupWatch = () => {
663
- const watcher = (0, import_kubernetes_fluent_client2.K8s)(PeprStore, { name: this.#name, namespace }).Watch(this.#receive);
664
- watcher.start().catch((e) => logger_default.error(e, "Error starting Pepr store watch"));
665
- };
666
- #receive = (store) => {
667
- logger_default.debug(store, "Pepr Store update");
668
- const debounced = () => {
669
- const data = store.data || {};
670
- for (const name of Object.keys(this.#stores)) {
671
- const offset = `${name}-`.length;
672
- const filtered = {};
673
- for (const key of Object.keys(data)) {
674
- if ((0, import_ramda3.startsWith)(name, key)) {
675
- filtered[key.slice(offset)] = data[key];
676
- }
677
- }
678
- this.#stores[name].receive(filtered);
679
- }
680
- if (this.#onReady) {
681
- this.#onReady();
682
- this.#onReady = void 0;
683
- }
684
- };
685
- clearTimeout(this.#sendDebounce);
686
- this.#sendDebounce = setTimeout(debounced, this.#onReady ? 0 : debounceBackoff);
687
- };
688
- #send = (capabilityName) => {
689
- const sendCache = {};
690
- const fillCache = (op, key, val) => {
691
- if (op === "add") {
692
- const path = `/data/${capabilityName}-${key}`;
693
- const value = val || "";
694
- const cacheIdx = [op, path, value].join(":");
695
- sendCache[cacheIdx] = { op, path, value };
696
- return;
697
- }
698
- if (op === "remove") {
699
- if (key.length < 1) {
700
- throw new Error(`Key is required for REMOVE operation`);
701
- }
702
- for (const k of key) {
703
- const path = `/data/${capabilityName}-${k}`;
704
- const cacheIdx = [op, path].join(":");
705
- sendCache[cacheIdx] = { op, path };
706
- }
707
- return;
708
- }
709
- throw new Error(`Unsupported operation: ${op}`);
710
- };
711
- const flushCache = async () => {
712
- const indexes = Object.keys(sendCache);
713
- const payload = Object.values(sendCache);
714
- for (const idx of indexes) {
715
- delete sendCache[idx];
716
- }
717
- try {
718
- await (0, import_kubernetes_fluent_client2.K8s)(PeprStore, { namespace, name: this.#name }).Patch(payload);
719
- } catch (err) {
720
- logger_default.error(err, "Pepr store update failure");
721
- for (const idx of indexes) {
722
- sendCache[idx] = payload[Number(idx)];
723
- }
724
- }
725
- };
726
- const sender = async (op, key, val) => {
727
- fillCache(op, key, val);
728
- };
729
- setInterval(() => {
730
- if (Object.keys(sendCache).length > 0) {
731
- logger_default.debug(sendCache, "Sending updates to Pepr store");
732
- void flushCache();
733
- }
734
- }, debounceBackoff);
735
- return sender;
736
- };
737
- #createStoreResource = async (e) => {
738
- logger_default.info(`Pepr store not found, creating...`);
739
- logger_default.debug(e);
740
- try {
741
- await (0, import_kubernetes_fluent_client2.K8s)(PeprStore).Apply({
742
- metadata: {
743
- name: this.#name,
744
- namespace
745
- },
746
- data: {
747
- // JSON Patch will die if the data is empty, so we need to add a placeholder
748
- __pepr_do_not_delete__: "k-thx-bye"
749
- }
750
- });
751
- this.#setupWatch();
752
- } catch (err) {
753
- logger_default.error(err, "Failed to create Pepr store");
754
- }
755
- };
756
- };
757
-
758
- // src/lib/controller/index.ts
759
- var Controller = class _Controller {
760
- // Track whether the server is running
761
- #running = false;
762
- // Metrics collector
763
- #metricsCollector = new MetricsCollector("pepr");
764
- // The token used to authenticate requests
765
- #token = "";
766
- // The express app instance
767
- #app = (0, import_express.default)();
768
- // Initialized with the constructor
769
- #config;
770
- #capabilities;
771
- #beforeHook;
772
- #afterHook;
773
- constructor(config, capabilities, beforeHook, afterHook, onReady) {
774
- this.#config = config;
775
- this.#capabilities = capabilities;
776
- new PeprControllerStore(capabilities, `pepr-${config.uuid}-store`, () => {
777
- this.#bindEndpoints();
778
- onReady && onReady();
779
- logger_default.info("\u2705 Controller startup complete");
780
- new PeprControllerStore(capabilities, `pepr-${config.uuid}-schedule`, () => {
781
- logger_default.info("\u2705 Scheduling processed");
782
- });
783
- });
784
- this.#app.use(_Controller.#logger);
785
- this.#app.use(import_express.default.json({ limit: "2mb" }));
786
- if (beforeHook) {
787
- logger_default.info(`Using beforeHook: ${beforeHook}`);
788
- this.#beforeHook = beforeHook;
789
- }
790
- if (afterHook) {
791
- logger_default.info(`Using afterHook: ${afterHook}`);
792
- this.#afterHook = afterHook;
793
- }
794
- }
795
- /** Start the webhook server */
796
- startServer = (port) => {
797
- if (this.#running) {
798
- throw new Error("Cannot start Pepr module: Pepr module was not instantiated with deferStart=true");
799
- }
800
- const options = {
801
- key: import_fs.default.readFileSync(process.env.SSL_KEY_PATH || "/etc/certs/tls.key"),
802
- cert: import_fs.default.readFileSync(process.env.SSL_CERT_PATH || "/etc/certs/tls.crt")
803
- };
804
- if (!isWatchMode()) {
805
- this.#token = process.env.PEPR_API_TOKEN || import_fs.default.readFileSync("/app/api-token/value").toString().trim();
806
- logger_default.info(`Using API token: ${this.#token}`);
807
- if (!this.#token) {
808
- throw new Error("API token not found");
809
- }
810
- }
811
- const server = import_https.default.createServer(options, this.#app).listen(port);
812
- server.on("listening", () => {
813
- logger_default.info(`Server listening on port ${port}`);
814
- this.#running = true;
815
- });
816
- server.on("error", (e) => {
817
- if (e.code === "EADDRINUSE") {
818
- logger_default.warn(
819
- `Address in use, retrying in 2 seconds. If this persists, ensure ${port} is not in use, e.g. "lsof -i :${port}"`
820
- );
821
- setTimeout(() => {
822
- server.close();
823
- server.listen(port);
824
- }, 2e3);
825
- }
826
- });
827
- process.on("SIGTERM", () => {
828
- logger_default.info("Received SIGTERM, closing server");
829
- server.close(() => {
830
- logger_default.info("Server closed");
831
- process.exit(0);
832
- });
833
- });
834
- };
835
- #bindEndpoints = () => {
836
- this.#app.get("/healthz", _Controller.#healthz);
837
- this.#app.get("/metrics", this.#metrics);
838
- if (isWatchMode()) {
839
- return;
840
- }
841
- this.#app.use(["/mutate/:token", "/validate/:token"], this.#validateToken);
842
- this.#app.post("/mutate/:token", this.#admissionReq("Mutate"));
843
- this.#app.post("/validate/:token", this.#admissionReq("Validate"));
844
- };
845
- /**
846
- * Validate the token in the request path
847
- *
848
- * @param req The incoming request
849
- * @param res The outgoing response
850
- * @param next The next middleware function
851
- * @returns
852
- */
853
- #validateToken = (req, res, next) => {
854
- const { token } = req.params;
855
- if (token !== this.#token) {
856
- const err = `Unauthorized: invalid token '${token.replace(/[^\w]/g, "_")}'`;
857
- logger_default.warn(err);
858
- res.status(401).send(err);
859
- this.#metricsCollector.alert();
860
- return;
861
- }
862
- next();
863
- };
864
- /**
865
- * Metrics endpoint handler
866
- *
867
- * @param req the incoming request
868
- * @param res the outgoing response
869
- */
870
- #metrics = async (req, res) => {
871
- try {
872
- res.send(await this.#metricsCollector.getMetrics());
873
- } catch (err) {
874
- logger_default.error(err);
875
- res.status(500).send("Internal Server Error");
876
- }
877
- };
878
- /**
879
- * Admission request handler for both mutate and validate requests
880
- *
881
- * @param admissionKind the type of admission request
882
- * @returns the request handler
883
- */
884
- #admissionReq = (admissionKind) => {
885
- return async (req, res) => {
886
- const startTime = MetricsCollector.observeStart();
887
- try {
888
- const request = req.body?.request || {};
889
- this.#beforeHook && this.#beforeHook(request || {});
890
- const name = request?.name ? `/${request.name}` : "";
891
- const namespace2 = request?.namespace || "";
892
- const gvk = request?.kind || { group: "", version: "", kind: "" };
893
- const reqMetadata = {
894
- uid: request.uid,
895
- namespace: namespace2,
896
- name
897
- };
898
- logger_default.info({ ...reqMetadata, gvk, operation: request.operation, admissionKind }, "Incoming request");
899
- logger_default.debug({ ...reqMetadata, request }, "Incoming request body");
900
- let response;
901
- if (admissionKind === "Mutate") {
902
- response = await mutateProcessor(this.#config, this.#capabilities, request, reqMetadata);
903
- } else {
904
- response = await validateProcessor(this.#capabilities, request, reqMetadata);
905
- }
906
- const responseList = Array.isArray(response) ? response : [response];
907
- responseList.map((res2) => {
908
- this.#afterHook && this.#afterHook(res2);
909
- logger_default.info({ ...reqMetadata, res: res2 }, "Check response");
910
- });
911
- let kubeAdmissionResponse;
912
- if (admissionKind === "Mutate") {
913
- kubeAdmissionResponse = response;
914
- logger_default.debug({ ...reqMetadata, response }, "Outgoing response");
915
- res.send({
916
- apiVersion: "admission.k8s.io/v1",
917
- kind: "AdmissionReview",
918
- response: kubeAdmissionResponse
919
- });
920
- } else {
921
- kubeAdmissionResponse = responseList.length === 0 ? {
922
- uid: request.uid,
923
- allowed: true,
924
- status: { message: "no in-scope validations -- allowed!" }
925
- } : {
926
- uid: responseList[0].uid,
927
- allowed: responseList.filter((r) => !r.allowed).length === 0,
928
- status: {
929
- message: responseList.filter((rl) => !rl.allowed).map((curr) => curr.status?.message).join("; ")
930
- }
931
- };
932
- res.send({
933
- apiVersion: "admission.k8s.io/v1",
934
- kind: "AdmissionReview",
935
- response: kubeAdmissionResponse
936
- });
937
- }
938
- logger_default.debug({ ...reqMetadata, kubeAdmissionResponse }, "Outgoing response");
939
- this.#metricsCollector.observeEnd(startTime, admissionKind);
940
- } catch (err) {
941
- logger_default.error(err);
942
- res.status(500).send("Internal Server Error");
943
- this.#metricsCollector.error();
944
- }
945
- };
946
- };
947
- /**
948
- * Middleware for logging requests
949
- *
950
- * @param req the incoming request
951
- * @param res the outgoing response
952
- * @param next the next middleware function
953
- */
954
- static #logger(req, res, next) {
955
- const startTime = Date.now();
956
- res.on("finish", () => {
957
- const elapsedTime = Date.now() - startTime;
958
- const message = {
959
- uid: req.body?.request?.uid,
960
- method: req.method,
961
- url: req.originalUrl,
962
- status: res.statusCode,
963
- duration: `${elapsedTime} ms`
964
- };
965
- res.statusCode >= 300 ? logger_default.warn(message) : logger_default.info(message);
966
- });
967
- next();
968
- }
969
- /**
970
- * Health check endpoint handler
971
- *
972
- * @param req the incoming request
973
- * @param res the outgoing response
974
- */
975
- static #healthz(req, res) {
976
- try {
977
- res.send("OK");
978
- } catch (err) {
979
- logger_default.error(err);
980
- res.status(500).send("Internal Server Error");
981
- }
982
- }
983
- };
984
-
985
- // src/lib/watch-processor.ts
986
- var import_kubernetes_fluent_client5 = require("kubernetes-fluent-client");
987
- var import_types2 = require("kubernetes-fluent-client/dist/fluent/types");
988
-
989
- // src/lib/helpers.ts
990
- var import_kubernetes_fluent_client4 = require("kubernetes-fluent-client");
991
-
992
- // src/sdk/sdk.ts
993
- var sdk_exports = {};
994
- __export(sdk_exports, {
995
- containers: () => containers,
996
- getOwnerRefFrom: () => getOwnerRefFrom,
997
- sanitizeResourceName: () => sanitizeResourceName,
998
- writeEvent: () => writeEvent
999
- });
1000
- var import_kubernetes_fluent_client3 = require("kubernetes-fluent-client");
1001
- function containers(request, containerType) {
1002
- const containers2 = request.Raw.spec?.containers || [];
1003
- const initContainers = request.Raw.spec?.initContainers || [];
1004
- const ephemeralContainers = request.Raw.spec?.ephemeralContainers || [];
1005
- if (containerType === "containers") {
1006
- return containers2;
1007
- }
1008
- if (containerType === "initContainers") {
1009
- return initContainers;
1010
- }
1011
- if (containerType === "ephemeralContainers") {
1012
- return ephemeralContainers;
1013
- }
1014
- return [...containers2, ...initContainers, ...ephemeralContainers];
1015
- }
1016
- async function writeEvent(cr, event, eventType, eventReason, reportingComponent, reportingInstance) {
1017
- logger_default.debug(cr.metadata, `Writing event: ${event.message}`);
1018
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.CoreEvent).Create({
1019
- type: eventType,
1020
- reason: eventReason,
1021
- ...event,
1022
- // Fixed values
1023
- metadata: {
1024
- namespace: cr.metadata.namespace,
1025
- generateName: cr.metadata.name
1026
- },
1027
- involvedObject: {
1028
- apiVersion: cr.apiVersion,
1029
- kind: cr.kind,
1030
- name: cr.metadata.name,
1031
- namespace: cr.metadata.namespace,
1032
- uid: cr.metadata.uid
1033
- },
1034
- firstTimestamp: /* @__PURE__ */ new Date(),
1035
- reportingComponent,
1036
- reportingInstance
1037
- });
1038
- }
1039
- function getOwnerRefFrom(cr) {
1040
- const { name, uid } = cr.metadata;
1041
- return [
1042
- {
1043
- apiVersion: cr.apiVersion,
1044
- kind: cr.kind,
1045
- uid,
1046
- name
1047
- }
1048
- ];
1049
- }
1050
- function sanitizeResourceName(name) {
1051
- return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 250).replace(/^[^a-z]+|[^a-z]+$/g, "");
1052
- }
1053
-
1054
- // src/lib/helpers.ts
1055
- function checkOverlap(bindingFilters, objectFilters) {
1056
- if (Object.keys(bindingFilters).length === 0) {
1057
- return true;
1058
- }
1059
- let matchCount = 0;
1060
- for (const key in bindingFilters) {
1061
- if (Object.prototype.hasOwnProperty.call(objectFilters, key)) {
1062
- const val1 = bindingFilters[key];
1063
- const val2 = objectFilters[key];
1064
- if (val1 === "" && key in objectFilters) {
1065
- matchCount++;
1066
- } else if (val1 !== "" && val1 === val2) {
1067
- matchCount++;
1068
- }
1069
- }
1070
- }
1071
- return matchCount === Object.keys(bindingFilters).length;
1072
- }
1073
- function filterNoMatchReason(binding, obj, capabilityNamespaces) {
1074
- if (binding.kind && binding.kind.kind === "Namespace" && binding.filters && binding.filters.namespaces.length !== 0) {
1075
- return `Ignoring Watch Callback: Cannot use a namespace filter in a namespace object.`;
1076
- }
1077
- if (typeof obj === "object" && obj !== null && "metadata" in obj && obj.metadata !== void 0 && binding.filters) {
1078
- if (obj.metadata.labels && !checkOverlap(binding.filters.labels, obj.metadata.labels)) {
1079
- return `Ignoring Watch Callback: No overlap between binding and object labels. Binding labels ${JSON.stringify(
1080
- binding.filters.labels
1081
- )}, Object Labels ${JSON.stringify(obj.metadata.labels)}.`;
1082
- }
1083
- if (obj.metadata.annotations && !checkOverlap(binding.filters.annotations, obj.metadata.annotations)) {
1084
- return `Ignoring Watch Callback: No overlap between binding and object annotations. Binding annotations ${JSON.stringify(
1085
- binding.filters.annotations
1086
- )}, Object annotations ${JSON.stringify(obj.metadata.annotations)}.`;
1087
- }
1088
- }
1089
- if (Array.isArray(capabilityNamespaces) && capabilityNamespaces.length > 0 && obj.metadata && obj.metadata.namespace && !capabilityNamespaces.includes(obj.metadata.namespace)) {
1090
- return `Ignoring Watch Callback: Object is not in the capability namespace. Capability namespaces: ${capabilityNamespaces.join(
1091
- ", "
1092
- )}, Object namespace: ${obj.metadata.namespace}.`;
1093
- }
1094
- 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))) {
1095
- return `Ignoring Watch Callback: Binding namespace is not part of capability namespaces. Capability namespaces: ${capabilityNamespaces.join(
1096
- ", "
1097
- )}, Binding namespaces: ${binding.filters.namespaces.join(", ")}.`;
1098
- }
1099
- 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)) {
1100
- return `Ignoring Watch Callback: Binding namespace and object namespace are not the same. Binding namespaces: ${binding.filters.namespaces.join(
1101
- ", "
1102
- )}, Object namespace: ${obj.metadata.namespace}.`;
1103
- }
1104
- return "";
1105
- }
1106
-
1107
- // src/lib/queue.ts
1108
- var Queue = class {
1109
- #queue = [];
1110
- #pendingPromise = false;
1111
- #reconcile;
1112
- constructor() {
1113
- this.#reconcile = async () => await new Promise((resolve) => resolve());
1114
- }
1115
- setReconcile(reconcile) {
1116
- this.#reconcile = reconcile;
1117
- }
1118
- /**
1119
- * Enqueue adds an item to the queue and returns a promise that resolves when the item is
1120
- * reconciled.
1121
- *
1122
- * @param item The object to reconcile
1123
- * @returns A promise that resolves when the object is reconciled
1124
- */
1125
- enqueue(item, type) {
1126
- logger_default.debug(`Enqueueing ${item.metadata.namespace}/${item.metadata.name}`);
1127
- return new Promise((resolve, reject) => {
1128
- this.#queue.push({ item, type, resolve, reject });
1129
- return this.#dequeue();
1130
- });
1131
- }
1132
- /**
1133
- * Dequeue reconciles the next item in the queue
1134
- *
1135
- * @returns A promise that resolves when the webapp is reconciled
1136
- */
1137
- async #dequeue() {
1138
- if (this.#pendingPromise) {
1139
- logger_default.debug("Pending promise, not dequeuing");
1140
- return false;
1141
- }
1142
- const element = this.#queue.shift();
1143
- if (!element) {
1144
- logger_default.debug("No element, not dequeuing");
1145
- return false;
1146
- }
1147
- try {
1148
- this.#pendingPromise = true;
1149
- if (this.#reconcile) {
1150
- logger_default.debug(`Reconciling ${element.item.metadata.name}`);
1151
- await this.#reconcile(element.item, element.type);
1152
- }
1153
- element.resolve();
1154
- } catch (e) {
1155
- logger_default.debug(`Error reconciling ${element.item.metadata.name}`, { error: e });
1156
- element.reject(e);
1157
- } finally {
1158
- logger_default.debug("Resetting pending promise and dequeuing");
1159
- this.#pendingPromise = false;
1160
- await this.#dequeue();
1161
- }
1162
- }
1163
- };
1164
-
1165
- // src/lib/watch-processor.ts
1166
- var watchCfg = {
1167
- retryMax: process.env.PEPR_RETRYMAX ? parseInt(process.env.PEPR_RETRYMAX, 10) : 5,
1168
- retryDelaySec: process.env.PEPR_RETRYDELAYSECONDS ? parseInt(process.env.PEPR_RETRYDELAYSECONDS, 10) : 5,
1169
- resyncIntervalSec: process.env.PEPR_RESYNCINTERVALSECONDS ? parseInt(process.env.PEPR_RESYNCINTERVALSECONDS, 10) : 300
1170
- };
1171
- var eventToPhaseMap = {
1172
- ["CREATE" /* Create */]: [import_types2.WatchPhase.Added],
1173
- ["UPDATE" /* Update */]: [import_types2.WatchPhase.Modified],
1174
- ["CREATEORUPDATE" /* CreateOrUpdate */]: [import_types2.WatchPhase.Added, import_types2.WatchPhase.Modified],
1175
- ["DELETE" /* Delete */]: [import_types2.WatchPhase.Deleted],
1176
- ["*" /* Any */]: [import_types2.WatchPhase.Added, import_types2.WatchPhase.Modified, import_types2.WatchPhase.Deleted]
1177
- };
1178
- function setupWatch(capabilities) {
1179
- capabilities.map(
1180
- (capability) => capability.bindings.filter((binding) => binding.isWatch).forEach((bindingElement) => runBinding(bindingElement, capability.namespaces))
1181
- );
1182
- }
1183
- async function runBinding(binding, capabilityNamespaces) {
1184
- const phaseMatch = eventToPhaseMap[binding.event] || eventToPhaseMap["*" /* Any */];
1185
- logger_default.debug({ watchCfg }, "Effective WatchConfig");
1186
- const watchCallback = async (obj, type) => {
1187
- if (phaseMatch.includes(type)) {
1188
- try {
1189
- const filterMatch = filterNoMatchReason(binding, obj, capabilityNamespaces);
1190
- if (filterMatch === "") {
1191
- await binding.watchCallback?.(obj, type);
1192
- } else {
1193
- logger_default.debug(filterMatch);
1194
- }
1195
- } catch (e) {
1196
- logger_default.error(e, "Error executing watch callback");
1197
- }
1198
- }
1199
- };
1200
- const queue = new Queue();
1201
- queue.setReconcile(watchCallback);
1202
- const watcher = (0, import_kubernetes_fluent_client5.K8s)(binding.model, binding.filters).Watch(async (obj, type) => {
1203
- logger_default.debug(obj, `Watch event ${type} received`);
1204
- if (binding.isQueue) {
1205
- await queue.enqueue(obj, type);
1206
- } else {
1207
- await watchCallback(obj, type);
1208
- }
1209
- }, watchCfg);
1210
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.GIVE_UP, (err) => {
1211
- logger_default.error(err, "Watch failed after 5 attempts, giving up");
1212
- process.exit(1);
1213
- });
1214
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.CONNECT, (url) => logEvent(import_kubernetes_fluent_client5.WatchEvent.CONNECT, url));
1215
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.DATA_ERROR, (err) => logEvent(import_kubernetes_fluent_client5.WatchEvent.DATA_ERROR, err.message));
1216
- watcher.events.on(
1217
- import_kubernetes_fluent_client5.WatchEvent.RECONNECT,
1218
- (err, retryCount) => logEvent(import_kubernetes_fluent_client5.WatchEvent.RECONNECT, err ? `Reconnecting after ${retryCount} attempts` : "")
1219
- );
1220
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.RECONNECT_PENDING, () => logEvent(import_kubernetes_fluent_client5.WatchEvent.RECONNECT_PENDING));
1221
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.GIVE_UP, (err) => logEvent(import_kubernetes_fluent_client5.WatchEvent.GIVE_UP, err.message));
1222
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.ABORT, (err) => logEvent(import_kubernetes_fluent_client5.WatchEvent.ABORT, err.message));
1223
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.OLD_RESOURCE_VERSION, (err) => logEvent(import_kubernetes_fluent_client5.WatchEvent.OLD_RESOURCE_VERSION, err));
1224
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.NETWORK_ERROR, (err) => logEvent(import_kubernetes_fluent_client5.WatchEvent.NETWORK_ERROR, err.message));
1225
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.LIST_ERROR, (err) => logEvent(import_kubernetes_fluent_client5.WatchEvent.LIST_ERROR, err.message));
1226
- watcher.events.on(import_kubernetes_fluent_client5.WatchEvent.LIST, (list) => logEvent(import_kubernetes_fluent_client5.WatchEvent.LIST, JSON.stringify(list, void 0, 2)));
1227
- try {
1228
- await watcher.start();
1229
- } catch (err) {
1230
- logger_default.error(err, "Error starting watch");
1231
- process.exit(1);
1232
- }
1233
- }
1234
- function logEvent(type, message = "", obj) {
1235
- const logMessage = `Watch event ${type} received${message ? `. ${message}.` : "."}`;
1236
- if (obj) {
1237
- logger_default.debug(obj, logMessage);
1238
- } else {
1239
- logger_default.debug(logMessage);
1240
- }
1241
- }
1242
-
1243
- // src/lib/module.ts
1244
- var isWatchMode = () => process.env.PEPR_WATCH_MODE === "true";
1245
- var isBuildMode = () => process.env.PEPR_MODE === "build";
1246
- var isDevMode = () => process.env.PEPR_MODE === "dev";
1247
- var PeprModule = class {
1248
- #controller;
1249
- /**
1250
- * Create a new Pepr runtime
1251
- *
1252
- * @param config The configuration for the Pepr runtime
1253
- * @param capabilities The capabilities to be loaded into the Pepr runtime
1254
- * @param opts Options for the Pepr runtime
1255
- */
1256
- constructor({ description, pepr }, capabilities = [], opts = {}) {
1257
- const config = (0, import_ramda4.clone)(pepr);
1258
- config.description = description;
1259
- ValidateError(config.onError);
1260
- if (isBuildMode()) {
1261
- if (!process.send) {
1262
- throw new Error("process.send is not defined");
1263
- }
1264
- const exportedCapabilities = [];
1265
- for (const capability of capabilities) {
1266
- exportedCapabilities.push({
1267
- name: capability.name,
1268
- description: capability.description,
1269
- namespaces: capability.namespaces,
1270
- bindings: capability.bindings,
1271
- hasSchedule: capability.hasSchedule
1272
- });
1273
- }
1274
- process.send(exportedCapabilities);
1275
- return;
1276
- }
1277
- this.#controller = new Controller(config, capabilities, opts.beforeHook, opts.afterHook, () => {
1278
- if (isWatchMode() || isDevMode()) {
1279
- try {
1280
- setupWatch(capabilities);
1281
- } catch (e) {
1282
- logger_default.error(e, "Error setting up watch");
1283
- process.exit(1);
1284
- }
1285
- }
1286
- });
1287
- if (opts.deferStart) {
1288
- return;
1289
- }
1290
- this.start();
1291
- }
1292
- /**
1293
- * Start the Pepr runtime manually.
1294
- * Normally this is called automatically when the Pepr module is instantiated, but can be called manually if `deferStart` is set to `true` in the constructor.
1295
- *
1296
- * @param port
1297
- */
1298
- start = (port = 3e3) => {
1299
- this.#controller.startServer(port);
1300
- };
1301
- };
1302
-
1303
- // src/lib/storage.ts
1304
- var import_ramda5 = require("ramda");
1305
- var MAX_WAIT_TIME = 15e3;
1306
- var Storage = class {
1307
- #store = {};
1308
- #send;
1309
- #subscribers = {};
1310
- #subscriberId = 0;
1311
- #readyHandlers = [];
1312
- registerSender = (send) => {
1313
- this.#send = send;
1314
- };
1315
- receive = (data) => {
1316
- logger_default.debug(data, `Pepr store data received`);
1317
- this.#store = data || {};
1318
- this.#onReady();
1319
- for (const idx in this.#subscribers) {
1320
- this.#subscribers[idx]((0, import_ramda5.clone)(this.#store));
1321
- }
1322
- };
1323
- getItem = (key) => {
1324
- return this.#store[key] || null;
1325
- };
1326
- clear = () => {
1327
- this.#dispatchUpdate("remove", Object.keys(this.#store));
1328
- };
1329
- removeItem = (key) => {
1330
- this.#dispatchUpdate("remove", [key]);
1331
- };
1332
- setItem = (key, value) => {
1333
- this.#dispatchUpdate("add", [key], value);
1334
- };
1335
- /**
1336
- * Creates a promise and subscribes to the store, the promise resolves when
1337
- * the key and value are seen in the store.
1338
- *
1339
- * @param key - The key to add into the store
1340
- * @param value - The value of the key
1341
- * @returns
1342
- */
1343
- setItemAndWait = (key, value) => {
1344
- this.#dispatchUpdate("add", [key], value);
1345
- return new Promise((resolve, reject) => {
1346
- const unsubscribe = this.subscribe((data) => {
1347
- if (data[key] === value) {
1348
- unsubscribe();
1349
- resolve();
1350
- }
1351
- });
1352
- setTimeout(() => {
1353
- unsubscribe();
1354
- return reject();
1355
- }, MAX_WAIT_TIME);
1356
- });
1357
- };
1358
- /**
1359
- * Creates a promise and subscribes to the store, the promise resolves when
1360
- * the key is removed from the store.
1361
- *
1362
- * @param key - The key to add into the store
1363
- * @returns
1364
- */
1365
- removeItemAndWait = (key) => {
1366
- this.#dispatchUpdate("remove", [key]);
1367
- return new Promise((resolve, reject) => {
1368
- const unsubscribe = this.subscribe((data) => {
1369
- if (!Object.hasOwn(data, key)) {
1370
- unsubscribe();
1371
- resolve();
1372
- }
1373
- });
1374
- setTimeout(() => {
1375
- unsubscribe();
1376
- return reject();
1377
- }, MAX_WAIT_TIME);
1378
- });
1379
- };
1380
- subscribe = (subscriber) => {
1381
- const idx = this.#subscriberId++;
1382
- this.#subscribers[idx] = subscriber;
1383
- return () => this.unsubscribe(idx);
1384
- };
1385
- onReady = (callback) => {
1386
- this.#readyHandlers.push(callback);
1387
- };
1388
- /**
1389
- * Remove a subscriber from the list of subscribers.
1390
- * @param idx - The index of the subscriber to remove.
1391
- */
1392
- unsubscribe = (idx) => {
1393
- delete this.#subscribers[idx];
1394
- };
1395
- #onReady = () => {
1396
- for (const handler of this.#readyHandlers) {
1397
- handler((0, import_ramda5.clone)(this.#store));
1398
- }
1399
- this.#onReady = () => {
1400
- };
1401
- };
1402
- /**
1403
- * Dispatch an update to the store and notify all subscribers.
1404
- * @param op - The type of operation to perform.
1405
- * @param keys - The keys to update.
1406
- * @param [value] - The new value.
1407
- */
1408
- #dispatchUpdate = (op, keys, value) => {
1409
- this.#send(op, keys, value);
1410
- };
1411
- };
1412
-
1413
- // src/lib/schedule.ts
1414
- var OnSchedule = class {
1415
- intervalId = null;
1416
- store;
1417
- name;
1418
- completions;
1419
- every;
1420
- unit;
1421
- run;
1422
- startTime;
1423
- duration;
1424
- lastTimestamp;
1425
- constructor(schedule) {
1426
- this.name = schedule.name;
1427
- this.run = schedule.run;
1428
- this.every = schedule.every;
1429
- this.unit = schedule.unit;
1430
- this.startTime = schedule?.startTime;
1431
- this.completions = schedule?.completions;
1432
- }
1433
- setStore(store) {
1434
- this.store = store;
1435
- this.startInterval();
1436
- }
1437
- startInterval() {
1438
- this.checkStore();
1439
- this.getDuration();
1440
- this.setupInterval();
1441
- }
1442
- /**
1443
- * Checks the store for this schedule and sets the values if it exists
1444
- * @returns
1445
- */
1446
- checkStore() {
1447
- const result = this.store && this.store.getItem(this.name);
1448
- if (result) {
1449
- const storedSchedule = JSON.parse(result);
1450
- this.completions = storedSchedule?.completions;
1451
- this.startTime = storedSchedule?.startTime;
1452
- this.lastTimestamp = storedSchedule?.lastTimestamp;
1453
- }
1454
- }
1455
- /**
1456
- * Saves the schedule to the store
1457
- * @returns
1458
- */
1459
- saveToStore() {
1460
- const schedule = {
1461
- completions: this.completions,
1462
- startTime: this.startTime,
1463
- lastTimestamp: /* @__PURE__ */ new Date(),
1464
- name: this.name
1465
- };
1466
- this.store && this.store.setItem(this.name, JSON.stringify(schedule));
1467
- }
1468
- /**
1469
- * Gets the durations in milliseconds
1470
- */
1471
- getDuration() {
1472
- switch (this.unit) {
1473
- case "seconds":
1474
- if (this.every < 10)
1475
- throw new Error("10 Seconds in the smallest interval allowed");
1476
- this.duration = 1e3 * this.every;
1477
- break;
1478
- case "minutes":
1479
- case "minute":
1480
- this.duration = 1e3 * 60 * this.every;
1481
- break;
1482
- case "hours":
1483
- case "hour":
1484
- this.duration = 1e3 * 60 * 60 * this.every;
1485
- break;
1486
- default:
1487
- throw new Error("Invalid time unit");
1488
- }
1489
- }
1490
- /**
1491
- * Sets up the interval
1492
- */
1493
- setupInterval() {
1494
- const now = /* @__PURE__ */ new Date();
1495
- let delay;
1496
- if (this.lastTimestamp && this.startTime) {
1497
- this.startTime = void 0;
1498
- }
1499
- if (this.startTime) {
1500
- delay = this.startTime.getTime() - now.getTime();
1501
- } else if (this.lastTimestamp && this.duration) {
1502
- const lastTimestamp = new Date(this.lastTimestamp);
1503
- delay = this.duration - (now.getTime() - lastTimestamp.getTime());
1504
- }
1505
- if (delay === void 0 || delay <= 0) {
1506
- this.start();
1507
- } else {
1508
- setTimeout(() => {
1509
- this.start();
1510
- }, delay);
1511
- }
1512
- }
1513
- /**
1514
- * Starts the interval
1515
- */
1516
- start() {
1517
- this.intervalId = setInterval(() => {
1518
- if (this.completions === 0) {
1519
- this.stop();
1520
- return;
1521
- } else {
1522
- this.run();
1523
- if (this.completions && this.completions !== 0) {
1524
- this.completions -= 1;
1525
- }
1526
- this.saveToStore();
1527
- }
1528
- }, this.duration);
1529
- }
1530
- /**
1531
- * Stops the interval
1532
- */
1533
- stop() {
1534
- if (this.intervalId) {
1535
- clearInterval(this.intervalId);
1536
- this.intervalId = null;
1537
- }
1538
- this.store && this.store.removeItem(this.name);
1539
- }
1540
- };
1541
-
1542
- // src/lib/capability.ts
1543
- var registerAdmission = isBuildMode() || !isWatchMode();
1544
- var registerWatch = isBuildMode() || isWatchMode() || isDevMode();
1545
- var Capability = class {
1546
- #name;
1547
- #description;
1548
- #namespaces;
1549
- #bindings = [];
1550
- #store = new Storage();
1551
- #scheduleStore = new Storage();
1552
- #registered = false;
1553
- #scheduleRegistered = false;
1554
- hasSchedule;
1555
- /**
1556
- * Run code on a schedule with the capability.
1557
- *
1558
- * @param schedule The schedule to run the code on
1559
- * @returns
1560
- */
1561
- OnSchedule = (schedule) => {
1562
- const { name, every, unit, run, startTime, completions } = schedule;
1563
- this.hasSchedule = true;
1564
- if (process.env.PEPR_WATCH_MODE === "true" || process.env.PEPR_MODE === "dev") {
1565
- const newSchedule = {
1566
- name,
1567
- every,
1568
- unit,
1569
- run,
1570
- startTime,
1571
- completions
1572
- };
1573
- this.#scheduleStore.onReady(() => {
1574
- new OnSchedule(newSchedule).setStore(this.#scheduleStore);
1575
- });
1576
- }
1577
- };
1578
- /**
1579
- * Store is a key-value data store that can be used to persist data that should be shared
1580
- * between requests. Each capability has its own store, and the data is persisted in Kubernetes
1581
- * in the `pepr-system` namespace.
1582
- *
1583
- * Note: You should only access the store from within an action.
1584
- */
1585
- Store = {
1586
- clear: this.#store.clear,
1587
- getItem: this.#store.getItem,
1588
- removeItem: this.#store.removeItem,
1589
- removeItemAndWait: this.#store.removeItemAndWait,
1590
- setItem: this.#store.setItem,
1591
- subscribe: this.#store.subscribe,
1592
- onReady: this.#store.onReady,
1593
- setItemAndWait: this.#store.setItemAndWait
1594
- };
1595
- /**
1596
- * ScheduleStore is a key-value data store used to persist schedule data that should be shared
1597
- * between intervals. Each Schedule shares store, and the data is persisted in Kubernetes
1598
- * in the `pepr-system` namespace.
1599
- *
1600
- * Note: There is no direct access to schedule store
1601
- */
1602
- ScheduleStore = {
1603
- clear: this.#scheduleStore.clear,
1604
- getItem: this.#scheduleStore.getItem,
1605
- removeItemAndWait: this.#scheduleStore.removeItemAndWait,
1606
- removeItem: this.#scheduleStore.removeItem,
1607
- setItemAndWait: this.#scheduleStore.setItemAndWait,
1608
- setItem: this.#scheduleStore.setItem,
1609
- subscribe: this.#scheduleStore.subscribe,
1610
- onReady: this.#scheduleStore.onReady
1611
- };
1612
- get bindings() {
1613
- return this.#bindings;
1614
- }
1615
- get name() {
1616
- return this.#name;
1617
- }
1618
- get description() {
1619
- return this.#description;
1620
- }
1621
- get namespaces() {
1622
- return this.#namespaces || [];
1623
- }
1624
- constructor(cfg) {
1625
- this.#name = cfg.name;
1626
- this.#description = cfg.description;
1627
- this.#namespaces = cfg.namespaces;
1628
- this.hasSchedule = false;
1629
- logger_default.info(`Capability ${this.#name} registered`);
1630
- logger_default.debug(cfg);
1631
- }
1632
- /**
1633
- * Register the store with the capability. This is called automatically by the Pepr controller.
1634
- *
1635
- * @param store
1636
- */
1637
- registerScheduleStore = () => {
1638
- logger_default.info(`Registering schedule store for ${this.#name}`);
1639
- if (this.#scheduleRegistered) {
1640
- throw new Error(`Schedule store already registered for ${this.#name}`);
1641
- }
1642
- this.#scheduleRegistered = true;
1643
- return {
1644
- scheduleStore: this.#scheduleStore
1645
- };
1646
- };
1647
- /**
1648
- * Register the store with the capability. This is called automatically by the Pepr controller.
1649
- *
1650
- * @param store
1651
- */
1652
- registerStore = () => {
1653
- logger_default.info(`Registering store for ${this.#name}`);
1654
- if (this.#registered) {
1655
- throw new Error(`Store already registered for ${this.#name}`);
1656
- }
1657
- this.#registered = true;
1658
- return {
1659
- store: this.#store
1660
- };
1661
- };
1662
- /**
1663
- * The When method is used to register a action to be executed when a Kubernetes resource is
1664
- * processed by Pepr. The action will be executed if the resource matches the specified kind and any
1665
- * filters that are applied.
1666
- *
1667
- * @param model the KubernetesObject model to match
1668
- * @param kind if using a custom KubernetesObject not available in `a.*`, specify the GroupVersionKind
1669
- * @returns
1670
- */
1671
- When = (model, kind4) => {
1672
- const matchedKind = (0, import_kubernetes_fluent_client6.modelToGroupVersionKind)(model.name);
1673
- if (!matchedKind && !kind4) {
1674
- throw new Error(`Kind not specified for ${model.name}`);
1675
- }
1676
- const binding = {
1677
- model,
1678
- // If the kind is not specified, use the matched kind from the model
1679
- kind: kind4 || matchedKind,
1680
- event: "*" /* Any */,
1681
- filters: {
1682
- name: "",
1683
- namespaces: [],
1684
- labels: {},
1685
- annotations: {}
1686
- }
1687
- };
1688
- const bindings = this.#bindings;
1689
- const prefix = `${this.#name}: ${model.name}`;
1690
- const commonChain = { WithLabel, WithAnnotation, Mutate, Validate, Watch, Reconcile };
1691
- const isNotEmpty = (value) => Object.keys(value).length > 0;
1692
- const log = (message, cbString) => {
1693
- const filteredObj = (0, import_ramda6.pickBy)(isNotEmpty, binding.filters);
1694
- logger_default.info(`${message} configured for ${binding.event}`, prefix);
1695
- logger_default.info(filteredObj, prefix);
1696
- logger_default.debug(cbString, prefix);
1697
- };
1698
- function Validate(validateCallback) {
1699
- if (registerAdmission) {
1700
- log("Validate Action", validateCallback.toString());
1701
- bindings.push({
1702
- ...binding,
1703
- isValidate: true,
1704
- validateCallback
1705
- });
1706
- }
1707
- return { Watch, Reconcile };
1708
- }
1709
- function Mutate(mutateCallback) {
1710
- if (registerAdmission) {
1711
- log("Mutate Action", mutateCallback.toString());
1712
- bindings.push({
1713
- ...binding,
1714
- isMutate: true,
1715
- mutateCallback
1716
- });
1717
- }
1718
- return { Watch, Validate, Reconcile };
1719
- }
1720
- function Watch(watchCallback) {
1721
- if (registerWatch) {
1722
- log("Watch Action", watchCallback.toString());
1723
- bindings.push({
1724
- ...binding,
1725
- isWatch: true,
1726
- watchCallback
1727
- });
1728
- }
1729
- }
1730
- function Reconcile(watchCallback) {
1731
- if (registerWatch) {
1732
- log("Reconcile Action", watchCallback.toString());
1733
- bindings.push({
1734
- ...binding,
1735
- isWatch: true,
1736
- isQueue: true,
1737
- watchCallback
1738
- });
1739
- }
1740
- }
1741
- function InNamespace(...namespaces) {
1742
- logger_default.debug(`Add namespaces filter ${namespaces}`, prefix);
1743
- binding.filters.namespaces.push(...namespaces);
1744
- return { ...commonChain, WithName };
1745
- }
1746
- function WithName(name) {
1747
- logger_default.debug(`Add name filter ${name}`, prefix);
1748
- binding.filters.name = name;
1749
- return commonChain;
1750
- }
1751
- function WithLabel(key, value = "") {
1752
- logger_default.debug(`Add label filter ${key}=${value}`, prefix);
1753
- binding.filters.labels[key] = value;
1754
- return commonChain;
1755
- }
1756
- function WithAnnotation(key, value = "") {
1757
- logger_default.debug(`Add annotation filter ${key}=${value}`, prefix);
1758
- binding.filters.annotations[key] = value;
1759
- return commonChain;
1760
- }
1761
- function bindEvent(event) {
1762
- binding.event = event;
1763
- return {
1764
- ...commonChain,
1765
- InNamespace,
1766
- WithName
1767
- };
1768
- }
1769
- return {
1770
- IsCreatedOrUpdated: () => bindEvent("CREATEORUPDATE" /* CreateOrUpdate */),
1771
- IsCreated: () => bindEvent("CREATE" /* Create */),
1772
- IsUpdated: () => bindEvent("UPDATE" /* Update */),
1773
- IsDeleted: () => bindEvent("DELETE" /* Delete */)
1774
- };
1775
- };
1776
- };
1777
- // Annotate the CommonJS export names for ESM import in node:
1778
- 0 && (module.exports = {
1779
- Capability,
1780
- K8s,
1781
- Log,
1782
- PeprModule,
1783
- PeprMutateRequest,
1784
- PeprUtils,
1785
- PeprValidateRequest,
1786
- R,
1787
- RegisterKind,
1788
- a,
1789
- fetch,
1790
- fetchStatus,
1791
- kind,
1792
- sdk
1793
- });
1794
- //# sourceMappingURL=lib.js.map