pepr 0.42.2 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/cli/build.d.ts +25 -11
  2. package/dist/cli/build.d.ts.map +1 -1
  3. package/dist/cli/build.helpers.d.ts +1 -8
  4. package/dist/cli/build.helpers.d.ts.map +1 -1
  5. package/dist/cli/deploy.d.ts.map +1 -1
  6. package/dist/cli/dev.d.ts.map +1 -1
  7. package/dist/cli/init/templates.d.ts +3 -0
  8. package/dist/cli/init/templates.d.ts.map +1 -1
  9. package/dist/cli.js +1292 -1247
  10. package/dist/controller.js +1 -1
  11. package/dist/fixtures/loader.d.ts.map +1 -1
  12. package/dist/lib/assets/assets.d.ts +30 -0
  13. package/dist/lib/assets/assets.d.ts.map +1 -0
  14. package/dist/lib/assets/deploy.d.ts +2 -2
  15. package/dist/lib/assets/deploy.d.ts.map +1 -1
  16. package/dist/lib/assets/helm.d.ts.map +1 -1
  17. package/dist/lib/assets/index.d.ts +4 -23
  18. package/dist/lib/assets/index.d.ts.map +1 -1
  19. package/dist/lib/assets/pods.d.ts +1 -1
  20. package/dist/lib/assets/pods.d.ts.map +1 -1
  21. package/dist/lib/assets/webhooks.d.ts +5 -2
  22. package/dist/lib/assets/webhooks.d.ts.map +1 -1
  23. package/dist/lib/assets/yaml/generateAllYaml.d.ts +9 -0
  24. package/dist/lib/assets/yaml/generateAllYaml.d.ts.map +1 -0
  25. package/dist/lib/assets/yaml/generateZarfYaml.d.ts +5 -0
  26. package/dist/lib/assets/yaml/generateZarfYaml.d.ts.map +1 -0
  27. package/dist/lib/assets/yaml/overridesFile.d.ts +15 -0
  28. package/dist/lib/assets/yaml/overridesFile.d.ts.map +1 -0
  29. package/dist/lib/core/module.d.ts.map +1 -1
  30. package/dist/lib/enums.d.ts +4 -0
  31. package/dist/lib/enums.d.ts.map +1 -1
  32. package/dist/lib/processors/mutate-processor.d.ts.map +1 -1
  33. package/dist/lib/processors/validate-processor.d.ts.map +1 -1
  34. package/dist/lib.js +37 -18
  35. package/dist/lib.js.map +4 -4
  36. package/package.json +5 -2
  37. package/src/cli/build.helpers.ts +8 -23
  38. package/src/cli/build.ts +73 -17
  39. package/src/cli/deploy.ts +8 -7
  40. package/src/cli/dev.ts +8 -7
  41. package/src/fixtures/loader.ts +2 -2
  42. package/src/lib/assets/assets.ts +189 -0
  43. package/src/lib/assets/deploy.ts +33 -31
  44. package/src/lib/assets/helm.ts +22 -4
  45. package/src/lib/assets/index.ts +40 -150
  46. package/src/lib/assets/pods.ts +3 -3
  47. package/src/lib/assets/webhooks.ts +23 -17
  48. package/src/lib/assets/yaml/generateAllYaml.ts +50 -0
  49. package/src/lib/assets/yaml/generateZarfYaml.ts +38 -0
  50. package/src/lib/assets/{yaml.ts → yaml/overridesFile.ts} +19 -109
  51. package/src/lib/core/module.ts +2 -1
  52. package/src/lib/enums.ts +6 -0
  53. package/src/lib/processors/mutate-processor.ts +2 -1
  54. package/src/lib/processors/validate-processor.ts +7 -1
  55. package/dist/lib/assets/yaml.d.ts +0 -6
  56. package/dist/lib/assets/yaml.d.ts.map +0 -1
package/dist/cli.js CHANGED
@@ -91,9 +91,8 @@ var import_esbuild2 = require("esbuild");
91
91
  var import_fs9 = require("fs");
92
92
  var import_path3 = require("path");
93
93
 
94
- // src/lib/assets/index.ts
95
- var import_crypto3 = __toESM(require("crypto"));
96
- var import_client_node2 = require("@kubernetes/client-node");
94
+ // src/lib/assets/assets.ts
95
+ var import_crypto = __toESM(require("crypto"));
97
96
 
98
97
  // src/lib/tls.ts
99
98
  var import_node_forge = __toESM(require("node-forge"));
@@ -152,264 +151,460 @@ function genCert(key, name2, issuer) {
152
151
  return crt;
153
152
  }
154
153
 
