pepr 0.37.2 → 0.37.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/lib.js DELETED
@@ -1,2193 +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_client8.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_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
- sdk: () => sdk_exports
47
- });
48
- module.exports = __toCommonJS(lib_exports);
49
- var import_kubernetes_fluent_client8 = require("kubernetes-fluent-client");
50
- var R = __toESM(require("ramda"));
51
-
52
- // src/lib/capability.ts
53
- var import_kubernetes_fluent_client7 = require("kubernetes-fluent-client");
54
- var import_ramda7 = 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_ramda5 = 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
- #gauges = /* @__PURE__ */ new Map();
92
- #summaries = /* @__PURE__ */ new Map();
93
- #prefix;
94
- #cacheMissWindows = /* @__PURE__ */ new Map();
95
- #metricNames = {
96
- errors: "errors",
97
- alerts: "alerts",
98
- mutate: "mutate",
99
- validate: "validate",
100
- cacheMiss: "cache_miss",
101
- resyncFailureCount: "resync_failure_count"
102
- };
103
- /**
104
- * Creates a MetricsCollector instance with prefixed metrics.
105
- * @param [prefix='pepr'] - The prefix for the metric names.
106
- */
107
- constructor(prefix = "pepr") {
108
- this.#registry = new import_prom_client.Registry();
109
- this.#prefix = prefix;
110
- this.addCounter(this.#metricNames.errors, "Mutation/Validate errors encountered");
111
- this.addCounter(this.#metricNames.alerts, "Mutation/Validate bad api token received");
112
- this.addSummary(this.#metricNames.mutate, "Mutation operation summary");
113
- this.addSummary(this.#metricNames.validate, "Validation operation summary");
114
- this.addGauge(this.#metricNames.cacheMiss, "Number of cache misses per window", ["window"]);
115
- this.addGauge(this.#metricNames.resyncFailureCount, "Number of failures per resync operation", ["count"]);
116
- }
117
- #getMetricName = (name) => `${this.#prefix}_${name}`;
118
- #addMetric = (collection, MetricType, name, help, labelNames) => {
119
- if (collection.has(this.#getMetricName(name))) {
120
- logger_default.debug(`Metric for ${name} already exists`, loggingPrefix);
121
- return;
122
- }
123
- const metric = new MetricType({
124
- name: this.#getMetricName(name),
125
- help,
126
- registers: [this.#registry],
127
- labelNames
128
- });
129
- collection.set(this.#getMetricName(name), metric);
130
- };
131
- addCounter = (name, help) => {
132
- this.#addMetric(this.#counters, import_prom_client.default.Counter, name, help, []);
133
- };
134
- addSummary = (name, help) => {
135
- this.#addMetric(this.#summaries, import_prom_client.default.Summary, name, help, []);
136
- };
137
- addGauge = (name, help, labelNames) => {
138
- this.#addMetric(this.#gauges, import_prom_client.default.Gauge, name, help, labelNames);
139
- };
140
- incCounter = (name) => {
141
- this.#counters.get(this.#getMetricName(name))?.inc();
142
- };
143
- incGauge = (name, labels, value = 1) => {
144
- this.#gauges.get(this.#getMetricName(name))?.inc(labels || {}, value);
145
- };
146
- /**
147
- * Increments the error counter.
148
- */
149
- error = () => this.incCounter(this.#metricNames.errors);
150
- /**
151
- * Increments the alerts counter.
152
- */
153
- alert = () => this.incCounter(this.#metricNames.alerts);
154
- /**
155
- * Observes the duration since the provided start time and updates the summary.
156
- * @param startTime - The start time.
157
- * @param name - The metrics summary to increment.
158
- */
159
- observeEnd = (startTime, name = this.#metricNames.mutate) => {
160
- this.#summaries.get(this.#getMetricName(name))?.observe(import_perf_hooks.performance.now() - startTime);
161
- };
162
- /**
163
- * Fetches the current metrics from the registry.
164
- * @returns The metrics.
165
- */
166
- getMetrics = () => this.#registry.metrics();
167
- /**
168
- * Returns the current timestamp from performance.now() method. Useful for start timing an operation.
169
- * @returns The timestamp.
170
- */
171
- static observeStart() {
172
- return import_perf_hooks.performance.now();
173
- }
174
- /**
175
- * Increments the cache miss gauge for a given label.
176
- * @param label - The label for the cache miss.
177
- */
178
- incCacheMiss = (window) => {
179
- this.incGauge(this.#metricNames.cacheMiss, { window });
180
- };
181
- /**
182
- * Increments the retry count gauge.
183
- * @param count - The count to increment by.
184
- */
185
- incRetryCount = (count) => {
186
- this.incGauge(this.#metricNames.resyncFailureCount, { count });
187
- };
188
- /**
189
- * Initializes the cache miss gauge for a given label.
190
- * @param label - The label for the cache miss.
191
- */
192
- initCacheMissWindow = (window) => {
193
- this.#rollCacheMissWindows();
194
- this.#gauges.get(this.#getMetricName(this.#metricNames.cacheMiss))?.set({ window }, 0);
195
- this.#cacheMissWindows.set(window, 0);
196
- };
197
- /**
198
- * Manages the size of the cache miss gauge map.
199
- */
200
- #rollCacheMissWindows = () => {
201
- const maxCacheMissWindows = process.env.PEPR_MAX_CACHE_MISS_WINDOWS ? parseInt(process.env.PEPR_MAX_CACHE_MISS_WINDOWS, 10) : void 0;
202
- if (maxCacheMissWindows !== void 0 && this.#cacheMissWindows.size >= maxCacheMissWindows) {
203
- const firstKey = this.#cacheMissWindows.keys().next().value;
204
- this.#cacheMissWindows.delete(firstKey);
205
- this.#gauges.get(this.#getMetricName(this.#metricNames.cacheMiss))?.remove({ window: firstKey });
206
- }
207
- };
208
- };
209
- var metricsCollector = new MetricsCollector("pepr");
210
-
211
- // src/lib/mutate-processor.ts
212
- var import_fast_json_patch = __toESM(require("fast-json-patch"));
213
-
214
- // src/lib/errors.ts
215
- var Errors = {
216
- audit: "audit",
217
- ignore: "ignore",
218
- reject: "reject"
219
- };
220
- var ErrorList = Object.values(Errors);
221
- function ValidateError(error = "") {
222
- if (!ErrorList.includes(error)) {
223
- throw new Error(`Invalid error: ${error}. Must be one of: ${ErrorList.join(", ")}`);
224
- }
225
- }
226
-
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
- const valDiffers = result.carried[key] !== result.defined[key];
313
- return keyMissing ? { [key]: val } : noValue ? {} : valMissing ? { [key]: val } : valDiffers ? { [key]: val } : {};
314
- }).reduce((acc, cur) => ({ ...acc, ...cur }), {});
315
- return result.unalike;
316
- },
317
- (unalike) => Object.keys(unalike).length > 0
318
- );
319
- var mismatchedAnnotations = (0, import_ramda.allPass)([
320
- (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), definesAnnotations),
321
- (0, import_ramda.pipe)((bnd, obj) => metasMismatch(definedAnnotations(bnd), carriedAnnotations(obj)))
322
- ]);
323
- var mismatchedLabels = (0, import_ramda.allPass)([
324
- (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), definesLabels),
325
- (0, import_ramda.pipe)((bnd, obj) => metasMismatch(definedLabels(bnd), carriedLabels(obj)))
326
- ]);
327
- var uncarryableNamespace = (0, import_ramda.allPass)([
328
- (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), import_ramda.length, (0, import_ramda.gt)(import_ramda.__, 0)),
329
- (0, import_ramda.pipe)((0, import_ramda.nthArg)(1), carriesNamespace),
330
- (0, import_ramda.pipe)((nss, obj) => nss.includes(carriedNamespace(obj)), import_ramda.not)
331
- ]);
332
- var carriesIgnoredNamespace = (0, import_ramda.allPass)([
333
- (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), import_ramda.length, (0, import_ramda.gt)(import_ramda.__, 0)),
334
- (0, import_ramda.pipe)((0, import_ramda.nthArg)(1), carriesNamespace),
335
- (0, import_ramda.pipe)((nss, obj) => nss.includes(carriedNamespace(obj)))
336
- ]);
337
- var unbindableNamespaces = (0, import_ramda.allPass)([
338
- (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), import_ramda.length, (0, import_ramda.gt)(import_ramda.__, 0)),
339
- (0, import_ramda.pipe)((0, import_ramda.nthArg)(1), definesNamespaces),
340
- (0, import_ramda.pipe)((nss, bnd) => (0, import_ramda.difference)(definedNamespaces(bnd), nss), import_ramda.length, (0, import_ramda.equals)(0), import_ramda.not)
341
- ]);
342
- var misboundDeleteWithDeletionTimestamp = (0, import_ramda.allPass)([definesDelete, definesDeletionTimestamp]);
343
- var operationMatchesEvent = (0, import_ramda.anyPass)([
344
- (0, import_ramda.pipe)((0, import_ramda.nthArg)(1), (0, import_ramda.equals)("*" /* Any */)),
345
- (0, import_ramda.pipe)((op, evt) => op === evt),
346
- (0, import_ramda.pipe)((op, evt) => op ? evt.includes(op) : false)
347
- ]);
348
- var mismatchedEvent = (0, import_ramda.pipe)(
349
- (binding, request) => operationMatchesEvent(declaredOperation(request), definedEvent(binding)),
350
- import_ramda.not
351
- );
352
- var mismatchedGroup = (0, import_ramda.allPass)([
353
- (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), definesGroup),
354
- (0, import_ramda.pipe)((binding, request) => definedGroup(binding) !== declaredGroup(request))
355
- ]);
356
- var mismatchedVersion = (0, import_ramda.allPass)([
357
- (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), definesVersion),
358
- (0, import_ramda.pipe)((binding, request) => definedVersion(binding) !== declaredVersion(request))
359
- ]);
360
- var mismatchedKind = (0, import_ramda.allPass)([
361
- (0, import_ramda.pipe)((0, import_ramda.nthArg)(0), definesKind),
362
- (0, import_ramda.pipe)((binding, request) => definedKind(binding) !== declaredKind(request))
363
- ]);
364
-
365
- // src/lib/filter.ts
366
- function shouldSkipRequest(binding, req, capabilityNamespaces, ignoredNamespaces) {
367
- const prefix = "Ignoring Admission Callback:";
368
- const obj = req.operation === "DELETE" /* DELETE */ ? req.oldObject : req.object;
369
- return misboundDeleteWithDeletionTimestamp(binding) ? `${prefix} Cannot use deletionTimestamp filter on a DELETE operation.` : mismatchedDeletionTimestamp(binding, obj) ? `${prefix} Binding defines deletionTimestamp but Object does not carry it.` : mismatchedEvent(binding, req) ? `${prefix} Binding defines event '${definedEvent(binding)}' but Request declares '${declaredOperation(req)}'.` : mismatchedName(binding, obj) ? `${prefix} Binding defines name '${definedName(binding)}' but Object carries '${carriedName(obj)}'.` : mismatchedGroup(binding, req) ? `${prefix} Binding defines group '${definedGroup(binding)}' but Request declares '${declaredGroup(req)}'.` : mismatchedVersion(binding, req) ? `${prefix} Binding defines version '${definedVersion(binding)}' but Request declares '${declaredVersion(req)}'.` : mismatchedKind(binding, req) ? `${prefix} Binding defines kind '${definedKind(binding)}' but Request declares '${declaredKind(req)}'.` : unbindableNamespaces(capabilityNamespaces, binding) ? `${prefix} Binding defines namespaces ${JSON.stringify(definedNamespaces(binding))} but namespaces allowed by Capability are '${JSON.stringify(capabilityNamespaces)}'.` : uncarryableNamespace(capabilityNamespaces, obj) ? `${prefix} Object carries namespace '${carriedNamespace(obj)}' 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)}'.` : 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))}'.` : 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)}'.` : "";
370
- }
371
-
372
- // src/lib/mutate-request.ts
373
- var import_ramda2 = require("ramda");
374
- var PeprMutateRequest = class {
375
- Raw;
376
- #input;
377
- get PermitSideEffects() {
378
- return !this.#input.dryRun;
379
- }
380
- /**
381
- * Indicates whether the request is a dry run.
382
- * @returns true if the request is a dry run, false otherwise.
383
- */
384
- get IsDryRun() {
385
- return this.#input.dryRun;
386
- }
387
- /**
388
- * Provides access to the old resource in the request if available.
389
- * @returns The old Kubernetes resource object or null if not available.
390
- */
391
- get OldResource() {
392
- return this.#input.oldObject;
393
- }
394
- /**
395
- * Provides access to the request object.
396
- * @returns The request object containing the Kubernetes resource.
397
- */
398
- get Request() {
399
- return this.#input;
400
- }
401
- /**
402
- * Creates a new instance of the action class.
403
- * @param input - The request object containing the Kubernetes resource to modify.
404
- */
405
- constructor(input) {
406
- this.#input = input;
407
- if (input.operation.toUpperCase() === "DELETE" /* DELETE */) {
408
- this.Raw = (0, import_ramda2.clone)(input.oldObject);
409
- } else {
410
- this.Raw = (0, import_ramda2.clone)(input.object);
411
- }
412
- if (!this.Raw) {
413
- throw new Error("unable to load the request object into PeprRequest.RawP");
414
- }
415
- }
416
- /**
417
- * Deep merges the provided object with the current resource.
418
- *
419
- * @param obj - The object to merge with the current resource.
420
- */
421
- Merge = (obj) => {
422
- this.Raw = (0, import_ramda2.mergeDeepRight)(this.Raw, obj);
423
- };
424
- /**
425
- * Updates a label on the Kubernetes resource.
426
- * @param key - The key of the label to update.
427
- * @param value - The value of the label.
428
- * @returns The current action instance for method chaining.
429
- */
430
- SetLabel = (key, value) => {
431
- const ref = this.Raw;
432
- ref.metadata = ref.metadata ?? {};
433
- ref.metadata.labels = ref.metadata.labels ?? {};
434
- ref.metadata.labels[key] = value;
435
- return this;
436
- };
437
- /**
438
- * Updates an annotation on the Kubernetes resource.
439
- * @param key - The key of the annotation to update.
440
- * @param value - The value of the annotation.
441
- * @returns The current action instance for method chaining.
442
- */
443
- SetAnnotation = (key, value) => {
444
- const ref = this.Raw;
445
- ref.metadata = ref.metadata ?? {};
446
- ref.metadata.annotations = ref.metadata.annotations ?? {};
447
- ref.metadata.annotations[key] = value;
448
- return this;
449
- };
450
- /**
451
- * Removes a label from the Kubernetes resource.
452
- * @param key - The key of the label to remove.
453
- * @returns The current Action instance for method chaining.
454
- */
455
- RemoveLabel = (key) => {
456
- if (this.Raw.metadata?.labels?.[key]) {
457
- delete this.Raw.metadata.labels[key];
458
- }
459
- return this;
460
- };
461
- /**
462
- * Removes an annotation from the Kubernetes resource.
463
- * @param key - The key of the annotation to remove.
464
- * @returns The current Action instance for method chaining.
465
- */
466
- RemoveAnnotation = (key) => {
467
- if (this.Raw.metadata?.annotations?.[key]) {
468
- delete this.Raw.metadata.annotations[key];
469
- }
470
- return this;
471
- };
472
- /**
473
- * Check if a label exists on the Kubernetes resource.
474
- *
475
- * @param key the label key to check
476
- * @returns
477
- */
478
- HasLabel = (key) => {
479
- return this.Raw.metadata?.labels?.[key] !== void 0;
480
- };
481
- /**
482
- * Check if an annotation exists on the Kubernetes resource.
483
- *
484
- * @param key the annotation key to check
485
- * @returns
486
- */
487
- HasAnnotation = (key) => {
488
- return this.Raw.metadata?.annotations?.[key] !== void 0;
489
- };
490
- };
491
-
492
- // src/lib/utils.ts
493
- var utils_exports = {};
494
- __export(utils_exports, {
495
- base64Decode: () => base64Decode,
496
- base64Encode: () => base64Encode,
497
- convertFromBase64Map: () => convertFromBase64Map,
498
- convertToBase64Map: () => convertToBase64Map,
499
- isAscii: () => isAscii
500
- });
501
- var isAscii = /^[\s\x20-\x7E]*$/;
502
- function convertToBase64Map(obj, skip) {
503
- obj.data = obj.data ?? {};
504
- for (const key in obj.data) {
505
- const value = obj.data[key];
506
- obj.data[key] = skip.includes(key) ? value : base64Encode(value);
507
- }
508
- }
509
- function convertFromBase64Map(obj) {
510
- const skip = [];
511
- obj.data = obj.data ?? {};
512
- for (const key in obj.data) {
513
- if (obj.data[key] == void 0) {
514
- obj.data[key] = "";
515
- } else {
516
- const decoded = base64Decode(obj.data[key]);
517
- if (isAscii.test(decoded)) {
518
- obj.data[key] = decoded;
519
- } else {
520
- skip.push(key);
521
- }
522
- }
523
- }
524
- logger_default.debug(`Non-ascii data detected in keys: ${skip}, skipping automatic base64 decoding`);
525
- return skip;
526
- }
527
- function base64Decode(data) {
528
- return Buffer.from(data, "base64").toString("utf-8");
529
- }
530
- function base64Encode(data) {
531
- return Buffer.from(data).toString("base64");
532
- }
533
-
534
- // src/lib/mutate-processor.ts
535
- async function mutateProcessor(config, capabilities, req, reqMetadata) {
536
- const wrapped = new PeprMutateRequest(req);
537
- const response = {
538
- uid: req.uid,
539
- warnings: [],
540
- allowed: false
541
- };
542
- let matchedAction = false;
543
- let skipDecode = [];
544
- const isSecret = req.kind.version == "v1" && req.kind.kind == "Secret";
545
- if (isSecret) {
546
- skipDecode = convertFromBase64Map(wrapped.Raw);
547
- }
548
- logger_default.info(reqMetadata, `Processing request`);
549
- for (const { name, bindings, namespaces } of capabilities) {
550
- const actionMetadata = { ...reqMetadata, name };
551
- for (const action of bindings) {
552
- if (!action.mutateCallback) {
553
- continue;
554
- }
555
- const shouldSkip = shouldSkipRequest(action, req, namespaces, config?.alwaysIgnore?.namespaces);
556
- if (shouldSkip !== "") {
557
- logger_default.debug(shouldSkip);
558
- continue;
559
- }
560
- const label = action.mutateCallback.name;
561
- logger_default.info(actionMetadata, `Processing mutation action (${label})`);
562
- matchedAction = true;
563
- const updateStatus = (status) => {
564
- if (req.operation == "DELETE") {
565
- return;
566
- }
567
- const identifier = `${config.uuid}.pepr.dev/${name}`;
568
- wrapped.Raw.metadata = wrapped.Raw.metadata || {};
569
- wrapped.Raw.metadata.annotations = wrapped.Raw.metadata.annotations || {};
570
- wrapped.Raw.metadata.annotations[identifier] = status;
571
- };
572
- updateStatus("started");
573
- try {
574
- await action.mutateCallback(wrapped);
575
- logger_default.info(actionMetadata, `Mutation action succeeded (${label})`);
576
- updateStatus("succeeded");
577
- } catch (e) {
578
- updateStatus("warning");
579
- response.warnings = response.warnings || [];
580
- let errorMessage = "";
581
- try {
582
- if (e.message && e.message !== "[object Object]") {
583
- errorMessage = e.message;
584
- } else {
585
- throw new Error("An error occurred in the mutate action.");
586
- }
587
- } catch (e2) {
588
- errorMessage = "An error occurred with the mutate action.";
589
- }
590
- logger_default.error(actionMetadata, `Action failed: ${errorMessage}`);
591
- response.warnings.push(`Action failed: ${errorMessage}`);
592
- switch (config.onError) {
593
- case Errors.reject:
594
- logger_default.error(actionMetadata, `Action failed: ${errorMessage}`);
595
- response.result = "Pepr module configured to reject on error";
596
- return response;
597
- case Errors.audit:
598
- response.auditAnnotations = response.auditAnnotations || {};
599
- response.auditAnnotations[Date.now()] = `Action failed: ${errorMessage}`;
600
- break;
601
- }
602
- }
603
- }
604
- }
605
- response.allowed = true;
606
- if (!matchedAction) {
607
- logger_default.info(reqMetadata, `No matching actions found`);
608
- return response;
609
- }
610
- if (req.operation == "DELETE") {
611
- return response;
612
- }
613
- const transformed = wrapped.Raw;
614
- if (isSecret) {
615
- convertToBase64Map(transformed, skipDecode);
616
- }
617
- const patches = import_fast_json_patch.default.compare(req.object, transformed);
618
- if (patches.length > 0) {
619
- response.patchType = "JSONPatch";
620
- response.patch = base64Encode(JSON.stringify(patches));
621
- }
622
- if (response.warnings && response.warnings.length < 1) {
623
- delete response.warnings;
624
- }
625
- logger_default.debug({ ...reqMetadata, patches }, `Patches generated`);
626
- return response;
627
- }
628
-
629
- // src/lib/validate-request.ts
630
- var import_ramda3 = require("ramda");
631
- var PeprValidateRequest = class {
632
- Raw;
633
- #input;
634
- /**
635
- * Provides access to the old resource in the request if available.
636
- * @returns The old Kubernetes resource object or null if not available.
637
- */
638
- get OldResource() {
639
- return this.#input.oldObject;
640
- }
641
- /**
642
- * Provides access to the request object.
643
- * @returns The request object containing the Kubernetes resource.
644
- */
645
- get Request() {
646
- return this.#input;
647
- }
648
- /**
649
- * Creates a new instance of the Action class.
650
- * @param input - The request object containing the Kubernetes resource to modify.
651
- */
652
- constructor(input) {
653
- this.#input = input;
654
- if (input.operation.toUpperCase() === "DELETE" /* DELETE */) {
655
- this.Raw = (0, import_ramda3.clone)(input.oldObject);
656
- } else {
657
- this.Raw = (0, import_ramda3.clone)(input.object);
658
- }
659
- if (!this.Raw) {
660
- throw new Error("unable to load the request object into PeprRequest.Raw");
661
- }
662
- }
663
- /**
664
- * Check if a label exists on the Kubernetes resource.
665
- *
666
- * @param key the label key to check
667
- * @returns
668
- */
669
- HasLabel = (key) => {
670
- return this.Raw.metadata?.labels?.[key] !== void 0;
671
- };
672
- /**
673
- * Check if an annotation exists on the Kubernetes resource.
674
- *
675
- * @param key the annotation key to check
676
- * @returns
677
- */
678
- HasAnnotation = (key) => {
679
- return this.Raw.metadata?.annotations?.[key] !== void 0;
680
- };
681
- /**
682
- * Create a validation response that allows the request.
683
- *
684
- * @returns The validation response.
685
- */
686
- Approve = () => {
687
- return {
688
- allowed: true
689
- };
690
- };
691
- /**
692
- * Create a validation response that denies the request.
693
- *
694
- * @param statusMessage Optional status message to return to the user.
695
- * @param statusCode Optional status code to return to the user.
696
- * @returns The validation response.
697
- */
698
- Deny = (statusMessage, statusCode) => {
699
- return {
700
- allowed: false,
701
- statusCode,
702
- statusMessage
703
- };
704
- };
705
- };
706
-
707
- // src/lib/validate-processor.ts
708
- async function validateProcessor(config, capabilities, req, reqMetadata) {
709
- const wrapped = new PeprValidateRequest(req);
710
- const response = [];
711
- const isSecret = req.kind.version == "v1" && req.kind.kind == "Secret";
712
- if (isSecret) {
713
- convertFromBase64Map(wrapped.Raw);
714
- }
715
- logger_default.info(reqMetadata, `Processing validation request`);
716
- for (const { name, bindings, namespaces } of capabilities) {
717
- const actionMetadata = { ...reqMetadata, name };
718
- for (const action of bindings) {
719
- if (!action.validateCallback) {
720
- continue;
721
- }
722
- const localResponse = {
723
- uid: req.uid,
724
- allowed: true
725
- // Assume it's allowed until a validation check fails
726
- };
727
- const shouldSkip = shouldSkipRequest(action, req, namespaces, config?.alwaysIgnore?.namespaces);
728
- if (shouldSkip !== "") {
729
- logger_default.debug(shouldSkip);
730
- continue;
731
- }
732
- const label = action.validateCallback.name;
733
- logger_default.info(actionMetadata, `Processing validation action (${label})`);
734
- try {
735
- const resp = await action.validateCallback(wrapped);
736
- localResponse.allowed = resp.allowed;
737
- if (resp.statusCode || resp.statusMessage) {
738
- localResponse.status = {
739
- code: resp.statusCode || 400,
740
- message: resp.statusMessage || `Validation failed for ${name}`
741
- };
742
- }
743
- logger_default.info(actionMetadata, `Validation action complete (${label}): ${resp.allowed ? "allowed" : "denied"}`);
744
- } catch (e) {
745
- logger_default.error(actionMetadata, `Action failed: ${JSON.stringify(e)}`);
746
- localResponse.allowed = false;
747
- localResponse.status = {
748
- code: 500,
749
- message: `Action failed with error: ${JSON.stringify(e)}`
750
- };
751
- return [localResponse];
752
- }
753
- response.push(localResponse);
754
- }
755
- }
756
- return response;
757
- }
758
-
759
- // src/lib/controller/store.ts
760
- var import_kubernetes_fluent_client2 = require("kubernetes-fluent-client");
761
- var import_ramda4 = require("ramda");
762
-
763
- // src/lib/k8s.ts
764
- var import_kubernetes_fluent_client = require("kubernetes-fluent-client");
765
- var PeprStore = class extends import_kubernetes_fluent_client.GenericKind {
766
- };
767
- var peprStoreGVK = {
768
- kind: "PeprStore",
769
- version: "v1",
770
- group: "pepr.dev"
771
- };
772
- (0, import_kubernetes_fluent_client.RegisterKind)(PeprStore, peprStoreGVK);
773
-
774
- // src/lib/controller/store.ts
775
- var redactedValue = "**redacted**";
776
- var namespace = "pepr-system";
777
- var debounceBackoff = 5e3;
778
- var PeprControllerStore = class {
779
- #name;
780
- #stores = {};
781
- #sendDebounce;
782
- #onReady;
783
- constructor(capabilities, name, onReady) {
784
- this.#onReady = onReady;
785
- this.#name = name;
786
- if (name.includes("schedule")) {
787
- for (const { name: name2, registerScheduleStore, hasSchedule } of capabilities) {
788
- if (hasSchedule !== true) {
789
- continue;
790
- }
791
- const { scheduleStore } = registerScheduleStore();
792
- scheduleStore.registerSender(this.#send(name2));
793
- this.#stores[name2] = scheduleStore;
794
- }
795
- } else {
796
- for (const { name: name2, registerStore } of capabilities) {
797
- const { store } = registerStore();
798
- store.registerSender(this.#send(name2));
799
- this.#stores[name2] = store;
800
- }
801
- }
802
- setTimeout(
803
- () => (0, import_kubernetes_fluent_client2.K8s)(PeprStore).InNamespace(namespace).Get(this.#name).then(async (store) => await this.#migrateAndSetupWatch(store)).catch(this.#createStoreResource),
804
- Math.random() * 3e3
805
- );
806
- }
807
- #setupWatch = () => {
808
- const watcher = (0, import_kubernetes_fluent_client2.K8s)(PeprStore, { name: this.#name, namespace }).Watch(this.#receive);
809
- watcher.start().catch((e) => logger_default.error(e, "Error starting Pepr store watch"));
810
- };
811
- #migrateAndSetupWatch = async (store) => {
812
- logger_default.debug(redactedStore(store), "Pepr Store migration");
813
- const data = store.data || {};
814
- const migrateCache = {};
815
- const flushCache = async () => {
816
- const indexes = Object.keys(migrateCache);
817
- const payload = Object.values(migrateCache);
818
- for (const idx of indexes) {
819
- delete migrateCache[idx];
820
- }
821
- try {
822
- if (payload.length > 0) {
823
- await (0, import_kubernetes_fluent_client2.K8s)(PeprStore, { namespace, name: this.#name }).Patch(payload);
824
- }
825
- } catch (err) {
826
- logger_default.error(err, "Pepr store update failure");
827
- if (err.status === 422) {
828
- Object.keys(migrateCache).forEach((key) => delete migrateCache[key]);
829
- } else {
830
- for (const idx of indexes) {
831
- migrateCache[idx] = payload[Number(idx)];
832
- }
833
- }
834
- }
835
- };
836
- const fillCache = (name, op, key, val) => {
837
- if (op === "add") {
838
- const path = `/data/${name}-v2-${key}`;
839
- const value = val || "";
840
- const cacheIdx = [op, path, value].join(":");
841
- migrateCache[cacheIdx] = { op, path, value };
842
- return;
843
- }
844
- if (op === "remove") {
845
- if (key.length < 1) {
846
- throw new Error(`Key is required for REMOVE operation`);
847
- }
848
- for (const k of key) {
849
- const path = `/data/${name}-${k}`;
850
- const cacheIdx = [op, path].join(":");
851
- migrateCache[cacheIdx] = { op, path };
852
- }
853
- return;
854
- }
855
- throw new Error(`Unsupported operation: ${op}`);
856
- };
857
- for (const name of Object.keys(this.#stores)) {
858
- const offset = `${name}-`.length;
859
- for (const key of Object.keys(data)) {
860
- if ((0, import_ramda4.startsWith)(name, key) && !(0, import_ramda4.startsWith)(`${name}-v2`, key)) {
861
- fillCache(name, "remove", [key.slice(offset)], data[key]);
862
- fillCache(name, "add", [key.slice(offset)], data[key]);
863
- }
864
- }
865
- }
866
- await flushCache();
867
- this.#setupWatch();
868
- };
869
- #receive = (store) => {
870
- logger_default.debug(redactedStore(store), "Pepr Store update");
871
- const debounced = () => {
872
- const data = store.data || {};
873
- for (const name of Object.keys(this.#stores)) {
874
- const offset = `${name}-`.length;
875
- const filtered = {};
876
- for (const key of Object.keys(data)) {
877
- if ((0, import_ramda4.startsWith)(name, key)) {
878
- filtered[key.slice(offset)] = data[key];
879
- }
880
- }
881
- this.#stores[name].receive(filtered);
882
- }
883
- if (this.#onReady) {
884
- this.#onReady();
885
- this.#onReady = void 0;
886
- }
887
- };
888
- clearTimeout(this.#sendDebounce);
889
- this.#sendDebounce = setTimeout(debounced, this.#onReady ? 0 : debounceBackoff);
890
- };
891
- #send = (capabilityName) => {
892
- const sendCache = {};
893
- const fillCache = (op, key, val) => {
894
- if (op === "add") {
895
- const path = `/data/${capabilityName}-${key}`;
896
- const value = val || "";
897
- const cacheIdx = [op, path, value].join(":");
898
- sendCache[cacheIdx] = { op, path, value };
899
- return;
900
- }
901
- if (op === "remove") {
902
- if (key.length < 1) {
903
- throw new Error(`Key is required for REMOVE operation`);
904
- }
905
- for (const k of key) {
906
- const path = `/data/${capabilityName}-${k}`;
907
- const cacheIdx = [op, path].join(":");
908
- sendCache[cacheIdx] = { op, path };
909
- }
910
- return;
911
- }
912
- throw new Error(`Unsupported operation: ${op}`);
913
- };
914
- const flushCache = async () => {
915
- const indexes = Object.keys(sendCache);
916
- const payload = Object.values(sendCache);
917
- for (const idx of indexes) {
918
- delete sendCache[idx];
919
- }
920
- try {
921
- if (payload.length > 0) {
922
- await (0, import_kubernetes_fluent_client2.K8s)(PeprStore, { namespace, name: this.#name }).Patch(payload);
923
- }
924
- } catch (err) {
925
- logger_default.error(err, "Pepr store update failure");
926
- if (err.status === 422) {
927
- Object.keys(sendCache).forEach((key) => delete sendCache[key]);
928
- } else {
929
- for (const idx of indexes) {
930
- sendCache[idx] = payload[Number(idx)];
931
- }
932
- }
933
- }
934
- };
935
- const sender = async (op, key, val) => {
936
- fillCache(op, key, val);
937
- };
938
- setInterval(() => {
939
- if (Object.keys(sendCache).length > 0) {
940
- logger_default.debug(redactedPatch(sendCache), "Sending updates to Pepr store");
941
- void flushCache();
942
- }
943
- }, debounceBackoff);
944
- return sender;
945
- };
946
- #createStoreResource = async (e) => {
947
- logger_default.info(`Pepr store not found, creating...`);
948
- logger_default.debug(e);
949
- try {
950
- await (0, import_kubernetes_fluent_client2.K8s)(PeprStore).Apply({
951
- metadata: {
952
- name: this.#name,
953
- namespace
954
- },
955
- data: {
956
- // JSON Patch will die if the data is empty, so we need to add a placeholder
957
- __pepr_do_not_delete__: "k-thx-bye"
958
- }
959
- });
960
- this.#setupWatch();
961
- } catch (err) {
962
- logger_default.error(err, "Failed to create Pepr store");
963
- }
964
- };
965
- };
966
- function redactedStore(store) {
967
- const redacted = process.env.PEPR_STORE_REDACT_VALUES === "true";
968
- return {
969
- ...store,
970
- data: Object.keys(store.data).reduce((acc, key) => {
971
- acc[key] = redacted ? redactedValue : store.data[key];
972
- return acc;
973
- }, {})
974
- };
975
- }
976
- function redactedPatch(patch = {}) {
977
- const redacted = process.env.PEPR_STORE_REDACT_VALUES === "true";
978
- if (!redacted) {
979
- return patch;
980
- }
981
- const redactedCache = {};
982
- Object.keys(patch).forEach((key) => {
983
- const operation = patch[key];
984
- const redactedKey = key.includes(":") ? key.substring(0, key.lastIndexOf(":")) + ":**redacted**" : key;
985
- const redactedOperation = {
986
- ...operation,
987
- ..."value" in operation ? { value: "**redacted**" } : {}
988
- };
989
- redactedCache[redactedKey] = redactedOperation;
990
- });
991
- return redactedCache;
992
- }
993
-
994
- // src/lib/controller/index.ts
995
- if (!process.env.PEPR_NODE_WARNINGS) {
996
- process.removeAllListeners("warning");
997
- }
998
- var Controller = class _Controller {
999
- // Track whether the server is running
1000
- #running = false;
1001
- // Metrics collector
1002
- #metricsCollector = metricsCollector;
1003
- // The token used to authenticate requests
1004
- #token = "";
1005
- // The express app instance
1006
- #app = (0, import_express.default)();
1007
- // Initialized with the constructor
1008
- #config;
1009
- #capabilities;
1010
- #beforeHook;
1011
- #afterHook;
1012
- constructor(config, capabilities, beforeHook, afterHook, onReady) {
1013
- this.#config = config;
1014
- this.#capabilities = capabilities;
1015
- new PeprControllerStore(capabilities, `pepr-${config.uuid}-store`, () => {
1016
- this.#bindEndpoints();
1017
- onReady && onReady();
1018
- logger_default.info("\u2705 Controller startup complete");
1019
- new PeprControllerStore(capabilities, `pepr-${config.uuid}-schedule`, () => {
1020
- logger_default.info("\u2705 Scheduling processed");
1021
- });
1022
- });
1023
- this.#app.use(_Controller.#logger);
1024
- this.#app.use(import_express.default.json({ limit: "2mb" }));
1025
- if (beforeHook) {
1026
- logger_default.info(`Using beforeHook: ${beforeHook}`);
1027
- this.#beforeHook = beforeHook;
1028
- }
1029
- if (afterHook) {
1030
- logger_default.info(`Using afterHook: ${afterHook}`);
1031
- this.#afterHook = afterHook;
1032
- }
1033
- }
1034
- /** Start the webhook server */
1035
- startServer = (port) => {
1036
- if (this.#running) {
1037
- throw new Error("Cannot start Pepr module: Pepr module was not instantiated with deferStart=true");
1038
- }
1039
- const options = {
1040
- key: import_fs.default.readFileSync(process.env.SSL_KEY_PATH || "/etc/certs/tls.key"),
1041
- cert: import_fs.default.readFileSync(process.env.SSL_CERT_PATH || "/etc/certs/tls.crt")
1042
- };
1043
- if (!isWatchMode()) {
1044
- this.#token = process.env.PEPR_API_TOKEN || import_fs.default.readFileSync("/app/api-token/value").toString().trim();
1045
- logger_default.info(`Using API token: ${this.#token}`);
1046
- if (!this.#token) {
1047
- throw new Error("API token not found");
1048
- }
1049
- }
1050
- const server = import_https.default.createServer(options, this.#app).listen(port);
1051
- server.on("listening", () => {
1052
- logger_default.info(`Server listening on port ${port}`);
1053
- this.#running = true;
1054
- });
1055
- server.on("error", (e) => {
1056
- if (e.code === "EADDRINUSE") {
1057
- logger_default.info(
1058
- `Address in use, retrying in 2 seconds. If this persists, ensure ${port} is not in use, e.g. "lsof -i :${port}"`
1059
- );
1060
- setTimeout(() => {
1061
- server.close();
1062
- server.listen(port);
1063
- }, 2e3);
1064
- }
1065
- });
1066
- process.on("SIGTERM", () => {
1067
- logger_default.info("Received SIGTERM, closing server");
1068
- server.close(() => {
1069
- logger_default.info("Server closed");
1070
- process.exit(0);
1071
- });
1072
- });
1073
- };
1074
- #bindEndpoints = () => {
1075
- this.#app.get("/healthz", _Controller.#healthz);
1076
- this.#app.get("/metrics", this.#metrics);
1077
- if (isWatchMode()) {
1078
- return;
1079
- }
1080
- this.#app.use(["/mutate/:token", "/validate/:token"], this.#validateToken);
1081
- this.#app.post("/mutate/:token", this.#admissionReq("Mutate"));
1082
- this.#app.post("/validate/:token", this.#admissionReq("Validate"));
1083
- };
1084
- /**
1085
- * Validate the token in the request path
1086
- *
1087
- * @param req The incoming request
1088
- * @param res The outgoing response
1089
- * @param next The next middleware function
1090
- * @returns
1091
- */
1092
- #validateToken = (req, res, next) => {
1093
- const { token } = req.params;
1094
- if (token !== this.#token) {
1095
- const err = `Unauthorized: invalid token '${token.replace(/[^\w]/g, "_")}'`;
1096
- logger_default.info(err);
1097
- res.status(401).send(err);
1098
- this.#metricsCollector.alert();
1099
- return;
1100
- }
1101
- next();
1102
- };
1103
- /**
1104
- * Metrics endpoint handler
1105
- *
1106
- * @param req the incoming request
1107
- * @param res the outgoing response
1108
- */
1109
- #metrics = async (req, res) => {
1110
- try {
1111
- res.send(await this.#metricsCollector.getMetrics());
1112
- } catch (err) {
1113
- logger_default.error(err, `Error getting metrics`);
1114
- res.status(500).send("Internal Server Error");
1115
- }
1116
- };
1117
- /**
1118
- * Admission request handler for both mutate and validate requests
1119
- *
1120
- * @param admissionKind the type of admission request
1121
- * @returns the request handler
1122
- */
1123
- #admissionReq = (admissionKind) => {
1124
- return async (req, res) => {
1125
- const startTime = MetricsCollector.observeStart();
1126
- try {
1127
- const request = req.body?.request || {};
1128
- this.#beforeHook && this.#beforeHook(request || {});
1129
- const name = request?.name ? `/${request.name}` : "";
1130
- const namespace2 = request?.namespace || "";
1131
- const gvk = request?.kind || { group: "", version: "", kind: "" };
1132
- const reqMetadata = {
1133
- uid: request.uid,
1134
- namespace: namespace2,
1135
- name
1136
- };
1137
- logger_default.info({ ...reqMetadata, gvk, operation: request.operation, admissionKind }, "Incoming request");
1138
- logger_default.debug({ ...reqMetadata, request }, "Incoming request body");
1139
- let response;
1140
- if (admissionKind === "Mutate") {
1141
- response = await mutateProcessor(this.#config, this.#capabilities, request, reqMetadata);
1142
- } else {
1143
- response = await validateProcessor(this.#config, this.#capabilities, request, reqMetadata);
1144
- }
1145
- const responseList = Array.isArray(response) ? response : [response];
1146
- responseList.map((res2) => {
1147
- this.#afterHook && this.#afterHook(res2);
1148
- logger_default.info({ ...reqMetadata, res: res2 }, "Check response");
1149
- });
1150
- let kubeAdmissionResponse;
1151
- if (admissionKind === "Mutate") {
1152
- kubeAdmissionResponse = response;
1153
- logger_default.debug({ ...reqMetadata, response }, "Outgoing response");
1154
- res.send({
1155
- apiVersion: "admission.k8s.io/v1",
1156
- kind: "AdmissionReview",
1157
- response: kubeAdmissionResponse
1158
- });
1159
- } else {
1160
- kubeAdmissionResponse = responseList.length === 0 ? {
1161
- uid: request.uid,
1162
- allowed: true,
1163
- status: { message: "no in-scope validations -- allowed!" }
1164
- } : {
1165
- uid: responseList[0].uid,
1166
- allowed: responseList.filter((r) => !r.allowed).length === 0,
1167
- status: {
1168
- message: responseList.filter((rl) => !rl.allowed).map((curr) => curr.status?.message).join("; ")
1169
- }
1170
- };
1171
- res.send({
1172
- apiVersion: "admission.k8s.io/v1",
1173
- kind: "AdmissionReview",
1174
- response: kubeAdmissionResponse
1175
- });
1176
- }
1177
- logger_default.debug({ ...reqMetadata, kubeAdmissionResponse }, "Outgoing response");
1178
- this.#metricsCollector.observeEnd(startTime, admissionKind);
1179
- } catch (err) {
1180
- logger_default.error(err, `Error processing ${admissionKind} request`);
1181
- res.status(500).send("Internal Server Error");
1182
- this.#metricsCollector.error();
1183
- }
1184
- };
1185
- };
1186
- /**
1187
- * Middleware for logging requests
1188
- *
1189
- * @param req the incoming request
1190
- * @param res the outgoing response
1191
- * @param next the next middleware function
1192
- */
1193
- static #logger(req, res, next) {
1194
- const startTime = Date.now();
1195
- res.on("finish", () => {
1196
- const elapsedTime = Date.now() - startTime;
1197
- const message = {
1198
- uid: req.body?.request?.uid,
1199
- method: req.method,
1200
- url: req.originalUrl,
1201
- status: res.statusCode,
1202
- duration: `${elapsedTime} ms`
1203
- };
1204
- logger_default.info(message);
1205
- });
1206
- next();
1207
- }
1208
- /**
1209
- * Health check endpoint handler
1210
- *
1211
- * @param req the incoming request
1212
- * @param res the outgoing response
1213
- */
1214
- static #healthz(req, res) {
1215
- try {
1216
- res.send("OK");
1217
- } catch (err) {
1218
- logger_default.error(err, `Error processing health check`);
1219
- res.status(500).send("Internal Server Error");
1220
- }
1221
- }
1222
- };
1223
-
1224
- // src/lib/watch-processor.ts
1225
- var import_kubernetes_fluent_client6 = require("kubernetes-fluent-client");
1226
- var import_types6 = require("kubernetes-fluent-client/dist/fluent/types");
1227
-
1228
- // src/lib/helpers.ts
1229
- var import_kubernetes_fluent_client4 = require("kubernetes-fluent-client");
1230
-
1231
- // src/sdk/sdk.ts
1232
- var sdk_exports = {};
1233
- __export(sdk_exports, {
1234
- containers: () => containers,
1235
- getOwnerRefFrom: () => getOwnerRefFrom,
1236
- sanitizeResourceName: () => sanitizeResourceName,
1237
- writeEvent: () => writeEvent
1238
- });
1239
- var import_kubernetes_fluent_client3 = require("kubernetes-fluent-client");
1240
- function containers(request, containerType) {
1241
- const containers2 = request.Raw.spec?.containers || [];
1242
- const initContainers = request.Raw.spec?.initContainers || [];
1243
- const ephemeralContainers = request.Raw.spec?.ephemeralContainers || [];
1244
- if (containerType === "containers") {
1245
- return containers2;
1246
- }
1247
- if (containerType === "initContainers") {
1248
- return initContainers;
1249
- }
1250
- if (containerType === "ephemeralContainers") {
1251
- return ephemeralContainers;
1252
- }
1253
- return [...containers2, ...initContainers, ...ephemeralContainers];
1254
- }
1255
- async function writeEvent(cr, event, eventType, eventReason, reportingComponent, reportingInstance) {
1256
- logger_default.debug(cr.metadata, `Writing event: ${event.message}`);
1257
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.CoreEvent).Create({
1258
- type: eventType,
1259
- reason: eventReason,
1260
- ...event,
1261
- // Fixed values
1262
- metadata: {
1263
- namespace: cr.metadata.namespace,
1264
- generateName: cr.metadata.name
1265
- },
1266
- involvedObject: {
1267
- apiVersion: cr.apiVersion,
1268
- kind: cr.kind,
1269
- name: cr.metadata.name,
1270
- namespace: cr.metadata.namespace,
1271
- uid: cr.metadata.uid
1272
- },
1273
- firstTimestamp: /* @__PURE__ */ new Date(),
1274
- reportingComponent,
1275
- reportingInstance
1276
- });
1277
- }
1278
- function getOwnerRefFrom(customResource, blockOwnerDeletion, controller) {
1279
- const { apiVersion, kind: kind4, metadata } = customResource;
1280
- const { name, uid } = metadata;
1281
- return [
1282
- {
1283
- apiVersion,
1284
- kind: kind4,
1285
- uid,
1286
- name,
1287
- ...blockOwnerDeletion !== void 0 && { blockOwnerDeletion },
1288
- ...controller !== void 0 && { controller }
1289
- }
1290
- ];
1291
- }
1292
- function sanitizeResourceName(name) {
1293
- return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 250).replace(/^[^a-z]+|[^a-z]+$/g, "");
1294
- }
1295
-
1296
- // src/lib/helpers.ts
1297
- function filterNoMatchReason(binding, obj, capabilityNamespaces, ignoredNamespaces) {
1298
- const prefix = "Ignoring Watch Callback:";
1299
- 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)}'.` : "";
1300
- }
1301
-
1302
- // src/lib/finalizer.ts
1303
- var import_kubernetes_fluent_client5 = require("kubernetes-fluent-client");
1304
- function addFinalizer(request) {
1305
- if (request.Request.operation === "DELETE" /* DELETE */) {
1306
- return;
1307
- }
1308
- if (request.Request.operation === "UPDATE" /* UPDATE */ && request.Raw.metadata?.deletionTimestamp) {
1309
- return;
1310
- }
1311
- const peprFinal = "pepr.dev/finalizer";
1312
- const finalizers = request.Raw.metadata?.finalizers || [];
1313
- if (!finalizers.includes(peprFinal)) {
1314
- finalizers.push(peprFinal);
1315
- }
1316
- request.Merge({ metadata: { finalizers } });
1317
- }
1318
- async function removeFinalizer(binding, obj) {
1319
- const peprFinal = "pepr.dev/finalizer";
1320
- const meta = obj.metadata;
1321
- const resource = `${meta.namespace || "ClusterScoped"}/${meta.name}`;
1322
- logger_default.debug({ obj }, `Removing finalizer '${peprFinal}' from '${resource}'`);
1323
- const { model, kind: kind4 } = binding;
1324
- try {
1325
- (0, import_kubernetes_fluent_client5.RegisterKind)(model, kind4);
1326
- } catch (e) {
1327
- const expected = e.message === `GVK ${model.name} already registered`;
1328
- if (!expected) {
1329
- logger_default.error({ model, kind: kind4, error: e }, `Error registering "${kind4}" during finalization.`);
1330
- return;
1331
- }
1332
- }
1333
- const finalizers = meta.finalizers?.filter((f) => f !== peprFinal) || [];
1334
- obj = await (0, import_kubernetes_fluent_client5.K8s)(model, meta).Patch([
1335
- {
1336
- op: "replace",
1337
- path: `/metadata/finalizers`,
1338
- value: finalizers
1339
- }
1340
- ]);
1341
- logger_default.debug({ obj }, `Removed finalizer '${peprFinal}' from '${resource}'`);
1342
- }
1343
-
1344
- // src/lib/queue.ts
1345
- var import_node_crypto = require("node:crypto");
1346
- var Queue = class {
1347
- #name;
1348
- #uid;
1349
- #queue = [];
1350
- #pendingPromise = false;
1351
- constructor(name) {
1352
- this.#name = name;
1353
- this.#uid = `${Date.now()}-${(0, import_node_crypto.randomBytes)(2).toString("hex")}`;
1354
- }
1355
- label() {
1356
- return { name: this.#name, uid: this.#uid };
1357
- }
1358
- stats() {
1359
- return {
1360
- queue: this.label(),
1361
- stats: {
1362
- length: this.#queue.length
1363
- }
1364
- };
1365
- }
1366
- /**
1367
- * Enqueue adds an item to the queue and returns a promise that resolves when the item is
1368
- * reconciled.
1369
- *
1370
- * @param item The object to reconcile
1371
- * @param type The watch phase requested for reconcile
1372
- * @param reconcile The callback to enqueue for reconcile
1373
- * @returns A promise that resolves when the object is reconciled
1374
- */
1375
- enqueue(item, phase, reconcile) {
1376
- const note = {
1377
- queue: this.label(),
1378
- item: {
1379
- name: item.metadata?.name,
1380
- namespace: item.metadata?.namespace,
1381
- resourceVersion: item.metadata?.resourceVersion
1382
- }
1383
- };
1384
- logger_default.debug(note, "Enqueueing");
1385
- return new Promise((resolve, reject) => {
1386
- this.#queue.push({ item, phase, callback: reconcile, resolve, reject });
1387
- logger_default.debug(this.stats(), "Queue stats - push");
1388
- return this.#dequeue();
1389
- });
1390
- }
1391
- /**
1392
- * Dequeue reconciles the next item in the queue
1393
- *
1394
- * @returns A promise that resolves when the webapp is reconciled
1395
- */
1396
- async #dequeue() {
1397
- if (this.#pendingPromise) {
1398
- logger_default.debug("Pending promise, not dequeuing");
1399
- return false;
1400
- }
1401
- const element = this.#queue.shift();
1402
- if (!element) {
1403
- logger_default.debug("No element, not dequeuing");
1404
- return false;
1405
- }
1406
- try {
1407
- this.#pendingPromise = true;
1408
- const note = {
1409
- queue: this.label(),
1410
- item: {
1411
- name: element.item.metadata?.name,
1412
- namespace: element.item.metadata?.namespace,
1413
- resourceVersion: element.item.metadata?.resourceVersion
1414
- }
1415
- };
1416
- logger_default.debug(note, "Reconciling");
1417
- await element.callback(element.item, element.phase);
1418
- logger_default.debug(note, "Reconciled");
1419
- element.resolve();
1420
- } catch (e) {
1421
- logger_default.debug(`Error reconciling ${element.item.metadata.name}`, { error: e });
1422
- element.reject(e);
1423
- } finally {
1424
- logger_default.debug(this.stats(), "Queue stats - shift");
1425
- logger_default.debug("Resetting pending promise and dequeuing");
1426
- this.#pendingPromise = false;
1427
- await this.#dequeue();
1428
- }
1429
- }
1430
- };
1431
-
1432
- // src/lib/watch-processor.ts
1433
- var queues = {};
1434
- function queueKey(obj) {
1435
- const options = ["kind", "kindNs", "kindNsName", "global"];
1436
- const d3fault = "kind";
1437
- let strat = process.env.PEPR_RECONCILE_STRATEGY || d3fault;
1438
- strat = options.includes(strat) ? strat : d3fault;
1439
- const ns = obj.metadata?.namespace ?? "cluster-scoped";
1440
- const kind4 = obj.kind ?? "UnknownKind";
1441
- const name = obj.metadata?.name ?? "Unnamed";
1442
- const lookup = {
1443
- kind: `${kind4}`,
1444
- kindNs: `${kind4}/${ns}`,
1445
- kindNsName: `${kind4}/${ns}/${name}`,
1446
- global: "global"
1447
- };
1448
- return lookup[strat];
1449
- }
1450
- function getOrCreateQueue(obj) {
1451
- const key = queueKey(obj);
1452
- if (!queues[key]) {
1453
- queues[key] = new Queue(key);
1454
- }
1455
- return queues[key];
1456
- }
1457
- var watchCfg = {
1458
- resyncFailureMax: process.env.PEPR_RESYNC_FAILURE_MAX ? parseInt(process.env.PEPR_RESYNC_FAILURE_MAX, 10) : 5,
1459
- resyncDelaySec: process.env.PEPR_RESYNC_DELAY_SECONDS ? parseInt(process.env.PEPR_RESYNC_DELAY_SECONDS, 10) : 5,
1460
- lastSeenLimitSeconds: process.env.PEPR_LAST_SEEN_LIMIT_SECONDS ? parseInt(process.env.PEPR_LAST_SEEN_LIMIT_SECONDS, 10) : 300,
1461
- relistIntervalSec: process.env.PEPR_RELIST_INTERVAL_SECONDS ? parseInt(process.env.PEPR_RELIST_INTERVAL_SECONDS, 10) : 600
1462
- };
1463
- var eventToPhaseMap = {
1464
- ["CREATE" /* Create */]: [import_types6.WatchPhase.Added],
1465
- ["UPDATE" /* Update */]: [import_types6.WatchPhase.Modified],
1466
- ["CREATEORUPDATE" /* CreateOrUpdate */]: [import_types6.WatchPhase.Added, import_types6.WatchPhase.Modified],
1467
- ["DELETE" /* Delete */]: [import_types6.WatchPhase.Deleted],
1468
- ["*" /* Any */]: [import_types6.WatchPhase.Added, import_types6.WatchPhase.Modified, import_types6.WatchPhase.Deleted]
1469
- };
1470
- function setupWatch(capabilities, ignoredNamespaces) {
1471
- capabilities.map(
1472
- (capability) => capability.bindings.filter((binding) => binding.isWatch).forEach((bindingElement) => runBinding(bindingElement, capability.namespaces, ignoredNamespaces))
1473
- );
1474
- }
1475
- async function runBinding(binding, capabilityNamespaces, ignoredNamespaces) {
1476
- const phaseMatch = eventToPhaseMap[binding.event] || eventToPhaseMap["*" /* Any */];
1477
- logger_default.debug({ watchCfg }, "Effective WatchConfig");
1478
- const watchCallback = async (obj, phase) => {
1479
- if (phaseMatch.includes(phase)) {
1480
- try {
1481
- const filterMatch = filterNoMatchReason(binding, obj, capabilityNamespaces, ignoredNamespaces);
1482
- if (filterMatch === "") {
1483
- if (binding.isFinalize) {
1484
- if (!obj.metadata?.deletionTimestamp) {
1485
- return;
1486
- }
1487
- try {
1488
- await binding.finalizeCallback?.(obj);
1489
- } finally {
1490
- await removeFinalizer(binding, obj);
1491
- }
1492
- } else {
1493
- await binding.watchCallback?.(obj, phase);
1494
- }
1495
- } else {
1496
- logger_default.debug(filterMatch);
1497
- }
1498
- } catch (e) {
1499
- logger_default.error(e, "Error executing watch callback");
1500
- }
1501
- }
1502
- };
1503
- const watcher = (0, import_kubernetes_fluent_client6.K8s)(binding.model, binding.filters).Watch(async (obj, phase) => {
1504
- logger_default.debug(obj, `Watch event ${phase} received`);
1505
- if (binding.isQueue) {
1506
- const queue = getOrCreateQueue(obj);
1507
- await queue.enqueue(obj, phase, watchCallback);
1508
- } else {
1509
- await watchCallback(obj, phase);
1510
- }
1511
- }, watchCfg);
1512
- watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.GIVE_UP, (err) => {
1513
- logger_default.error(err, "Watch failed after 5 attempts, giving up");
1514
- process.exit(1);
1515
- });
1516
- watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.CONNECT, (url) => logEvent(import_kubernetes_fluent_client6.WatchEvent.CONNECT, url));
1517
- watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.DATA_ERROR, (err) => logEvent(import_kubernetes_fluent_client6.WatchEvent.DATA_ERROR, err.message));
1518
- watcher.events.on(
1519
- import_kubernetes_fluent_client6.WatchEvent.RECONNECT,
1520
- (retryCount) => logEvent(import_kubernetes_fluent_client6.WatchEvent.RECONNECT, `Reconnecting after ${retryCount} attempt${retryCount === 1 ? "" : "s"}`)
1521
- );
1522
- watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.RECONNECT_PENDING, () => logEvent(import_kubernetes_fluent_client6.WatchEvent.RECONNECT_PENDING));
1523
- watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.GIVE_UP, (err) => logEvent(import_kubernetes_fluent_client6.WatchEvent.GIVE_UP, err.message));
1524
- watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.ABORT, (err) => logEvent(import_kubernetes_fluent_client6.WatchEvent.ABORT, err.message));
1525
- watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.OLD_RESOURCE_VERSION, (err) => logEvent(import_kubernetes_fluent_client6.WatchEvent.OLD_RESOURCE_VERSION, err));
1526
- watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.NETWORK_ERROR, (err) => logEvent(import_kubernetes_fluent_client6.WatchEvent.NETWORK_ERROR, err.message));
1527
- watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.LIST_ERROR, (err) => logEvent(import_kubernetes_fluent_client6.WatchEvent.LIST_ERROR, err.message));
1528
- watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.LIST, (list) => logEvent(import_kubernetes_fluent_client6.WatchEvent.LIST, JSON.stringify(list, void 0, 2)));
1529
- watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.CACHE_MISS, (windowName) => {
1530
- metricsCollector.incCacheMiss(windowName);
1531
- });
1532
- watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.INIT_CACHE_MISS, (windowName) => {
1533
- metricsCollector.initCacheMissWindow(windowName);
1534
- });
1535
- watcher.events.on(import_kubernetes_fluent_client6.WatchEvent.INC_RESYNC_FAILURE_COUNT, (retryCount) => {
1536
- metricsCollector.incRetryCount(retryCount);
1537
- });
1538
- try {
1539
- await watcher.start();
1540
- } catch (err) {
1541
- logger_default.error(err, "Error starting watch");
1542
- process.exit(1);
1543
- }
1544
- }
1545
- function logEvent(event, message = "", obj) {
1546
- const logMessage = `Watch event ${event} received${message ? `. ${message}.` : "."}`;
1547
- if (obj) {
1548
- logger_default.debug(obj, logMessage);
1549
- } else {
1550
- logger_default.debug(logMessage);
1551
- }
1552
- }
1553
-
1554
- // src/lib/module.ts
1555
- var isWatchMode = () => process.env.PEPR_WATCH_MODE === "true";
1556
- var isBuildMode = () => process.env.PEPR_MODE === "build";
1557
- var isDevMode = () => process.env.PEPR_MODE === "dev";
1558
- var PeprModule = class {
1559
- #controller;
1560
- /**
1561
- * Create a new Pepr runtime
1562
- *
1563
- * @param config The configuration for the Pepr runtime
1564
- * @param capabilities The capabilities to be loaded into the Pepr runtime
1565
- * @param opts Options for the Pepr runtime
1566
- */
1567
- constructor({ description, pepr }, capabilities = [], opts = {}) {
1568
- const config = (0, import_ramda5.clone)(pepr);
1569
- config.description = description;
1570
- ValidateError(config.onError);
1571
- if (isBuildMode()) {
1572
- if (!process.send) {
1573
- throw new Error("process.send is not defined");
1574
- }
1575
- const exportedCapabilities = [];
1576
- for (const capability of capabilities) {
1577
- exportedCapabilities.push({
1578
- name: capability.name,
1579
- description: capability.description,
1580
- namespaces: capability.namespaces,
1581
- bindings: capability.bindings,
1582
- hasSchedule: capability.hasSchedule
1583
- });
1584
- }
1585
- process.send(exportedCapabilities);
1586
- return;
1587
- }
1588
- this.#controller = new Controller(config, capabilities, opts.beforeHook, opts.afterHook, () => {
1589
- if (isWatchMode() || isDevMode()) {
1590
- try {
1591
- setupWatch(capabilities, pepr?.alwaysIgnore?.namespaces);
1592
- } catch (e) {
1593
- logger_default.error(e, "Error setting up watch");
1594
- process.exit(1);
1595
- }
1596
- }
1597
- });
1598
- if (opts.deferStart) {
1599
- return;
1600
- }
1601
- this.start();
1602
- }
1603
- /**
1604
- * Start the Pepr runtime manually.
1605
- * 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.
1606
- *
1607
- * @param port
1608
- */
1609
- start = (port = 3e3) => {
1610
- this.#controller.startServer(port);
1611
- };
1612
- };
1613
-
1614
- // src/lib/storage.ts
1615
- var import_ramda6 = require("ramda");
1616
- var import_json_pointer = __toESM(require("json-pointer"));
1617
- var MAX_WAIT_TIME = 15e3;
1618
- var STORE_VERSION_PREFIX = "v2";
1619
- function v2StoreKey(key) {
1620
- return `${STORE_VERSION_PREFIX}-${import_json_pointer.default.escape(key)}`;
1621
- }
1622
- function v2UnescapedStoreKey(key) {
1623
- return `${STORE_VERSION_PREFIX}-${key}`;
1624
- }
1625
- var Storage = class {
1626
- #store = {};
1627
- #send;
1628
- #subscribers = {};
1629
- #subscriberId = 0;
1630
- #readyHandlers = [];
1631
- registerSender = (send) => {
1632
- this.#send = send;
1633
- };
1634
- receive = (data) => {
1635
- this.#store = data || {};
1636
- this.#onReady();
1637
- for (const idx in this.#subscribers) {
1638
- this.#subscribers[idx]((0, import_ramda6.clone)(this.#store));
1639
- }
1640
- };
1641
- getItem = (key) => {
1642
- const result = this.#store[v2UnescapedStoreKey(key)] || null;
1643
- if (result !== null && typeof result !== "function" && typeof result !== "object") {
1644
- return result;
1645
- }
1646
- return null;
1647
- };
1648
- clear = () => {
1649
- Object.keys(this.#store).length > 0 && this.#dispatchUpdate(
1650
- "remove",
1651
- Object.keys(this.#store).map((key) => import_json_pointer.default.escape(key))
1652
- );
1653
- };
1654
- removeItem = (key) => {
1655
- this.#dispatchUpdate("remove", [v2StoreKey(key)]);
1656
- };
1657
- setItem = (key, value) => {
1658
- this.#dispatchUpdate("add", [v2StoreKey(key)], value);
1659
- };
1660
- /**
1661
- * Creates a promise and subscribes to the store, the promise resolves when
1662
- * the key and value are seen in the store.
1663
- *
1664
- * @param key - The key to add into the store
1665
- * @param value - The value of the key
1666
- * @returns
1667
- */
1668
- setItemAndWait = (key, value) => {
1669
- this.#dispatchUpdate("add", [v2StoreKey(key)], value);
1670
- return new Promise((resolve, reject) => {
1671
- const unsubscribe = this.subscribe((data) => {
1672
- if (data[`${v2UnescapedStoreKey(key)}`] === value) {
1673
- unsubscribe();
1674
- resolve();
1675
- }
1676
- });
1677
- setTimeout(() => {
1678
- unsubscribe();
1679
- return reject();
1680
- }, MAX_WAIT_TIME);
1681
- });
1682
- };
1683
- /**
1684
- * Creates a promise and subscribes to the store, the promise resolves when
1685
- * the key is removed from the store.
1686
- *
1687
- * @param key - The key to add into the store
1688
- * @returns
1689
- */
1690
- removeItemAndWait = (key) => {
1691
- this.#dispatchUpdate("remove", [v2StoreKey(key)]);
1692
- return new Promise((resolve, reject) => {
1693
- const unsubscribe = this.subscribe((data) => {
1694
- if (!Object.hasOwn(data, `${v2UnescapedStoreKey(key)}`)) {
1695
- unsubscribe();
1696
- resolve();
1697
- }
1698
- });
1699
- setTimeout(() => {
1700
- unsubscribe();
1701
- return reject();
1702
- }, MAX_WAIT_TIME);
1703
- });
1704
- };
1705
- subscribe = (subscriber) => {
1706
- const idx = this.#subscriberId++;
1707
- this.#subscribers[idx] = subscriber;
1708
- return () => this.unsubscribe(idx);
1709
- };
1710
- onReady = (callback) => {
1711
- this.#readyHandlers.push(callback);
1712
- };
1713
- /**
1714
- * Remove a subscriber from the list of subscribers.
1715
- * @param idx - The index of the subscriber to remove.
1716
- */
1717
- unsubscribe = (idx) => {
1718
- delete this.#subscribers[idx];
1719
- };
1720
- #onReady = () => {
1721
- for (const handler of this.#readyHandlers) {
1722
- handler((0, import_ramda6.clone)(this.#store));
1723
- }
1724
- this.#onReady = () => {
1725
- };
1726
- };
1727
- /**
1728
- * Dispatch an update to the store and notify all subscribers.
1729
- * @param op - The type of operation to perform.
1730
- * @param keys - The keys to update.
1731
- * @param [value] - The new value.
1732
- */
1733
- #dispatchUpdate = (op, keys, value) => {
1734
- this.#send(op, keys, value);
1735
- };
1736
- };
1737
-
1738
- // src/lib/schedule.ts
1739
- var OnSchedule = class {
1740
- intervalId = null;
1741
- store;
1742
- name;
1743
- completions;
1744
- every;
1745
- unit;
1746
- run;
1747
- startTime;
1748
- duration;
1749
- lastTimestamp;
1750
- constructor(schedule) {
1751
- this.name = schedule.name;
1752
- this.run = schedule.run;
1753
- this.every = schedule.every;
1754
- this.unit = schedule.unit;
1755
- this.startTime = schedule?.startTime;
1756
- this.completions = schedule?.completions;
1757
- }
1758
- setStore(store) {
1759
- this.store = store;
1760
- this.startInterval();
1761
- }
1762
- startInterval() {
1763
- this.checkStore();
1764
- this.getDuration();
1765
- this.setupInterval();
1766
- }
1767
- /**
1768
- * Checks the store for this schedule and sets the values if it exists
1769
- * @returns
1770
- */
1771
- checkStore() {
1772
- const result = this.store && this.store.getItem(this.name);
1773
- if (result) {
1774
- const storedSchedule = JSON.parse(result);
1775
- this.completions = storedSchedule?.completions;
1776
- this.startTime = storedSchedule?.startTime;
1777
- this.lastTimestamp = storedSchedule?.lastTimestamp;
1778
- }
1779
- }
1780
- /**
1781
- * Saves the schedule to the store
1782
- * @returns
1783
- */
1784
- saveToStore() {
1785
- const schedule = {
1786
- completions: this.completions,
1787
- startTime: this.startTime,
1788
- lastTimestamp: /* @__PURE__ */ new Date(),
1789
- name: this.name
1790
- };
1791
- this.store && this.store.setItem(this.name, JSON.stringify(schedule));
1792
- }
1793
- /**
1794
- * Gets the durations in milliseconds
1795
- */
1796
- getDuration() {
1797
- switch (this.unit) {
1798
- case "seconds":
1799
- if (this.every < 10) throw new Error("10 Seconds in the smallest interval allowed");
1800
- this.duration = 1e3 * this.every;
1801
- break;
1802
- case "minutes":
1803
- case "minute":
1804
- this.duration = 1e3 * 60 * this.every;
1805
- break;
1806
- case "hours":
1807
- case "hour":
1808
- this.duration = 1e3 * 60 * 60 * this.every;
1809
- break;
1810
- default:
1811
- throw new Error("Invalid time unit");
1812
- }
1813
- }
1814
- /**
1815
- * Sets up the interval
1816
- */
1817
- setupInterval() {
1818
- const now = /* @__PURE__ */ new Date();
1819
- let delay;
1820
- if (this.lastTimestamp && this.startTime) {
1821
- this.startTime = void 0;
1822
- }
1823
- if (this.startTime) {
1824
- delay = this.startTime.getTime() - now.getTime();
1825
- } else if (this.lastTimestamp && this.duration) {
1826
- const lastTimestamp = new Date(this.lastTimestamp);
1827
- delay = this.duration - (now.getTime() - lastTimestamp.getTime());
1828
- }
1829
- if (delay === void 0 || delay <= 0) {
1830
- this.start();
1831
- } else {
1832
- setTimeout(() => {
1833
- this.start();
1834
- }, delay);
1835
- }
1836
- }
1837
- /**
1838
- * Starts the interval
1839
- */
1840
- start() {
1841
- this.intervalId = setInterval(() => {
1842
- if (this.completions === 0) {
1843
- this.stop();
1844
- return;
1845
- } else {
1846
- this.run();
1847
- if (this.completions && this.completions !== 0) {
1848
- this.completions -= 1;
1849
- }
1850
- this.saveToStore();
1851
- }
1852
- }, this.duration);
1853
- }
1854
- /**
1855
- * Stops the interval
1856
- */
1857
- stop() {
1858
- if (this.intervalId) {
1859
- clearInterval(this.intervalId);
1860
- this.intervalId = null;
1861
- }
1862
- this.store && this.store.removeItem(this.name);
1863
- }
1864
- };
1865
-
1866
- // src/lib/capability.ts
1867
- var registerAdmission = isBuildMode() || !isWatchMode();
1868
- var registerWatch = isBuildMode() || isWatchMode() || isDevMode();
1869
- var Capability = class {
1870
- #name;
1871
- #description;
1872
- #namespaces;
1873
- #bindings = [];
1874
- #store = new Storage();
1875
- #scheduleStore = new Storage();
1876
- #registered = false;
1877
- #scheduleRegistered = false;
1878
- hasSchedule;
1879
- /**
1880
- * Run code on a schedule with the capability.
1881
- *
1882
- * @param schedule The schedule to run the code on
1883
- * @returns
1884
- */
1885
- OnSchedule = (schedule) => {
1886
- const { name, every, unit, run, startTime, completions } = schedule;
1887
- this.hasSchedule = true;
1888
- if (process.env.PEPR_WATCH_MODE === "true" || process.env.PEPR_MODE === "dev") {
1889
- const newSchedule = {
1890
- name,
1891
- every,
1892
- unit,
1893
- run,
1894
- startTime,
1895
- completions
1896
- };
1897
- this.#scheduleStore.onReady(() => {
1898
- new OnSchedule(newSchedule).setStore(this.#scheduleStore);
1899
- });
1900
- }
1901
- };
1902
- getScheduleStore() {
1903
- return this.#scheduleStore;
1904
- }
1905
- /**
1906
- * Store is a key-value data store that can be used to persist data that should be shared
1907
- * between requests. Each capability has its own store, and the data is persisted in Kubernetes
1908
- * in the `pepr-system` namespace.
1909
- *
1910
- * Note: You should only access the store from within an action.
1911
- */
1912
- Store = {
1913
- clear: this.#store.clear,
1914
- getItem: this.#store.getItem,
1915
- removeItem: this.#store.removeItem,
1916
- removeItemAndWait: this.#store.removeItemAndWait,
1917
- setItem: this.#store.setItem,
1918
- subscribe: this.#store.subscribe,
1919
- onReady: this.#store.onReady,
1920
- setItemAndWait: this.#store.setItemAndWait
1921
- };
1922
- /**
1923
- * ScheduleStore is a key-value data store used to persist schedule data that should be shared
1924
- * between intervals. Each Schedule shares store, and the data is persisted in Kubernetes
1925
- * in the `pepr-system` namespace.
1926
- *
1927
- * Note: There is no direct access to schedule store
1928
- */
1929
- ScheduleStore = {
1930
- clear: this.#scheduleStore.clear,
1931
- getItem: this.#scheduleStore.getItem,
1932
- removeItemAndWait: this.#scheduleStore.removeItemAndWait,
1933
- removeItem: this.#scheduleStore.removeItem,
1934
- setItemAndWait: this.#scheduleStore.setItemAndWait,
1935
- setItem: this.#scheduleStore.setItem,
1936
- subscribe: this.#scheduleStore.subscribe,
1937
- onReady: this.#scheduleStore.onReady
1938
- };
1939
- get bindings() {
1940
- return this.#bindings;
1941
- }
1942
- get name() {
1943
- return this.#name;
1944
- }
1945
- get description() {
1946
- return this.#description;
1947
- }
1948
- get namespaces() {
1949
- return this.#namespaces || [];
1950
- }
1951
- constructor(cfg) {
1952
- this.#name = cfg.name;
1953
- this.#description = cfg.description;
1954
- this.#namespaces = cfg.namespaces;
1955
- this.hasSchedule = false;
1956
- logger_default.info(`Capability ${this.#name} registered`);
1957
- logger_default.debug(cfg);
1958
- }
1959
- /**
1960
- * Register the store with the capability. This is called automatically by the Pepr controller.
1961
- *
1962
- * @param store
1963
- */
1964
- registerScheduleStore = () => {
1965
- logger_default.info(`Registering schedule store for ${this.#name}`);
1966
- if (this.#scheduleRegistered) {
1967
- throw new Error(`Schedule store already registered for ${this.#name}`);
1968
- }
1969
- this.#scheduleRegistered = true;
1970
- return {
1971
- scheduleStore: this.#scheduleStore
1972
- };
1973
- };
1974
- /**
1975
- * Register the store with the capability. This is called automatically by the Pepr controller.
1976
- *
1977
- * @param store
1978
- */
1979
- registerStore = () => {
1980
- logger_default.info(`Registering store for ${this.#name}`);
1981
- if (this.#registered) {
1982
- throw new Error(`Store already registered for ${this.#name}`);
1983
- }
1984
- this.#registered = true;
1985
- return {
1986
- store: this.#store
1987
- };
1988
- };
1989
- /**
1990
- * The When method is used to register a action to be executed when a Kubernetes resource is
1991
- * processed by Pepr. The action will be executed if the resource matches the specified kind and any
1992
- * filters that are applied.
1993
- *
1994
- * @param model the KubernetesObject model to match
1995
- * @param kind if using a custom KubernetesObject not available in `a.*`, specify the GroupVersionKind
1996
- * @returns
1997
- */
1998
- When = (model, kind4) => {
1999
- const matchedKind = (0, import_kubernetes_fluent_client7.modelToGroupVersionKind)(model.name);
2000
- if (!matchedKind && !kind4) {
2001
- throw new Error(`Kind not specified for ${model.name}`);
2002
- }
2003
- const binding = {
2004
- model,
2005
- // If the kind is not specified, use the matched kind from the model
2006
- kind: kind4 || matchedKind,
2007
- event: "*" /* Any */,
2008
- filters: {
2009
- name: "",
2010
- namespaces: [],
2011
- regexNamespaces: [],
2012
- regexName: "",
2013
- labels: {},
2014
- annotations: {},
2015
- deletionTimestamp: false
2016
- }
2017
- };
2018
- const bindings = this.#bindings;
2019
- const prefix = `${this.#name}: ${model.name}`;
2020
- const commonChain = { WithLabel, WithAnnotation, WithDeletionTimestamp, Mutate, Validate, Watch, Reconcile, Alias };
2021
- const isNotEmpty = (value) => Object.keys(value).length > 0;
2022
- const log = (message, cbString) => {
2023
- const filteredObj = (0, import_ramda7.pickBy)(isNotEmpty, binding.filters);
2024
- logger_default.info(`${message} configured for ${binding.event}`, prefix);
2025
- logger_default.info(filteredObj, prefix);
2026
- logger_default.debug(cbString, prefix);
2027
- };
2028
- function Validate(validateCallback) {
2029
- if (registerAdmission) {
2030
- log("Validate Action", validateCallback.toString());
2031
- const aliasLogger = logger_default.child({ alias: binding.alias || "no alias provided" });
2032
- bindings.push({
2033
- ...binding,
2034
- isValidate: true,
2035
- validateCallback: async (req, logger = aliasLogger) => {
2036
- logger_default.info(`Executing validate action with alias: ${binding.alias || "no alias provided"}`);
2037
- return await validateCallback(req, logger);
2038
- }
2039
- });
2040
- }
2041
- return { Watch, Reconcile };
2042
- }
2043
- function Mutate(mutateCallback) {
2044
- if (registerAdmission) {
2045
- log("Mutate Action", mutateCallback.toString());
2046
- const aliasLogger = logger_default.child({ alias: binding.alias || "no alias provided" });
2047
- bindings.push({
2048
- ...binding,
2049
- isMutate: true,
2050
- mutateCallback: async (req, logger = aliasLogger) => {
2051
- logger_default.info(`Executing mutation action with alias: ${binding.alias || "no alias provided"}`);
2052
- await mutateCallback(req, logger);
2053
- }
2054
- });
2055
- }
2056
- return { Watch, Validate, Reconcile };
2057
- }
2058
- function Watch(watchCallback) {
2059
- if (registerWatch) {
2060
- log("Watch Action", watchCallback.toString());
2061
- const aliasLogger = logger_default.child({ alias: binding.alias || "no alias provided" });
2062
- bindings.push({
2063
- ...binding,
2064
- isWatch: true,
2065
- watchCallback: async (update, phase, logger = aliasLogger) => {
2066
- logger_default.info(`Executing watch action with alias: ${binding.alias || "no alias provided"}`);
2067
- await watchCallback(update, phase, logger);
2068
- }
2069
- });
2070
- }
2071
- return { Finalize };
2072
- }
2073
- function Reconcile(reconcileCallback) {
2074
- if (registerWatch) {
2075
- log("Reconcile Action", reconcileCallback.toString());
2076
- const aliasLogger = logger_default.child({ alias: binding.alias || "no alias provided" });
2077
- bindings.push({
2078
- ...binding,
2079
- isWatch: true,
2080
- isQueue: true,
2081
- watchCallback: async (update, phase, logger = aliasLogger) => {
2082
- logger_default.info(`Executing reconcile action with alias: ${binding.alias || "no alias provided"}`);
2083
- await reconcileCallback(update, phase, logger);
2084
- }
2085
- });
2086
- }
2087
- return { Finalize };
2088
- }
2089
- function Finalize(finalizeCallback) {
2090
- log("Finalize Action", finalizeCallback.toString());
2091
- const aliasLogger = logger_default.child({ alias: binding.alias || "no alias provided" });
2092
- if (registerAdmission) {
2093
- const mutateBinding = {
2094
- ...binding,
2095
- isMutate: true,
2096
- isFinalize: true,
2097
- event: "*" /* Any */,
2098
- mutateCallback: addFinalizer
2099
- };
2100
- bindings.push(mutateBinding);
2101
- }
2102
- if (registerWatch) {
2103
- const watchBinding = {
2104
- ...binding,
2105
- isWatch: true,
2106
- isFinalize: true,
2107
- event: "UPDATE" /* Update */,
2108
- finalizeCallback: async (update, logger = aliasLogger) => {
2109
- logger_default.info(`Executing finalize action with alias: ${binding.alias || "no alias provided"}`);
2110
- await finalizeCallback(update, logger);
2111
- }
2112
- };
2113
- bindings.push(watchBinding);
2114
- }
2115
- }
2116
- function InNamespace(...namespaces) {
2117
- logger_default.debug(`Add namespaces filter ${namespaces}`, prefix);
2118
- binding.filters.namespaces.push(...namespaces);
2119
- return { ...commonChain, WithName, WithNameRegex };
2120
- }
2121
- function InNamespaceRegex(...namespaces) {
2122
- logger_default.debug(`Add regex namespaces filter ${namespaces}`, prefix);
2123
- binding.filters.regexNamespaces.push(...namespaces.map((regex) => regex.source));
2124
- return { ...commonChain, WithName, WithNameRegex };
2125
- }
2126
- function WithDeletionTimestamp() {
2127
- logger_default.debug("Add deletionTimestamp filter");
2128
- binding.filters.deletionTimestamp = true;
2129
- return commonChain;
2130
- }
2131
- function WithNameRegex(regexName) {
2132
- logger_default.debug(`Add regex name filter ${regexName}`, prefix);
2133
- binding.filters.regexName = regexName.source;
2134
- return commonChain;
2135
- }
2136
- function WithName(name) {
2137
- logger_default.debug(`Add name filter ${name}`, prefix);
2138
- binding.filters.name = name;
2139
- return commonChain;
2140
- }
2141
- function WithLabel(key, value = "") {
2142
- logger_default.debug(`Add label filter ${key}=${value}`, prefix);
2143
- binding.filters.labels[key] = value;
2144
- return commonChain;
2145
- }
2146
- function WithAnnotation(key, value = "") {
2147
- logger_default.debug(`Add annotation filter ${key}=${value}`, prefix);
2148
- binding.filters.annotations[key] = value;
2149
- return commonChain;
2150
- }
2151
- function Alias(alias) {
2152
- logger_default.debug(`Adding prefix alias ${alias}`, prefix);
2153
- binding.alias = alias;
2154
- return commonChain;
2155
- }
2156
- function bindEvent(event) {
2157
- binding.event = event;
2158
- return {
2159
- ...commonChain,
2160
- InNamespace,
2161
- InNamespaceRegex,
2162
- WithName,
2163
- WithNameRegex,
2164
- WithDeletionTimestamp,
2165
- Alias
2166
- };
2167
- }
2168
- return {
2169
- IsCreatedOrUpdated: () => bindEvent("CREATEORUPDATE" /* CreateOrUpdate */),
2170
- IsCreated: () => bindEvent("CREATE" /* Create */),
2171
- IsUpdated: () => bindEvent("UPDATE" /* Update */),
2172
- IsDeleted: () => bindEvent("DELETE" /* Delete */)
2173
- };
2174
- };
2175
- };
2176
- // Annotate the CommonJS export names for ESM import in node:
2177
- 0 && (module.exports = {
2178
- Capability,
2179
- K8s,
2180
- Log,
2181
- PeprModule,
2182
- PeprMutateRequest,
2183
- PeprUtils,
2184
- PeprValidateRequest,
2185
- R,
2186
- RegisterKind,
2187
- a,
2188
- fetch,
2189
- fetchStatus,
2190
- kind,
2191
- sdk
2192
- });
2193
- //# sourceMappingURL=lib.js.map