155
- // src/lib/assets/deploy.ts
156
- var import_crypto = __toESM(require("crypto"));
157
- var import_fs = require("fs");
158
- var import_kubernetes_fluent_client3 = require("kubernetes-fluent-client");
159
-
160
- // src/lib/telemetry/logger.ts
161
- var import_pino = require("pino");
162
- var isPrettyLog = true;
163
- var pretty = {
164
- target: "pino-pretty",
165
- options: {
166
- colorize: true
167
- }
168
- };
169
- var transport = isPrettyLog ? pretty : void 0;
170
- var pinoTimeFunction = process.env.PINO_TIME_STAMP === "iso" ? () => import_pino.stdTimeFunctions.isoTime() : () => import_pino.stdTimeFunctions.epochTime();
171
- var Log = (0, import_pino.pino)({
172
- transport,
173
- timestamp: pinoTimeFunction
174
- });
175
- if (process.env.LOG_LEVEL) {
176
- Log.level = process.env.LOG_LEVEL;
177
- }
178
- var logger_default = Log;
179
-
180
- // src/lib/assets/networking.ts
181
- function apiTokenSecret(name2, apiToken) {
182
- return {
183
- apiVersion: "v1",
184
- kind: "Secret",
185
- metadata: {
186
- name: `${name2}-api-token`,
187
- namespace: "pepr-system"
188
- },
189
- type: "Opaque",
190
- data: {
191
- value: Buffer.from(apiToken).toString("base64")
192
- }
193
- };
194
- }
195
- function tlsSecret(name2, tls) {
196
- return {
197
- apiVersion: "v1",
198
- kind: "Secret",
199
- metadata: {
200
- name: `${name2}-tls`,
201
- namespace: "pepr-system"
202
- },
203
- type: "kubernetes.io/tls",
204
- data: {
205
- "tls.crt": tls.crt,
206
- "tls.key": tls.key
207
- }
208
- };
209
- }
210
- function service(name2) {
211
- return {
212
- apiVersion: "v1",
213
- kind: "Service",
214
- metadata: {
215
- name: name2,
216
- namespace: "pepr-system",
217
- labels: {
218
- "pepr.dev/controller": "admission"
219
- }
220
- },
221
- spec: {
222
- selector: {
223
- app: name2,
224
- "pepr.dev/controller": "admission"
225
- },
226
- ports: [
227
- {
228
- port: 443,
229
- targetPort: 3e3
230
- }
231
- ]
232
- }
233
- };
154
+ // src/lib/assets/helm.ts
155
+ function clusterRoleTemplate() {
156
+ return `
157
+ apiVersion: rbac.authorization.k8s.io/v1
158
+ kind: ClusterRole
159
+ metadata:
160
+ name: {{ .Values.uuid }}
161
+ namespace: pepr-system
162
+ rules:
163
+ {{- if .Values.rbac }}
164
+ {{- toYaml .Values.rbac | nindent 2 }}
165
+ {{- end }}
166
+ `;
234
167
  }
235
- function watcherService(name2) {
236
- return {
237
- apiVersion: "v1",
238
- kind: "Service",
239
- metadata: {
240
- name: `${name2}-watcher`,
241
- namespace: "pepr-system",
242
- labels: {
243
- "pepr.dev/controller": "watcher"
244
- }
245
- },
246
- spec: {
247
- selector: {
248
- app: `${name2}-watcher`,
249
- "pepr.dev/controller": "watcher"
250
- },
251
- ports: [
252
- {
253
- port: 443,
254
- targetPort: 3e3
255
- }
256
- ]
257
- }
258
- };
168
+ function namespaceTemplate() {
169
+ return `
170
+ apiVersion: v1
171
+ kind: Namespace
172
+ metadata:
173
+ name: pepr-system
174
+ {{- if .Values.namespace.annotations }}
175
+ annotations:
176
+ {{- toYaml .Values.namespace.annotations | nindent 6 }}
177
+ {{- end }}
178
+ {{- if .Values.namespace.labels }}
179
+ labels:
180
+ {{- toYaml .Values.namespace.labels | nindent 6 }}
181
+ {{- end }}
182
+ `;
259
183
  }
184
+ function chartYaml(name2, description) {
185
+ return `
186
+ apiVersion: v2
187
+ name: ${name2}
188
+ description: ${description || ""}
260
189
 
261
- // src/lib/assets/pods.ts
262
- var import_zlib = require("zlib");
190
+ # A chart can be either an 'application' or a 'library' chart.
191
+ #
192
+ # Application charts are a collection of templates that can be packaged into versioned archives
193
+ # to be deployed.
194
+ #
195
+ # Library charts provide useful utilities or functions for the chart developer. They're included as
196
+ # a dependency of application charts to inject those utilities and functions into the rendering
197
+ # pipeline. Library charts do not define any templates and therefore cannot be deployed.
198
+ type: application
263
199
 
264
- // src/sdk/sdk.ts
265
- var import_kubernetes_fluent_client = require("kubernetes-fluent-client");
266
- function sanitizeResourceName(name2) {
267
- return name2.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 250).replace(/^[^a-z]+|[^a-z]+$/g, "");
268
- }
200
+ # This is the chart version. This version number should be incremented each time you make changes
201
+ # to the chart and its templates, including the app version.
202
+ # Versions are expected to follow Semantic Versioning (https://semver.org/)
203
+ version: 0.1.0
269
204
 
270
- // src/lib/helpers.ts
271
- function matchesRegex(pattern, testString) {
272
- return new RegExp(pattern).test(testString);
273
- }
274
- var ValidationError = class extends Error {
275
- };
276
- function validateCapabilityNames(capabilities) {
277
- if (capabilities && capabilities.length > 0) {
278
- for (let i = 0; i < capabilities.length; i++) {
279
- if (capabilities[i].name !== sanitizeResourceName(capabilities[i].name)) {
280
- throw new ValidationError(`Capability name is not a valid Kubernetes resource name: ${capabilities[i].name}`);
281
- }
282
- }
283
- }
284
- }
285
- function createRBACMap(capabilities) {
286
- return capabilities.reduce((acc, capability) => {
287
- capability.bindings.forEach((binding) => {
288
- const key = `${binding.kind.group}/${binding.kind.version}/${binding.kind.kind}`;
289
- acc["pepr.dev/v1/peprstore"] = {
290
- verbs: ["create", "get", "patch", "watch"],
291
- plural: "peprstores"
292
- };
293
- acc["apiextensions.k8s.io/v1/customresourcedefinition"] = {
294
- verbs: ["patch", "create"],
295
- plural: "customresourcedefinitions"
296
- };
297
- if (!acc[key] && binding.isWatch) {
298
- acc[key] = {
299
- verbs: ["watch"],
300
- plural: binding.kind.plural || `${binding.kind.kind.toLowerCase()}s`
301
- };
302
- }
303
- if (binding.isFinalize) {
304
- acc[key] = {
305
- verbs: ["patch"],
306
- plural: binding.kind.plural || `${binding.kind.kind.toLowerCase()}s`
307
- };
308
- }
309
- });
310
- return acc;
311
- }, {});
312
- }
313
- function hasEveryOverlap(array1, array2) {
314
- if (!Array.isArray(array1) || !Array.isArray(array2)) {
315
- return false;
316
- }
317
- return array1.every((element) => array2.includes(element));
318
- }
319
- function hasAnyOverlap(array1, array2) {
320
- if (!Array.isArray(array1) || !Array.isArray(array2)) {
321
- return false;
322
- }
323
- return array1.some((element) => array2.includes(element));
324
- }
325
- function ignoredNamespaceConflict(ignoreNamespaces, bindingNamespaces) {
326
- return hasAnyOverlap(bindingNamespaces, ignoreNamespaces);
327
- }
328
- function bindingAndCapabilityNSConflict(bindingNamespaces, capabilityNamespaces) {
329
- if (!capabilityNamespaces) {
330
- return false;
331
- }
332
- return capabilityNamespaces.length !== 0 && !hasEveryOverlap(bindingNamespaces, capabilityNamespaces);
333
- }
334
- function generateWatchNamespaceError(ignoredNamespaces, bindingNamespaces, capabilityNamespaces) {
335
- let err = "";
336
- if (ignoredNamespaceConflict(ignoredNamespaces, bindingNamespaces)) {
337
- err += `Binding uses a Pepr ignored namespace: ignoredNamespaces: [${ignoredNamespaces.join(
338
- ", "
339
- )}] bindingNamespaces: [${bindingNamespaces.join(", ")}].`;
340
- }
341
- if (bindingAndCapabilityNSConflict(bindingNamespaces, capabilityNamespaces)) {
342
- err += `Binding uses namespace not governed by capability: bindingNamespaces: [${bindingNamespaces.join(
343
- ", "
344
- )}] capabilityNamespaces: [${capabilityNamespaces.join(", ")}].`;
345
- }
346
- return err.replace(/\.([^ ])/g, ". $1");
205
+ # This is the version number of the application being deployed. This version number should be
206
+ # incremented each time you make changes to the application. Versions are not expected to
207
+ # follow Semantic Versioning. They should reflect the version the application is using.
208
+ # It is recommended to use it with quotes.
209
+ appVersion: "1.16.0"
210
+ `;
347
211
  }
348
- function namespaceComplianceValidator(capability, ignoredNamespaces) {
349
- const { namespaces: capabilityNamespaces, bindings, name: name2 } = capability;
350
- const bindingNamespaces = bindings.flatMap((binding) => binding.filters.namespaces);
351
- const bindingRegexNamespaces = bindings.flatMap(
352
- (binding) => binding.filters.regexNamespaces || []
353
- );
354
- const namespaceError = generateWatchNamespaceError(
355
- ignoredNamespaces ? ignoredNamespaces : [],
356
- bindingNamespaces,
357
- capabilityNamespaces ? capabilityNamespaces : []
358
- );
359
- if (namespaceError !== "") {
360
- throw new Error(
361
- `Error in ${name2} capability. A binding violates namespace rules. Please check ignoredNamespaces and capability namespaces: ${namespaceError}`
362
- );
363
- }
364
- matchRegexToCapababilityNamespace(bindingRegexNamespaces, capabilityNamespaces);
365
- checkRegexNamespaces(bindingRegexNamespaces, ignoredNamespaces);
212
+ function watcherDeployTemplate(buildTimestamp) {
213
+ return `
214
+ apiVersion: apps/v1
215
+ kind: Deployment
216
+ metadata:
217
+ name: {{ .Values.uuid }}-watcher
218
+ namespace: pepr-system
219
+ annotations:
220
+ {{- toYaml .Values.watcher.annotations | nindent 4 }}
221
+ labels:
222
+ {{- toYaml .Values.watcher.labels | nindent 4 }}
223
+ spec:
224
+ replicas: 1
225
+ strategy:
226
+ type: Recreate
227
+ selector:
228
+ matchLabels:
229
+ app: {{ .Values.uuid }}-watcher
230
+ pepr.dev/controller: watcher
231
+ template:
232
+ metadata:
233
+ annotations:
234
+ buildTimestamp: "${buildTimestamp}"
235
+ {{- if .Values.watcher.podAnnotations }}
236
+ {{- toYaml .Values.watcher.podAnnotations | nindent 8 }}
237
+ {{- end }}
238
+ labels:
239
+ app: {{ .Values.uuid }}-watcher
240
+ pepr.dev/controller: watcher
241
+ spec:
242
+ terminationGracePeriodSeconds: {{ .Values.watcher.terminationGracePeriodSeconds }}
243
+ serviceAccountName: {{ .Values.uuid }}
244
+ securityContext:
245
+ {{- toYaml .Values.admission.securityContext | nindent 8 }}
246
+ containers:
247
+ - name: watcher
248
+ image: {{ .Values.watcher.image }}
249
+ imagePullPolicy: IfNotPresent
250
+ {{- if gt (len .Values.imagePullSecrets) 0 }}
251
+ imagePullSecrets:
252
+ {{- range .Values.imagePullSecrets }}
253
+ - name: {{ . }}
254
+ {{- end }}
255
+ {{- end }}
256
+ args:
257
+ - /app/node_modules/pepr/dist/controller.js
258
+ - {{ .Values.hash }}
259
+ readinessProbe:
260
+ {{- toYaml .Values.watcher.readinessProbe | nindent 12 }}
261
+ livenessProbe:
262
+ {{- toYaml .Values.watcher.livenessProbe | nindent 12 }}
263
+ ports:
264
+ - containerPort: 3000
265
+ resources:
266
+ {{- toYaml .Values.watcher.resources | nindent 12 }}
267
+ env:
268
+ {{- toYaml .Values.watcher.env | nindent 12 }}
269
+ - name: PEPR_WATCH_MODE
270
+ value: "true"
271
+ {{- if .Values.additionalIgnoredNamespaces }}
272
+ - name: PEPR_ADDITIONAL_IGNORED_NAMESPACES
273
+ value: "{{ join ", " .Values.additionalIgnoredNamespaces }}"
274
+ {{- end }}
275
+ envFrom:
276
+ {{- toYaml .Values.watcher.envFrom | nindent 12 }}
277
+ securityContext:
278
+ {{- toYaml .Values.watcher.containerSecurityContext | nindent 12 }}
279
+ volumeMounts:
280
+ - name: tls-certs
281
+ mountPath: /etc/certs
282
+ readOnly: true
283
+ - name: module
284
+ mountPath: /app/load
285
+ readOnly: true
286
+ {{- if .Values.watcher.extraVolumeMounts }}
287
+ {{- toYaml .Values.watcher.extraVolumeMounts | nindent 12 }}
288
+ {{- end }}
289
+ volumes:
290
+ - name: tls-certs
291
+ secret:
292
+ secretName: {{ .Values.uuid }}-tls
293
+ - name: module
294
+ secret:
295
+ secretName: {{ .Values.uuid }}-module
296
+ {{- if .Values.watcher.extraVolumes }}
297
+ {{- toYaml .Values.watcher.extraVolumes | nindent 8 }}
298
+ {{- end }}
299
+ `;
366
300
  }
367
- var matchRegexToCapababilityNamespace = (bindingRegexNamespaces, capabilityNamespaces) => {
368
- if (bindingRegexNamespaces.length > 0 && capabilityNamespaces && capabilityNamespaces.length > 0) {
369
- for (const regexNamespace of bindingRegexNamespaces) {
370
- let matches = false;
371
- matches = regexNamespace !== "" && capabilityNamespaces.some((capabilityNamespace) => matchesRegex(regexNamespace, capabilityNamespace));
372
- if (!matches) {
373
- throw new Error(
374
- `Ignoring Watch Callback: Object namespace does not match any capability namespace with regex ${regexNamespace}.`
375
- );
376
- }
377
- }
378
- }
379
- };
380
- var checkRegexNamespaces = (bindingRegexNamespaces, ignoredNamespaces) => {
381
- if (bindingRegexNamespaces.length > 0 && ignoredNamespaces && ignoredNamespaces.length > 0) {
382
- for (const regexNamespace of bindingRegexNamespaces) {
383
- const matchedNS = ignoredNamespaces.find((ignoredNS) => matchesRegex(regexNamespace, ignoredNS));
384
- if (matchedNS) {
385
- throw new Error(
386
- `Ignoring Watch Callback: Regex namespace: ${regexNamespace}, is an ignored namespace: ${matchedNS}.`
387
- );
388
- }
301
+ function admissionDeployTemplate(buildTimestamp) {
302
+ return `
303
+ apiVersion: apps/v1
304
+ kind: Deployment
305
+ metadata:
306
+ name: {{ .Values.uuid }}
307
+ namespace: pepr-system
308
+ annotations:
309
+ {{- toYaml .Values.admission.annotations | nindent 4 }}
310
+ labels:
311
+ {{- toYaml .Values.admission.labels | nindent 4 }}
312
+ spec:
313
+ replicas: 2
314
+ selector:
315
+ matchLabels:
316
+ app: {{ .Values.uuid }}
317
+ pepr.dev/controller: admission
318
+ template:
319
+ metadata:
320
+ annotations:
321
+ buildTimestamp: "${buildTimestamp}"
322
+ {{- if .Values.admission.podAnnotations }}
323
+ {{- toYaml .Values.admission.podAnnotations | nindent 8 }}
324
+ {{- end }}
325
+ labels:
326
+ app: {{ .Values.uuid }}
327
+ pepr.dev/controller: admission
328
+ spec:
329
+ terminationGracePeriodSeconds: {{ .Values.admission.terminationGracePeriodSeconds }}
330
+ priorityClassName: system-node-critical
331
+ serviceAccountName: {{ .Values.uuid }}
332
+ securityContext:
333
+ {{- toYaml .Values.admission.securityContext | nindent 8 }}
334
+ containers:
335
+ - name: server
336
+ image: {{ .Values.admission.image }}
337
+ imagePullPolicy: IfNotPresent
338
+ {{- if gt (len .Values.imagePullSecrets) 0 }}
339
+ imagePullSecrets:
340
+ {{- range .Values.imagePullSecrets }}
341
+ - name: {{ . }}
342
+ {{- end }}
343
+ {{- end }}
344
+ args:
345
+ - /app/node_modules/pepr/dist/controller.js
346
+ - {{ .Values.hash }}
347
+ readinessProbe:
348
+ {{- toYaml .Values.admission.readinessProbe | nindent 12 }}
349
+ livenessProbe:
350
+ {{- toYaml .Values.admission.livenessProbe | nindent 12 }}
351
+ ports:
352
+ - containerPort: 3000
353
+ resources:
354
+ {{- toYaml .Values.admission.resources | nindent 12 }}
355
+ env:
356
+ {{- toYaml .Values.admission.env | nindent 12 }}
357
+ - name: PEPR_WATCH_MODE
358
+ value: "false"
359
+ {{- if .Values.additionalIgnoredNamespaces }}
360
+ - name: PEPR_ADDITIONAL_IGNORED_NAMESPACES
361
+ value: "{{ join ", " .Values.additionalIgnoredNamespaces }}"
362
+ {{- end }}
363
+ envFrom:
364
+ {{- toYaml .Values.admission.envFrom | nindent 12 }}
365
+ securityContext:
366
+ {{- toYaml .Values.admission.containerSecurityContext | nindent 12 }}
367
+ volumeMounts:
368
+ - name: tls-certs
369
+ mountPath: /etc/certs
370
+ readOnly: true
371
+ - name: api-token
372
+ mountPath: /app/api-token
373
+ readOnly: true
374
+ - name: module
375
+ mountPath: /app/load
376
+ readOnly: true
377
+ {{- if .Values.admission.extraVolumeMounts }}
378
+ {{- toYaml .Values.admission.extraVolumeMounts | nindent 12 }}
379
+ {{- end }}
380
+ volumes:
381
+ - name: tls-certs
382
+ secret:
383
+ secretName: {{ .Values.uuid }}-tls
384
+ - name: api-token
385
+ secret:
386
+ secretName: {{ .Values.uuid }}-api-token
387
+ - name: module
388
+ secret:
389
+ secretName: {{ .Values.uuid }}-module
390
+ {{- if .Values.admission.extraVolumes }}
391
+ {{- toYaml .Values.admission.extraVolumes | nindent 8 }}
392
+ {{- end }}
393
+ `;
394
+ }
395
+ function serviceMonitorTemplate(name2) {
396
+ return `
397
+ {{- if .Values.${name2}.serviceMonitor.enabled }}
398
+ apiVersion: monitoring.coreos.com/v1
399
+ kind: ServiceMonitor
400
+ metadata:
401
+ name: ${name2}
402
+ annotations:
403
+ {{- toYaml .Values.${name2}.serviceMonitor.annotations | nindent 4 }}
404
+ labels:
405
+ {{- toYaml .Values.${name2}.serviceMonitor.labels | nindent 4 }}
406
+ spec:
407
+ selector:
408
+ matchLabels:
409
+ pepr.dev/controller: ${name2}
410
+ namespaceSelector:
411
+ matchNames:
412
+ - pepr-system
413
+ endpoints:
414
+ - targetPort: 3000
415
+ scheme: https
416
+ tlsConfig:
417
+ insecureSkipVerify: true
418
+ {{- end }}
419
+ `;
420
+ }
421
+
422
+ // src/lib/filesystemService.ts
423
+ var import_fs = require("fs");
424
+ async function createDirectoryIfNotExists(path) {
425
+ try {
426
+ await import_fs.promises.access(path);
427
+ } catch (error) {
428
+ if (error.code === "ENOENT") {
429
+ await import_fs.promises.mkdir(path, { recursive: true });
430
+ } else {
431
+ throw error;
389
432
  }
390
433
  }
391
- };
392
- function secretOverLimit(str) {
393
- const encoder = new TextEncoder();
394
- const encoded = encoder.encode(str);
395
- const sizeInBytes = encoded.length;
396
- const oneMiBInBytes = 1048576;
397
- return sizeInBytes > oneMiBInBytes;
398
434
  }
399
- var parseTimeout = (value) => {
400
- const parsedValue = parseInt(value, 10);
401
- const floatValue = parseFloat(value);
402
- if (isNaN(parsedValue)) {
403
- throw new Error("Not a number.");
404
- } else if (parsedValue !== floatValue) {
405
- throw new Error("Value must be an integer.");
406
- } else if (parsedValue < 1 || parsedValue > 30) {
407
- throw new Error("Number must be between 1 and 30.");
435
+
436
+ // src/lib/assets/pods.ts
437
+ var import_zlib = require("zlib");
438
+
439
+ // src/lib/telemetry/logger.ts
440
+ var import_pino = require("pino");
441
+ var isPrettyLog = true;
442
+ var pretty = {
443
+ target: "pino-pretty",
444
+ options: {
445
+ colorize: true
408
446
  }
409
- return parsedValue;
410
447
  };
411
- function dedent(file) {
412
- const lines = file.split("\n");
448
+ var transport = isPrettyLog ? pretty : void 0;
449
+ var pinoTimeFunction = process.env.PINO_TIME_STAMP === "iso" ? () => import_pino.stdTimeFunctions.isoTime() : () => import_pino.stdTimeFunctions.epochTime();
450
+ var Log = (0, import_pino.pino)({
451
+ transport,
452
+ timestamp: pinoTimeFunction
453
+ });
454
+ if (process.env.LOG_LEVEL) {
455
+ Log.level = process.env.LOG_LEVEL;
456
+ }
457
+ var logger_default = Log;
458
+
459
+ // src/sdk/sdk.ts
460
+ var import_kubernetes_fluent_client = require("kubernetes-fluent-client");
461
+ function sanitizeResourceName(name2) {
462
+ return name2.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 250).replace(/^[^a-z]+|[^a-z]+$/g, "");
463
+ }
464
+
465
+ // src/lib/helpers.ts
466
+ function matchesRegex(pattern, testString) {
467
+ return new RegExp(pattern).test(testString);
468
+ }
469
+ var ValidationError = class extends Error {
470
+ };
471
+ function validateCapabilityNames(capabilities) {
472
+ if (capabilities && capabilities.length > 0) {
473
+ for (let i = 0; i < capabilities.length; i++) {
474
+ if (capabilities[i].name !== sanitizeResourceName(capabilities[i].name)) {
475
+ throw new ValidationError(`Capability name is not a valid Kubernetes resource name: ${capabilities[i].name}`);
476
+ }
477
+ }
478
+ }
479
+ }
480
+ function createRBACMap(capabilities) {
481
+ return capabilities.reduce((acc, capability) => {
482
+ capability.bindings.forEach((binding) => {
483
+ const key = `${binding.kind.group}/${binding.kind.version}/${binding.kind.kind}`;
484
+ acc["pepr.dev/v1/peprstore"] = {
485
+ verbs: ["create", "get", "patch", "watch"],
486
+ plural: "peprstores"
487
+ };
488
+ acc["apiextensions.k8s.io/v1/customresourcedefinition"] = {
489
+ verbs: ["patch", "create"],
490
+ plural: "customresourcedefinitions"
491
+ };
492
+ if (!acc[key] && binding.isWatch) {
493
+ acc[key] = {
494
+ verbs: ["watch"],
495
+ plural: binding.kind.plural || `${binding.kind.kind.toLowerCase()}s`
496
+ };
497
+ }
498
+ if (binding.isFinalize) {
499
+ acc[key] = {
500
+ verbs: ["patch"],
501
+ plural: binding.kind.plural || `${binding.kind.kind.toLowerCase()}s`
502
+ };
503
+ }
504
+ });
505
+ return acc;
506
+ }, {});
507
+ }
508
+ function hasEveryOverlap(array1, array2) {
509
+ if (!Array.isArray(array1) || !Array.isArray(array2)) {
510
+ return false;
511
+ }
512
+ return array1.every((element) => array2.includes(element));
513
+ }
514
+ function hasAnyOverlap(array1, array2) {
515
+ if (!Array.isArray(array1) || !Array.isArray(array2)) {
516
+ return false;
517
+ }
518
+ return array1.some((element) => array2.includes(element));
519
+ }
520
+ function ignoredNamespaceConflict(ignoreNamespaces, bindingNamespaces) {
521
+ return hasAnyOverlap(bindingNamespaces, ignoreNamespaces);
522
+ }
523
+ function bindingAndCapabilityNSConflict(bindingNamespaces, capabilityNamespaces) {
524
+ if (!capabilityNamespaces) {
525
+ return false;
526
+ }
527
+ return capabilityNamespaces.length !== 0 && !hasEveryOverlap(bindingNamespaces, capabilityNamespaces);
528
+ }
529
+ function generateWatchNamespaceError(ignoredNamespaces, bindingNamespaces, capabilityNamespaces) {
530
+ let err = "";
531
+ if (ignoredNamespaceConflict(ignoredNamespaces, bindingNamespaces)) {
532
+ err += `Binding uses a Pepr ignored namespace: ignoredNamespaces: [${ignoredNamespaces.join(
533
+ ", "
534
+ )}] bindingNamespaces: [${bindingNamespaces.join(", ")}].`;
535
+ }
536
+ if (bindingAndCapabilityNSConflict(bindingNamespaces, capabilityNamespaces)) {
537
+ err += `Binding uses namespace not governed by capability: bindingNamespaces: [${bindingNamespaces.join(
538
+ ", "
539
+ )}] capabilityNamespaces: [${capabilityNamespaces.join(", ")}].`;
540
+ }
541
+ return err.replace(/\.([^ ])/g, ". $1");
542
+ }
543
+ function namespaceComplianceValidator(capability, ignoredNamespaces) {
544
+ const { namespaces: capabilityNamespaces, bindings, name: name2 } = capability;
545
+ const bindingNamespaces = bindings.flatMap((binding) => binding.filters.namespaces);
546
+ const bindingRegexNamespaces = bindings.flatMap(
547
+ (binding) => binding.filters.regexNamespaces || []
548
+ );
549
+ const namespaceError = generateWatchNamespaceError(
550
+ ignoredNamespaces ? ignoredNamespaces : [],
551
+ bindingNamespaces,
552
+ capabilityNamespaces ? capabilityNamespaces : []
553
+ );
554
+ if (namespaceError !== "") {
555
+ throw new Error(
556
+ `Error in ${name2} capability. A binding violates namespace rules. Please check ignoredNamespaces and capability namespaces: ${namespaceError}`
557
+ );
558
+ }
559
+ matchRegexToCapababilityNamespace(bindingRegexNamespaces, capabilityNamespaces);
560
+ checkRegexNamespaces(bindingRegexNamespaces, ignoredNamespaces);
561
+ }
562
+ var matchRegexToCapababilityNamespace = (bindingRegexNamespaces, capabilityNamespaces) => {
563
+ if (bindingRegexNamespaces.length > 0 && capabilityNamespaces && capabilityNamespaces.length > 0) {
564
+ for (const regexNamespace of bindingRegexNamespaces) {
565
+ let matches = false;
566
+ matches = regexNamespace !== "" && capabilityNamespaces.some((capabilityNamespace) => matchesRegex(regexNamespace, capabilityNamespace));
567
+ if (!matches) {
568
+ throw new Error(
569
+ `Ignoring Watch Callback: Object namespace does not match any capability namespace with regex ${regexNamespace}.`
570
+ );
571
+ }
572
+ }
573
+ }
574
+ };
575
+ var checkRegexNamespaces = (bindingRegexNamespaces, ignoredNamespaces) => {
576
+ if (bindingRegexNamespaces.length > 0 && ignoredNamespaces && ignoredNamespaces.length > 0) {
577
+ for (const regexNamespace of bindingRegexNamespaces) {
578
+ const matchedNS = ignoredNamespaces.find((ignoredNS) => matchesRegex(regexNamespace, ignoredNS));
579
+ if (matchedNS) {
580
+ throw new Error(
581
+ `Ignoring Watch Callback: Regex namespace: ${regexNamespace}, is an ignored namespace: ${matchedNS}.`
582
+ );
583
+ }
584
+ }
585
+ }
586
+ };
587
+ function secretOverLimit(str) {
588
+ const encoder = new TextEncoder();
589
+ const encoded = encoder.encode(str);
590
+ const sizeInBytes = encoded.length;
591
+ const oneMiBInBytes = 1048576;
592
+ return sizeInBytes > oneMiBInBytes;
593
+ }
594
+ var parseTimeout = (value) => {
595
+ const parsedValue = parseInt(value, 10);
596
+ const floatValue = parseFloat(value);
597
+ if (isNaN(parsedValue)) {
598
+ throw new Error("Not a number.");
599
+ } else if (parsedValue !== floatValue) {
600
+ throw new Error("Value must be an integer.");
601
+ } else if (parsedValue < 1 || parsedValue > 30) {
602
+ throw new Error("Number must be between 1 and 30.");
603
+ }
604
+ return parsedValue;
605
+ };
606
+ function dedent(file) {
607
+ const lines = file.split("\n");
413
608
  if (lines[0].trim() === "") {
414
609
  lines.shift();
415
610
  file = lines.join("\n");
@@ -464,7 +659,7 @@ function getWatcher(assets, hash, buildTimestamp, imagePullSecret) {
464
659
  if (bindings.length < 1 && !hasSchedule) {
465
660
  return null;
466
661
  }
467
- const deploy2 = {
662
+ const deploy = {
468
663
  apiVersion: "apps/v1",
469
664
  kind: "Deployment",
470
665
  metadata: {
@@ -514,7 +709,7 @@ function getWatcher(assets, hash, buildTimestamp, imagePullSecret) {
514
709
  name: "watcher",
515
710
  image,
516
711
  imagePullPolicy: "IfNotPresent",
517
- command: ["node", "/app/node_modules/pepr/dist/controller.js", hash],
712
+ args: ["/app/node_modules/pepr/dist/controller.js", hash],
518
713
  readinessProbe: {
519
714
  httpGet: {
520
715
  path: "/healthz",
@@ -589,14 +784,14 @@ function getWatcher(assets, hash, buildTimestamp, imagePullSecret) {
589
784
  }
590
785
  };
591
786
  if (imagePullSecret) {
592
- deploy2.spec.template.spec.imagePullSecrets = [{ name: imagePullSecret }];
787
+ deploy.spec.template.spec.imagePullSecrets = [{ name: imagePullSecret }];
593
788
  }
594
- return deploy2;
789
+ return deploy;
595
790
  }
596
791
  function getDeployment(assets, hash, buildTimestamp, imagePullSecret) {
597
792
  const { name: name2, image, config } = assets;
598
793
  const app = name2;
599
- const deploy2 = {
794
+ const deploy = {
600
795
  apiVersion: "apps/v1",
601
796
  kind: "Deployment",
602
797
  metadata: {
@@ -644,7 +839,7 @@ function getDeployment(assets, hash, buildTimestamp, imagePullSecret) {
644
839
  name: "server",
645
840
  image,
646
841
  imagePullPolicy: "IfNotPresent",
647
- command: ["node", "/app/node_modules/pepr/dist/controller.js", hash],
842
+ args: ["/app/node_modules/pepr/dist/controller.js", hash],
648
843
  readinessProbe: {
649
844
  httpGet: {
650
845
  path: "/healthz",
@@ -730,9 +925,9 @@ function getDeployment(assets, hash, buildTimestamp, imagePullSecret) {
730
925
  }
731
926
  };
732
927
  if (imagePullSecret) {
733
- deploy2.spec.template.spec.imagePullSecrets = [{ name: imagePullSecret }];
928
+ deploy.spec.template.spec.imagePullSecrets = [{ name: imagePullSecret }];
734
929
  }
735
- return deploy2;
930
+ return deploy;
736
931
  }
737
932
  function getModuleSecret(name2, data, hash) {
738
933
  const compressed = (0, import_zlib.gzipSync)(data);
@@ -773,6 +968,9 @@ function genEnv(config, watchMode = false, ignoreWatchMode = false) {
773
968
  return ignoreWatchMode ? Object.entries({ ...noWatchDef, ...cfg }).map(([name2, value]) => ({ name: name2, value })) : Object.entries({ ...def, ...cfg }).map(([name2, value]) => ({ name: name2, value }));
774
969
  }
775
970
 
971
+ // src/lib/assets/yaml/overridesFile.ts
972
+ var import_client_node = require("@kubernetes/client-node");
973
+
776
974
  // src/lib/assets/rbac.ts
777
975
  function clusterRole(name2, capabilities, rbacMode = "admin", customRbac) {
778
976
  const rbacMap = createRBACMap(capabilities);
@@ -867,897 +1065,422 @@ function storeRoleBinding(name2) {
867
1065
  },
868
1066
  subjects: [
869
1067
  {
870
- kind: "ServiceAccount",
871
- name: name2,
872
- namespace: "pepr-system"
873
- }
874
- ]
875
- };
876
- }
877
-
878
- // src/lib/k8s.ts
879
- var import_kubernetes_fluent_client2 = require("kubernetes-fluent-client");
880
- var Store = class extends import_kubernetes_fluent_client2.GenericKind {
881
- };
882
- var peprStoreGVK = {
883
- kind: "PeprStore",
884
- version: "v1",
885
- group: "pepr.dev"
886
- };
887
- (0, import_kubernetes_fluent_client2.RegisterKind)(Store, peprStoreGVK);
888
-
889
- // src/lib/assets/store.ts
890
- var { group, version, kind: kind2 } = peprStoreGVK;
891
- var singular = kind2.toLocaleLowerCase();
892
- var plural = `${singular}s`;
893
- var name = `${plural}.${group}`;
894
- var peprStoreCRD = {
895
- apiVersion: "apiextensions.k8s.io/v1",
896
- kind: "CustomResourceDefinition",
897
- metadata: {
898
- name
899
- },
900
- spec: {
901
- group,
902
- versions: [
903
- {
904
- // typescript doesn't know this is really already set, which is kind of annoying
905
- name: version || "v1",
906
- served: true,
907
- storage: true,
908
- schema: {
909
- openAPIV3Schema: {
910
- type: "object",
911
- properties: {
912
- data: {
913
- type: "object",
914
- additionalProperties: {
915
- type: "string"
916
- }
917
- }
918
- }
919
- }
920
- }
921
- }
922
- ],
923
- scope: "Namespaced",
924
- names: {
925
- plural,
926
- singular,
927
- kind: kind2
928
- }
929
- }
930
- };
931
-
932
- // src/lib/assets/webhooks.ts
933
- var import_ramda = require("ramda");
934
- var peprIgnoreLabel = {
935
- key: "pepr.dev",
936
- operator: "NotIn",
937
- values: ["ignore"]
938
- };
939
- var peprIgnoreNamespaces = ["kube-system", "pepr-system"];
940
- var validateRule = (binding, isMutateWebhook) => {
941
- const { event, kind: kind8, isMutate, isValidate } = binding;
942
- if (isMutateWebhook && !isMutate || !isMutateWebhook && !isValidate) {
943
- return void 0;
944
- }
945
- const operations = event === "CREATEORUPDATE" /* CREATE_OR_UPDATE */ ? ["CREATE" /* CREATE */, "UPDATE" /* UPDATE */] : [event];
946
- const resource = kind8.plural || `${kind8.kind.toLowerCase()}s`;
947
- const ruleObject = {
948
- apiGroups: [kind8.group],
949
- apiVersions: [kind8.version || "*"],
950
- operations,
951
- resources: [resource, ...resource === "pods" ? ["pods/ephemeralcontainers"] : []]
952
- };
953
- return ruleObject;
954
- };
955
- async function generateWebhookRules(assets, isMutateWebhook) {
956
- const { config, capabilities } = assets;
957
- const rules = capabilities.flatMap((capability) => {
958
- console.info(`Module ${config.uuid} has capability: ${capability.name}`);
959
- return capability.bindings.map((binding) => validateRule(binding, isMutateWebhook)).filter((rule) => !!rule);
960
- });
961
- return (0, import_ramda.uniqWith)(import_ramda.equals, rules);
962
- }
963
- async function webhookConfig(assets, mutateOrValidate, timeoutSeconds = 10) {
964
- const ignore = [peprIgnoreLabel];
965
- const { name: name2, tls, config, apiToken, host } = assets;
966
- const ignoreNS = (0, import_ramda.concat)(peprIgnoreNamespaces, config?.alwaysIgnore?.namespaces || []);
967
- if (ignoreNS) {
968
- ignore.push({
969
- key: "kubernetes.io/metadata.name",
970
- operator: "NotIn",
971
- values: ignoreNS
972
- });
973
- }
974
- const clientConfig = {
975
- caBundle: tls.ca
976
- };
977
- const apiPath = `/${mutateOrValidate}/${apiToken}`;
978
- if (host) {
979
- clientConfig.url = `https://${host}:3000${apiPath}`;
980
- } else {
981
- clientConfig.service = {
982
- name: name2,
983
- namespace: "pepr-system",
984
- path: apiPath
985
- };
986
- }
987
- const isMutate = mutateOrValidate === "mutate";
988
- const rules = await generateWebhookRules(assets, isMutate);
989
- if (rules.length < 1) {
990
- return null;
991
- }
992
- return {
993
- apiVersion: "admissionregistration.k8s.io/v1",
994
- kind: isMutate ? "MutatingWebhookConfiguration" : "ValidatingWebhookConfiguration",
995
- metadata: { name: name2 },
996
- webhooks: [
997
- {
998
- name: `${name2}.pepr.dev`,
999
- admissionReviewVersions: ["v1", "v1beta1"],
1000
- clientConfig,
1001
- failurePolicy: config.onError === "reject" ? "Fail" : "Ignore",
1002
- matchPolicy: "Equivalent",
1003
- timeoutSeconds,
1004
- namespaceSelector: {
1005
- matchExpressions: ignore
1006
- },
1007
- objectSelector: {
1008
- matchExpressions: ignore
1009
- },
1010
- rules,
1011
- // @todo: track side effects state
1012
- sideEffects: "None"
1013
- }
1014
- ]
1015
- };
1016
- }
1017
-
1018
- // src/lib/assets/deploy.ts
1019
- async function deployImagePullSecret(imagePullSecret, name2) {
1020
- try {
1021
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Namespace).Get("pepr-system");
1022
- } catch {
1023
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Namespace).Apply(getNamespace());
1024
- }
1025
- try {
1026
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(
1027
- {
1028
- apiVersion: "v1",
1029
- kind: "Secret",
1030
- metadata: {
1031
- name: name2,
1032
- namespace: "pepr-system"
1033
- },
1034
- type: "kubernetes.io/dockerconfigjson",
1035
- data: {
1036
- ".dockerconfigjson": Buffer.from(JSON.stringify(imagePullSecret)).toString("base64")
1037
- }
1038
- },
1039
- { force: true }
1040
- );
1041
- } catch (e) {
1042
- logger_default.error(e);
1043
- }
1044
- }
1045
- async function deploy(assets, force, webhookTimeout) {
1046
- logger_default.info("Establishing connection to Kubernetes");
1047
- const { name: name2, host, path } = assets;
1048
- logger_default.info("Applying pepr-system namespace");
1049
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Namespace).Apply(getNamespace(assets.config.customLabels?.namespace));
1050
- const mutateWebhook = await webhookConfig(assets, "mutate", webhookTimeout);
1051
- if (mutateWebhook) {
1052
- logger_default.info("Applying mutating webhook");
1053
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.MutatingWebhookConfiguration).Apply(mutateWebhook, { force });
1054
- } else {
1055
- logger_default.info("Mutating webhook not needed, removing if it exists");
1056
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.MutatingWebhookConfiguration).Delete(name2);
1057
- }
1058
- const validateWebhook = await webhookConfig(assets, "validate", webhookTimeout);
1059
- if (validateWebhook) {
1060
- logger_default.info("Applying validating webhook");
1061
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ValidatingWebhookConfiguration).Apply(validateWebhook, { force });
1062
- } else {
1063
- logger_default.info("Validating webhook not needed, removing if it exists");
1064
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ValidatingWebhookConfiguration).Delete(name2);
1065
- }
1066
- logger_default.info("Applying the Pepr Store CRD if it doesn't exist");
1067
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.CustomResourceDefinition).Apply(peprStoreCRD, { force });
1068
- if (host) {
1069
- return;
1070
- }
1071
- const code = await import_fs.promises.readFile(path);
1072
- const hash = import_crypto.default.createHash("sha256").update(code).digest("hex");
1073
- if (code.length < 1) {
1074
- throw new Error("No code provided");
1075
- }
1076
- await setupRBAC(name2, assets.capabilities, force, assets.config);
1077
- await setupController(assets, code, hash, force);
1078
- await setupWatcher(assets, hash, force);
1079
- }
1080
- async function setupRBAC(name2, capabilities, force, config) {
1081
- const { rbacMode, rbac } = config;
1082
- logger_default.info("Applying cluster role binding");
1083
- const crb = clusterRoleBinding(name2);
1084
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ClusterRoleBinding).Apply(crb, { force });
1085
- logger_default.info("Applying cluster role");
1086
- const cr = clusterRole(name2, capabilities, rbacMode, rbac);
1087
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ClusterRole).Apply(cr, { force });
1088
- logger_default.info("Applying service account");
1089
- const sa = serviceAccount(name2);
1090
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ServiceAccount).Apply(sa, { force });
1091
- logger_default.info("Applying store role");
1092
- const role = storeRole(name2);
1093
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Role).Apply(role, { force });
1094
- logger_default.info("Applying store role binding");
1095
- const roleBinding = storeRoleBinding(name2);
1096
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.RoleBinding).Apply(roleBinding, { force });
1097
- }
1098
- async function setupController(assets, code, hash, force) {
1099
- const { name: name2 } = assets;
1100
- logger_default.info("Applying module secret");
1101
- const mod = getModuleSecret(name2, code, hash);
1102
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(mod, { force });
1103
- logger_default.info("Applying controller service");
1104
- const svc = service(name2);
1105
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Service).Apply(svc, { force });
1106
- logger_default.info("Applying TLS secret");
1107
- const tls = tlsSecret(name2, assets.tls);
1108
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(tls, { force });
1109
- logger_default.info("Applying API token secret");
1110
- const apiToken = apiTokenSecret(name2, assets.apiToken);
1111
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(apiToken, { force });
1112
- logger_default.info("Applying deployment");
1113
- const dep = getDeployment(assets, hash, assets.buildTimestamp);
1114
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Deployment).Apply(dep, { force });
1115
- }
1116
- async function setupWatcher(assets, hash, force) {
1117
- const watchDeployment = getWatcher(assets, hash, assets.buildTimestamp);
1118
- if (watchDeployment) {
1119
- logger_default.info("Applying watcher deployment");
1120
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Deployment).Apply(watchDeployment, { force });
1121
- logger_default.info("Applying watcher service");
1122
- const watchSvc = watcherService(assets.name);
1123
- await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Service).Apply(watchSvc, { force });
1124
- }
1125
- }
1126
-
1127
- // src/lib/assets/loader.ts
1128
- var import_child_process = require("child_process");
1129
- function loadCapabilities(path) {
1130
- return new Promise((resolve6, reject) => {
1131
- const program2 = (0, import_child_process.fork)(path, {
1132
- env: {
1133
- ...process.env,
1134
- LOG_LEVEL: "warn",
1135
- PEPR_MODE: "build",
1136
- NODE_OPTIONS: "--disable-warning=DEP0040"
1137
- }
1138
- });
1139
- program2.on("message", (message) => {
1140
- const capabilities = message.valueOf();
1141
- for (const capability of capabilities) {
1142
- console.info(`Registered Pepr Capability "${capability.name}"`);
1143
- }
1144
- resolve6(capabilities);
1145
- });
1146
- program2.on("error", (error) => {
1147
- reject(error);
1148
- });
1149
- });
1150
- }
1151
-
1152
- // src/lib/assets/yaml.ts
1153
- var import_client_node = require("@kubernetes/client-node");
1154
- var import_crypto2 = __toESM(require("crypto"));
1155
- var import_fs2 = require("fs");
1156
- async function overridesFile({ hash, name: name2, image, config, apiToken, capabilities }, path) {
1157
- const rbacOverrides = clusterRole(name2, capabilities, config.rbacMode, config.rbac).rules;
1158
- const overrides = {
1159
- rbac: rbacOverrides,
1160
- secrets: {
1161
- apiToken: Buffer.from(apiToken).toString("base64")
1162
- },
1163
- hash,
1164
- namespace: {
1165
- annotations: {},
1166
- labels: {
1167
- "pepr.dev": ""
1168
- }
1169
- },
1170
- uuid: name2,
1171
- admission: {
1172
- terminationGracePeriodSeconds: 5,
1173
- failurePolicy: config.onError === "reject" ? "Fail" : "Ignore",
1174
- webhookTimeout: config.webhookTimeout,
1175
- env: genEnv(config, false, true),
1176
- envFrom: [],
1177
- image,
1178
- annotations: {
1179
- "pepr.dev/description": `${config.description}` || ""
1180
- },
1181
- labels: {
1182
- app: name2,
1183
- "pepr.dev/controller": "admission",
1184
- "pepr.dev/uuid": config.uuid
1185
- },
1186
- securityContext: {
1187
- runAsUser: 65532,
1188
- runAsGroup: 65532,
1189
- runAsNonRoot: true,
1190
- fsGroup: 65532
1191
- },
1192
- readinessProbe: {
1193
- httpGet: {
1194
- path: "/healthz",
1195
- port: 3e3,
1196
- scheme: "HTTPS"
1197
- },
1198
- initialDelaySeconds: 10
1199
- },
1200
- livenessProbe: {
1201
- httpGet: {
1202
- path: "/healthz",
1203
- port: 3e3,
1204
- scheme: "HTTPS"
1205
- },
1206
- initialDelaySeconds: 10
1207
- },
1208
- resources: {
1209
- requests: {
1210
- memory: "256Mi",
1211
- cpu: "200m"
1212
- },
1213
- limits: {
1214
- memory: "512Mi",
1215
- cpu: "500m"
1216
- }
1217
- },
1218
- containerSecurityContext: {
1219
- runAsUser: 65532,
1220
- runAsGroup: 65532,
1221
- runAsNonRoot: true,
1222
- allowPrivilegeEscalation: false,
1223
- capabilities: {
1224
- drop: ["ALL"]
1225
- }
1226
- },
1227
- podAnnotations: {},
1228
- nodeSelector: {},
1229
- tolerations: [],
1230
- extraVolumeMounts: [],
1231
- extraVolumes: [],
1232
- affinity: {},
1233
- serviceMonitor: {
1234
- enabled: false,
1235
- labels: {},
1236
- annotations: {}
1237
- }
1238
- },
1239
- watcher: {
1240
- terminationGracePeriodSeconds: 5,
1241
- env: genEnv(config, true, true),
1242
- envFrom: [],
1243
- image,
1244
- annotations: {
1245
- "pepr.dev/description": `${config.description}` || ""
1246
- },
1247
- labels: {
1248
- app: `${name2}-watcher`,
1249
- "pepr.dev/controller": "watcher",
1250
- "pepr.dev/uuid": config.uuid
1251
- },
1252
- securityContext: {
1253
- runAsUser: 65532,
1254
- runAsGroup: 65532,
1255
- runAsNonRoot: true,
1256
- fsGroup: 65532
1257
- },
1258
- readinessProbe: {
1259
- httpGet: {
1260
- path: "/healthz",
1261
- port: 3e3,
1262
- scheme: "HTTPS"
1263
- },
1264
- initialDelaySeconds: 10
1265
- },
1266
- livenessProbe: {
1267
- httpGet: {
1268
- path: "/healthz",
1269
- port: 3e3,
1270
- scheme: "HTTPS"
1271
- },
1272
- initialDelaySeconds: 10
1273
- },
1274
- resources: {
1275
- requests: {
1276
- memory: "256Mi",
1277
- cpu: "200m"
1278
- },
1279
- limits: {
1280
- memory: "512Mi",
1281
- cpu: "500m"
1282
- }
1283
- },
1284
- containerSecurityContext: {
1285
- runAsUser: 65532,
1286
- runAsGroup: 65532,
1287
- runAsNonRoot: true,
1288
- allowPrivilegeEscalation: false,
1289
- capabilities: {
1290
- drop: ["ALL"]
1291
- }
1292
- },
1293
- nodeSelector: {},
1294
- tolerations: [],
1295
- extraVolumeMounts: [],
1296
- extraVolumes: [],
1297
- affinity: {},
1298
- podAnnotations: {},
1299
- serviceMonitor: {
1300
- enabled: false,
1301
- labels: {},
1302
- annotations: {}
1303
- }
1304
- }
1305
- };
1306
- await import_fs2.promises.writeFile(path, (0, import_client_node.dumpYaml)(overrides, { noRefs: true, forceQuotes: true }));
1307
- }
1308
- function zarfYaml({ name: name2, image, config }, path) {
1309
- const zarfCfg = {
1310
- kind: "ZarfPackageConfig",
1311
- metadata: {
1312
- name: name2,
1313
- description: `Pepr Module: ${config.description}`,
1314
- url: "https://github.com/defenseunicorns/pepr",
1315
- version: `${config.appVersion || "0.0.1"}`
1316
- },
1317
- components: [
1318
- {
1319
- name: "module",
1320
- required: true,
1321
- manifests: [
1322
- {
1323
- name: "module",
1324
- namespace: "pepr-system",
1325
- files: [path]
1326
- }
1327
- ],
1328
- images: [image]
1329
- }
1330
- ]
1331
- };
1332
- return (0, import_client_node.dumpYaml)(zarfCfg, { noRefs: true });
1333
- }
1334
- function zarfYamlChart({ name: name2, image, config }, path) {
1335
- const zarfCfg = {
1336
- kind: "ZarfPackageConfig",
1337
- metadata: {
1338
- name: name2,
1339
- description: `Pepr Module: ${config.description}`,
1340
- url: "https://github.com/defenseunicorns/pepr",
1341
- version: `${config.appVersion || "0.0.1"}`
1342
- },
1343
- components: [
1344
- {
1345
- name: "module",
1346
- required: true,
1347
- charts: [
1348
- {
1349
- name: "module",
1350
- namespace: "pepr-system",
1351
- version: `${config.appVersion || "0.0.1"}`,
1352
- localPath: path
1353
- }
1354
- ],
1355
- images: [image]
1356
- }
1357
- ]
1358
- };
1359
- return (0, import_client_node.dumpYaml)(zarfCfg, { noRefs: true });
1360
- }
1361
- async function allYaml(assets, imagePullSecret) {
1362
- const { name: name2, tls, apiToken, path, config } = assets;
1363
- const code = await import_fs2.promises.readFile(path);
1364
- assets.hash = import_crypto2.default.createHash("sha256").update(code).digest("hex");
1365
- const mutateWebhook = await webhookConfig(assets, "mutate", assets.config.webhookTimeout);
1366
- const validateWebhook = await webhookConfig(assets, "validate", assets.config.webhookTimeout);
1367
- const watchDeployment = getWatcher(assets, assets.hash, assets.buildTimestamp, imagePullSecret);
1368
- const resources = [
1369
- getNamespace(assets.config.customLabels?.namespace),
1370
- clusterRole(name2, assets.capabilities, config.rbacMode, config.rbac),
1371
- clusterRoleBinding(name2),
1372
- serviceAccount(name2),
1373
- apiTokenSecret(name2, apiToken),
1374
- tlsSecret(name2, tls),
1375
- getDeployment(assets, assets.hash, assets.buildTimestamp, imagePullSecret),
1376
- service(name2),
1377
- watcherService(name2),
1378
- getModuleSecret(name2, code, assets.hash),
1379
- storeRole(name2),
1380
- storeRoleBinding(name2)
1381
- ];
1382
- if (mutateWebhook) {
1383
- resources.push(mutateWebhook);
1384
- }
1385
- if (validateWebhook) {
1386
- resources.push(validateWebhook);
1387
- }
1388
- if (watchDeployment) {
1389
- resources.push(watchDeployment);
1390
- }
1391
- return resources.map((r) => (0, import_client_node.dumpYaml)(r, { noRefs: true })).join("---\n");
1392
- }
1393
-
1394
- // src/lib/assets/index.ts
1395
- var import_path = require("path");
1396
-
1397
- // src/lib/assets/helm.ts
1398
- function clusterRoleTemplate() {
1399
- return `
1400
- apiVersion: rbac.authorization.k8s.io/v1
1401
- kind: ClusterRole
1402
- metadata:
1403
- name: {{ .Values.uuid }}
1404
- namespace: pepr-system
1405
- rules:
1406
- {{- if .Values.rbac }}
1407
- {{- toYaml .Values.rbac | nindent 2 }}
1408
- {{- end }}
1409
- `;
1410
- }
1411
- function namespaceTemplate() {
1412
- return `
1413
- apiVersion: v1
1414
- kind: Namespace
1415
- metadata:
1416
- name: pepr-system
1417
- {{- if .Values.namespace.annotations }}
1418
- annotations:
1419
- {{- toYaml .Values.namespace.annotations | nindent 6 }}
1420
- {{- end }}
1421
- {{- if .Values.namespace.labels }}
1422
- labels:
1423
- {{- toYaml .Values.namespace.labels | nindent 6 }}
1424
- {{- end }}
1425
- `;
1426
- }
1427
- function chartYaml(name2, description) {
1428
- return `
1429
- apiVersion: v2
1430
- name: ${name2}
1431
- description: ${description || ""}
1432
-
1433
- # A chart can be either an 'application' or a 'library' chart.
1434
- #
1435
- # Application charts are a collection of templates that can be packaged into versioned archives
1436
- # to be deployed.
1437
- #
1438
- # Library charts provide useful utilities or functions for the chart developer. They're included as
1439
- # a dependency of application charts to inject those utilities and functions into the rendering
1440
- # pipeline. Library charts do not define any templates and therefore cannot be deployed.
1441
- type: application
1442
-
1443
- # This is the chart version. This version number should be incremented each time you make changes
1444
- # to the chart and its templates, including the app version.
1445
- # Versions are expected to follow Semantic Versioning (https://semver.org/)
1446
- version: 0.1.0
1447
-
1448
- # This is the version number of the application being deployed. This version number should be
1449
- # incremented each time you make changes to the application. Versions are not expected to
1450
- # follow Semantic Versioning. They should reflect the version the application is using.
1451
- # It is recommended to use it with quotes.
1452
- appVersion: "1.16.0"
1453
- `;
1454
- }
1455
- function watcherDeployTemplate(buildTimestamp) {
1456
- return `
1457
- apiVersion: apps/v1
1458
- kind: Deployment
1459
- metadata:
1460
- name: {{ .Values.uuid }}-watcher
1461
- namespace: pepr-system
1462
- annotations:
1463
- {{- toYaml .Values.watcher.annotations | nindent 4 }}
1464
- labels:
1465
- {{- toYaml .Values.watcher.labels | nindent 4 }}
1466
- spec:
1467
- replicas: 1
1468
- strategy:
1469
- type: Recreate
1470
- selector:
1471
- matchLabels:
1472
- app: {{ .Values.uuid }}-watcher
1473
- pepr.dev/controller: watcher
1474
- template:
1475
- metadata:
1476
- annotations:
1477
- buildTimestamp: "${buildTimestamp}"
1478
- {{- if .Values.watcher.podAnnotations }}
1479
- {{- toYaml .Values.watcher.podAnnotations | nindent 8 }}
1480
- {{- end }}
1481
- labels:
1482
- app: {{ .Values.uuid }}-watcher
1483
- pepr.dev/controller: watcher
1484
- spec:
1485
- terminationGracePeriodSeconds: {{ .Values.watcher.terminationGracePeriodSeconds }}
1486
- serviceAccountName: {{ .Values.uuid }}
1487
- securityContext:
1488
- {{- toYaml .Values.admission.securityContext | nindent 8 }}
1489
- containers:
1490
- - name: watcher
1491
- image: {{ .Values.watcher.image }}
1492
- imagePullPolicy: IfNotPresent
1493
- command:
1494
- - node
1495
- - /app/node_modules/pepr/dist/controller.js
1496
- - {{ .Values.hash }}
1497
- readinessProbe:
1498
- {{- toYaml .Values.watcher.readinessProbe | nindent 12 }}
1499
- livenessProbe:
1500
- {{- toYaml .Values.watcher.livenessProbe | nindent 12 }}
1501
- ports:
1502
- - containerPort: 3000
1503
- resources:
1504
- {{- toYaml .Values.watcher.resources | nindent 12 }}
1505
- env:
1506
- {{- toYaml .Values.watcher.env | nindent 12 }}
1507
- - name: PEPR_WATCH_MODE
1508
- value: "true"
1509
- envFrom:
1510
- {{- toYaml .Values.watcher.envFrom | nindent 12 }}
1511
- securityContext:
1512
- {{- toYaml .Values.watcher.containerSecurityContext | nindent 12 }}
1513
- volumeMounts:
1514
- - name: tls-certs
1515
- mountPath: /etc/certs
1516
- readOnly: true
1517
- - name: module
1518
- mountPath: /app/load
1519
- readOnly: true
1520
- {{- if .Values.watcher.extraVolumeMounts }}
1521
- {{- toYaml .Values.watcher.extraVolumeMounts | nindent 12 }}
1522
- {{- end }}
1523
- volumes:
1524
- - name: tls-certs
1525
- secret:
1526
- secretName: {{ .Values.uuid }}-tls
1527
- - name: module
1528
- secret:
1529
- secretName: {{ .Values.uuid }}-module
1530
- {{- if .Values.watcher.extraVolumes }}
1531
- {{- toYaml .Values.watcher.extraVolumes | nindent 8 }}
1532
- {{- end }}
1533
- `;
1534
- }
1535
- function admissionDeployTemplate(buildTimestamp) {
1536
- return `
1537
- apiVersion: apps/v1
1538
- kind: Deployment
1539
- metadata:
1540
- name: {{ .Values.uuid }}
1541
- namespace: pepr-system
1542
- annotations:
1543
- {{- toYaml .Values.admission.annotations | nindent 4 }}
1544
- labels:
1545
- {{- toYaml .Values.admission.labels | nindent 4 }}
1546
- spec:
1547
- replicas: 2
1548
- selector:
1549
- matchLabels:
1550
- app: {{ .Values.uuid }}
1551
- pepr.dev/controller: admission
1552
- template:
1553
- metadata:
1554
- annotations:
1555
- buildTimestamp: "${buildTimestamp}"
1556
- {{- if .Values.admission.podAnnotations }}
1557
- {{- toYaml .Values.admission.podAnnotations | nindent 8 }}
1558
- {{- end }}
1559
- labels:
1560
- app: {{ .Values.uuid }}
1561
- pepr.dev/controller: admission
1562
- spec:
1563
- terminationGracePeriodSeconds: {{ .Values.admission.terminationGracePeriodSeconds }}
1564
- priorityClassName: system-node-critical
1565
- serviceAccountName: {{ .Values.uuid }}
1566
- securityContext:
1567
- {{- toYaml .Values.admission.securityContext | nindent 8 }}
1568
- containers:
1569
- - name: server
1570
- image: {{ .Values.admission.image }}
1571
- imagePullPolicy: IfNotPresent
1572
- command:
1573
- - node
1574
- - /app/node_modules/pepr/dist/controller.js
1575
- - {{ .Values.hash }}
1576
- readinessProbe:
1577
- {{- toYaml .Values.admission.readinessProbe | nindent 12 }}
1578
- livenessProbe:
1579
- {{- toYaml .Values.admission.livenessProbe | nindent 12 }}
1580
- ports:
1581
- - containerPort: 3000
1582
- resources:
1583
- {{- toYaml .Values.admission.resources | nindent 12 }}
1584
- env:
1585
- {{- toYaml .Values.admission.env | nindent 12 }}
1586
- - name: PEPR_WATCH_MODE
1587
- value: "false"
1588
- envFrom:
1589
- {{- toYaml .Values.admission.envFrom | nindent 12 }}
1590
- securityContext:
1591
- {{- toYaml .Values.admission.containerSecurityContext | nindent 12 }}
1592
- volumeMounts:
1593
- - name: tls-certs
1594
- mountPath: /etc/certs
1595
- readOnly: true
1596
- - name: api-token
1597
- mountPath: /app/api-token
1598
- readOnly: true
1599
- - name: module
1600
- mountPath: /app/load
1601
- readOnly: true
1602
- {{- if .Values.admission.extraVolumeMounts }}
1603
- {{- toYaml .Values.admission.extraVolumeMounts | nindent 12 }}
1604
- {{- end }}
1605
- volumes:
1606
- - name: tls-certs
1607
- secret:
1608
- secretName: {{ .Values.uuid }}-tls
1609
- - name: api-token
1610
- secret:
1611
- secretName: {{ .Values.uuid }}-api-token
1612
- - name: module
1613
- secret:
1614
- secretName: {{ .Values.uuid }}-module
1615
- {{- if .Values.admission.extraVolumes }}
1616
- {{- toYaml .Values.admission.extraVolumes | nindent 8 }}
1617
- {{- end }}
1618
- `;
1619
- }
1620
- function serviceMonitorTemplate(name2) {
1621
- return `
1622
- {{- if .Values.${name2}.serviceMonitor.enabled }}
1623
- apiVersion: monitoring.coreos.com/v1
1624
- kind: ServiceMonitor
1625
- metadata:
1626
- name: ${name2}
1627
- annotations:
1628
- {{- toYaml .Values.${name2}.serviceMonitor.annotations | nindent 4 }}
1629
- labels:
1630
- {{- toYaml .Values.${name2}.serviceMonitor.labels | nindent 4 }}
1631
- spec:
1632
- selector:
1633
- matchLabels:
1634
- pepr.dev/controller: ${name2}
1635
- namespaceSelector:
1636
- matchNames:
1637
- - pepr-system
1638
- endpoints:
1639
- - targetPort: 3000
1640
- scheme: https
1641
- tlsConfig:
1642
- insecureSkipVerify: true
1643
- {{- end }}
1644
- `;
1068
+ kind: "ServiceAccount",
1069
+ name: name2,
1070
+ namespace: "pepr-system"
1071
+ }
1072
+ ]
1073
+ };
1645
1074
  }
1646
1075
 
1647
- // src/lib/assets/index.ts
1648
- var import_fs4 = require("fs");
1649
-
1650
- // src/lib/filesystemService.ts
1651
- var import_fs3 = require("fs");
1652
- async function createDirectoryIfNotExists(path) {
1653
- try {
1654
- await import_fs3.promises.access(path);
1655
- } catch (error) {
1656
- if (error.code === "ENOENT") {
1657
- await import_fs3.promises.mkdir(path, { recursive: true });
1658
- } else {
1659
- throw error;
1076
+ // src/lib/assets/yaml/overridesFile.ts
1077
+ var import_fs2 = require("fs");
1078
+ async function overridesFile({ hash, name: name2, image, config, apiToken, capabilities }, path, imagePullSecrets) {
1079
+ const rbacOverrides = clusterRole(name2, capabilities, config.rbacMode, config.rbac).rules;
1080
+ const overrides = {
1081
+ imagePullSecrets,
1082
+ additionalIgnoredNamespaces: [],
1083
+ rbac: rbacOverrides,
1084
+ secrets: {
1085
+ apiToken: Buffer.from(apiToken).toString("base64")
1086
+ },
1087
+ hash,
1088
+ namespace: {
1089
+ annotations: {},
1090
+ labels: {
1091
+ "pepr.dev": ""
1092
+ }
1093
+ },
1094
+ uuid: name2,
1095
+ admission: {
1096
+ terminationGracePeriodSeconds: 5,
1097
+ failurePolicy: config.onError === "reject" ? "Fail" : "Ignore",
1098
+ webhookTimeout: config.webhookTimeout,
1099
+ env: genEnv(config, false, true),
1100
+ envFrom: [],
1101
+ image,
1102
+ annotations: {
1103
+ "pepr.dev/description": `${config.description}` || ""
1104
+ },
1105
+ labels: {
1106
+ app: name2,
1107
+ "pepr.dev/controller": "admission",
1108
+ "pepr.dev/uuid": config.uuid
1109
+ },
1110
+ securityContext: {
1111
+ runAsUser: 65532,
1112
+ runAsGroup: 65532,
1113
+ runAsNonRoot: true,
1114
+ fsGroup: 65532
1115
+ },
1116
+ readinessProbe: {
1117
+ httpGet: {
1118
+ path: "/healthz",
1119
+ port: 3e3,
1120
+ scheme: "HTTPS"
1121
+ },
1122
+ initialDelaySeconds: 10
1123
+ },
1124
+ livenessProbe: {
1125
+ httpGet: {
1126
+ path: "/healthz",
1127
+ port: 3e3,
1128
+ scheme: "HTTPS"
1129
+ },
1130
+ initialDelaySeconds: 10
1131
+ },
1132
+ resources: {
1133
+ requests: {
1134
+ memory: "256Mi",
1135
+ cpu: "200m"
1136
+ },
1137
+ limits: {
1138
+ memory: "512Mi",
1139
+ cpu: "500m"
1140
+ }
1141
+ },
1142
+ containerSecurityContext: {
1143
+ runAsUser: 65532,
1144
+ runAsGroup: 65532,
1145
+ runAsNonRoot: true,
1146
+ allowPrivilegeEscalation: false,
1147
+ capabilities: {
1148
+ drop: ["ALL"]
1149
+ }
1150
+ },
1151
+ podAnnotations: {},
1152
+ nodeSelector: {},
1153
+ tolerations: [],
1154
+ extraVolumeMounts: [],
1155
+ extraVolumes: [],
1156
+ affinity: {},
1157
+ serviceMonitor: {
1158
+ enabled: false,
1159
+ labels: {},
1160
+ annotations: {}
1161
+ }
1162
+ },
1163
+ watcher: {
1164
+ terminationGracePeriodSeconds: 5,
1165
+ env: genEnv(config, true, true),
1166
+ envFrom: [],
1167
+ image,
1168
+ annotations: {
1169
+ "pepr.dev/description": `${config.description}` || ""
1170
+ },
1171
+ labels: {
1172
+ app: `${name2}-watcher`,
1173
+ "pepr.dev/controller": "watcher",
1174
+ "pepr.dev/uuid": config.uuid
1175
+ },
1176
+ securityContext: {
1177
+ runAsUser: 65532,
1178
+ runAsGroup: 65532,
1179
+ runAsNonRoot: true,
1180
+ fsGroup: 65532
1181
+ },
1182
+ readinessProbe: {
1183
+ httpGet: {
1184
+ path: "/healthz",
1185
+ port: 3e3,
1186
+ scheme: "HTTPS"
1187
+ },
1188
+ initialDelaySeconds: 10
1189
+ },
1190
+ livenessProbe: {
1191
+ httpGet: {
1192
+ path: "/healthz",
1193
+ port: 3e3,
1194
+ scheme: "HTTPS"
1195
+ },
1196
+ initialDelaySeconds: 10
1197
+ },
1198
+ resources: {
1199
+ requests: {
1200
+ memory: "256Mi",
1201
+ cpu: "200m"
1202
+ },
1203
+ limits: {
1204
+ memory: "512Mi",
1205
+ cpu: "500m"
1206
+ }
1207
+ },
1208
+ containerSecurityContext: {
1209
+ runAsUser: 65532,
1210
+ runAsGroup: 65532,
1211
+ runAsNonRoot: true,
1212
+ allowPrivilegeEscalation: false,
1213
+ capabilities: {
1214
+ drop: ["ALL"]
1215
+ }
1216
+ },
1217
+ nodeSelector: {},
1218
+ tolerations: [],
1219
+ extraVolumeMounts: [],
1220
+ extraVolumes: [],
1221
+ affinity: {},
1222
+ podAnnotations: {},
1223
+ serviceMonitor: {
1224
+ enabled: false,
1225
+ labels: {},
1226
+ annotations: {}
1227
+ }
1660
1228
  }
1661
- }
1229
+ };
1230
+ await import_fs2.promises.writeFile(path, (0, import_client_node.dumpYaml)(overrides, { noRefs: true, forceQuotes: true }));
1662
1231
  }
1663
1232
 
1664
1233
  // src/lib/assets/index.ts
1234
+ var import_client_node2 = require("@kubernetes/client-node");
1235
+ var import_path = require("path");
1665
1236
  function toYaml(obj) {
1666
1237
  return (0, import_client_node2.dumpYaml)(obj, { noRefs: true });
1667
1238
  }
1668
- function createWebhookYaml(assets, webhookConfiguration) {
1239
+ function createWebhookYaml(name2, config, webhookConfiguration) {
1669
1240
  const yaml = toYaml(webhookConfiguration);
1670
- return replaceString(
1671
- replaceString(
1672
- replaceString(yaml, assets.name, "{{ .Values.uuid }}"),
1673
- assets.config.onError === "reject" ? "Fail" : "Ignore",
1674
- "{{ .Values.admission.failurePolicy }}"
1675
- ),
1676
- `${assets.config.webhookTimeout}` || "10",
1677
- "{{ .Values.admission.webhookTimeout }}"
1678
- );
1241
+ const replacements = [
1242
+ { search: name2, replace: "{{ .Values.uuid }}" },
1243
+ {
1244
+ search: config.onError === "reject" ? "Fail" : "Ignore",
1245
+ replace: "{{ .Values.admission.failurePolicy }}"
1246
+ },
1247
+ {
1248
+ search: `${config.webhookTimeout}` || "10",
1249
+ replace: "{{ .Values.admission.webhookTimeout }}"
1250
+ },
1251
+ {
1252
+ search: `
1253
+ - key: kubernetes.io/metadata.name
1254
+ operator: NotIn
1255
+ values:
1256
+ - kube-system
1257
+ - pepr-system
1258
+ `,
1259
+ replace: `
1260
+ - key: kubernetes.io/metadata.name
1261
+ operator: NotIn
1262
+ values:
1263
+ - kube-system
1264
+ - pepr-system
1265
+ {{- range .Values.additionalIgnoredNamespaces }}
1266
+ - {{ . }}
1267
+ {{- end }}
1268
+ `
1269
+ }
1270
+ ];
1271
+ return replacements.reduce((updatedYaml, { search, replace }) => replaceString(updatedYaml, search, replace), yaml);
1272
+ }
1273
+ function helmLayout(basePath, unique) {
1274
+ const helm = {
1275
+ dirs: {
1276
+ chart: (0, import_path.resolve)(`${basePath}/${unique}-chart`)
1277
+ },
1278
+ files: {}
1279
+ };
1280
+ helm.dirs = {
1281
+ ...helm.dirs,
1282
+ charts: `${helm.dirs.chart}/charts`,
1283
+ tmpls: `${helm.dirs.chart}/templates`
1284
+ };
1285
+ helm.files = {
1286
+ ...helm.files,
1287
+ valuesYaml: `${helm.dirs.chart}/values.yaml`,
1288
+ chartYaml: `${helm.dirs.chart}/Chart.yaml`,
1289
+ namespaceYaml: `${helm.dirs.tmpls}/namespace.yaml`,
1290
+ watcherServiceYaml: `${helm.dirs.tmpls}/watcher-service.yaml`,
1291
+ admissionServiceYaml: `${helm.dirs.tmpls}/admission-service.yaml`,
1292
+ mutationWebhookYaml: `${helm.dirs.tmpls}/mutation-webhook.yaml`,
1293
+ validationWebhookYaml: `${helm.dirs.tmpls}/validation-webhook.yaml`,
1294
+ admissionDeploymentYaml: `${helm.dirs.tmpls}/admission-deployment.yaml`,
1295
+ admissionServiceMonitorYaml: `${helm.dirs.tmpls}/admission-service-monitor.yaml`,
1296
+ watcherDeploymentYaml: `${helm.dirs.tmpls}/watcher-deployment.yaml`,
1297
+ watcherServiceMonitorYaml: `${helm.dirs.tmpls}/watcher-service-monitor.yaml`,
1298
+ tlsSecretYaml: `${helm.dirs.tmpls}/tls-secret.yaml`,
1299
+ apiTokenSecretYaml: `${helm.dirs.tmpls}/api-token-secret.yaml`,
1300
+ moduleSecretYaml: `${helm.dirs.tmpls}/module-secret.yaml`,
1301
+ storeRoleYaml: `${helm.dirs.tmpls}/store-role.yaml`,
1302
+ storeRoleBindingYaml: `${helm.dirs.tmpls}/store-role-binding.yaml`,
1303
+ clusterRoleYaml: `${helm.dirs.tmpls}/cluster-role.yaml`,
1304
+ clusterRoleBindingYaml: `${helm.dirs.tmpls}/cluster-role-binding.yaml`,
1305
+ serviceAccountYaml: `${helm.dirs.tmpls}/service-account.yaml`
1306
+ };
1307
+ return helm;
1308
+ }
1309
+
1310
+ // src/lib/assets/loader.ts
1311
+ var import_child_process = require("child_process");
1312
+ function loadCapabilities(path) {
1313
+ return new Promise((resolve6, reject) => {
1314
+ const program2 = (0, import_child_process.fork)(path, {
1315
+ env: {
1316
+ ...process.env,
1317
+ LOG_LEVEL: "warn",
1318
+ PEPR_MODE: "build",
1319
+ NODE_OPTIONS: "--disable-warning=DEP0040"
1320
+ }
1321
+ });
1322
+ program2.on("message", (message) => {
1323
+ const capabilities = message.valueOf();
1324
+ for (const capability of capabilities) {
1325
+ console.info(`Registered Pepr Capability "${capability.name}"`);
1326
+ }
1327
+ resolve6(capabilities);
1328
+ });
1329
+ program2.on("error", (error) => {
1330
+ reject(error);
1331
+ });
1332
+ });
1333
+ }
1334
+
1335
+ // src/lib/assets/assets.ts
1336
+ var import_fs3 = require("fs");
1337
+
1338
+ // src/lib/assets/networking.ts
1339
+ function apiTokenSecret(name2, apiToken) {
1340
+ return {
1341
+ apiVersion: "v1",
1342
+ kind: "Secret",
1343
+ metadata: {
1344
+ name: `${name2}-api-token`,
1345
+ namespace: "pepr-system"
1346
+ },
1347
+ type: "Opaque",
1348
+ data: {
1349
+ value: Buffer.from(apiToken).toString("base64")
1350
+ }
1351
+ };
1679
1352
  }
1680
- function helmLayout(basePath, unique) {
1681
- const helm = {
1682
- dirs: {
1683
- chart: (0, import_path.resolve)(`${basePath}/${unique}-chart`)
1353
+ function tlsSecret(name2, tls) {
1354
+ return {
1355
+ apiVersion: "v1",
1356
+ kind: "Secret",
1357
+ metadata: {
1358
+ name: `${name2}-tls`,
1359
+ namespace: "pepr-system"
1684
1360
  },
1685
- files: {}
1361
+ type: "kubernetes.io/tls",
1362
+ data: {
1363
+ "tls.crt": tls.crt,
1364
+ "tls.key": tls.key
1365
+ }
1686
1366
  };
1687
- helm.dirs = {
1688
- ...helm.dirs,
1689
- charts: `${helm.dirs.chart}/charts`,
1690
- tmpls: `${helm.dirs.chart}/templates`
1367
+ }
1368
+ function service(name2) {
1369
+ return {
1370
+ apiVersion: "v1",
1371
+ kind: "Service",
1372
+ metadata: {
1373
+ name: name2,
1374
+ namespace: "pepr-system",
1375
+ labels: {
1376
+ "pepr.dev/controller": "admission"
1377
+ }
1378
+ },
1379
+ spec: {
1380
+ selector: {
1381
+ app: name2,
1382
+ "pepr.dev/controller": "admission"
1383
+ },
1384
+ ports: [
1385
+ {
1386
+ port: 443,
1387
+ targetPort: 3e3
1388
+ }
1389
+ ]
1390
+ }
1691
1391
  };
1692
- helm.files = {
1693
- ...helm.files,
1694
- valuesYaml: `${helm.dirs.chart}/values.yaml`,
1695
- chartYaml: `${helm.dirs.chart}/Chart.yaml`,
1696
- namespaceYaml: `${helm.dirs.tmpls}/namespace.yaml`,
1697
- watcherServiceYaml: `${helm.dirs.tmpls}/watcher-service.yaml`,
1698
- admissionServiceYaml: `${helm.dirs.tmpls}/admission-service.yaml`,
1699
- mutationWebhookYaml: `${helm.dirs.tmpls}/mutation-webhook.yaml`,
1700
- validationWebhookYaml: `${helm.dirs.tmpls}/validation-webhook.yaml`,
1701
- admissionDeploymentYaml: `${helm.dirs.tmpls}/admission-deployment.yaml`,
1702
- admissionServiceMonitorYaml: `${helm.dirs.tmpls}/admission-service-monitor.yaml`,
1703
- watcherDeploymentYaml: `${helm.dirs.tmpls}/watcher-deployment.yaml`,
1704
- watcherServiceMonitorYaml: `${helm.dirs.tmpls}/watcher-service-monitor.yaml`,
1705
- tlsSecretYaml: `${helm.dirs.tmpls}/tls-secret.yaml`,
1706
- apiTokenSecretYaml: `${helm.dirs.tmpls}/api-token-secret.yaml`,
1707
- moduleSecretYaml: `${helm.dirs.tmpls}/module-secret.yaml`,
1708
- storeRoleYaml: `${helm.dirs.tmpls}/store-role.yaml`,
1709
- storeRoleBindingYaml: `${helm.dirs.tmpls}/store-role-binding.yaml`,
1710
- clusterRoleYaml: `${helm.dirs.tmpls}/cluster-role.yaml`,
1711
- clusterRoleBindingYaml: `${helm.dirs.tmpls}/cluster-role-binding.yaml`,
1712
- serviceAccountYaml: `${helm.dirs.tmpls}/service-account.yaml`
1392
+ }
1393
+ function watcherService(name2) {
1394
+ return {
1395
+ apiVersion: "v1",
1396
+ kind: "Service",
1397
+ metadata: {
1398
+ name: `${name2}-watcher`,
1399
+ namespace: "pepr-system",
1400
+ labels: {
1401
+ "pepr.dev/controller": "watcher"
1402
+ }
1403
+ },
1404
+ spec: {
1405
+ selector: {
1406
+ app: `${name2}-watcher`,
1407
+ "pepr.dev/controller": "watcher"
1408
+ },
1409
+ ports: [
1410
+ {
1411
+ port: 443,
1412
+ targetPort: 3e3
1413
+ }
1414
+ ]
1415
+ }
1713
1416
  };
1714
- return helm;
1715
1417
  }
1418
+
1419
+ // src/lib/assets/assets.ts
1716
1420
  var Assets = class {
1717
- constructor(config, path, host) {
1718
- this.config = config;
1719
- this.path = path;
1720
- this.host = host;
1721
- this.name = `pepr-${config.uuid}`;
1722
- this.buildTimestamp = `${Date.now()}`;
1723
- this.alwaysIgnore = config.alwaysIgnore;
1724
- this.image = `ghcr.io/defenseunicorns/pepr/controller:v${config.peprVersion}`;
1725
- this.hash = "";
1726
- this.tls = genTLS(this.host || `${this.name}.pepr-system.svc`);
1727
- this.apiToken = import_crypto3.default.randomBytes(32).toString("hex");
1728
- }
1729
1421
  name;
1730
1422
  tls;
1731
1423
  apiToken;
1424
+ config;
1425
+ path;
1732
1426
  alwaysIgnore;
1427
+ imagePullSecrets;
1733
1428
  capabilities;
1734
1429
  image;
1735
1430
  buildTimestamp;
1736
- hash;
1737
- setHash = (hash) => {
1738
- this.hash = hash;
1739
- };
1740
- deploy = async (force, webhookTimeout) => {
1431
+ host;
1432
+ constructor(config, path, imagePullSecrets, host) {
1433
+ this.name = `pepr-${config.uuid}`;
1434
+ this.imagePullSecrets = imagePullSecrets;
1435
+ this.buildTimestamp = `${Date.now()}`;
1436
+ this.config = config;
1437
+ this.path = path;
1438
+ this.host = host;
1439
+ this.alwaysIgnore = config.alwaysIgnore;
1440
+ this.image = `ghcr.io/defenseunicorns/pepr/controller:v${config.peprVersion}`;
1441
+ this.tls = genTLS(host || `${this.name}.pepr-system.svc`);
1442
+ this.apiToken = import_crypto.default.randomBytes(32).toString("hex");
1443
+ }
1444
+ async deploy(deployFunction, force, webhookTimeout) {
1741
1445
  this.capabilities = await loadCapabilities(this.path);
1742
- await deploy(this, force, webhookTimeout);
1743
- };
1744
- zarfYaml = (path) => zarfYaml(this, path);
1745
- zarfYamlChart = (path) => zarfYamlChart(this, path);
1746
- allYaml = async (imagePullSecret) => {
1446
+ const timeout = typeof webhookTimeout === "number" ? webhookTimeout : 10;
1447
+ await deployFunction(this, force, timeout);
1448
+ }
1449
+ zarfYaml = (zarfYamlGenerator, path) => zarfYamlGenerator(this, path, "manifests");
1450
+ zarfYamlChart = (zarfYamlGenerator, path) => zarfYamlGenerator(this, path, "charts");
1451
+ allYaml = async (yamlGenerationFunction, imagePullSecret) => {
1747
1452
  this.capabilities = await loadCapabilities(this.path);
1748
1453
  for (const capability of this.capabilities) {
1749
1454
  namespaceComplianceValidator(capability, this.alwaysIgnore?.namespaces);
1750
1455
  }
1751
- return allYaml(this, imagePullSecret);
1456
+ const code = await import_fs3.promises.readFile(this.path);
1457
+ const moduleHash = import_crypto.default.createHash("sha256").update(code).digest("hex");
1458
+ const deployments = {
1459
+ default: getDeployment(this, moduleHash, this.buildTimestamp, imagePullSecret),
1460
+ watch: getWatcher(this, moduleHash, this.buildTimestamp, imagePullSecret)
1461
+ };
1462
+ return yamlGenerationFunction(this, deployments);
1463
+ };
1464
+ writeWebhookFiles = async (validateWebhook, mutateWebhook, helm) => {
1465
+ if (validateWebhook || mutateWebhook) {
1466
+ await import_fs3.promises.writeFile(helm.files.admissionDeploymentYaml, dedent(admissionDeployTemplate(this.buildTimestamp)));
1467
+ await import_fs3.promises.writeFile(helm.files.admissionServiceMonitorYaml, dedent(serviceMonitorTemplate("admission")));
1468
+ }
1469
+ if (mutateWebhook) {
1470
+ await import_fs3.promises.writeFile(helm.files.mutationWebhookYaml, createWebhookYaml(this.name, this.config, mutateWebhook));
1471
+ }
1472
+ if (validateWebhook) {
1473
+ await import_fs3.promises.writeFile(helm.files.validationWebhookYaml, createWebhookYaml(this.name, this.config, validateWebhook));
1474
+ }
1752
1475
  };
1753
- /* eslint max-statements: ["warn", 21] */
1754
- generateHelmChart = async (basePath) => {
1476
+ generateHelmChart = async (webhookGeneratorFunction, basePath) => {
1755
1477
  const helm = helmLayout(basePath, this.config.uuid);
1756
1478
  try {
1757
1479
  await Promise.all(
1758
1480
  Object.values(helm.dirs).sort((l, r) => l.split("/").length - r.split("/").length).map(async (dir) => await createDirectoryIfNotExists(dir))
1759
1481
  );
1760
- const code = await import_fs4.promises.readFile(this.path);
1482
+ const code = await import_fs3.promises.readFile(this.path);
1483
+ const moduleHash = import_crypto.default.createHash("sha256").update(code).digest("hex");
1761
1484
  const pairs = [
1762
1485
  [helm.files.chartYaml, () => dedent(chartYaml(this.config.uuid, this.config.description || ""))],
1763
1486
  [helm.files.namespaceYaml, () => dedent(namespaceTemplate())],
@@ -1770,28 +1493,27 @@ var Assets = class {
1770
1493
  [helm.files.clusterRoleYaml, () => dedent(clusterRoleTemplate())],
1771
1494
  [helm.files.clusterRoleBindingYaml, () => toYaml(clusterRoleBinding(this.name))],
1772
1495
  [helm.files.serviceAccountYaml, () => toYaml(serviceAccount(this.name))],
1773
- [helm.files.moduleSecretYaml, () => toYaml(getModuleSecret(this.name, code, this.hash))]
1496
+ [helm.files.moduleSecretYaml, () => toYaml(getModuleSecret(this.name, code, moduleHash))]
1774
1497
  ];
1775
- await Promise.all(pairs.map(async ([file, content]) => await import_fs4.promises.writeFile(file, content())));
1776
- await overridesFile(this, helm.files.valuesYaml);
1777
- const [mutateWebhook, validateWebhook] = await Promise.all([
1778
- webhookConfig(this, "mutate", this.config.webhookTimeout),
1779
- webhookConfig(this, "validate", this.config.webhookTimeout)
1780
- ]);
1781
- if (validateWebhook || mutateWebhook) {
1782
- await import_fs4.promises.writeFile(helm.files.admissionDeploymentYaml, dedent(admissionDeployTemplate(this.buildTimestamp)));
1783
- await import_fs4.promises.writeFile(helm.files.admissionServiceMonitorYaml, dedent(serviceMonitorTemplate("admission")));
1784
- }
1785
- if (mutateWebhook) {
1786
- await import_fs4.promises.writeFile(helm.files.mutationWebhookYaml, createWebhookYaml(this, mutateWebhook));
1787
- }
1788
- if (validateWebhook) {
1789
- await import_fs4.promises.writeFile(helm.files.validationWebhookYaml, createWebhookYaml(this, validateWebhook));
1790
- }
1791
- const watchDeployment = getWatcher(this, this.hash, this.buildTimestamp);
1498
+ await Promise.all(pairs.map(async ([file, content]) => await import_fs3.promises.writeFile(file, content())));
1499
+ const overrideData = {
1500
+ hash: moduleHash,
1501
+ name: this.name,
1502
+ image: this.image,
1503
+ config: this.config,
1504
+ apiToken: this.apiToken,
1505
+ capabilities: this.capabilities
1506
+ };
1507
+ await overridesFile(overrideData, helm.files.valuesYaml, this.imagePullSecrets);
1508
+ const webhooks = {
1509
+ mutate: await webhookGeneratorFunction(this, "mutate" /* MUTATE */, this.config.webhookTimeout),
1510
+ validate: await webhookGeneratorFunction(this, "validate" /* VALIDATE */, this.config.webhookTimeout)
1511
+ };
1512
+ await this.writeWebhookFiles(webhooks.validate, webhooks.mutate, helm);
1513
+ const watchDeployment = getWatcher(this, moduleHash, this.buildTimestamp);
1792
1514
  if (watchDeployment) {
1793
- await import_fs4.promises.writeFile(helm.files.watcherDeploymentYaml, dedent(watcherDeployTemplate(this.buildTimestamp)));
1794
- await import_fs4.promises.writeFile(helm.files.watcherServiceMonitorYaml, dedent(serviceMonitorTemplate("watcher")));
1515
+ await import_fs3.promises.writeFile(helm.files.watcherDeploymentYaml, dedent(watcherDeployTemplate(this.buildTimestamp)));
1516
+ await import_fs3.promises.writeFile(helm.files.watcherServiceMonitorYaml, dedent(serviceMonitorTemplate("watcher")));
1795
1517
  }
1796
1518
  } catch (err) {
1797
1519
  console.error(`Error generating helm chart: ${err.message}`);
@@ -2007,7 +1729,7 @@ var gitIgnore = "# Ignore node_modules and Pepr build artifacts\nnode_modules\nd
2007
1729
  var readmeMd = '# Pepr Module\n\nThis is a Pepr Module. [Pepr](https://github.com/defenseunicorns/pepr) is a type-safe Kubernetes middleware system.\n\nThe `capabilities` directory contains all the capabilities for this module. By default,\na capability is a single typescript file in the format of `capability-name.ts` that is\nimported in the root `pepr.ts` file as `import { HelloPepr } from "./capabilities/hello-pepr";`.\nBecause this is typescript, you can organize this however you choose, e.g. creating a sub-folder\nper-capability or common logic in shared files or folders.\n\nExample Structure:\n\n```\nModule Root\n\u251C\u2500\u2500 package.json\n\u251C\u2500\u2500 pepr.ts\n\u2514\u2500\u2500 capabilities\n \u251C\u2500\u2500 example-one.ts\n \u251C\u2500\u2500 example-three.ts\n \u2514\u2500\u2500 example-two.ts\n```\n';
2008
1730
  var peprTS = 'import { PeprModule } from "pepr";\n// cfg loads your pepr configuration from package.json\nimport cfg from "./package.json";\n\n// HelloPepr is a demo capability that is included with Pepr. Comment or delete the line below to remove it.\nimport { HelloPepr } from "./capabilities/hello-pepr";\n\n/**\n * This is the main entrypoint for this Pepr module. It is run when the module is started.\n * This is where you register your Pepr configurations and capabilities.\n */\nnew PeprModule(cfg, [\n // "HelloPepr" is a demo capability that is included with Pepr. Comment or delete the line below to remove it.\n HelloPepr,\n\n // Your additional capabilities go here\n]);\n';
2009
1731
  var helloPeprTS = 'import {\n Capability,\n K8s,\n Log,\n PeprMutateRequest,\n RegisterKind,\n a,\n fetch,\n fetchStatus,\n kind,\n} from "pepr";\nimport { MockAgent, setGlobalDispatcher } from "undici";\n\n/**\n * The HelloPepr Capability is an example capability to demonstrate some general concepts of Pepr.\n * To test this capability you run `pepr dev`and then run the following command:\n * `kubectl apply -f capabilities/hello-pepr.samples.yaml`\n */\nexport const HelloPepr = new Capability({\n name: "hello-pepr",\n description: "A simple example capability to show how things work.",\n namespaces: ["pepr-demo", "pepr-demo-2"],\n});\n\n// Use the \'When\' function to create a new action, use \'Store\' to persist data\nconst { When, Store } = HelloPepr;\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (Namespace) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action removes the label `remove-me` when a Namespace is created.\n * Note we don\'t need to specify the namespace here, because we\'ve already specified\n * it in the Capability definition above.\n */\nWhen(a.Namespace)\n .IsCreated()\n .Mutate(ns => ns.RemoveLabel("remove-me"));\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Watch Action with K8s SSA (Namespace) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action watches for the `pepr-demo-2` namespace to be created, then creates a ConfigMap with\n * the name `pepr-ssa-demo` and adds the namespace UID to the ConfigMap data. Because Pepr uses\n * server-side apply for this operation, the ConfigMap will be created or updated if it already exists.\n */\nWhen(a.Namespace)\n .IsCreated()\n .WithName("pepr-demo-2")\n .Watch(async ns => {\n Log.info("Namespace pepr-demo-2 was created.");\n\n try {\n // Apply the ConfigMap using K8s server-side apply\n await K8s(kind.ConfigMap).Apply({\n metadata: {\n name: "pepr-ssa-demo",\n namespace: "pepr-demo-2",\n },\n data: {\n "ns-uid": ns.metadata.uid,\n },\n });\n } catch (error) {\n // You can use the Log object to log messages to the Pepr controller pod\n Log.error(error, "Failed to apply ConfigMap using server-side apply.");\n }\n\n // You can share data between actions using the Store, including between different types of actions\n Store.setItem("watch-data", "This data was stored by a Watch Action.");\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 1) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This is a single action. They can be in the same file or put imported from other files.\n * In this example, when a ConfigMap is created with the name `example-1`, then add a label and annotation.\n *\n * Equivalent to manually running:\n * `kubectl label configmap example-1 pepr=was-here`\n * `kubectl annotate configmap example-1 pepr.dev=annotations-work-too`\n */\nWhen(a.ConfigMap)\n .IsCreated()\n .WithName("example-1")\n .Mutate(request => {\n request\n .SetLabel("pepr", "was-here")\n .SetAnnotation("pepr.dev", "annotations-work-too");\n\n // Use the Store to persist data between requests and Pepr controller pods\n Store.setItem("example-1", "was-here");\n\n // This data is written asynchronously and can be read back via `Store.getItem()` or `Store.subscribe()`\n Store.setItem("example-1-data", JSON.stringify(request.Raw.data));\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate & Validate Actions (CM Example 2) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This combines 3 different types of actions: \'Mutate\', \'Validate\', and \'Watch\'. The order\n * of the actions is required, but each action is optional. In this example, when a ConfigMap is created\n * with the name `example-2`, then add a label and annotation, validate that the ConfigMap has the label\n * `pepr`, and log the request.\n */\nWhen(a.ConfigMap)\n .IsCreated()\n .WithName("example-2")\n .Mutate(request => {\n // This Mutate Action will mutate the request before it is persisted to the cluster\n\n // Use `request.Merge()` to merge the new data with the existing data\n request.Merge({\n metadata: {\n labels: {\n pepr: "was-here",\n },\n annotations: {\n "pepr.dev": "annotations-work-too",\n },\n },\n });\n })\n .Validate(request => {\n // This Validate Action will validate the request before it is persisted to the cluster\n\n // Approve the request if the ConfigMap has the label \'pepr\'\n if (request.HasLabel("pepr")) {\n return request.Approve();\n }\n\n // Otherwise, deny the request with an error message (optional)\n return request.Deny("ConfigMap must have label \'pepr\'");\n })\n .Watch((cm, phase) => {\n // This Watch Action will watch the ConfigMap after it has been persisted to the cluster\n Log.info(cm, `ConfigMap was ${phase} with the name example-2`);\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 2a) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action shows a simple validation that will deny any ConfigMap that has the\n * annotation `evil`. Note that the `Deny()` function takes an optional second parameter that is a\n * user-defined status code to return.\n */\nWhen(a.ConfigMap)\n .IsCreated()\n .Validate(request => {\n if (request.HasAnnotation("evil")) {\n return request.Deny("No evil CM annotations allowed.", 400);\n }\n\n return request.Approve();\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 3) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action combines different styles. Unlike the previous actions, this one will look\n * for any ConfigMap in the `pepr-demo` namespace that has the label `change=by-label` during either\n * CREATE or UPDATE. Note that all conditions added such as `WithName()`, `WithLabel()`, `InNamespace()`,\n * are ANDs so all conditions must be true for the request to be processed.\n */\nWhen(a.ConfigMap)\n .IsCreatedOrUpdated()\n .WithLabel("change", "by-label")\n .Mutate(request => {\n // The K8s object e are going to mutate\n const cm = request.Raw;\n\n // Get the username and uid of the K8s request\n const { username, uid } = request.Request.userInfo;\n\n // Store some data about the request in the configmap\n cm.data["username"] = username;\n cm.data["uid"] = uid;\n\n // You can still mix other ways of making changes too\n request.SetAnnotation("pepr.dev", "making-waves");\n });\n\n// This action validates the label `change=by-label` is deleted\nWhen(a.ConfigMap)\n .IsDeleted()\n .WithLabel("change", "by-label")\n .Validate(request => {\n // Log and then always approve the request\n Log.info("CM with label \'change=by-label\' was deleted.");\n return request.Approve();\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 4) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action show how you can use the `Mutate()` function without an inline function.\n * This is useful if you want to keep your actions small and focused on a single task,\n * or if you want to reuse the same function in multiple actions.\n */\nWhen(a.ConfigMap).IsCreated().WithName("example-4").Mutate(example4Cb);\n\n// This function uses the complete type definition, but is not required.\nfunction example4Cb(cm: PeprMutateRequest<a.ConfigMap>) {\n cm.SetLabel("pepr.dev/first", "true");\n cm.SetLabel("pepr.dev/second", "true");\n cm.SetLabel("pepr.dev/third", "true");\n}\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 4a) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This is the same as Example 4, except this only operates on a CM in the `pepr-demo-2` namespace.\n * Note because the Capability defines namespaces, the namespace specified here must be one of those.\n * Alternatively, you can remove the namespace from the Capability definition and specify it here.\n */\nWhen(a.ConfigMap)\n .IsCreated()\n .InNamespace("pepr-demo-2")\n .WithName("example-4a")\n .Mutate(example4Cb);\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 5) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action is a bit more complex. It will look for any ConfigMap in the `pepr-demo`\n * namespace that has the label `chuck-norris` during CREATE. When it finds one, it will fetch a\n * random Chuck Norris joke from the API and add it to the ConfigMap. This is a great example of how\n * you can use Pepr to make changes to your K8s objects based on external data.\n *\n * Note the use of the `async` keyword. This is required for any action that uses `await` or `fetch()`.\n *\n * Also note we are passing a type to the `fetch()` function. This is optional, but it will help you\n * avoid mistakes when working with the data returned from the API. You can also use the `as` keyword to\n * cast the data returned from the API.\n *\n * These are equivalent:\n * ```ts\n * const joke = await fetch<TheChuckNorrisJoke>("https://icanhazdadjoke.com/");\n * const joke = await fetch("https://icanhazdadjoke.com/") as TheChuckNorrisJoke;\n * ```\n *\n * Alternatively, you can drop the type completely:\n *\n * ```ts\n * fetch("https://icanhazdadjoke.com")\n * ```\n */\ninterface TheChuckNorrisJoke {\n id: string;\n joke: string;\n status: number;\n}\n\nWhen(a.ConfigMap)\n .IsCreatedOrUpdated()\n .WithLabel("chuck-norris")\n .Mutate(cm => cm.SetLabel("got-jokes", "true"))\n .Watch(async cm => {\n const jokeURL = "https://icanhazdadjoke.com";\n\n const mockAgent: MockAgent = new MockAgent();\n setGlobalDispatcher(mockAgent);\n const mockClient = mockAgent.get(jokeURL);\n mockClient.intercept({ path: "/", method: "GET" }).reply(\n 200,\n {\n id: "R7UfaahVfFd",\n joke: "Funny joke goes here.",\n status: 200,\n },\n {\n headers: {\n "Content-Type": "application/json; charset=utf-8",\n },\n },\n );\n\n // Try/catch is not needed as a response object will always be returned\n const response = await fetch<TheChuckNorrisJoke>(jokeURL, {\n headers: {\n Accept: "application/json",\n },\n });\n\n // Instead, check the `response.ok` field\n if (response.ok) {\n const { joke } = response.data;\n // Add Joke to the Store\n await Store.setItemAndWait(jokeURL, joke);\n // Add the Chuck Norris joke to the configmap\n try {\n await K8s(kind.ConfigMap).Apply({\n metadata: {\n name: cm.metadata.name,\n namespace: cm.metadata.namespace,\n },\n data: {\n "chuck-says": Store.getItem(jokeURL),\n },\n });\n } catch (error) {\n Log.error(error, "Failed to apply ConfigMap using server-side apply.", {\n cm,\n });\n }\n }\n\n // You can also assert on different HTTP response codes\n if (response.status === fetchStatus.NOT_FOUND) {\n // Do something else\n return;\n }\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (Secret Base64 Handling) *\n * ---------------------------------------------------------------------------------------------------\n *\n * The K8s JS client provides incomplete support for base64 encoding/decoding handling for secrets,\n * unlike the GO client. To make this less painful, Pepr automatically handles base64 encoding/decoding\n * secret data before and after the action is executed.\n */\nWhen(a.Secret)\n .IsCreated()\n .WithName("secret-1")\n .Mutate(request => {\n const secret = request.Raw;\n\n // This will be encoded at the end of all processing back to base64: "Y2hhbmdlLXdpdGhvdXQtZW5jb2Rpbmc="\n secret.data.magic = "change-without-encoding";\n\n // You can modify the data directly, and it will be encoded at the end of all processing\n secret.data.example += " - modified by Pepr";\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (Untyped Custom Resource) *\n * ---------------------------------------------------------------------------------------------------\n *\n * Out of the box, Pepr supports all the standard Kubernetes objects. However, you can also create\n * your own types. This is useful if you are working with an Operator that creates custom resources.\n * There are two ways to do this, the first is to use the `When()` function with a `GenericKind`,\n * the second is to create a new class that extends `GenericKind` and use the `RegisterKind()` function.\n *\n * This example shows how to use the `When()` function with a `GenericKind`. Note that you\n * must specify the `group`, `version`, and `kind` of the object (if applicable). This is how Pepr knows\n * if the action should be triggered or not. Since we are using a `GenericKind`,\n * Pepr will not be able to provide any intellisense for the object, so you will need to refer to the\n * Kubernetes API documentation for the object you are working with.\n *\n * You will need to wait for the CRD in `hello-pepr.samples.yaml` to be created, then you can apply\n *\n * ```yaml\n * apiVersion: pepr.dev/v1\n * kind: Unicorn\n * metadata:\n * name: example-1\n * namespace: pepr-demo\n * spec:\n * message: replace-me\n * counter: 0\n * ```\n */\nWhen(a.GenericKind, {\n group: "pepr.dev",\n version: "v1",\n kind: "Unicorn",\n})\n .IsCreated()\n .WithName("example-1")\n .Mutate(request => {\n request.Merge({\n spec: {\n message: "Hello Pepr without type data!",\n counter: Math.random(),\n },\n });\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (Typed Custom Resource) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This example shows how to use the `RegisterKind()` function to create a new type. This is useful\n * if you are working with an Operator that creates custom resources and you want to have intellisense\n * for the object. Note that you must specify the `group`, `version`, and `kind` of the object (if applicable)\n * as this is how Pepr knows if the action should be triggered or not.\n *\n * Once you register a new Kind with Pepr, you can use the `When()` function with the new Kind. Ideally,\n * you should register custom Kinds at the top of your Capability file or Pepr Module so they are available\n * to all actions, but we are putting it here for demonstration purposes.\n *\n * You will need to wait for the CRD in `hello-pepr.samples.yaml` to be created, then you can apply\n *\n * ```yaml\n * apiVersion: pepr.dev/v1\n * kind: Unicorn\n * metadata:\n * name: example-2\n * namespace: pepr-demo\n * spec:\n * message: replace-me\n * counter: 0\n * ```*\n */\nclass UnicornKind extends a.GenericKind {\n spec: {\n /**\n * JSDoc comments can be added to explain more details about the field.\n *\n * @example\n * ```ts\n * request.Raw.spec.message = "Hello Pepr!";\n * ```\n * */\n message: string;\n counter: number;\n };\n}\n\nRegisterKind(UnicornKind, {\n group: "pepr.dev",\n version: "v1",\n kind: "Unicorn",\n});\n\nWhen(UnicornKind)\n .IsCreated()\n .WithName("example-2")\n .Mutate(request => {\n request.Merge({\n spec: {\n message: "Hello Pepr with type data!",\n counter: Math.random(),\n },\n });\n });\n\n/**\n * A callback function that is called once the Pepr Store is fully loaded.\n */\nStore.onReady(data => {\n Log.info(data, "Pepr Store Ready");\n});\n';
2010
- var packageJSON = { name: "pepr", description: "Kubernetes application engine", author: "Defense Unicorns", homepage: "https://github.com/defenseunicorns/pepr", license: "Apache-2.0", bin: "dist/cli.js", repository: "defenseunicorns/pepr", engines: { node: ">=18.0.0" }, files: ["/dist", "/src", "!src/**/*.test.ts", "!dist/**/*.test.d.ts*"], version: "0.42.2", main: "dist/lib.js", types: "dist/lib.d.ts", scripts: { ci: "npm ci", "gen-data-json": "node hack/build-template-data.js", prebuild: "rm -fr dist/* && npm run gen-data-json", version: "node scripts/set-version.js", build: "tsc && node build.mjs && npm pack", "build:image": "npm run build && docker buildx build --output type=docker --tag pepr:dev .", test: "npm run test:unit && npm run test:journey", "test:unit": "npm run gen-data-json && jest src --coverage --detectOpenHandles --coverageDirectory=./coverage --testPathIgnorePatterns='cosign.e2e.test.ts'", "test:journey": "npm run test:journey:k3d && npm run build && npm run test:journey:image && npm run test:journey:run", "test:journey:prep": "if [ ! -d ./pepr-upgrade-test ]; then git clone https://github.com/defenseunicorns/pepr-upgrade-test.git ; fi", "test:journey-wasm": "npm run test:journey:k3d && npm run build && npm run test:journey:image && npm run test:journey:run-wasm", "test:journey:k3d": "k3d cluster delete pepr-dev && k3d cluster create pepr-dev --k3s-arg '--debug@server:0' --wait && kubectl rollout status deployment -n kube-system", "test:journey:image": "docker buildx build --output type=docker --tag pepr:dev . && k3d image import pepr:dev -c pepr-dev", "test:journey:run": "jest --detectOpenHandles journey/entrypoint.test.ts && npm run test:journey:prep && npm run test:journey:upgrade", "test:journey:run-wasm": "jest --detectOpenHandles journey/entrypoint-wasm.test.ts", "test:journey:upgrade": "npm run test:journey:k3d && npm run test:journey:image && jest --detectOpenHandles journey/pepr-upgrade.test.ts", "format:check": "eslint src && prettier src --check", "format:fix": "eslint src --fix && prettier src --write", prepare: `if [ "$NODE_ENV" != 'production' ]; then husky; fi` }, dependencies: { "@types/ramda": "0.30.2", express: "4.21.2", "fast-json-patch": "3.1.1", "follow-redirects": "1.15.9", "http-status-codes": "^2.3.0", "json-pointer": "^0.6.2", "kubernetes-fluent-client": "3.3.7", pino: "9.6.0", "pino-pretty": "13.0.0", "prom-client": "15.1.3", ramda: "0.30.1", sigstore: "3.0.0" }, devDependencies: { "@commitlint/cli": "19.6.1", "@commitlint/config-conventional": "19.6.0", "@fast-check/jest": "^2.0.1", "@jest/globals": "29.7.0", "@types/eslint": "9.6.1", "@types/express": "5.0.0", "@types/follow-redirects": "1.14.4", "@types/json-pointer": "^1.0.34", "@types/node": "22.x.x", "@types/node-forge": "1.3.11", "@types/uuid": "10.0.0", "fast-check": "^3.19.0", husky: "^9.1.6", jest: "29.7.0", "js-yaml": "^4.1.0", "ts-jest": "29.2.5", undici: "^7.0.1" }, peerDependencies: { "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", "@types/prompts": "2.4.9", eslint: "8.57.0", commander: "12.1.0", esbuild: "0.24.0", "node-forge": "1.3.1", prettier: "3.4.2", prompts: "2.4.2", typescript: "^5.3.3", uuid: "11.0.3" } };
1732
+ var packageJSON = { name: "pepr", description: "Kubernetes application engine", author: "Defense Unicorns", homepage: "https://github.com/defenseunicorns/pepr", license: "Apache-2.0", bin: "dist/cli.js", repository: "defenseunicorns/pepr", engines: { node: ">=18.0.0" }, files: ["/dist", "/src", "!src/**/*.test.ts", "!dist/**/*.test.d.ts*"], version: "0.43.0", main: "dist/lib.js", types: "dist/lib.d.ts", scripts: { ci: "npm ci", "gen-data-json": "node hack/build-template-data.js", prebuild: "rm -fr dist/* && npm run gen-data-json", version: "node scripts/set-version.js", build: "tsc && node build.mjs && npm pack", "build:image": "npm run build && docker buildx build --output type=docker --tag pepr:dev .", test: "npm run test:unit && npm run test:journey", "test:unit": "npm run gen-data-json && jest src --coverage --detectOpenHandles --coverageDirectory=./coverage --testPathIgnorePatterns='cosign.e2e.test.ts'", "test:integration": "npm run test:integration:prep && npm run test:integration:run", "test:integration:prep": "./integration/prep.sh", "test:integration:run": "jest --maxWorkers=4 integration", "test:journey": "npm run test:journey:k3d && npm run build && npm run test:journey:image && npm run test:journey:run", "test:journey:prep": "if [ ! -d ./pepr-upgrade-test ]; then git clone https://github.com/defenseunicorns/pepr-upgrade-test.git ; fi", "test:journey-wasm": "npm run test:journey:k3d && npm run build && npm run test:journey:image && npm run test:journey:run-wasm", "test:journey:k3d": "k3d cluster delete pepr-dev && k3d cluster create pepr-dev --k3s-arg '--debug@server:0' --wait && kubectl rollout status deployment -n kube-system", "test:journey:image": "docker buildx build --output type=docker --tag pepr:dev . && k3d image import pepr:dev -c pepr-dev", "test:journey:run": "jest --detectOpenHandles journey/entrypoint.test.ts && npm run test:journey:prep && npm run test:journey:upgrade", "test:journey:run-wasm": "jest --detectOpenHandles journey/entrypoint-wasm.test.ts", "test:journey:upgrade": "npm run test:journey:k3d && npm run test:journey:image && jest --detectOpenHandles journey/pepr-upgrade.test.ts", "format:check": "eslint src && prettier src --check", "format:fix": "eslint src --fix && prettier src --write", prepare: `if [ "$NODE_ENV" != 'production' ]; then husky; fi` }, dependencies: { "@types/ramda": "0.30.2", express: "4.21.2", "fast-json-patch": "3.1.1", "follow-redirects": "1.15.9", "http-status-codes": "^2.3.0", "json-pointer": "^0.6.2", "kubernetes-fluent-client": "3.3.8", pino: "9.6.0", "pino-pretty": "13.0.0", "prom-client": "15.1.3", ramda: "0.30.1", sigstore: "3.0.0" }, devDependencies: { "@commitlint/cli": "19.6.1", "@commitlint/config-conventional": "19.6.0", "@fast-check/jest": "^2.0.1", "@jest/globals": "29.7.0", "@types/eslint": "9.6.1", "@types/express": "5.0.0", "@types/follow-redirects": "1.14.4", "@types/json-pointer": "^1.0.34", "@types/node": "22.x.x", "@types/node-forge": "1.3.11", "@types/uuid": "10.0.0", "fast-check": "^3.19.0", husky: "^9.1.6", jest: "29.7.0", "js-yaml": "^4.1.0", "ts-jest": "29.2.5", undici: "^7.0.1" }, peerDependencies: { "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", "@types/prompts": "2.4.9", eslint: "8.57.0", commander: "12.1.0", esbuild: "0.24.0", "node-forge": "1.3.1", prettier: "3.4.2", prompts: "2.4.2", typescript: "^5.3.3", uuid: "11.0.3" } };
2011
1733
 
2012
1734
  // src/templates/pepr.code-snippets.json
2013
1735
  var pepr_code_snippets_default = {
@@ -2066,7 +1788,7 @@ var tsconfig_module_default = {
2066
1788
  };
2067
1789
 
2068
1790
  // src/cli/init/utils.ts
2069
- var import_fs5 = require("fs");
1791
+ var import_fs4 = require("fs");
2070
1792
  function sanitizeName(name2) {
2071
1793
  if (typeof name2 !== "string") {
2072
1794
  throw TypeError(
@@ -2080,7 +1802,7 @@ function sanitizeName(name2) {
2080
1802
  }
2081
1803
  async function createDir(dir) {
2082
1804
  try {
2083
- await import_fs5.promises.mkdir(dir);
1805
+ await import_fs4.promises.mkdir(dir);
2084
1806
  } catch (err) {
2085
1807
  if (err && err.code === "EEXIST") {
2086
1808
  throw new Error(`Directory ${dir} already exists`);
@@ -2093,11 +1815,11 @@ function write(path, data) {
2093
1815
  if (typeof data !== "string") {
2094
1816
  data = JSON.stringify(data, null, 2);
2095
1817
  }
2096
- return import_fs5.promises.writeFile(path, data);
1818
+ return import_fs4.promises.writeFile(path, data);
2097
1819
  }
2098
1820
 
2099
1821
  // src/cli/init/templates.ts
2100
- var { dependencies, devDependencies, peerDependencies, scripts, version: version2 } = packageJSON;
1822
+ var { dependencies, devDependencies, peerDependencies, scripts, version } = packageJSON;
2101
1823
  function genPkgJSON(opts, pgkVerOverride) {
2102
1824
  const uuid = (0, import_uuid.v5)(opts.name, (0, import_uuid.v4)());
2103
1825
  const name2 = sanitizeName(opts.name);
@@ -2133,7 +1855,7 @@ function genPkgJSON(opts, pgkVerOverride) {
2133
1855
  "k3d-setup": scripts["test:journey:k3d"]
2134
1856
  },
2135
1857
  dependencies: {
2136
- pepr: pgkVerOverride || version2,
1858
+ pepr: pgkVerOverride || version,
2137
1859
  undici: "^7.0.1"
2138
1860
  },
2139
1861
  devDependencies: {
@@ -2196,85 +1918,244 @@ var import_commander = require("commander");
2196
1918
  var import_eslint = require("eslint");
2197
1919
 
2198
1920
  // src/cli/format.helpers.ts
2199
- var import_fs6 = require("fs");
1921
+ var import_fs5 = require("fs");
2200
1922
  var import_prettier = require("prettier");
2201
1923
  async function formatWithPrettier(results, validateOnly) {
2202
1924
  let hasFailure = false;
2203
1925
  for (const { filePath } of results) {
2204
- const content = await import_fs6.promises.readFile(filePath, "utf8");
1926
+ const content = await import_fs5.promises.readFile(filePath, "utf8");
2205
1927
  const cfg = await (0, import_prettier.resolveConfig)(filePath);
2206
1928
  const formatted = await (0, import_prettier.format)(content, { filepath: filePath, ...cfg });
2207
1929
  if (validateOnly && formatted !== content) {
2208
1930
  hasFailure = true;
2209
1931
  console.error(`File ${filePath} is not formatted correctly`);
2210
1932
  } else {
2211
- await import_fs6.promises.writeFile(filePath, formatted);
1933
+ await import_fs5.promises.writeFile(filePath, formatted);
2212
1934
  }
2213
1935
  }
2214
1936
  return hasFailure;
2215
1937
  }
2216
1938
 
2217
- // src/cli/format.ts
2218
- function format_default(program2) {
2219
- program2.command("format").description("Lint and format this Pepr module").option("-v, --validate-only", "Do not modify files, only validate formatting").action(async (opts) => {
2220
- const success = await peprFormat(opts.validateOnly);
2221
- if (success) {
2222
- console.info("\u2705 Module formatted");
2223
- } else {
2224
- process.exit(1);
2225
- }
2226
- });
2227
- }
2228
- async function peprFormat(validateOnly) {
2229
- {
2230
- try {
2231
- const eslint2 = new import_eslint.ESLint();
2232
- const results = await eslint2.lintFiles(["./**/*.ts"]);
2233
- let hasFailure = false;
2234
- results.forEach(async (result) => {
2235
- const errorCount = result.fatalErrorCount + result.errorCount;
2236
- if (errorCount > 0) {
2237
- hasFailure = true;
2238
- }
2239
- });
2240
- const formatter = await eslint2.loadFormatter("stylish");
2241
- const resultText = await formatter.format(results, {});
2242
- if (resultText) {
2243
- console.log(resultText);
2244
- }
2245
- if (!validateOnly) {
2246
- await import_eslint.ESLint.outputFixes(results);
2247
- }
2248
- hasFailure = await formatWithPrettier(results, validateOnly);
2249
- return !hasFailure;
2250
- } catch (e) {
2251
- console.error(`Error formatting module:`, e);
2252
- return false;
2253
- }
2254
- }
1939
+ // src/cli/format.ts
1940
+ function format_default(program2) {
1941
+ program2.command("format").description("Lint and format this Pepr module").option("-v, --validate-only", "Do not modify files, only validate formatting").action(async (opts) => {
1942
+ const success = await peprFormat(opts.validateOnly);
1943
+ if (success) {
1944
+ console.info("\u2705 Module formatted");
1945
+ } else {
1946
+ process.exit(1);
1947
+ }
1948
+ });
1949
+ }
1950
+ async function peprFormat(validateOnly) {
1951
+ {
1952
+ try {
1953
+ const eslint2 = new import_eslint.ESLint();
1954
+ const results = await eslint2.lintFiles(["./**/*.ts"]);
1955
+ let hasFailure = false;
1956
+ results.forEach(async (result) => {
1957
+ const errorCount = result.fatalErrorCount + result.errorCount;
1958
+ if (errorCount > 0) {
1959
+ hasFailure = true;
1960
+ }
1961
+ });
1962
+ const formatter = await eslint2.loadFormatter("stylish");
1963
+ const resultText = await formatter.format(results, {});
1964
+ if (resultText) {
1965
+ console.log(resultText);
1966
+ }
1967
+ if (!validateOnly) {
1968
+ await import_eslint.ESLint.outputFixes(results);
1969
+ }
1970
+ hasFailure = await formatWithPrettier(results, validateOnly);
1971
+ return !hasFailure;
1972
+ } catch (e) {
1973
+ console.error(`Error formatting module:`, e);
1974
+ return false;
1975
+ }
1976
+ }
1977
+ }
1978
+
1979
+ // src/lib/included-files.ts
1980
+ var import_fs6 = require("fs");
1981
+ async function createDockerfile(version3, description, includedFiles) {
1982
+ const file = `
1983
+ # Use an official Node.js runtime as the base image
1984
+ FROM ghcr.io/defenseunicorns/pepr/controller:v${version3}
1985
+
1986
+ LABEL description="${description}"
1987
+
1988
+ # Add the included files to the image
1989
+ ${includedFiles.map((f) => `ADD ${f} ${f}`).join("\n")}
1990
+
1991
+ `;
1992
+ await import_fs6.promises.writeFile("Dockerfile.controller", file, { encoding: "utf-8" });
1993
+ }
1994
+
1995
+ // src/cli/build.helpers.ts
1996
+ var import_child_process2 = require("child_process");
1997
+ var import_esbuild = require("esbuild");
1998
+ var import_path2 = require("path");
1999
+ var import_fs8 = require("fs");
2000
+
2001
+ // src/lib/assets/yaml/generateAllYaml.ts
2002
+ var import_crypto2 = __toESM(require("crypto"));
2003
+ var import_client_node4 = require("@kubernetes/client-node");
2004
+ var import_fs7 = require("fs");
2005
+
2006
+ // src/lib/assets/webhooks.ts
2007
+ var import_ramda = require("ramda");
2008
+ var peprIgnoreNamespaces = ["kube-system", "pepr-system"];
2009
+ var validateRule = (binding, isMutateWebhook) => {
2010
+ const { event, kind: kind8, isMutate, isValidate } = binding;
2011
+ if (isMutateWebhook && !isMutate || !isMutateWebhook && !isValidate) {
2012
+ return void 0;
2013
+ }
2014
+ const operations = event === "CREATEORUPDATE" /* CREATE_OR_UPDATE */ ? ["CREATE" /* CREATE */, "UPDATE" /* UPDATE */] : [event];
2015
+ const resource = kind8.plural || `${kind8.kind.toLowerCase()}s`;
2016
+ const ruleObject = {
2017
+ apiGroups: [kind8.group],
2018
+ apiVersions: [kind8.version || "*"],
2019
+ operations,
2020
+ resources: [resource, ...resource === "pods" ? ["pods/ephemeralcontainers"] : []]
2021
+ };
2022
+ return ruleObject;
2023
+ };
2024
+ function resolveIgnoreNamespaces(ignoredNSConfig = []) {
2025
+ const ignoredNSEnv = process.env.PEPR_ADDITIONAL_IGNORED_NAMESPACES;
2026
+ if (!ignoredNSEnv) {
2027
+ return ignoredNSConfig;
2028
+ }
2029
+ const namespaces = ignoredNSEnv.split(",").map((ns) => ns.trim());
2030
+ if (ignoredNSConfig) {
2031
+ namespaces.push(...ignoredNSConfig);
2032
+ }
2033
+ return namespaces.filter((ns) => ns.length > 0);
2034
+ }
2035
+ async function generateWebhookRules(assets, isMutateWebhook) {
2036
+ const { config, capabilities } = assets;
2037
+ const rules = capabilities.flatMap((capability) => {
2038
+ console.info(`Module ${config.uuid} has capability: ${capability.name}`);
2039
+ return capability.bindings.map((binding) => validateRule(binding, isMutateWebhook)).filter((rule) => !!rule);
2040
+ });
2041
+ return (0, import_ramda.uniqWith)(import_ramda.equals, rules);
2042
+ }
2043
+ async function webhookConfigGenerator(assets, mutateOrValidate, timeoutSeconds = 10) {
2044
+ const ignore = [];
2045
+ const { name: name2, tls, config, apiToken, host } = assets;
2046
+ const ignoreNS = (0, import_ramda.concat)(peprIgnoreNamespaces, resolveIgnoreNamespaces(config?.alwaysIgnore?.namespaces));
2047
+ if (ignoreNS) {
2048
+ ignore.push({
2049
+ key: "kubernetes.io/metadata.name",
2050
+ operator: "NotIn",
2051
+ values: ignoreNS
2052
+ });
2053
+ }
2054
+ const clientConfig = {
2055
+ caBundle: tls.ca
2056
+ };
2057
+ const apiPath = `/${mutateOrValidate}/${apiToken}`;
2058
+ if (host) {
2059
+ clientConfig.url = `https://${host}:3000${apiPath}`;
2060
+ } else {
2061
+ clientConfig.service = {
2062
+ name: name2,
2063
+ namespace: "pepr-system",
2064
+ path: apiPath
2065
+ };
2066
+ }
2067
+ const isMutate = mutateOrValidate === "mutate" /* MUTATE */;
2068
+ const rules = await generateWebhookRules(assets, isMutate);
2069
+ if (rules.length < 1) {
2070
+ return null;
2071
+ }
2072
+ return {
2073
+ apiVersion: "admissionregistration.k8s.io/v1",
2074
+ kind: isMutate ? "MutatingWebhookConfiguration" : "ValidatingWebhookConfiguration",
2075
+ metadata: { name: name2 },
2076
+ webhooks: [
2077
+ {
2078
+ name: `${name2}.pepr.dev`,
2079
+ admissionReviewVersions: ["v1", "v1beta1"],
2080
+ clientConfig,
2081
+ failurePolicy: config.onError === "reject" ? "Fail" : "Ignore",
2082
+ matchPolicy: "Equivalent",
2083
+ timeoutSeconds,
2084
+ namespaceSelector: {
2085
+ matchExpressions: ignore
2086
+ },
2087
+ rules,
2088
+ // @todo: track side effects state
2089
+ sideEffects: "None"
2090
+ }
2091
+ ]
2092
+ };
2093
+ }
2094
+
2095
+ // src/lib/assets/yaml/generateAllYaml.ts
2096
+ async function generateAllYaml(assets, deployments) {
2097
+ const { name: name2, tls, apiToken, path, config } = assets;
2098
+ const code = await import_fs7.promises.readFile(path);
2099
+ const hash = import_crypto2.default.createHash("sha256").update(code).digest("hex");
2100
+ const resources = [
2101
+ getNamespace(assets.config.customLabels?.namespace),
2102
+ clusterRole(name2, assets.capabilities, config.rbacMode, config.rbac),
2103
+ clusterRoleBinding(name2),
2104
+ serviceAccount(name2),
2105
+ apiTokenSecret(name2, apiToken),
2106
+ tlsSecret(name2, tls),
2107
+ deployments.default,
2108
+ service(name2),
2109
+ watcherService(name2),
2110
+ getModuleSecret(name2, code, hash),
2111
+ storeRole(name2),
2112
+ storeRoleBinding(name2)
2113
+ ];
2114
+ const webhooks = {
2115
+ mutate: await webhookConfigGenerator(assets, "mutate" /* MUTATE */, assets.config.webhookTimeout),
2116
+ validate: await webhookConfigGenerator(assets, "validate" /* VALIDATE */, assets.config.webhookTimeout)
2117
+ };
2118
+ const additionalResources = [webhooks.mutate, webhooks.validate, deployments.watch].filter(
2119
+ (resource) => resource !== null && resource !== void 0
2120
+ );
2121
+ resources.push(...additionalResources);
2122
+ return resources.map((resource) => (0, import_client_node4.dumpYaml)(resource, { noRefs: true })).join("---\n");
2255
2123
  }
2256
2124
 
2257
- // src/lib/included-files.ts
2258
- var import_fs7 = require("fs");
2259
- async function createDockerfile(version3, description, includedFiles) {
2260
- const file = `
2261
- # Use an official Node.js runtime as the base image
2262
- FROM ghcr.io/defenseunicorns/pepr/controller:v${version3}
2263
-
2264
- LABEL description="${description}"
2265
-
2266
- # Add the included files to the image
2267
- ${includedFiles.map((f) => `ADD ${f} ${f}`).join("\n")}
2268
-
2269
- `;
2270
- await import_fs7.promises.writeFile("Dockerfile.controller", file, { encoding: "utf-8" });
2125
+ // src/lib/assets/yaml/generateZarfYaml.ts
2126
+ var import_client_node5 = require("@kubernetes/client-node");
2127
+ function generateZarfYamlGeneric(assets, path, type) {
2128
+ const manifestSettings = {
2129
+ name: "module",
2130
+ namespace: "pepr-system",
2131
+ files: [path]
2132
+ };
2133
+ const chartSettings = {
2134
+ name: "module",
2135
+ namespace: "pepr-system",
2136
+ version: `${assets.config.appVersion || "0.0.1"}`,
2137
+ localPath: path
2138
+ };
2139
+ const component = {
2140
+ name: "module",
2141
+ required: true,
2142
+ images: [assets.image],
2143
+ [type]: [type === "manifests" ? manifestSettings : chartSettings]
2144
+ };
2145
+ const zarfCfg = {
2146
+ kind: "ZarfPackageConfig",
2147
+ metadata: {
2148
+ name: assets.name,
2149
+ description: `Pepr Module: ${assets.config.description}`,
2150
+ url: "https://github.com/defenseunicorns/pepr",
2151
+ version: `${assets.config.appVersion || "0.0.1"}`
2152
+ },
2153
+ components: [component]
2154
+ };
2155
+ return (0, import_client_node5.dumpYaml)(zarfCfg, { noRefs: true });
2271
2156
  }
2272
2157
 
2273
2158
  // src/cli/build.helpers.ts
2274
- var import_child_process2 = require("child_process");
2275
- var import_esbuild = require("esbuild");
2276
- var import_path2 = require("path");
2277
- var import_fs8 = require("fs");
2278
2159
  function determineRbacMode(opts, cfg) {
2279
2160
  if (opts.rbacMode) {
2280
2161
  return opts.rbacMode;
@@ -2309,17 +2190,6 @@ function validImagePullSecret(imagePullSecretName) {
2309
2190
  }
2310
2191
  }
2311
2192
  }
2312
- function handleCustomImage(customImage, registry) {
2313
- let defaultImage = "";
2314
- if (customImage) {
2315
- if (registry) {
2316
- console.error(`Custom Image and registry cannot be used together.`);
2317
- process.exit(1);
2318
- }
2319
- defaultImage = customImage;
2320
- }
2321
- return defaultImage;
2322
- }
2323
2193
  async function handleCustomImageBuild(includedFiles, peprVersion, description, image) {
2324
2194
  if (includedFiles.length > 0) {
2325
2195
  await createDockerfile(peprVersion, description, includedFiles);
@@ -2358,17 +2228,17 @@ async function generateYamlAndWriteToDisk(obj) {
2358
2228
  const yamlFile = `pepr-module-${uuid}.yaml`;
2359
2229
  const chartPath = `${uuid}-chart`;
2360
2230
  const yamlPath = (0, import_path2.resolve)(outputDir2, yamlFile);
2361
- const yaml = await assets.allYaml(imagePullSecret);
2231
+ const yaml = await assets.allYaml(generateAllYaml, imagePullSecret);
2362
2232
  const zarfPath = (0, import_path2.resolve)(outputDir2, "zarf.yaml");
2363
2233
  let localZarf = "";
2364
2234
  if (zarf === "chart") {
2365
- localZarf = assets.zarfYamlChart(chartPath);
2235
+ localZarf = assets.zarfYamlChart(generateZarfYamlGeneric, chartPath);
2366
2236
  } else {
2367
- localZarf = assets.zarfYaml(yamlFile);
2237
+ localZarf = assets.zarfYaml(generateZarfYamlGeneric, yamlFile);
2368
2238
  }
2369
2239
  await import_fs8.promises.writeFile(yamlPath, yaml);
2370
2240
  await import_fs8.promises.writeFile(zarfPath, localZarf);
2371
- await assets.generateHelmChart(outputDir2);
2241
+ await assets.generateHelmChart(webhookConfigGenerator, outputDir2);
2372
2242
  console.info(`\u2705 K8s resource for the module saved to ${yamlPath}`);
2373
2243
  }
2374
2244
 
@@ -2379,27 +2249,34 @@ function build_default(program2) {
2379
2249
  program2.command("build").description("Build a Pepr Module for deployment").option("-e, --entry-point [file]", "Specify the entry point file to build with.", peprTS2).option(
2380
2250
  "-n, --no-embed",
2381
2251
  "Disables embedding of deployment files into output module. Useful when creating library modules intended solely for reuse/distribution via NPM."
2382
- ).option(
2383
- "-i, --custom-image <custom-image>",
2384
- "Custom Image: Use custom image for Admission and Watch Deployments."
2385
- ).option(
2386
- "-r, --registry-info [<registry>/<username>]",
2387
- "Registry Info: Image registry and username. Note: You must be signed into the registry"
2252
+ ).addOption(
2253
+ new import_commander.Option(
2254
+ "-i, --custom-image <custom-image>",
2255
+ "Specify a custom image (including version) for Admission and Watch Deployments. Example: 'docker.io/username/custom-pepr-controller:v1.0.0'"
2256
+ ).conflicts(["version", "registryInfo", "registry"])
2257
+ ).addOption(
2258
+ new import_commander.Option(
2259
+ "-r, --registry-info [<registry>/<username>]",
2260
+ "Provide the image registry and username for building and pushing a custom WASM container. Requires authentication. Builds and pushes 'registry/username/custom-pepr-controller:<current-version>'."
2261
+ ).conflicts(["customImage", "version", "registry"])
2388
2262
  ).option("-o, --output-dir <output directory>", "Define where to place build output").option(
2389
2263
  "--timeout <timeout>",
2390
2264
  "How long the API server should wait for a webhook to respond before treating the call as a failure",
2391
2265
  parseTimeout
2392
- ).option(
2393
- "-v, --version <version>. Example: '0.27.3'",
2394
- "The version of the Pepr image to use in the deployment manifests."
2266
+ ).addOption(
2267
+ new import_commander.Option(
2268
+ "-v, --version <version>",
2269
+ "The version of the Pepr image to use in the deployment manifests. Example: '0.27.3'."
2270
+ ).conflicts(["customImage", "registryInfo"])
2395
2271
  ).option(
2396
2272
  "--withPullSecret <imagePullSecret>",
2397
- "Image Pull Secret: Use image pull secret for controller Deployment."
2273
+ "Image Pull Secret: Use image pull secret for controller Deployment.",
2274
+ ""
2398
2275
  ).addOption(
2399
2276
  new import_commander.Option(
2400
2277
  "--registry <GitHub|Iron Bank>",
2401
2278
  "Container registry: Choose container registry for deployment manifests. Can't be used with --custom-image."
2402
- ).choices(["GitHub", "Iron Bank"])
2279
+ ).conflicts(["customImage", "registryInfo"]).choices(["GitHub", "Iron Bank"])
2403
2280
  ).addOption(
2404
2281
  new import_commander.Option(
2405
2282
  "-z, --zarf [manifest|chart]",
@@ -2415,7 +2292,7 @@ function build_default(program2) {
2415
2292
  if (buildModuleResult?.cfg && buildModuleResult.path && buildModuleResult.uuid) {
2416
2293
  const { cfg, path, uuid } = buildModuleResult;
2417
2294
  const { includedFiles } = cfg.pepr;
2418
- let image = handleCustomImage(opts.customImage, opts.registry);
2295
+ let image = opts.customImage || "";
2419
2296
  if (opts.timeout !== void 0) {
2420
2297
  cfg.pepr.webhookTimeout = opts.timeout;
2421
2298
  }
@@ -2431,10 +2308,14 @@ function build_default(program2) {
2431
2308
  ...cfg.pepr,
2432
2309
  appVersion: cfg.version,
2433
2310
  description: cfg.description,
2311
+ alwaysIgnore: {
2312
+ namespaces: cfg.pepr.alwaysIgnore?.namespaces
2313
+ },
2434
2314
  // Can override the rbacMode with the CLI option
2435
2315
  rbacMode: determineRbacMode(opts, cfg)
2436
2316
  },
2437
- path
2317
+ path,
2318
+ opts.withPullSecret === "" ? [] : [opts.withPullSecret]
2438
2319
  );
2439
2320
  image = checkIronBankImage(opts.registry, image, cfg.pepr.peprVersion);
2440
2321
  image !== "" ? assets.image = image : null;
@@ -2470,7 +2351,7 @@ async function loadModule(entryPoint = peprTS2) {
2470
2351
  const cfg = JSON.parse(moduleText);
2471
2352
  const { uuid } = cfg.pepr;
2472
2353
  const name2 = `pepr-${uuid}.js`;
2473
- cfg.pepr.peprVersion = version2;
2354
+ cfg.pepr.peprVersion = version;
2474
2355
  if (!uuid) {
2475
2356
  throw new Error("Could not load the uuid in package.json");
2476
2357
  }
@@ -2576,6 +2457,169 @@ async function checkFormat() {
2576
2457
  // src/cli/deploy.ts
2577
2458
  var import_prompts = __toESM(require("prompts"));
2578
2459
 
2460
+ // src/lib/assets/deploy.ts
2461
+ var import_crypto3 = __toESM(require("crypto"));
2462
+ var import_fs10 = require("fs");
2463
+ var import_kubernetes_fluent_client3 = require("kubernetes-fluent-client");
2464
+
2465
+ // src/lib/k8s.ts
2466
+ var import_kubernetes_fluent_client2 = require("kubernetes-fluent-client");
2467
+ var Store = class extends import_kubernetes_fluent_client2.GenericKind {
2468
+ };
2469
+ var peprStoreGVK = {
2470
+ kind: "PeprStore",
2471
+ version: "v1",
2472
+ group: "pepr.dev"
2473
+ };
2474
+ (0, import_kubernetes_fluent_client2.RegisterKind)(Store, peprStoreGVK);
2475
+
2476
+ // src/lib/assets/store.ts
2477
+ var { group, version: version2, kind: kind2 } = peprStoreGVK;
2478
+ var singular = kind2.toLocaleLowerCase();
2479
+ var plural = `${singular}s`;
2480
+ var name = `${plural}.${group}`;
2481
+ var peprStoreCRD = {
2482
+ apiVersion: "apiextensions.k8s.io/v1",
2483
+ kind: "CustomResourceDefinition",
2484
+ metadata: {
2485
+ name
2486
+ },
2487
+ spec: {
2488
+ group,
2489
+ versions: [
2490
+ {
2491
+ // typescript doesn't know this is really already set, which is kind of annoying
2492
+ name: version2 || "v1",
2493
+ served: true,
2494
+ storage: true,
2495
+ schema: {
2496
+ openAPIV3Schema: {
2497
+ type: "object",
2498
+ properties: {
2499
+ data: {
2500
+ type: "object",
2501
+ additionalProperties: {
2502
+ type: "string"
2503
+ }
2504
+ }
2505
+ }
2506
+ }
2507
+ }
2508
+ }
2509
+ ],
2510
+ scope: "Namespaced",
2511
+ names: {
2512
+ plural,
2513
+ singular,
2514
+ kind: kind2
2515
+ }
2516
+ }
2517
+ };
2518
+
2519
+ // src/lib/assets/deploy.ts
2520
+ async function deployImagePullSecret(imagePullSecret, name2) {
2521
+ try {
2522
+ await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Namespace).Get("pepr-system");
2523
+ } catch {
2524
+ await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Namespace).Apply(getNamespace());
2525
+ }
2526
+ try {
2527
+ await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(
2528
+ {
2529
+ apiVersion: "v1",
2530
+ kind: "Secret",
2531
+ metadata: {
2532
+ name: name2,
2533
+ namespace: "pepr-system"
2534
+ },
2535
+ type: "kubernetes.io/dockerconfigjson",
2536
+ data: {
2537
+ ".dockerconfigjson": Buffer.from(JSON.stringify(imagePullSecret)).toString("base64")
2538
+ }
2539
+ },
2540
+ { force: true }
2541
+ );
2542
+ } catch (e) {
2543
+ logger_default.error(e);
2544
+ }
2545
+ }
2546
+ async function handleWebhookConfiguration(assets, type, webhookTimeout, force) {
2547
+ const kindMap = {
2548
+ mutate: import_kubernetes_fluent_client3.kind.MutatingWebhookConfiguration,
2549
+ validate: import_kubernetes_fluent_client3.kind.ValidatingWebhookConfiguration
2550
+ };
2551
+ const webhookConfig = await webhookConfigGenerator(assets, type, webhookTimeout);
2552
+ if (webhookConfig) {
2553
+ logger_default.info(`Applying ${type} webhook`);
2554
+ await (0, import_kubernetes_fluent_client3.K8s)(kindMap[type]).Apply(webhookConfig, { force });
2555
+ } else {
2556
+ logger_default.info(`${type.charAt(0).toUpperCase() + type.slice(1)} webhook not needed, removing if it exists`);
2557
+ await (0, import_kubernetes_fluent_client3.K8s)(kindMap[type]).Delete(assets.name);
2558
+ }
2559
+ }
2560
+ async function deployWebhook(assets, force, webhookTimeout) {
2561
+ logger_default.info("Establishing connection to Kubernetes");
2562
+ logger_default.info("Applying pepr-system namespace");
2563
+ await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Namespace).Apply(getNamespace(assets.config.customLabels?.namespace));
2564
+ await handleWebhookConfiguration(assets, "mutate" /* MUTATE */, webhookTimeout, force);
2565
+ await handleWebhookConfiguration(assets, "validate" /* VALIDATE */, webhookTimeout, force);
2566
+ logger_default.info("Applying the Pepr Store CRD if it doesn't exist");
2567
+ await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.CustomResourceDefinition).Apply(peprStoreCRD, { force });
2568
+ if (assets.host) return;
2569
+ const code = await import_fs10.promises.readFile(assets.path);
2570
+ if (!code.length) throw new Error("No code provided");
2571
+ const hash = import_crypto3.default.createHash("sha256").update(code).digest("hex");
2572
+ await setupRBAC(assets.name, assets.capabilities, force, assets.config);
2573
+ await setupController(assets, code, hash, force);
2574
+ await setupWatcher(assets, hash, force);
2575
+ }
2576
+ async function setupRBAC(name2, capabilities, force, config) {
2577
+ const { rbacMode, rbac } = config;
2578
+ logger_default.info("Applying cluster role binding");
2579
+ const crb = clusterRoleBinding(name2);
2580
+ await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ClusterRoleBinding).Apply(crb, { force });
2581
+ logger_default.info("Applying cluster role");
2582
+ const cr = clusterRole(name2, capabilities, rbacMode, rbac);
2583
+ await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ClusterRole).Apply(cr, { force });
2584
+ logger_default.info("Applying service account");
2585
+ const sa = serviceAccount(name2);
2586
+ await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ServiceAccount).Apply(sa, { force });
2587
+ logger_default.info("Applying store role");
2588
+ const role = storeRole(name2);
2589
+ await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Role).Apply(role, { force });
2590
+ logger_default.info("Applying store role binding");
2591
+ const roleBinding = storeRoleBinding(name2);
2592
+ await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.RoleBinding).Apply(roleBinding, { force });
2593
+ }
2594
+ async function setupController(assets, code, hash, force) {
2595
+ const { name: name2 } = assets;
2596
+ logger_default.info("Applying module secret");
2597
+ const mod = getModuleSecret(name2, code, hash);
2598
+ await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(mod, { force });
2599
+ logger_default.info("Applying controller service");
2600
+ const svc = service(name2);
2601
+ await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Service).Apply(svc, { force });
2602
+ logger_default.info("Applying TLS secret");
2603
+ const tls = tlsSecret(name2, assets.tls);
2604
+ await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(tls, { force });
2605
+ logger_default.info("Applying API token secret");
2606
+ const apiToken = apiTokenSecret(name2, assets.apiToken);
2607
+ await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(apiToken, { force });
2608
+ logger_default.info("Applying deployment");
2609
+ const dep = getDeployment(assets, hash, assets.buildTimestamp);
2610
+ await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Deployment).Apply(dep, { force });
2611
+ }
2612
+ async function setupWatcher(assets, hash, force) {
2613
+ const watchDeployment = getWatcher(assets, hash, assets.buildTimestamp);
2614
+ if (watchDeployment) {
2615
+ logger_default.info("Applying watcher deployment");
2616
+ await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Deployment).Apply(watchDeployment, { force });
2617
+ logger_default.info("Applying watcher service");
2618
+ const watchSvc = watcherService(assets.name);
2619
+ await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Service).Apply(watchSvc, { force });
2620
+ }
2621
+ }
2622
+
2579
2623
  // src/lib/deploymentChecks.ts
2580
2624
  var import_kubernetes_fluent_client4 = require("kubernetes-fluent-client");
2581
2625
  async function checkDeploymentStatus(namespace) {
@@ -2689,11 +2733,12 @@ function deploy_default(program2) {
2689
2733
  }
2690
2734
  const webhook = new Assets(
2691
2735
  { ...builtModule.cfg.pepr, description: builtModule.cfg.description },
2692
- builtModule.path
2736
+ builtModule.path,
2737
+ []
2693
2738
  );
2694
2739
  webhook.image = opts.image ?? webhook.image;
2695
2740
  try {
2696
- await webhook.deploy(opts.force, builtModule.cfg.pepr.webhookTimeout ?? 10);
2741
+ await webhook.deploy(deployWebhook, opts.force, builtModule.cfg.pepr.webhookTimeout ?? 10);
2697
2742
  validateCapabilityNames(webhook.capabilities);
2698
2743
  await namespaceDeploymentsReady();
2699
2744
  console.info(`\u2705 Module deployed successfully`);
@@ -2705,10 +2750,10 @@ function deploy_default(program2) {
2705
2750
  }
2706
2751
 
2707
2752
  // src/cli/dev.ts
2708
- var import_child_process4 = require("child_process");
2709
- var import_fs10 = require("fs");
2710
2753
  var import_prompts2 = __toESM(require("prompts"));
2754
+ var import_child_process4 = require("child_process");
2711
2755
  var import_kubernetes_fluent_client5 = require("kubernetes-fluent-client");
2756
+ var import_fs11 = require("fs");
2712
2757
  function dev_default(program2) {
2713
2758
  program2.command("dev").description("Setup a local webhook development environment").option("-h, --host [host]", "Host to listen on", "host.k3d.internal").option("--confirm", "Skip confirmation prompt").action(async (opts) => {
2714
2759
  if (!opts.confirm) {
@@ -2730,8 +2775,8 @@ function dev_default(program2) {
2730
2775
  path,
2731
2776
  opts.host
2732
2777
  );
2733
- await import_fs10.promises.writeFile("insecure-tls.crt", webhook.tls.pem.crt);
2734
- await import_fs10.promises.writeFile("insecure-tls.key", webhook.tls.pem.key);
2778
+ await import_fs11.promises.writeFile("insecure-tls.crt", webhook.tls.pem.crt);
2779
+ await import_fs11.promises.writeFile("insecure-tls.key", webhook.tls.pem.key);
2735
2780
  try {
2736
2781
  let program3;
2737
2782
  const name2 = `pepr-${cfg.pepr.uuid}`;
@@ -2739,7 +2784,7 @@ function dev_default(program2) {
2739
2784
  const store = `pepr-${cfg.pepr.uuid}-store`;
2740
2785
  const runFork = async () => {
2741
2786
  console.info(`Running module ${path}`);
2742
- await webhook.deploy(false, 30);
2787
+ await webhook.deploy(deployWebhook, false, 30);
2743
2788
  try {
2744
2789
  validateCapabilityNames(webhook.capabilities);
2745
2790
  } catch (e) {
@@ -2790,7 +2835,7 @@ function dev_default(program2) {
2790
2835
  }
2791
2836
 
2792
2837
  // src/cli/monitor.ts
2793
- var import_client_node4 = require("@kubernetes/client-node");
2838
+ var import_client_node6 = require("@kubernetes/client-node");
2794
2839
  var import_kubernetes_fluent_client6 = require("kubernetes-fluent-client");
2795
2840
  var import_stream = __toESM(require("stream"));
2796
2841
  function monitor_default(program2) {
@@ -2826,9 +2871,9 @@ function getLabelsAndErrorMessage(uuid) {
2826
2871
  return { labels, errorMessage };
2827
2872
  }
2828
2873
  function getK8sLogFromKubeConfig() {
2829
- const kc = new import_client_node4.KubeConfig();
2874
+ const kc = new import_client_node6.KubeConfig();
2830
2875
  kc.loadFromDefault();
2831
- return new import_client_node4.Log(kc);
2876
+ return new import_client_node6.Log(kc);
2832
2877
  }
2833
2878
  function createLogStream() {
2834
2879
  const logStream = new import_stream.default.PassThrough();
@@ -2884,7 +2929,7 @@ var import_path4 = require("path");
2884
2929
  var import_prompts4 = __toESM(require("prompts"));
2885
2930
 
2886
2931
  // src/cli/init/walkthrough.ts
2887
- var import_fs11 = require("fs");
2932
+ var import_fs12 = require("fs");
2888
2933
  var import_prompts3 = __toESM(require("prompts"));
2889
2934
 
2890
2935
  // src/cli/init/enums.ts
@@ -2915,7 +2960,7 @@ async function setName(name2) {
2915
2960
  validate: async (val) => {
2916
2961
  try {
2917
2962
  const name3 = sanitizeName(val);
2918
- await import_fs11.promises.access(name3, import_fs11.promises.constants.F_OK);
2963
+ await import_fs12.promises.access(name3, import_fs12.promises.constants.F_OK);
2919
2964
  return "A directory with this name already exists";
2920
2965
  } catch (e) {
2921
2966
  return val.length > 2 || "The name must be at least 3 characters long";
@@ -3083,9 +3128,9 @@ function uuid_default(program2) {
3083
3128
  } else {
3084
3129
  deployments = await (0, import_kubernetes_fluent_client7.K8s)(import_kubernetes_fluent_client7.kind.Deployment).InNamespace("pepr-system").WithLabel("pepr.dev/uuid", uuid).Get();
3085
3130
  }
3086
- deployments.items.map((deploy2) => {
3087
- const uuid2 = deploy2.metadata?.labels?.["pepr.dev/uuid"] || "";
3088
- const description = deploy2.metadata?.annotations?.["pepr.dev/description"] || "";
3131
+ deployments.items.map((deploy) => {
3132
+ const uuid2 = deploy.metadata?.labels?.["pepr.dev/uuid"] || "";
3133
+ const description = deploy.metadata?.annotations?.["pepr.dev/description"] || "";
3089
3134
  if (uuid2 !== "") {
3090
3135
  uuidTable[uuid2] = description;
3091
3136
  }
@@ -3110,7 +3155,7 @@ var RootCmd = class extends import_commander2.Command {
3110
3155
 
3111
3156
  // src/cli/update.ts
3112
3157
  var import_child_process6 = require("child_process");
3113
- var import_fs12 = __toESM(require("fs"));
3158
+ var import_fs13 = __toESM(require("fs"));
3114
3159
  var import_path5 = require("path");
3115
3160
  var import_prompts5 = __toESM(require("prompts"));
3116
3161
  function update_default(program2) {
@@ -3150,12 +3195,12 @@ function update_default(program2) {
3150
3195
  await write((0, import_path5.resolve)(".vscode", snippet.path), snippet.data);
3151
3196
  await write((0, import_path5.resolve)(".vscode", codeSettings.path), codeSettings.data);
3152
3197
  const samplePath = (0, import_path5.resolve)("capabilities", samplesYaml.path);
3153
- if (import_fs12.default.existsSync(samplePath)) {
3154
- import_fs12.default.unlinkSync(samplePath);
3198
+ if (import_fs13.default.existsSync(samplePath)) {
3199
+ import_fs13.default.unlinkSync(samplePath);
3155
3200
  await write(samplePath, samplesYaml.data);
3156
3201
  }
3157
3202
  const tsPath = (0, import_path5.resolve)("capabilities", helloPepr.path);
3158
- if (import_fs12.default.existsSync(tsPath)) {
3203
+ if (import_fs13.default.existsSync(tsPath)) {
3159
3204
  await write(tsPath, helloPepr.data);
3160
3205
  }
3161
3206
  }
@@ -3202,7 +3247,7 @@ var program = new RootCmd();
3202
3247
  if (!process.env.PEPR_NODE_WARNINGS) {
3203
3248
  process.removeAllListeners("warning");
3204
3249
  }
3205
- program.version(version2).description(`Pepr (v${version2}) - Type safe K8s middleware for humans`).action(() => {
3250
+ program.version(version).description(`Pepr (v${version}) - Type safe K8s middleware for humans`).action(() => {
3206
3251
  if (program.args.length < 1) {
3207
3252
  console.log(banner);
3208
3253
  program.help();