pepr 0.40.1 → 0.42.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -5
- package/dist/cli/build.d.ts +1 -0
- package/dist/cli/build.d.ts.map +1 -1
- package/dist/cli/build.helpers.d.ts +66 -0
- package/dist/cli/build.helpers.d.ts.map +1 -1
- package/dist/cli/deploy.d.ts.map +1 -1
- package/dist/cli/init/templates.d.ts +2 -2
- package/dist/cli/monitor.d.ts +23 -0
- package/dist/cli/monitor.d.ts.map +1 -1
- package/dist/cli.js +536 -429
- package/dist/controller.js +52 -27
- package/dist/lib/assets/destroy.d.ts.map +1 -1
- package/dist/lib/assets/helm.d.ts +1 -1
- package/dist/lib/assets/helm.d.ts.map +1 -1
- package/dist/lib/assets/index.d.ts.map +1 -1
- package/dist/lib/assets/pods.d.ts +5 -19
- package/dist/lib/assets/pods.d.ts.map +1 -1
- package/dist/lib/assets/webhooks.d.ts.map +1 -1
- package/dist/lib/assets/yaml.d.ts.map +1 -1
- package/dist/lib/capability.d.ts.map +1 -1
- package/dist/lib/controller/index.d.ts.map +1 -1
- package/dist/lib/controller/index.util.d.ts +10 -0
- package/dist/lib/controller/index.util.d.ts.map +1 -0
- package/dist/lib/controller/store.d.ts +0 -1
- package/dist/lib/controller/store.d.ts.map +1 -1
- package/dist/lib/controller/storeCache.d.ts +1 -0
- package/dist/lib/controller/storeCache.d.ts.map +1 -1
- package/dist/lib/deploymentChecks.d.ts +3 -0
- package/dist/lib/deploymentChecks.d.ts.map +1 -0
- package/dist/lib/enums.d.ts +5 -5
- package/dist/lib/enums.d.ts.map +1 -1
- package/dist/lib/filesystemService.d.ts +2 -0
- package/dist/lib/filesystemService.d.ts.map +1 -0
- package/dist/lib/filter/adjudicators/adjudicators.d.ts +73 -0
- package/dist/lib/filter/adjudicators/adjudicators.d.ts.map +1 -0
- package/dist/lib/filter/adjudicators/defaultTestObjects.d.ts +7 -0
- package/dist/lib/filter/adjudicators/defaultTestObjects.d.ts.map +1 -0
- package/dist/lib/helpers.d.ts +1 -4
- package/dist/lib/helpers.d.ts.map +1 -1
- package/dist/lib/mutate-request.d.ts +2 -2
- package/dist/lib/mutate-request.d.ts.map +1 -1
- package/dist/lib/queue.d.ts.map +1 -1
- package/dist/lib/schedule.d.ts.map +1 -1
- package/dist/lib/storage.d.ts +5 -5
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/{logger.d.ts → telemetry/logger.d.ts} +1 -1
- package/dist/lib/telemetry/logger.d.ts.map +1 -0
- package/dist/lib/{metrics.d.ts → telemetry/metrics.d.ts} +3 -1
- package/dist/lib/telemetry/metrics.d.ts.map +1 -0
- package/dist/lib/types.d.ts +10 -9
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/validate-processor.d.ts +4 -1
- package/dist/lib/validate-processor.d.ts.map +1 -1
- package/dist/lib/watch-processor.d.ts.map +1 -1
- package/dist/lib.d.ts +1 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +283 -231
- package/dist/lib.js.map +4 -4
- package/dist/sdk/sdk.d.ts +3 -4
- package/dist/sdk/sdk.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/cli/build.helpers.ts +180 -0
- package/src/cli/build.ts +85 -132
- package/src/cli/deploy.ts +2 -1
- package/src/cli/init/templates.ts +1 -1
- package/src/cli/monitor.ts +108 -65
- package/src/lib/assets/deploy.ts +7 -7
- package/src/lib/assets/destroy.ts +2 -2
- package/src/lib/assets/helm.ts +6 -6
- package/src/lib/assets/index.ts +110 -89
- package/src/lib/assets/pods.ts +10 -5
- package/src/lib/assets/webhooks.ts +3 -3
- package/src/lib/assets/yaml.ts +12 -9
- package/src/lib/capability.ts +29 -19
- package/src/lib/controller/index.ts +41 -69
- package/src/lib/controller/index.util.ts +47 -0
- package/src/lib/controller/store.ts +24 -11
- package/src/lib/controller/storeCache.ts +11 -2
- package/src/lib/deploymentChecks.ts +43 -0
- package/src/lib/enums.ts +5 -5
- package/src/lib/filesystemService.ts +16 -0
- package/src/lib/filter/{adjudicators.ts → adjudicators/adjudicators.ts} +67 -35
- package/src/lib/filter/adjudicators/defaultTestObjects.ts +46 -0
- package/src/lib/filter/filter.ts +1 -1
- package/src/lib/finalizer.ts +1 -1
- package/src/lib/helpers.ts +31 -88
- package/src/lib/mutate-processor.ts +1 -1
- package/src/lib/mutate-request.ts +11 -11
- package/src/lib/queue.ts +13 -5
- package/src/lib/schedule.ts +8 -8
- package/src/lib/storage.ts +48 -39
- package/src/lib/{logger.ts → telemetry/logger.ts} +1 -1
- package/src/lib/{metrics.ts → telemetry/metrics.ts} +18 -17
- package/src/lib/types.ts +12 -9
- package/src/lib/utils.ts +6 -6
- package/src/lib/validate-processor.ts +48 -40
- package/src/lib/watch-processor.ts +19 -15
- package/src/lib.ts +1 -1
- package/src/runtime/controller.ts +1 -1
- package/src/sdk/cosign.ts +4 -4
- package/src/sdk/sdk.ts +6 -9
- package/src/templates/capabilities/hello-pepr.ts +19 -9
- package/dist/lib/filter/adjudicators.d.ts +0 -69
- package/dist/lib/filter/adjudicators.d.ts.map +0 -1
- package/dist/lib/logger.d.ts.map +0 -1
- package/dist/lib/metrics.d.ts.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -86,26 +86,10 @@ var banner = `\x1B[107;40m\x1B[38;5;m \x1B[38;5;m \x1B[38;5;m \x1B[38;5;m \x1B[3
|
|
|
86
86
|
`;
|
|
87
87
|
|
|
88
88
|
// src/cli/build.ts
|
|
89
|
-
var
|
|
90
|
-
var
|
|
91
|
-
var
|
|
92
|
-
var
|
|
93
|
-
|
|
94
|
-
// src/lib/included-files.ts
|
|
95
|
-
var import_fs = require("fs");
|
|
96
|
-
async function createDockerfile(version3, description, includedFiles) {
|
|
97
|
-
const file = `
|
|
98
|
-
# Use an official Node.js runtime as the base image
|
|
99
|
-
FROM ghcr.io/defenseunicorns/pepr/controller:v${version3}
|
|
100
|
-
|
|
101
|
-
LABEL description="${description}"
|
|
102
|
-
|
|
103
|
-
# Add the included files to the image
|
|
104
|
-
${includedFiles.map((f) => `ADD ${f} ${f}`).join("\n")}
|
|
105
|
-
|
|
106
|
-
`;
|
|
107
|
-
await import_fs.promises.writeFile("Dockerfile.controller", file, { encoding: "utf-8" });
|
|
108
|
-
}
|
|
89
|
+
var import_child_process3 = require("child_process");
|
|
90
|
+
var import_esbuild2 = require("esbuild");
|
|
91
|
+
var import_fs9 = require("fs");
|
|
92
|
+
var import_path3 = require("path");
|
|
109
93
|
|
|
110
94
|
// src/lib/assets/index.ts
|
|
111
95
|
var import_crypto3 = __toESM(require("crypto"));
|
|
@@ -170,10 +154,10 @@ function genCert(key, name2, issuer) {
|
|
|
170
154
|
|
|
171
155
|
// src/lib/assets/deploy.ts
|
|
172
156
|
var import_crypto = __toESM(require("crypto"));
|
|
173
|
-
var
|
|
174
|
-
var
|
|
157
|
+
var import_fs = require("fs");
|
|
158
|
+
var import_kubernetes_fluent_client3 = require("kubernetes-fluent-client");
|
|
175
159
|
|
|
176
|
-
// src/lib/logger.ts
|
|
160
|
+
// src/lib/telemetry/logger.ts
|
|
177
161
|
var import_pino = require("pino");
|
|
178
162
|
var isPrettyLog = true;
|
|
179
163
|
var pretty = {
|
|
@@ -277,17 +261,13 @@ function watcherService(name2) {
|
|
|
277
261
|
// src/lib/assets/pods.ts
|
|
278
262
|
var import_zlib = require("zlib");
|
|
279
263
|
|
|
280
|
-
// src/lib/helpers.ts
|
|
281
|
-
var import_fs2 = require("fs");
|
|
282
|
-
var import_kubernetes_fluent_client2 = require("kubernetes-fluent-client");
|
|
283
|
-
|
|
284
264
|
// src/sdk/sdk.ts
|
|
285
265
|
var import_kubernetes_fluent_client = require("kubernetes-fluent-client");
|
|
286
266
|
function sanitizeResourceName(name2) {
|
|
287
267
|
return name2.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 250).replace(/^[^a-z]+|[^a-z]+$/g, "");
|
|
288
268
|
}
|
|
289
269
|
|
|
290
|
-
// src/lib/filter/adjudicators.ts
|
|
270
|
+
// src/lib/filter/adjudicators/adjudicators.ts
|
|
291
271
|
var import_ramda = require("ramda");
|
|
292
272
|
var declaredOperation = (0, import_ramda.pipe)(
|
|
293
273
|
(request) => request?.operation,
|
|
@@ -311,23 +291,49 @@ var carriesDeletionTimestamp = (0, import_ramda.pipe)(
|
|
|
311
291
|
(0, import_ramda.defaultTo)(false)
|
|
312
292
|
);
|
|
313
293
|
var missingDeletionTimestamp = (0, import_ramda.complement)(carriesDeletionTimestamp);
|
|
314
|
-
var carriedKind = (0, import_ramda.pipe)(
|
|
315
|
-
|
|
316
|
-
|
|
294
|
+
var carriedKind = (0, import_ramda.pipe)(
|
|
295
|
+
(kubernetesObject) => kubernetesObject?.kind,
|
|
296
|
+
(0, import_ramda.defaultTo)("not set")
|
|
297
|
+
);
|
|
298
|
+
var carriedVersion = (0, import_ramda.pipe)(
|
|
299
|
+
(kubernetesObject) => kubernetesObject?.metadata?.resourceVersion,
|
|
300
|
+
(0, import_ramda.defaultTo)("not set")
|
|
301
|
+
);
|
|
302
|
+
var carriedName = (0, import_ramda.pipe)(
|
|
303
|
+
(kubernetesObject) => kubernetesObject?.metadata?.name,
|
|
304
|
+
(0, import_ramda.defaultTo)("")
|
|
305
|
+
);
|
|
317
306
|
var carriesName = (0, import_ramda.pipe)(carriedName, (0, import_ramda.equals)(""), import_ramda.not);
|
|
318
307
|
var missingName = (0, import_ramda.complement)(carriesName);
|
|
319
|
-
var carriedNamespace = (0, import_ramda.pipe)(
|
|
308
|
+
var carriedNamespace = (0, import_ramda.pipe)(
|
|
309
|
+
(kubernetesObject) => kubernetesObject?.metadata?.namespace,
|
|
310
|
+
(0, import_ramda.defaultTo)("")
|
|
311
|
+
);
|
|
320
312
|
var carriesNamespace = (0, import_ramda.pipe)(carriedNamespace, (0, import_ramda.equals)(""), import_ramda.not);
|
|
321
|
-
var carriedAnnotations = (0, import_ramda.pipe)(
|
|
313
|
+
var carriedAnnotations = (0, import_ramda.pipe)(
|
|
314
|
+
(kubernetesObject) => kubernetesObject?.metadata?.annotations,
|
|
315
|
+
(0, import_ramda.defaultTo)({})
|
|
316
|
+
);
|
|
322
317
|
var carriesAnnotations = (0, import_ramda.pipe)(carriedAnnotations, (0, import_ramda.equals)({}), import_ramda.not);
|
|
323
|
-
var carriedLabels = (0, import_ramda.pipe)(
|
|
318
|
+
var carriedLabels = (0, import_ramda.pipe)(
|
|
319
|
+
(kubernetesObject) => kubernetesObject?.metadata?.labels,
|
|
320
|
+
(0, import_ramda.defaultTo)({})
|
|
321
|
+
);
|
|
324
322
|
var carriesLabels = (0, import_ramda.pipe)(carriedLabels, (0, import_ramda.equals)({}), import_ramda.not);
|
|
325
|
-
var definesDeletionTimestamp = (0, import_ramda.pipe)(
|
|
323
|
+
var definesDeletionTimestamp = (0, import_ramda.pipe)(
|
|
324
|
+
(binding) => binding?.filters?.deletionTimestamp ?? false,
|
|
325
|
+
(0, import_ramda.defaultTo)(false)
|
|
326
|
+
);
|
|
326
327
|
var ignoresDeletionTimestamp = (0, import_ramda.complement)(definesDeletionTimestamp);
|
|
327
|
-
var definedName = (0, import_ramda.pipe)((binding) =>
|
|
328
|
+
var definedName = (0, import_ramda.pipe)((binding) => {
|
|
329
|
+
return binding.filters.name;
|
|
330
|
+
}, (0, import_ramda.defaultTo)(""));
|
|
328
331
|
var definesName = (0, import_ramda.pipe)(definedName, (0, import_ramda.equals)(""), import_ramda.not);
|
|
329
332
|
var ignoresName = (0, import_ramda.complement)(definesName);
|
|
330
|
-
var definedNameRegex = (0, import_ramda.pipe)(
|
|
333
|
+
var definedNameRegex = (0, import_ramda.pipe)(
|
|
334
|
+
(binding) => binding.filters?.regexName,
|
|
335
|
+
(0, import_ramda.defaultTo)("")
|
|
336
|
+
);
|
|
331
337
|
var definesNameRegex = (0, import_ramda.pipe)(definedNameRegex, (0, import_ramda.equals)(""), import_ramda.not);
|
|
332
338
|
var definedNamespaces = (0, import_ramda.pipe)((binding) => binding?.filters?.namespaces, (0, import_ramda.defaultTo)([]));
|
|
333
339
|
var definesNamespaces = (0, import_ramda.pipe)(definedNamespaces, (0, import_ramda.equals)([]), import_ramda.not);
|
|
@@ -337,20 +343,22 @@ var definedAnnotations = (0, import_ramda.pipe)((binding) => binding?.filters?.a
|
|
|
337
343
|
var definesAnnotations = (0, import_ramda.pipe)(definedAnnotations, (0, import_ramda.equals)({}), import_ramda.not);
|
|
338
344
|
var definedLabels = (0, import_ramda.pipe)((binding) => binding?.filters?.labels, (0, import_ramda.defaultTo)({}));
|
|
339
345
|
var definesLabels = (0, import_ramda.pipe)(definedLabels, (0, import_ramda.equals)({}), import_ramda.not);
|
|
340
|
-
var definedEvent = (
|
|
346
|
+
var definedEvent = (binding) => {
|
|
347
|
+
return binding.event;
|
|
348
|
+
};
|
|
341
349
|
var definesDelete = (0, import_ramda.pipe)(definedEvent, (0, import_ramda.equals)("DELETE" /* DELETE */));
|
|
342
350
|
var definedGroup = (0, import_ramda.pipe)((binding) => binding?.kind?.group, (0, import_ramda.defaultTo)(""));
|
|
343
351
|
var definesGroup = (0, import_ramda.pipe)(definedGroup, (0, import_ramda.equals)(""), import_ramda.not);
|
|
344
|
-
var definedVersion = (0, import_ramda.pipe)(
|
|
352
|
+
var definedVersion = (0, import_ramda.pipe)(
|
|
353
|
+
(binding) => binding?.kind?.version,
|
|
354
|
+
(0, import_ramda.defaultTo)("")
|
|
355
|
+
);
|
|
345
356
|
var definesVersion = (0, import_ramda.pipe)(definedVersion, (0, import_ramda.equals)(""), import_ramda.not);
|
|
346
357
|
var definedKind = (0, import_ramda.pipe)((binding) => binding?.kind?.kind, (0, import_ramda.defaultTo)(""));
|
|
347
358
|
var definesKind = (0, import_ramda.pipe)(definedKind, (0, import_ramda.equals)(""), import_ramda.not);
|
|
348
|
-
var
|
|
349
|
-
return binding.isFinalize ? "Finalize" : binding.isWatch ? "Watch" : binding.isMutate ? "Mutate" : binding.isValidate ? "Validate" : "";
|
|
350
|
-
});
|
|
351
|
-
var definedCallback = (0, import_ramda.pipe)((binding) => {
|
|
359
|
+
var definedCallback = (binding) => {
|
|
352
360
|
return binding.isFinalize ? binding.finalizeCallback : binding.isWatch ? binding.watchCallback : binding.isMutate ? binding.mutateCallback : binding.isValidate ? binding.validateCallback : null;
|
|
353
|
-
}
|
|
361
|
+
};
|
|
354
362
|
var definedCallbackName = (0, import_ramda.pipe)(definedCallback, (0, import_ramda.defaultTo)({ name: "" }), (callback) => callback.name);
|
|
355
363
|
var mismatchedDeletionTimestamp = (0, import_ramda.allPass)([
|
|
356
364
|
(0, import_ramda.pipe)((0, import_ramda.nthArg)(0), definesDeletionTimestamp),
|
|
@@ -432,8 +440,8 @@ var unbindableNamespaces = (0, import_ramda.allPass)([
|
|
|
432
440
|
]);
|
|
433
441
|
var misboundDeleteWithDeletionTimestamp = (0, import_ramda.allPass)([definesDelete, definesDeletionTimestamp]);
|
|
434
442
|
var operationMatchesEvent = (0, import_ramda.anyPass)([
|
|
435
|
-
(0, import_ramda.pipe)((0, import_ramda.nthArg)(1), (0, import_ramda.equals)("*" /*
|
|
436
|
-
(0, import_ramda.pipe)((operation, event) => operation === event),
|
|
443
|
+
(0, import_ramda.pipe)((0, import_ramda.nthArg)(1), (0, import_ramda.equals)("*" /* ANY */)),
|
|
444
|
+
(0, import_ramda.pipe)((operation, event) => operation.valueOf() === event.valueOf()),
|
|
437
445
|
(0, import_ramda.pipe)((operation, event) => operation ? event.includes(operation) : false)
|
|
438
446
|
]);
|
|
439
447
|
var mismatchedEvent = (0, import_ramda.pipe)(
|
|
@@ -455,11 +463,7 @@ var mismatchedKind = (0, import_ramda.allPass)([
|
|
|
455
463
|
|
|
456
464
|
// src/lib/helpers.ts
|
|
457
465
|
function matchesRegex(pattern, testString) {
|
|
458
|
-
|
|
459
|
-
return false;
|
|
460
|
-
}
|
|
461
|
-
const regex = new RegExp(pattern);
|
|
462
|
-
return regex.test(testString);
|
|
466
|
+
return new RegExp(pattern).test(testString);
|
|
463
467
|
}
|
|
464
468
|
var ValidationError = class extends Error {
|
|
465
469
|
};
|
|
@@ -500,17 +504,6 @@ function createRBACMap(capabilities) {
|
|
|
500
504
|
return acc;
|
|
501
505
|
}, {});
|
|
502
506
|
}
|
|
503
|
-
async function createDirectoryIfNotExists(path) {
|
|
504
|
-
try {
|
|
505
|
-
await import_fs2.promises.access(path);
|
|
506
|
-
} catch (error) {
|
|
507
|
-
if (error.code === "ENOENT") {
|
|
508
|
-
await import_fs2.promises.mkdir(path, { recursive: true });
|
|
509
|
-
} else {
|
|
510
|
-
throw error;
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
507
|
function hasEveryOverlap(array1, array2) {
|
|
515
508
|
if (!Array.isArray(array1) || !Array.isArray(array2)) {
|
|
516
509
|
return false;
|
|
@@ -549,7 +542,9 @@ function generateWatchNamespaceError(ignoredNamespaces, bindingNamespaces, capab
|
|
|
549
542
|
function namespaceComplianceValidator(capability, ignoredNamespaces) {
|
|
550
543
|
const { namespaces: capabilityNamespaces, bindings, name: name2 } = capability;
|
|
551
544
|
const bindingNamespaces = bindings.flatMap((binding) => binding.filters.namespaces);
|
|
552
|
-
const bindingRegexNamespaces = bindings.flatMap(
|
|
545
|
+
const bindingRegexNamespaces = bindings.flatMap(
|
|
546
|
+
(binding) => binding.filters.regexNamespaces || []
|
|
547
|
+
);
|
|
553
548
|
const namespaceError = generateWatchNamespaceError(
|
|
554
549
|
ignoredNamespaces ? ignoredNamespaces : [],
|
|
555
550
|
bindingNamespaces,
|
|
@@ -582,40 +577,6 @@ function namespaceComplianceValidator(capability, ignoredNamespaces) {
|
|
|
582
577
|
}
|
|
583
578
|
}
|
|
584
579
|
}
|
|
585
|
-
async function checkDeploymentStatus(namespace2) {
|
|
586
|
-
const deployments = await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.Deployment).InNamespace(namespace2).Get();
|
|
587
|
-
let status = false;
|
|
588
|
-
let readyCount = 0;
|
|
589
|
-
for (const deployment2 of deployments.items) {
|
|
590
|
-
const readyReplicas = deployment2.status?.readyReplicas ? deployment2.status?.readyReplicas : 0;
|
|
591
|
-
if (deployment2.status?.readyReplicas !== deployment2.spec?.replicas) {
|
|
592
|
-
logger_default.info(
|
|
593
|
-
`Waiting for deployment ${deployment2.metadata?.name} rollout to finish: ${readyReplicas} of ${deployment2.spec?.replicas} replicas are available`
|
|
594
|
-
);
|
|
595
|
-
} else {
|
|
596
|
-
logger_default.info(
|
|
597
|
-
`Deployment ${deployment2.metadata?.name} rolled out: ${readyReplicas} of ${deployment2.spec?.replicas} replicas are available`
|
|
598
|
-
);
|
|
599
|
-
readyCount++;
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
if (readyCount === deployments.items.length) {
|
|
603
|
-
status = true;
|
|
604
|
-
}
|
|
605
|
-
return status;
|
|
606
|
-
}
|
|
607
|
-
async function namespaceDeploymentsReady(namespace2 = "pepr-system") {
|
|
608
|
-
logger_default.info(`Checking ${namespace2} deployments status...`);
|
|
609
|
-
let ready = false;
|
|
610
|
-
while (!ready) {
|
|
611
|
-
ready = await checkDeploymentStatus(namespace2);
|
|
612
|
-
if (ready) {
|
|
613
|
-
return ready;
|
|
614
|
-
}
|
|
615
|
-
await new Promise((resolve5) => setTimeout(resolve5, 1e3));
|
|
616
|
-
}
|
|
617
|
-
logger_default.info(`All ${namespace2} deployments are ready`);
|
|
618
|
-
}
|
|
619
580
|
function secretOverLimit(str) {
|
|
620
581
|
const encoder = new TextEncoder();
|
|
621
582
|
const encoded = encoder.encode(str);
|
|
@@ -656,7 +617,7 @@ function replaceString(str, stringA, stringB) {
|
|
|
656
617
|
}
|
|
657
618
|
|
|
658
619
|
// src/lib/assets/pods.ts
|
|
659
|
-
function
|
|
620
|
+
function getNamespace(namespaceLabels) {
|
|
660
621
|
if (namespaceLabels) {
|
|
661
622
|
return {
|
|
662
623
|
apiVersion: "v1",
|
|
@@ -676,7 +637,7 @@ function namespace(namespaceLabels) {
|
|
|
676
637
|
};
|
|
677
638
|
}
|
|
678
639
|
}
|
|
679
|
-
function
|
|
640
|
+
function getWatcher(assets, hash, buildTimestamp, imagePullSecret) {
|
|
680
641
|
const { name: name2, image, capabilities, config } = assets;
|
|
681
642
|
let hasSchedule = false;
|
|
682
643
|
const app = `${name2}-watcher`;
|
|
@@ -820,7 +781,7 @@ function watcher(assets, hash, buildTimestamp, imagePullSecret) {
|
|
|
820
781
|
}
|
|
821
782
|
return deploy2;
|
|
822
783
|
}
|
|
823
|
-
function
|
|
784
|
+
function getDeployment(assets, hash, buildTimestamp, imagePullSecret) {
|
|
824
785
|
const { name: name2, image, config } = assets;
|
|
825
786
|
const app = name2;
|
|
826
787
|
const deploy2 = {
|
|
@@ -961,7 +922,7 @@ function deployment(assets, hash, buildTimestamp, imagePullSecret) {
|
|
|
961
922
|
}
|
|
962
923
|
return deploy2;
|
|
963
924
|
}
|
|
964
|
-
function
|
|
925
|
+
function getModuleSecret(name2, data, hash) {
|
|
965
926
|
const compressed = (0, import_zlib.gzipSync)(data);
|
|
966
927
|
const path = `module-${hash}.js.gz`;
|
|
967
928
|
const compressedData = compressed.toString("base64");
|
|
@@ -1103,19 +1064,19 @@ function storeRoleBinding(name2) {
|
|
|
1103
1064
|
}
|
|
1104
1065
|
|
|
1105
1066
|
// src/lib/k8s.ts
|
|
1106
|
-
var
|
|
1107
|
-
var Store = class extends
|
|
1067
|
+
var import_kubernetes_fluent_client2 = require("kubernetes-fluent-client");
|
|
1068
|
+
var Store = class extends import_kubernetes_fluent_client2.GenericKind {
|
|
1108
1069
|
};
|
|
1109
1070
|
var peprStoreGVK = {
|
|
1110
1071
|
kind: "PeprStore",
|
|
1111
1072
|
version: "v1",
|
|
1112
1073
|
group: "pepr.dev"
|
|
1113
1074
|
};
|
|
1114
|
-
(0,
|
|
1075
|
+
(0, import_kubernetes_fluent_client2.RegisterKind)(Store, peprStoreGVK);
|
|
1115
1076
|
|
|
1116
1077
|
// src/lib/assets/store.ts
|
|
1117
|
-
var { group, version, kind:
|
|
1118
|
-
var singular =
|
|
1078
|
+
var { group, version, kind: kind2 } = peprStoreGVK;
|
|
1079
|
+
var singular = kind2.toLocaleLowerCase();
|
|
1119
1080
|
var plural = `${singular}s`;
|
|
1120
1081
|
var name = `${plural}.${group}`;
|
|
1121
1082
|
var peprStoreCRD = {
|
|
@@ -1151,7 +1112,7 @@ var peprStoreCRD = {
|
|
|
1151
1112
|
names: {
|
|
1152
1113
|
plural,
|
|
1153
1114
|
singular,
|
|
1154
|
-
kind:
|
|
1115
|
+
kind: kind2
|
|
1155
1116
|
}
|
|
1156
1117
|
}
|
|
1157
1118
|
};
|
|
@@ -1178,8 +1139,8 @@ async function generateWebhookRules(assets, isMutateWebhook) {
|
|
|
1178
1139
|
continue;
|
|
1179
1140
|
}
|
|
1180
1141
|
const operations = [];
|
|
1181
|
-
if (event === "CREATEORUPDATE" /*
|
|
1182
|
-
operations.push("CREATE" /*
|
|
1142
|
+
if (event === "CREATEORUPDATE" /* CREATE_OR_UPDATE */) {
|
|
1143
|
+
operations.push("CREATE" /* CREATE */, "UPDATE" /* UPDATE */);
|
|
1183
1144
|
} else {
|
|
1184
1145
|
operations.push(event);
|
|
1185
1146
|
}
|
|
@@ -1256,12 +1217,12 @@ async function webhookConfig(assets, mutateOrValidate, timeoutSeconds = 10) {
|
|
|
1256
1217
|
// src/lib/assets/deploy.ts
|
|
1257
1218
|
async function deployImagePullSecret(imagePullSecret, name2) {
|
|
1258
1219
|
try {
|
|
1259
|
-
await (0,
|
|
1220
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Namespace).Get("pepr-system");
|
|
1260
1221
|
} catch {
|
|
1261
|
-
await (0,
|
|
1222
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Namespace).Apply(getNamespace());
|
|
1262
1223
|
}
|
|
1263
1224
|
try {
|
|
1264
|
-
await (0,
|
|
1225
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(
|
|
1265
1226
|
{
|
|
1266
1227
|
apiVersion: "v1",
|
|
1267
1228
|
kind: "Secret",
|
|
@@ -1284,29 +1245,29 @@ async function deploy(assets, force, webhookTimeout) {
|
|
|
1284
1245
|
logger_default.info("Establishing connection to Kubernetes");
|
|
1285
1246
|
const { name: name2, host, path } = assets;
|
|
1286
1247
|
logger_default.info("Applying pepr-system namespace");
|
|
1287
|
-
await (0,
|
|
1248
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Namespace).Apply(getNamespace(assets.config.customLabels?.namespace));
|
|
1288
1249
|
const mutateWebhook = await webhookConfig(assets, "mutate", webhookTimeout);
|
|
1289
1250
|
if (mutateWebhook) {
|
|
1290
1251
|
logger_default.info("Applying mutating webhook");
|
|
1291
|
-
await (0,
|
|
1252
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.MutatingWebhookConfiguration).Apply(mutateWebhook, { force });
|
|
1292
1253
|
} else {
|
|
1293
1254
|
logger_default.info("Mutating webhook not needed, removing if it exists");
|
|
1294
|
-
await (0,
|
|
1255
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.MutatingWebhookConfiguration).Delete(name2);
|
|
1295
1256
|
}
|
|
1296
1257
|
const validateWebhook = await webhookConfig(assets, "validate", webhookTimeout);
|
|
1297
1258
|
if (validateWebhook) {
|
|
1298
1259
|
logger_default.info("Applying validating webhook");
|
|
1299
|
-
await (0,
|
|
1260
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ValidatingWebhookConfiguration).Apply(validateWebhook, { force });
|
|
1300
1261
|
} else {
|
|
1301
1262
|
logger_default.info("Validating webhook not needed, removing if it exists");
|
|
1302
|
-
await (0,
|
|
1263
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ValidatingWebhookConfiguration).Delete(name2);
|
|
1303
1264
|
}
|
|
1304
1265
|
logger_default.info("Applying the Pepr Store CRD if it doesn't exist");
|
|
1305
|
-
await (0,
|
|
1266
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.CustomResourceDefinition).Apply(peprStoreCRD, { force });
|
|
1306
1267
|
if (host) {
|
|
1307
1268
|
return;
|
|
1308
1269
|
}
|
|
1309
|
-
const code = await
|
|
1270
|
+
const code = await import_fs.promises.readFile(path);
|
|
1310
1271
|
const hash = import_crypto.default.createHash("sha256").update(code).digest("hex");
|
|
1311
1272
|
if (code.length < 1) {
|
|
1312
1273
|
throw new Error("No code provided");
|
|
@@ -1319,53 +1280,53 @@ async function setupRBAC(name2, capabilities, force, config) {
|
|
|
1319
1280
|
const { rbacMode, rbac } = config;
|
|
1320
1281
|
logger_default.info("Applying cluster role binding");
|
|
1321
1282
|
const crb = clusterRoleBinding(name2);
|
|
1322
|
-
await (0,
|
|
1283
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ClusterRoleBinding).Apply(crb, { force });
|
|
1323
1284
|
logger_default.info("Applying cluster role");
|
|
1324
1285
|
const cr = clusterRole(name2, capabilities, rbacMode, rbac);
|
|
1325
|
-
await (0,
|
|
1286
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ClusterRole).Apply(cr, { force });
|
|
1326
1287
|
logger_default.info("Applying service account");
|
|
1327
1288
|
const sa = serviceAccount(name2);
|
|
1328
|
-
await (0,
|
|
1289
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ServiceAccount).Apply(sa, { force });
|
|
1329
1290
|
logger_default.info("Applying store role");
|
|
1330
1291
|
const role = storeRole(name2);
|
|
1331
|
-
await (0,
|
|
1292
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Role).Apply(role, { force });
|
|
1332
1293
|
logger_default.info("Applying store role binding");
|
|
1333
1294
|
const roleBinding = storeRoleBinding(name2);
|
|
1334
|
-
await (0,
|
|
1295
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.RoleBinding).Apply(roleBinding, { force });
|
|
1335
1296
|
}
|
|
1336
1297
|
async function setupController(assets, code, hash, force) {
|
|
1337
1298
|
const { name: name2 } = assets;
|
|
1338
1299
|
logger_default.info("Applying module secret");
|
|
1339
|
-
const mod =
|
|
1340
|
-
await (0,
|
|
1300
|
+
const mod = getModuleSecret(name2, code, hash);
|
|
1301
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(mod, { force });
|
|
1341
1302
|
logger_default.info("Applying controller service");
|
|
1342
1303
|
const svc = service(name2);
|
|
1343
|
-
await (0,
|
|
1304
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Service).Apply(svc, { force });
|
|
1344
1305
|
logger_default.info("Applying TLS secret");
|
|
1345
1306
|
const tls = tlsSecret(name2, assets.tls);
|
|
1346
|
-
await (0,
|
|
1307
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(tls, { force });
|
|
1347
1308
|
logger_default.info("Applying API token secret");
|
|
1348
1309
|
const apiToken = apiTokenSecret(name2, assets.apiToken);
|
|
1349
|
-
await (0,
|
|
1310
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(apiToken, { force });
|
|
1350
1311
|
logger_default.info("Applying deployment");
|
|
1351
|
-
const dep =
|
|
1352
|
-
await (0,
|
|
1312
|
+
const dep = getDeployment(assets, hash, assets.buildTimestamp);
|
|
1313
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Deployment).Apply(dep, { force });
|
|
1353
1314
|
}
|
|
1354
1315
|
async function setupWatcher(assets, hash, force) {
|
|
1355
|
-
const watchDeployment =
|
|
1316
|
+
const watchDeployment = getWatcher(assets, hash, assets.buildTimestamp);
|
|
1356
1317
|
if (watchDeployment) {
|
|
1357
1318
|
logger_default.info("Applying watcher deployment");
|
|
1358
|
-
await (0,
|
|
1319
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Deployment).Apply(watchDeployment, { force });
|
|
1359
1320
|
logger_default.info("Applying watcher service");
|
|
1360
1321
|
const watchSvc = watcherService(assets.name);
|
|
1361
|
-
await (0,
|
|
1322
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Service).Apply(watchSvc, { force });
|
|
1362
1323
|
}
|
|
1363
1324
|
}
|
|
1364
1325
|
|
|
1365
1326
|
// src/lib/assets/loader.ts
|
|
1366
1327
|
var import_child_process = require("child_process");
|
|
1367
1328
|
function loadCapabilities(path) {
|
|
1368
|
-
return new Promise((
|
|
1329
|
+
return new Promise((resolve6, reject) => {
|
|
1369
1330
|
const program2 = (0, import_child_process.fork)(path, {
|
|
1370
1331
|
env: {
|
|
1371
1332
|
...process.env,
|
|
@@ -1379,7 +1340,7 @@ function loadCapabilities(path) {
|
|
|
1379
1340
|
for (const capability of capabilities) {
|
|
1380
1341
|
console.info(`Registered Pepr Capability "${capability.name}"`);
|
|
1381
1342
|
}
|
|
1382
|
-
|
|
1343
|
+
resolve6(capabilities);
|
|
1383
1344
|
});
|
|
1384
1345
|
program2.on("error", (error) => {
|
|
1385
1346
|
reject(error);
|
|
@@ -1390,7 +1351,7 @@ function loadCapabilities(path) {
|
|
|
1390
1351
|
// src/lib/assets/yaml.ts
|
|
1391
1352
|
var import_client_node = require("@kubernetes/client-node");
|
|
1392
1353
|
var import_crypto2 = __toESM(require("crypto"));
|
|
1393
|
-
var
|
|
1354
|
+
var import_fs2 = require("fs");
|
|
1394
1355
|
async function overridesFile({ hash, name: name2, image, config, apiToken, capabilities }, path) {
|
|
1395
1356
|
const rbacOverrides = clusterRole(name2, capabilities, config.rbacMode, config.rbac).rules;
|
|
1396
1357
|
const overrides = {
|
|
@@ -1541,7 +1502,7 @@ async function overridesFile({ hash, name: name2, image, config, apiToken, capab
|
|
|
1541
1502
|
}
|
|
1542
1503
|
}
|
|
1543
1504
|
};
|
|
1544
|
-
await
|
|
1505
|
+
await import_fs2.promises.writeFile(path, (0, import_client_node.dumpYaml)(overrides, { noRefs: true, forceQuotes: true }));
|
|
1545
1506
|
}
|
|
1546
1507
|
function zarfYaml({ name: name2, image, config }, path) {
|
|
1547
1508
|
const zarfCfg = {
|
|
@@ -1598,22 +1559,22 @@ function zarfYamlChart({ name: name2, image, config }, path) {
|
|
|
1598
1559
|
}
|
|
1599
1560
|
async function allYaml(assets, imagePullSecret) {
|
|
1600
1561
|
const { name: name2, tls, apiToken, path, config } = assets;
|
|
1601
|
-
const code = await
|
|
1562
|
+
const code = await import_fs2.promises.readFile(path);
|
|
1602
1563
|
assets.hash = import_crypto2.default.createHash("sha256").update(code).digest("hex");
|
|
1603
1564
|
const mutateWebhook = await webhookConfig(assets, "mutate", assets.config.webhookTimeout);
|
|
1604
1565
|
const validateWebhook = await webhookConfig(assets, "validate", assets.config.webhookTimeout);
|
|
1605
|
-
const watchDeployment =
|
|
1566
|
+
const watchDeployment = getWatcher(assets, assets.hash, assets.buildTimestamp, imagePullSecret);
|
|
1606
1567
|
const resources = [
|
|
1607
|
-
|
|
1568
|
+
getNamespace(assets.config.customLabels?.namespace),
|
|
1608
1569
|
clusterRole(name2, assets.capabilities, config.rbacMode, config.rbac),
|
|
1609
1570
|
clusterRoleBinding(name2),
|
|
1610
1571
|
serviceAccount(name2),
|
|
1611
1572
|
apiTokenSecret(name2, apiToken),
|
|
1612
1573
|
tlsSecret(name2, tls),
|
|
1613
|
-
|
|
1574
|
+
getDeployment(assets, assets.hash, assets.buildTimestamp, imagePullSecret),
|
|
1614
1575
|
service(name2),
|
|
1615
1576
|
watcherService(name2),
|
|
1616
|
-
|
|
1577
|
+
getModuleSecret(name2, code, assets.hash),
|
|
1617
1578
|
storeRole(name2),
|
|
1618
1579
|
storeRoleBinding(name2)
|
|
1619
1580
|
];
|
|
@@ -1646,7 +1607,7 @@ function clusterRoleTemplate() {
|
|
|
1646
1607
|
{{- end }}
|
|
1647
1608
|
`;
|
|
1648
1609
|
}
|
|
1649
|
-
function
|
|
1610
|
+
function namespaceTemplate() {
|
|
1650
1611
|
return `
|
|
1651
1612
|
apiVersion: v1
|
|
1652
1613
|
kind: Namespace
|
|
@@ -1883,7 +1844,74 @@ function serviceMonitorTemplate(name2) {
|
|
|
1883
1844
|
}
|
|
1884
1845
|
|
|
1885
1846
|
// src/lib/assets/index.ts
|
|
1886
|
-
var
|
|
1847
|
+
var import_fs4 = require("fs");
|
|
1848
|
+
|
|
1849
|
+
// src/lib/filesystemService.ts
|
|
1850
|
+
var import_fs3 = require("fs");
|
|
1851
|
+
async function createDirectoryIfNotExists(path) {
|
|
1852
|
+
try {
|
|
1853
|
+
await import_fs3.promises.access(path);
|
|
1854
|
+
} catch (error) {
|
|
1855
|
+
if (error.code === "ENOENT") {
|
|
1856
|
+
await import_fs3.promises.mkdir(path, { recursive: true });
|
|
1857
|
+
} else {
|
|
1858
|
+
throw error;
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
// src/lib/assets/index.ts
|
|
1864
|
+
function toYaml(obj) {
|
|
1865
|
+
return (0, import_client_node2.dumpYaml)(obj, { noRefs: true });
|
|
1866
|
+
}
|
|
1867
|
+
function createWebhookYaml(assets, webhookConfiguration) {
|
|
1868
|
+
const yaml = toYaml(webhookConfiguration);
|
|
1869
|
+
return replaceString(
|
|
1870
|
+
replaceString(
|
|
1871
|
+
replaceString(yaml, assets.name, "{{ .Values.uuid }}"),
|
|
1872
|
+
assets.config.onError === "reject" ? "Fail" : "Ignore",
|
|
1873
|
+
"{{ .Values.admission.failurePolicy }}"
|
|
1874
|
+
),
|
|
1875
|
+
`${assets.config.webhookTimeout}` || "10",
|
|
1876
|
+
"{{ .Values.admission.webhookTimeout }}"
|
|
1877
|
+
);
|
|
1878
|
+
}
|
|
1879
|
+
function helmLayout(basePath, unique) {
|
|
1880
|
+
const helm = {
|
|
1881
|
+
dirs: {
|
|
1882
|
+
chart: (0, import_path.resolve)(`${basePath}/${unique}-chart`)
|
|
1883
|
+
},
|
|
1884
|
+
files: {}
|
|
1885
|
+
};
|
|
1886
|
+
helm.dirs = {
|
|
1887
|
+
...helm.dirs,
|
|
1888
|
+
charts: `${helm.dirs.chart}/charts`,
|
|
1889
|
+
tmpls: `${helm.dirs.chart}/templates`
|
|
1890
|
+
};
|
|
1891
|
+
helm.files = {
|
|
1892
|
+
...helm.files,
|
|
1893
|
+
valuesYaml: `${helm.dirs.chart}/values.yaml`,
|
|
1894
|
+
chartYaml: `${helm.dirs.chart}/Chart.yaml`,
|
|
1895
|
+
namespaceYaml: `${helm.dirs.tmpls}/namespace.yaml`,
|
|
1896
|
+
watcherServiceYaml: `${helm.dirs.tmpls}/watcher-service.yaml`,
|
|
1897
|
+
admissionServiceYaml: `${helm.dirs.tmpls}/admission-service.yaml`,
|
|
1898
|
+
mutationWebhookYaml: `${helm.dirs.tmpls}/mutation-webhook.yaml`,
|
|
1899
|
+
validationWebhookYaml: `${helm.dirs.tmpls}/validation-webhook.yaml`,
|
|
1900
|
+
admissionDeploymentYaml: `${helm.dirs.tmpls}/admission-deployment.yaml`,
|
|
1901
|
+
admissionServiceMonitorYaml: `${helm.dirs.tmpls}/admission-service-monitor.yaml`,
|
|
1902
|
+
watcherDeploymentYaml: `${helm.dirs.tmpls}/watcher-deployment.yaml`,
|
|
1903
|
+
watcherServiceMonitorYaml: `${helm.dirs.tmpls}/watcher-service-monitor.yaml`,
|
|
1904
|
+
tlsSecretYaml: `${helm.dirs.tmpls}/tls-secret.yaml`,
|
|
1905
|
+
apiTokenSecretYaml: `${helm.dirs.tmpls}/api-token-secret.yaml`,
|
|
1906
|
+
moduleSecretYaml: `${helm.dirs.tmpls}/module-secret.yaml`,
|
|
1907
|
+
storeRoleYaml: `${helm.dirs.tmpls}/store-role.yaml`,
|
|
1908
|
+
storeRoleBindingYaml: `${helm.dirs.tmpls}/store-role-binding.yaml`,
|
|
1909
|
+
clusterRoleYaml: `${helm.dirs.tmpls}/cluster-role.yaml`,
|
|
1910
|
+
clusterRoleBindingYaml: `${helm.dirs.tmpls}/cluster-role-binding.yaml`,
|
|
1911
|
+
serviceAccountYaml: `${helm.dirs.tmpls}/service-account.yaml`
|
|
1912
|
+
};
|
|
1913
|
+
return helm;
|
|
1914
|
+
}
|
|
1887
1915
|
var Assets = class {
|
|
1888
1916
|
constructor(config, path, host) {
|
|
1889
1917
|
this.config = config;
|
|
@@ -1921,82 +1949,48 @@ var Assets = class {
|
|
|
1921
1949
|
}
|
|
1922
1950
|
return allYaml(this, imagePullSecret);
|
|
1923
1951
|
};
|
|
1952
|
+
/* eslint max-statements: ["warn", 21] */
|
|
1924
1953
|
generateHelmChart = async (basePath) => {
|
|
1925
|
-
const
|
|
1926
|
-
const CHAR_TEMPLATES_DIR = `${CHART_DIR}/templates`;
|
|
1927
|
-
const valuesPath = (0, import_path.resolve)(CHART_DIR, `values.yaml`);
|
|
1928
|
-
const chartPath = (0, import_path.resolve)(CHART_DIR, `Chart.yaml`);
|
|
1929
|
-
const nsPath = (0, import_path.resolve)(CHAR_TEMPLATES_DIR, `namespace.yaml`);
|
|
1930
|
-
const watcherSVCPath = (0, import_path.resolve)(CHAR_TEMPLATES_DIR, `watcher-service.yaml`);
|
|
1931
|
-
const admissionSVCPath = (0, import_path.resolve)(CHAR_TEMPLATES_DIR, `admission-service.yaml`);
|
|
1932
|
-
const mutationWebhookPath = (0, import_path.resolve)(CHAR_TEMPLATES_DIR, `mutation-webhook.yaml`);
|
|
1933
|
-
const validationWebhookPath = (0, import_path.resolve)(CHAR_TEMPLATES_DIR, `validation-webhook.yaml`);
|
|
1934
|
-
const admissionDeployPath = (0, import_path.resolve)(CHAR_TEMPLATES_DIR, `admission-deployment.yaml`);
|
|
1935
|
-
const admissionServiceMonitorPath = (0, import_path.resolve)(CHAR_TEMPLATES_DIR, `admission-service-monitor.yaml`);
|
|
1936
|
-
const watcherDeployPath = (0, import_path.resolve)(CHAR_TEMPLATES_DIR, `watcher-deployment.yaml`);
|
|
1937
|
-
const watcherServiceMonitorPath = (0, import_path.resolve)(CHAR_TEMPLATES_DIR, `watcher-service-monitor.yaml`);
|
|
1938
|
-
const tlsSecretPath = (0, import_path.resolve)(CHAR_TEMPLATES_DIR, `tls-secret.yaml`);
|
|
1939
|
-
const apiTokenSecretPath = (0, import_path.resolve)(CHAR_TEMPLATES_DIR, `api-token-secret.yaml`);
|
|
1940
|
-
const moduleSecretPath = (0, import_path.resolve)(CHAR_TEMPLATES_DIR, `module-secret.yaml`);
|
|
1941
|
-
const storeRolePath = (0, import_path.resolve)(CHAR_TEMPLATES_DIR, `store-role.yaml`);
|
|
1942
|
-
const storeRoleBindingPath = (0, import_path.resolve)(CHAR_TEMPLATES_DIR, `store-role-binding.yaml`);
|
|
1943
|
-
const clusterRolePath = (0, import_path.resolve)(CHAR_TEMPLATES_DIR, `cluster-role.yaml`);
|
|
1944
|
-
const clusterRoleBindingPath = (0, import_path.resolve)(CHAR_TEMPLATES_DIR, `cluster-role-binding.yaml`);
|
|
1945
|
-
const serviceAccountPath = (0, import_path.resolve)(CHAR_TEMPLATES_DIR, `service-account.yaml`);
|
|
1954
|
+
const helm = helmLayout(basePath, this.config.uuid);
|
|
1946
1955
|
try {
|
|
1947
|
-
await
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
await
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1956
|
+
await Promise.all(
|
|
1957
|
+
Object.values(helm.dirs).sort((l, r) => l.split("/").length - r.split("/").length).map(async (dir) => await createDirectoryIfNotExists(dir))
|
|
1958
|
+
);
|
|
1959
|
+
const code = await import_fs4.promises.readFile(this.path);
|
|
1960
|
+
const pairs = [
|
|
1961
|
+
[helm.files.chartYaml, () => dedent(chartYaml(this.config.uuid, this.config.description || ""))],
|
|
1962
|
+
[helm.files.namespaceYaml, () => dedent(namespaceTemplate())],
|
|
1963
|
+
[helm.files.watcherServiceYaml, () => toYaml(watcherService(this.name))],
|
|
1964
|
+
[helm.files.admissionServiceYaml, () => toYaml(service(this.name))],
|
|
1965
|
+
[helm.files.tlsSecretYaml, () => toYaml(tlsSecret(this.name, this.tls))],
|
|
1966
|
+
[helm.files.apiTokenSecretYaml, () => toYaml(apiTokenSecret(this.name, this.apiToken))],
|
|
1967
|
+
[helm.files.storeRoleYaml, () => toYaml(storeRole(this.name))],
|
|
1968
|
+
[helm.files.storeRoleBindingYaml, () => toYaml(storeRoleBinding(this.name))],
|
|
1969
|
+
[helm.files.clusterRoleYaml, () => dedent(clusterRoleTemplate())],
|
|
1970
|
+
[helm.files.clusterRoleBindingYaml, () => toYaml(clusterRoleBinding(this.name))],
|
|
1971
|
+
[helm.files.serviceAccountYaml, () => toYaml(serviceAccount(this.name))],
|
|
1972
|
+
[helm.files.moduleSecretYaml, () => toYaml(getModuleSecret(this.name, code, this.hash))]
|
|
1973
|
+
];
|
|
1974
|
+
await Promise.all(pairs.map(async ([file, content]) => await import_fs4.promises.writeFile(file, content())));
|
|
1975
|
+
await overridesFile(this, helm.files.valuesYaml);
|
|
1976
|
+
const [mutateWebhook, validateWebhook] = await Promise.all([
|
|
1977
|
+
webhookConfig(this, "mutate", this.config.webhookTimeout),
|
|
1978
|
+
webhookConfig(this, "validate", this.config.webhookTimeout)
|
|
1979
|
+
]);
|
|
1967
1980
|
if (validateWebhook || mutateWebhook) {
|
|
1968
|
-
await
|
|
1969
|
-
await
|
|
1981
|
+
await import_fs4.promises.writeFile(helm.files.admissionDeploymentYaml, dedent(admissionDeployTemplate(this.buildTimestamp)));
|
|
1982
|
+
await import_fs4.promises.writeFile(helm.files.admissionServiceMonitorYaml, dedent(serviceMonitorTemplate("admission")));
|
|
1970
1983
|
}
|
|
1971
1984
|
if (mutateWebhook) {
|
|
1972
|
-
|
|
1973
|
-
const mutateWebhookTemplate = replaceString(
|
|
1974
|
-
replaceString(
|
|
1975
|
-
replaceString(yamlMutateWebhook, this.name, "{{ .Values.uuid }}"),
|
|
1976
|
-
this.config.onError === "reject" ? "Fail" : "Ignore",
|
|
1977
|
-
"{{ .Values.admission.failurePolicy }}"
|
|
1978
|
-
),
|
|
1979
|
-
`${this.config.webhookTimeout}` || "10",
|
|
1980
|
-
"{{ .Values.admission.webhookTimeout }}"
|
|
1981
|
-
);
|
|
1982
|
-
await import_fs5.promises.writeFile(mutationWebhookPath, mutateWebhookTemplate);
|
|
1985
|
+
await import_fs4.promises.writeFile(helm.files.mutationWebhookYaml, createWebhookYaml(this, mutateWebhook));
|
|
1983
1986
|
}
|
|
1984
1987
|
if (validateWebhook) {
|
|
1985
|
-
|
|
1986
|
-
const validateWebhookTemplate = replaceString(
|
|
1987
|
-
replaceString(
|
|
1988
|
-
replaceString(yamlValidateWebhook, this.name, "{{ .Values.uuid }}"),
|
|
1989
|
-
this.config.onError === "reject" ? "Fail" : "Ignore",
|
|
1990
|
-
"{{ .Values.admission.failurePolicy }}"
|
|
1991
|
-
),
|
|
1992
|
-
`${this.config.webhookTimeout}` || "10",
|
|
1993
|
-
"{{ .Values.admission.webhookTimeout }}"
|
|
1994
|
-
);
|
|
1995
|
-
await import_fs5.promises.writeFile(validationWebhookPath, validateWebhookTemplate);
|
|
1988
|
+
await import_fs4.promises.writeFile(helm.files.validationWebhookYaml, createWebhookYaml(this, validateWebhook));
|
|
1996
1989
|
}
|
|
1990
|
+
const watchDeployment = getWatcher(this, this.hash, this.buildTimestamp);
|
|
1997
1991
|
if (watchDeployment) {
|
|
1998
|
-
await
|
|
1999
|
-
await
|
|
1992
|
+
await import_fs4.promises.writeFile(helm.files.watcherDeploymentYaml, dedent(watcherDeployTemplate(this.buildTimestamp)));
|
|
1993
|
+
await import_fs4.promises.writeFile(helm.files.watcherServiceMonitorYaml, dedent(serviceMonitorTemplate("watcher")));
|
|
2000
1994
|
}
|
|
2001
1995
|
} catch (err) {
|
|
2002
1996
|
console.error(`Error generating helm chart: ${err.message}`);
|
|
@@ -2211,8 +2205,8 @@ var hello_pepr_samples_default = [
|
|
|
2211
2205
|
var gitIgnore = "# Ignore node_modules and Pepr build artifacts\nnode_modules\ndist\ninsecure*\n";
|
|
2212
2206
|
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';
|
|
2213
2207
|
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';
|
|
2214
|
-
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 nock from "nock";\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 // Set up Nock to mock the API calls globally with header matching\n nock(jokeURL).get("/").reply(200, {\n id: "R7UfaahVfFd",\n joke: "Funny joke goes here.",\n status: 200,\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';
|
|
2215
|
-
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.
|
|
2208
|
+
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';
|
|
2209
|
+
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.1", 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.5.0", "pino-pretty": "13.0.0", "prom-client": "15.1.3", ramda: "0.30.1", sigstore: "3.0.0" }, devDependencies: { "@commitlint/cli": "19.6.0", "@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: { "@types/prompts": "2.4.9", "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", commander: "12.1.0", esbuild: "0.23.0", eslint: "8.57.0", "node-forge": "1.3.1", prettier: "3.3.3", prompts: "2.4.2", typescript: "5.3.3", uuid: "10.0.0" } };
|
|
2216
2210
|
|
|
2217
2211
|
// src/templates/pepr.code-snippets.json
|
|
2218
2212
|
var pepr_code_snippets_default = {
|
|
@@ -2271,7 +2265,7 @@ var tsconfig_module_default = {
|
|
|
2271
2265
|
};
|
|
2272
2266
|
|
|
2273
2267
|
// src/cli/init/utils.ts
|
|
2274
|
-
var
|
|
2268
|
+
var import_fs5 = require("fs");
|
|
2275
2269
|
function sanitizeName(name2) {
|
|
2276
2270
|
if (typeof name2 !== "string") {
|
|
2277
2271
|
throw TypeError(
|
|
@@ -2285,7 +2279,7 @@ function sanitizeName(name2) {
|
|
|
2285
2279
|
}
|
|
2286
2280
|
async function createDir(dir) {
|
|
2287
2281
|
try {
|
|
2288
|
-
await
|
|
2282
|
+
await import_fs5.promises.mkdir(dir);
|
|
2289
2283
|
} catch (err) {
|
|
2290
2284
|
if (err && err.code === "EEXIST") {
|
|
2291
2285
|
throw new Error(`Directory ${dir} already exists`);
|
|
@@ -2298,7 +2292,7 @@ function write(path, data) {
|
|
|
2298
2292
|
if (typeof data !== "string") {
|
|
2299
2293
|
data = JSON.stringify(data, null, 2);
|
|
2300
2294
|
}
|
|
2301
|
-
return
|
|
2295
|
+
return import_fs5.promises.writeFile(path, data);
|
|
2302
2296
|
}
|
|
2303
2297
|
|
|
2304
2298
|
// src/cli/init/templates.ts
|
|
@@ -2339,7 +2333,7 @@ function genPkgJSON(opts, pgkVerOverride) {
|
|
|
2339
2333
|
},
|
|
2340
2334
|
dependencies: {
|
|
2341
2335
|
pepr: pgkVerOverride || version2,
|
|
2342
|
-
|
|
2336
|
+
undici: "^7.0.1"
|
|
2343
2337
|
},
|
|
2344
2338
|
devDependencies: {
|
|
2345
2339
|
typescript
|
|
@@ -2394,9 +2388,12 @@ var eslint = {
|
|
|
2394
2388
|
data: eslintrc_template_default
|
|
2395
2389
|
};
|
|
2396
2390
|
|
|
2391
|
+
// src/cli/build.ts
|
|
2392
|
+
var import_commander = require("commander");
|
|
2393
|
+
|
|
2397
2394
|
// src/cli/format.ts
|
|
2398
2395
|
var import_eslint = require("eslint");
|
|
2399
|
-
var
|
|
2396
|
+
var import_fs6 = require("fs");
|
|
2400
2397
|
var import_prettier = require("prettier");
|
|
2401
2398
|
function format_default(program2) {
|
|
2402
2399
|
program2.command("format").description("Lint and format this Pepr module").option("-v, --validate-only", "Do not modify files, only validate formatting").action(async (opts) => {
|
|
@@ -2429,14 +2426,14 @@ async function peprFormat(validateOnly) {
|
|
|
2429
2426
|
await import_eslint.ESLint.outputFixes(results);
|
|
2430
2427
|
}
|
|
2431
2428
|
for (const { filePath } of results) {
|
|
2432
|
-
const content = await
|
|
2429
|
+
const content = await import_fs6.promises.readFile(filePath, "utf8");
|
|
2433
2430
|
const cfg = await (0, import_prettier.resolveConfig)(filePath);
|
|
2434
2431
|
const formatted = await (0, import_prettier.format)(content, { filepath: filePath, ...cfg });
|
|
2435
2432
|
if (validateOnly && formatted !== content) {
|
|
2436
2433
|
hasFailure = true;
|
|
2437
2434
|
console.error(`File ${filePath} is not formatted correctly`);
|
|
2438
2435
|
} else {
|
|
2439
|
-
await
|
|
2436
|
+
await import_fs6.promises.writeFile(filePath, formatted);
|
|
2440
2437
|
}
|
|
2441
2438
|
}
|
|
2442
2439
|
return !hasFailure;
|
|
@@ -2447,10 +2444,27 @@ async function peprFormat(validateOnly) {
|
|
|
2447
2444
|
}
|
|
2448
2445
|
}
|
|
2449
2446
|
|
|
2450
|
-
// src/
|
|
2451
|
-
var
|
|
2447
|
+
// src/lib/included-files.ts
|
|
2448
|
+
var import_fs7 = require("fs");
|
|
2449
|
+
async function createDockerfile(version3, description, includedFiles) {
|
|
2450
|
+
const file = `
|
|
2451
|
+
# Use an official Node.js runtime as the base image
|
|
2452
|
+
FROM ghcr.io/defenseunicorns/pepr/controller:v${version3}
|
|
2453
|
+
|
|
2454
|
+
LABEL description="${description}"
|
|
2455
|
+
|
|
2456
|
+
# Add the included files to the image
|
|
2457
|
+
${includedFiles.map((f) => `ADD ${f} ${f}`).join("\n")}
|
|
2458
|
+
|
|
2459
|
+
`;
|
|
2460
|
+
await import_fs7.promises.writeFile("Dockerfile.controller", file, { encoding: "utf-8" });
|
|
2461
|
+
}
|
|
2452
2462
|
|
|
2453
2463
|
// src/cli/build.helpers.ts
|
|
2464
|
+
var import_child_process2 = require("child_process");
|
|
2465
|
+
var import_esbuild = require("esbuild");
|
|
2466
|
+
var import_path2 = require("path");
|
|
2467
|
+
var import_fs8 = require("fs");
|
|
2454
2468
|
function determineRbacMode(opts, cfg) {
|
|
2455
2469
|
if (opts.rbacMode) {
|
|
2456
2470
|
return opts.rbacMode;
|
|
@@ -2460,6 +2474,93 @@ function determineRbacMode(opts, cfg) {
|
|
|
2460
2474
|
}
|
|
2461
2475
|
return cfg.pepr.rbacMode || "admin";
|
|
2462
2476
|
}
|
|
2477
|
+
async function handleCustomOutputDir(outputDir2) {
|
|
2478
|
+
const defaultOutputDir = "dist";
|
|
2479
|
+
if (outputDir2) {
|
|
2480
|
+
try {
|
|
2481
|
+
await createDirectoryIfNotExists(outputDir2);
|
|
2482
|
+
return outputDir2;
|
|
2483
|
+
} catch (error) {
|
|
2484
|
+
console.error(`Error creating output directory: ${error.message}`);
|
|
2485
|
+
process.exit(1);
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
return defaultOutputDir;
|
|
2489
|
+
}
|
|
2490
|
+
function checkIronBankImage(registry, image, peprVersion) {
|
|
2491
|
+
return registry === "Iron Bank" ? `registry1.dso.mil/ironbank/opensource/defenseunicorns/pepr/controller:v${peprVersion}` : image;
|
|
2492
|
+
}
|
|
2493
|
+
function validImagePullSecret(imagePullSecretName) {
|
|
2494
|
+
if (imagePullSecretName) {
|
|
2495
|
+
const error = "Invalid imagePullSecret. Please provide a valid name as defined in RFC 1123.";
|
|
2496
|
+
if (sanitizeResourceName(imagePullSecretName) !== imagePullSecretName) {
|
|
2497
|
+
console.error(error);
|
|
2498
|
+
process.exit(1);
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
function handleCustomImage(customImage, registry) {
|
|
2503
|
+
let defaultImage = "";
|
|
2504
|
+
if (customImage) {
|
|
2505
|
+
if (registry) {
|
|
2506
|
+
console.error(`Custom Image and registry cannot be used together.`);
|
|
2507
|
+
process.exit(1);
|
|
2508
|
+
}
|
|
2509
|
+
defaultImage = customImage;
|
|
2510
|
+
}
|
|
2511
|
+
return defaultImage;
|
|
2512
|
+
}
|
|
2513
|
+
async function handleCustomImageBuild(includedFiles, peprVersion, description, image) {
|
|
2514
|
+
if (includedFiles.length > 0) {
|
|
2515
|
+
await createDockerfile(peprVersion, description, includedFiles);
|
|
2516
|
+
(0, import_child_process2.execSync)(`docker build --tag ${image} -f Dockerfile.controller .`, {
|
|
2517
|
+
stdio: "inherit"
|
|
2518
|
+
});
|
|
2519
|
+
(0, import_child_process2.execSync)(`docker push ${image}`, { stdio: "inherit" });
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
function handleEmbedding(embed, path) {
|
|
2523
|
+
if (!embed) {
|
|
2524
|
+
console.info(`\u2705 Module built successfully at ${path}`);
|
|
2525
|
+
return;
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
function handleValidCapabilityNames(capabilities) {
|
|
2529
|
+
try {
|
|
2530
|
+
validateCapabilityNames(capabilities);
|
|
2531
|
+
} catch (e) {
|
|
2532
|
+
console.error(`Error loading capability:`, e);
|
|
2533
|
+
process.exit(1);
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
async function watchForChanges(ctxCfg, reloader) {
|
|
2537
|
+
const ctx = await (0, import_esbuild.context)(ctxCfg);
|
|
2538
|
+
if (reloader) {
|
|
2539
|
+
await ctx.watch();
|
|
2540
|
+
} else {
|
|
2541
|
+
await ctx.rebuild();
|
|
2542
|
+
await ctx.dispose();
|
|
2543
|
+
}
|
|
2544
|
+
return ctx;
|
|
2545
|
+
}
|
|
2546
|
+
async function generateYamlAndWriteToDisk(obj) {
|
|
2547
|
+
const { uuid, imagePullSecret, outputDir: outputDir2, assets, zarf } = obj;
|
|
2548
|
+
const yamlFile = `pepr-module-${uuid}.yaml`;
|
|
2549
|
+
const chartPath = `${uuid}-chart`;
|
|
2550
|
+
const yamlPath = (0, import_path2.resolve)(outputDir2, yamlFile);
|
|
2551
|
+
const yaml = await assets.allYaml(imagePullSecret);
|
|
2552
|
+
const zarfPath = (0, import_path2.resolve)(outputDir2, "zarf.yaml");
|
|
2553
|
+
let localZarf = "";
|
|
2554
|
+
if (zarf === "chart") {
|
|
2555
|
+
localZarf = assets.zarfYamlChart(chartPath);
|
|
2556
|
+
} else {
|
|
2557
|
+
localZarf = assets.zarfYaml(yamlFile);
|
|
2558
|
+
}
|
|
2559
|
+
await import_fs8.promises.writeFile(yamlPath, yaml);
|
|
2560
|
+
await import_fs8.promises.writeFile(zarfPath, localZarf);
|
|
2561
|
+
await assets.generateHelmChart(outputDir2);
|
|
2562
|
+
console.info(`\u2705 K8s resource for the module saved to ${yamlPath}`);
|
|
2563
|
+
}
|
|
2463
2564
|
|
|
2464
2565
|
// src/cli/build.ts
|
|
2465
2566
|
var peprTS2 = "pepr.ts";
|
|
@@ -2499,46 +2600,22 @@ function build_default(program2) {
|
|
|
2499
2600
|
["admin", "scoped"]
|
|
2500
2601
|
)
|
|
2501
2602
|
).action(async (opts) => {
|
|
2502
|
-
|
|
2503
|
-
outputDir = opts.outputDir;
|
|
2504
|
-
createDirectoryIfNotExists(outputDir).catch((error) => {
|
|
2505
|
-
console.error(`Error creating output directory: ${error.message}`);
|
|
2506
|
-
process.exit(1);
|
|
2507
|
-
});
|
|
2508
|
-
}
|
|
2603
|
+
outputDir = await handleCustomOutputDir(opts.outputDir);
|
|
2509
2604
|
const buildModuleResult = await buildModule(void 0, opts.entryPoint, opts.embed);
|
|
2510
2605
|
if (buildModuleResult?.cfg && buildModuleResult.path && buildModuleResult.uuid) {
|
|
2511
2606
|
const { cfg, path, uuid } = buildModuleResult;
|
|
2512
2607
|
const { includedFiles } = cfg.pepr;
|
|
2513
|
-
let image =
|
|
2514
|
-
if (opts.customImage) {
|
|
2515
|
-
if (opts.registry) {
|
|
2516
|
-
console.error(`Custom Image and registry cannot be used together.`);
|
|
2517
|
-
process.exit(1);
|
|
2518
|
-
}
|
|
2519
|
-
image = opts.customImage;
|
|
2520
|
-
}
|
|
2608
|
+
let image = handleCustomImage(opts.customImage, opts.registry);
|
|
2521
2609
|
if (opts.timeout !== void 0) {
|
|
2522
2610
|
cfg.pepr.webhookTimeout = opts.timeout;
|
|
2523
2611
|
}
|
|
2524
2612
|
if (opts.registryInfo !== void 0) {
|
|
2525
2613
|
console.info(`Including ${includedFiles.length} files in controller image.`);
|
|
2526
2614
|
image = `${opts.registryInfo}/custom-pepr-controller:${cfg.pepr.peprVersion}`;
|
|
2527
|
-
|
|
2528
|
-
await createDockerfile(cfg.pepr.peprVersion, cfg.description, includedFiles);
|
|
2529
|
-
(0, import_child_process2.execSync)(`docker build --tag ${image} -f Dockerfile.controller .`, {
|
|
2530
|
-
stdio: "inherit"
|
|
2531
|
-
});
|
|
2532
|
-
(0, import_child_process2.execSync)(`docker push ${image}`, { stdio: "inherit" });
|
|
2533
|
-
}
|
|
2534
|
-
}
|
|
2535
|
-
if (!opts.embed) {
|
|
2536
|
-
console.info(`\u2705 Module built successfully at ${path}`);
|
|
2537
|
-
return;
|
|
2538
|
-
}
|
|
2539
|
-
if (opts.version) {
|
|
2540
|
-
cfg.pepr.peprVersion = opts.version;
|
|
2615
|
+
await handleCustomImageBuild(includedFiles, cfg.pepr.peprVersion, cfg.description, image);
|
|
2541
2616
|
}
|
|
2617
|
+
handleEmbedding(opts.embed, path);
|
|
2618
|
+
opts.version ? cfg.pepr.peprVersion = opts.version : null;
|
|
2542
2619
|
const assets = new Assets(
|
|
2543
2620
|
{
|
|
2544
2621
|
...cfg.pepr,
|
|
@@ -2549,46 +2626,17 @@ function build_default(program2) {
|
|
|
2549
2626
|
},
|
|
2550
2627
|
path
|
|
2551
2628
|
);
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
assets
|
|
2562
|
-
}
|
|
2563
|
-
if (opts.withPullSecret) {
|
|
2564
|
-
if (sanitizeResourceName(opts.withPullSecret) !== opts.withPullSecret) {
|
|
2565
|
-
console.error(
|
|
2566
|
-
"Invalid imagePullSecret. Please provide a valid name as defined in RFC 1123."
|
|
2567
|
-
);
|
|
2568
|
-
process.exit(1);
|
|
2569
|
-
}
|
|
2570
|
-
}
|
|
2571
|
-
const yamlFile = `pepr-module-${uuid}.yaml`;
|
|
2572
|
-
const chartPath = `${uuid}-chart`;
|
|
2573
|
-
const yamlPath = (0, import_path2.resolve)(outputDir, yamlFile);
|
|
2574
|
-
const yaml = await assets.allYaml(opts.withPullSecret);
|
|
2575
|
-
try {
|
|
2576
|
-
validateCapabilityNames(assets.capabilities);
|
|
2577
|
-
} catch (e) {
|
|
2578
|
-
console.error(`Error loading capability:`, e);
|
|
2579
|
-
process.exit(1);
|
|
2580
|
-
}
|
|
2581
|
-
const zarfPath = (0, import_path2.resolve)(outputDir, "zarf.yaml");
|
|
2582
|
-
let zarf = "";
|
|
2583
|
-
if (opts.zarf === "chart") {
|
|
2584
|
-
zarf = assets.zarfYamlChart(chartPath);
|
|
2585
|
-
} else {
|
|
2586
|
-
zarf = assets.zarfYaml(yamlFile);
|
|
2587
|
-
}
|
|
2588
|
-
await import_fs8.promises.writeFile(yamlPath, yaml);
|
|
2589
|
-
await import_fs8.promises.writeFile(zarfPath, zarf);
|
|
2590
|
-
await assets.generateHelmChart(outputDir);
|
|
2591
|
-
console.info(`\u2705 K8s resource for the module saved to ${yamlPath}`);
|
|
2629
|
+
image = checkIronBankImage(opts.registry, image, cfg.pepr.peprVersion);
|
|
2630
|
+
image !== "" ? assets.image = image : null;
|
|
2631
|
+
validImagePullSecret(opts.withPullSecret);
|
|
2632
|
+
handleValidCapabilityNames(assets.capabilities);
|
|
2633
|
+
await generateYamlAndWriteToDisk({
|
|
2634
|
+
uuid,
|
|
2635
|
+
outputDir,
|
|
2636
|
+
imagePullSecret: opts.withPullSecret,
|
|
2637
|
+
zarf: opts.zarf,
|
|
2638
|
+
assets
|
|
2639
|
+
});
|
|
2592
2640
|
}
|
|
2593
2641
|
});
|
|
2594
2642
|
}
|
|
@@ -2596,19 +2644,19 @@ var externalLibs = Object.keys(dependencies);
|
|
|
2596
2644
|
externalLibs.push("pepr");
|
|
2597
2645
|
externalLibs.push("@kubernetes/client-node");
|
|
2598
2646
|
async function loadModule(entryPoint = peprTS2) {
|
|
2599
|
-
const entryPointPath = (0,
|
|
2600
|
-
const modulePath = (0,
|
|
2601
|
-
const cfgPath = (0,
|
|
2647
|
+
const entryPointPath = (0, import_path3.resolve)(".", entryPoint);
|
|
2648
|
+
const modulePath = (0, import_path3.dirname)(entryPointPath);
|
|
2649
|
+
const cfgPath = (0, import_path3.resolve)(modulePath, "package.json");
|
|
2602
2650
|
try {
|
|
2603
|
-
await
|
|
2604
|
-
await
|
|
2651
|
+
await import_fs9.promises.access(cfgPath);
|
|
2652
|
+
await import_fs9.promises.access(entryPointPath);
|
|
2605
2653
|
} catch (e) {
|
|
2606
2654
|
console.error(
|
|
2607
2655
|
`Could not find ${cfgPath} or ${entryPointPath} in the current directory. Please run this command from the root of your module's directory.`
|
|
2608
2656
|
);
|
|
2609
2657
|
process.exit(1);
|
|
2610
2658
|
}
|
|
2611
|
-
const moduleText = await
|
|
2659
|
+
const moduleText = await import_fs9.promises.readFile(cfgPath, { encoding: "utf-8" });
|
|
2612
2660
|
const cfg = JSON.parse(moduleText);
|
|
2613
2661
|
const { uuid } = cfg.pepr;
|
|
2614
2662
|
const name2 = `pepr-${uuid}.js`;
|
|
@@ -2621,23 +2669,17 @@ async function loadModule(entryPoint = peprTS2) {
|
|
|
2621
2669
|
entryPointPath,
|
|
2622
2670
|
modulePath,
|
|
2623
2671
|
name: name2,
|
|
2624
|
-
path: (0,
|
|
2672
|
+
path: (0, import_path3.resolve)(outputDir, name2),
|
|
2625
2673
|
uuid
|
|
2626
2674
|
};
|
|
2627
2675
|
}
|
|
2628
2676
|
async function buildModule(reloader, entryPoint = peprTS2, embed = true) {
|
|
2629
2677
|
try {
|
|
2630
2678
|
const { cfg, modulePath, path, uuid } = await loadModule(entryPoint);
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
console.log(
|
|
2634
|
-
"\x1B[33m%s\x1B[0m",
|
|
2635
|
-
"Formatting errors were found. The build will continue, but you may want to run `npx pepr format` to address any issues."
|
|
2636
|
-
);
|
|
2637
|
-
}
|
|
2638
|
-
const npmRoot = (0, import_child_process2.execFileSync)("npm", ["root"]).toString().trim();
|
|
2679
|
+
await checkFormat();
|
|
2680
|
+
const npmRoot = (0, import_child_process3.execFileSync)("npm", ["root"]).toString().trim();
|
|
2639
2681
|
const args = ["--project", `${modulePath}/tsconfig.json`, "--outdir", outputDir];
|
|
2640
|
-
(0,
|
|
2682
|
+
(0, import_child_process3.execFileSync)(`${npmRoot}/.bin/tsc`, args);
|
|
2641
2683
|
const ctxCfg = {
|
|
2642
2684
|
bundle: true,
|
|
2643
2685
|
entryPoints: [entryPoint],
|
|
@@ -2654,7 +2696,7 @@ async function buildModule(reloader, entryPoint = peprTS2, embed = true) {
|
|
|
2654
2696
|
setup(build) {
|
|
2655
2697
|
build.onEnd(async (r) => {
|
|
2656
2698
|
if (r?.metafile) {
|
|
2657
|
-
console.log(await (0,
|
|
2699
|
+
console.log(await (0, import_esbuild2.analyzeMetafile)(r.metafile));
|
|
2658
2700
|
}
|
|
2659
2701
|
if (reloader) {
|
|
2660
2702
|
await reloader(r);
|
|
@@ -2672,51 +2714,96 @@ async function buildModule(reloader, entryPoint = peprTS2, embed = true) {
|
|
|
2672
2714
|
}
|
|
2673
2715
|
if (!embed) {
|
|
2674
2716
|
ctxCfg.minify = false;
|
|
2675
|
-
ctxCfg.outfile = (0,
|
|
2717
|
+
ctxCfg.outfile = (0, import_path3.resolve)(outputDir, (0, import_path3.basename)(entryPoint, (0, import_path3.extname)(entryPoint))) + ".js";
|
|
2676
2718
|
ctxCfg.packages = "external";
|
|
2677
2719
|
ctxCfg.treeShaking = false;
|
|
2678
2720
|
}
|
|
2679
|
-
const ctx = await (
|
|
2680
|
-
if (reloader) {
|
|
2681
|
-
await ctx.watch();
|
|
2682
|
-
} else {
|
|
2683
|
-
await ctx.rebuild();
|
|
2684
|
-
await ctx.dispose();
|
|
2685
|
-
}
|
|
2721
|
+
const ctx = await watchForChanges(ctxCfg, reloader);
|
|
2686
2722
|
return { ctx, path, cfg, uuid };
|
|
2687
2723
|
} catch (e) {
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2724
|
+
handleModuleBuildError(e);
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
function handleModuleBuildError(e) {
|
|
2728
|
+
console.error(`Error building module:`, e);
|
|
2729
|
+
if (!e.stdout) process.exit(1);
|
|
2730
|
+
const out = e.stdout.toString();
|
|
2731
|
+
const err = e.stderr.toString();
|
|
2732
|
+
console.log(out);
|
|
2733
|
+
console.error(err);
|
|
2734
|
+
if (out.includes("Types have separate declarations of a private property '_name'.")) {
|
|
2735
|
+
const pgkErrMatch = /error TS2322: .*? 'import\("\/.*?\/node_modules\/(.*?)\/node_modules/g;
|
|
2736
|
+
out.matchAll(pgkErrMatch);
|
|
2737
|
+
const conflicts = [...out.matchAll(pgkErrMatch)];
|
|
2738
|
+
if (conflicts.length < 1) {
|
|
2739
|
+
console.info(
|
|
2740
|
+
`
|
|
2701
2741
|
One or more imported Pepr Capabilities seem to be using an incompatible version of Pepr.
|
|
2702
2742
|
Try updating your Pepr Capabilities to their latest versions.`,
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2743
|
+
"Version Conflict"
|
|
2744
|
+
);
|
|
2745
|
+
}
|
|
2746
|
+
conflicts.forEach((match) => {
|
|
2747
|
+
console.info(
|
|
2748
|
+
`
|
|
2709
2749
|
Package '${match[1]}' seems to be incompatible with your current version of Pepr.
|
|
2710
2750
|
Try updating to the latest version.`,
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2751
|
+
"Version Conflict"
|
|
2752
|
+
);
|
|
2753
|
+
});
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
async function checkFormat() {
|
|
2757
|
+
const validFormat = await peprFormat(true);
|
|
2758
|
+
if (!validFormat) {
|
|
2759
|
+
console.log(
|
|
2760
|
+
"\x1B[33m%s\x1B[0m",
|
|
2761
|
+
"Formatting errors were found. The build will continue, but you may want to run `npx pepr format` to address any issues."
|
|
2762
|
+
);
|
|
2715
2763
|
}
|
|
2716
2764
|
}
|
|
2717
2765
|
|
|
2718
2766
|
// src/cli/deploy.ts
|
|
2719
2767
|
var import_prompts = __toESM(require("prompts"));
|
|
2768
|
+
|
|
2769
|
+
// src/lib/deploymentChecks.ts
|
|
2770
|
+
var import_kubernetes_fluent_client4 = require("kubernetes-fluent-client");
|
|
2771
|
+
async function checkDeploymentStatus(namespace) {
|
|
2772
|
+
const deployments = await (0, import_kubernetes_fluent_client4.K8s)(import_kubernetes_fluent_client4.kind.Deployment).InNamespace(namespace).Get();
|
|
2773
|
+
let status = false;
|
|
2774
|
+
let readyCount = 0;
|
|
2775
|
+
for (const deployment of deployments.items) {
|
|
2776
|
+
const readyReplicas = deployment.status?.readyReplicas ? deployment.status?.readyReplicas : 0;
|
|
2777
|
+
if (deployment.status?.readyReplicas !== deployment.spec?.replicas) {
|
|
2778
|
+
logger_default.info(
|
|
2779
|
+
`Waiting for deployment ${deployment.metadata?.name} rollout to finish: ${readyReplicas} of ${deployment.spec?.replicas} replicas are available`
|
|
2780
|
+
);
|
|
2781
|
+
} else {
|
|
2782
|
+
logger_default.info(
|
|
2783
|
+
`Deployment ${deployment.metadata?.name} rolled out: ${readyReplicas} of ${deployment.spec?.replicas} replicas are available`
|
|
2784
|
+
);
|
|
2785
|
+
readyCount++;
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
if (readyCount === deployments.items.length) {
|
|
2789
|
+
status = true;
|
|
2790
|
+
}
|
|
2791
|
+
return status;
|
|
2792
|
+
}
|
|
2793
|
+
async function namespaceDeploymentsReady(namespace = "pepr-system") {
|
|
2794
|
+
logger_default.info(`Checking ${namespace} deployments status...`);
|
|
2795
|
+
let ready = false;
|
|
2796
|
+
while (!ready) {
|
|
2797
|
+
ready = await checkDeploymentStatus(namespace);
|
|
2798
|
+
if (ready) {
|
|
2799
|
+
return ready;
|
|
2800
|
+
}
|
|
2801
|
+
await new Promise((resolve6) => setTimeout(resolve6, 1e3));
|
|
2802
|
+
}
|
|
2803
|
+
logger_default.info(`All ${namespace} deployments are ready`);
|
|
2804
|
+
}
|
|
2805
|
+
|
|
2806
|
+
// src/cli/deploy.ts
|
|
2720
2807
|
function deploy_default(program2) {
|
|
2721
2808
|
program2.command("deploy").description("Deploy a Pepr Module").option("-i, --image [image]", "Override the image tag").option("--confirm", "Skip confirmation prompt").option("--pullSecret <name>", "Deploy imagePullSecret for Controller private registry").option("--docker-server <server>", "Docker server address").option("--docker-username <username>", "Docker registry username").option("--docker-email <email>", "Email for Docker registry").option("--docker-password <password>", "Password for Docker registry").option("--force", "Force deploy the module, override manager field").action(async (opts) => {
|
|
2722
2809
|
let imagePullSecret;
|
|
@@ -2782,8 +2869,8 @@ function deploy_default(program2) {
|
|
|
2782
2869
|
}
|
|
2783
2870
|
|
|
2784
2871
|
// src/cli/dev.ts
|
|
2785
|
-
var
|
|
2786
|
-
var
|
|
2872
|
+
var import_child_process4 = require("child_process");
|
|
2873
|
+
var import_fs10 = require("fs");
|
|
2787
2874
|
var import_prompts2 = __toESM(require("prompts"));
|
|
2788
2875
|
var import_kubernetes_fluent_client5 = require("kubernetes-fluent-client");
|
|
2789
2876
|
function dev_default(program2) {
|
|
@@ -2807,8 +2894,8 @@ function dev_default(program2) {
|
|
|
2807
2894
|
path,
|
|
2808
2895
|
opts.host
|
|
2809
2896
|
);
|
|
2810
|
-
await
|
|
2811
|
-
await
|
|
2897
|
+
await import_fs10.promises.writeFile("insecure-tls.crt", webhook.tls.pem.crt);
|
|
2898
|
+
await import_fs10.promises.writeFile("insecure-tls.key", webhook.tls.pem.key);
|
|
2812
2899
|
try {
|
|
2813
2900
|
let program3;
|
|
2814
2901
|
const name2 = `pepr-${cfg.pepr.uuid}`;
|
|
@@ -2823,7 +2910,7 @@ function dev_default(program2) {
|
|
|
2823
2910
|
console.error(`Error validating capability names:`, e);
|
|
2824
2911
|
process.exit(1);
|
|
2825
2912
|
}
|
|
2826
|
-
program3 = (0,
|
|
2913
|
+
program3 = (0, import_child_process4.fork)(path, {
|
|
2827
2914
|
env: {
|
|
2828
2915
|
...process.env,
|
|
2829
2916
|
LOG_LEVEL: "debug",
|
|
@@ -2872,59 +2959,15 @@ var import_kubernetes_fluent_client6 = require("kubernetes-fluent-client");
|
|
|
2872
2959
|
var import_stream = __toESM(require("stream"));
|
|
2873
2960
|
function monitor_default(program2) {
|
|
2874
2961
|
program2.command("monitor [module-uuid]").description("Monitor a Pepr Module").action(async (uuid) => {
|
|
2875
|
-
|
|
2876
|
-
let errorMessage;
|
|
2877
|
-
if (!uuid) {
|
|
2878
|
-
labels = ["pepr.dev/controller", "admission"];
|
|
2879
|
-
errorMessage = `No pods found with admission labels`;
|
|
2880
|
-
} else {
|
|
2881
|
-
labels = ["app", `pepr-${uuid}`];
|
|
2882
|
-
errorMessage = `No pods found for module ${uuid}`;
|
|
2883
|
-
}
|
|
2962
|
+
const { labels, errorMessage } = getLabelsAndErrorMessage(uuid);
|
|
2884
2963
|
const pods = await (0, import_kubernetes_fluent_client6.K8s)(import_kubernetes_fluent_client6.kind.Pod).InNamespace("pepr-system").WithLabel(labels[0], labels[1]).Get();
|
|
2885
|
-
const podNames = pods.items.flatMap((pod) => pod.metadata.name);
|
|
2964
|
+
const podNames = pods.items.flatMap((pod) => pod.metadata.name || "");
|
|
2886
2965
|
if (podNames.length < 1) {
|
|
2887
2966
|
console.error(errorMessage);
|
|
2888
2967
|
process.exit(1);
|
|
2889
2968
|
}
|
|
2890
|
-
const
|
|
2891
|
-
|
|
2892
|
-
const log = new import_client_node4.Log(kc);
|
|
2893
|
-
const logStream = new import_stream.default.PassThrough();
|
|
2894
|
-
logStream.on("data", async (chunk) => {
|
|
2895
|
-
const respMsg = `"msg":"Check response"`;
|
|
2896
|
-
const lines = await chunk.toString().split("\n");
|
|
2897
|
-
for (const line of lines) {
|
|
2898
|
-
if (!line.includes(respMsg)) continue;
|
|
2899
|
-
try {
|
|
2900
|
-
const payload = JSON.parse(line.trim());
|
|
2901
|
-
const isMutate = payload.res.patchType || payload.res.warnings;
|
|
2902
|
-
const name2 = `${payload.namespace}${payload.name}`;
|
|
2903
|
-
const uid = payload.res.uid;
|
|
2904
|
-
if (isMutate) {
|
|
2905
|
-
const plainPatch = payload.res?.patch !== void 0 && payload.res?.patch !== null ? atob(payload.res.patch) : "";
|
|
2906
|
-
const patch = plainPatch !== "" && JSON.stringify(JSON.parse(plainPatch), null, 2);
|
|
2907
|
-
const patchType = payload.res.patchType || payload.res.warnings || "";
|
|
2908
|
-
const allowOrDeny = payload.res.allowed ? "\u{1F500}" : "\u{1F6AB}";
|
|
2909
|
-
console.log(`
|
|
2910
|
-
${allowOrDeny} MUTATE ${name2} (${uid})`);
|
|
2911
|
-
patchType.length > 0 && console.log(`
|
|
2912
|
-
\x1B[1;34m${patch}\x1B[0m`);
|
|
2913
|
-
} else {
|
|
2914
|
-
const failures = Array.isArray(payload.res) ? payload.res : [payload.res];
|
|
2915
|
-
const filteredFailures = failures.filter((r) => !r.allowed).map((r) => r.status.message);
|
|
2916
|
-
console.log(
|
|
2917
|
-
`
|
|
2918
|
-
${filteredFailures.length > 0 ? "\u274C" : "\u2705"} VALIDATE ${name2} (${uid})`
|
|
2919
|
-
);
|
|
2920
|
-
console.log(
|
|
2921
|
-
filteredFailures.length > 0 ? `\x1B[1;31m${filteredFailures}\x1B[0m` : ""
|
|
2922
|
-
);
|
|
2923
|
-
}
|
|
2924
|
-
} catch {
|
|
2925
|
-
}
|
|
2926
|
-
}
|
|
2927
|
-
});
|
|
2969
|
+
const log = getK8sLogFromKubeConfig();
|
|
2970
|
+
const logStream = createLogStream();
|
|
2928
2971
|
for (const podName of podNames) {
|
|
2929
2972
|
await log.log("pepr-system", podName, "server", logStream, {
|
|
2930
2973
|
follow: true,
|
|
@@ -2934,14 +2977,78 @@ ${filteredFailures.length > 0 ? "\u274C" : "\u2705"} VALIDATE ${name2} (${uid
|
|
|
2934
2977
|
}
|
|
2935
2978
|
});
|
|
2936
2979
|
}
|
|
2980
|
+
function getLabelsAndErrorMessage(uuid) {
|
|
2981
|
+
let labels;
|
|
2982
|
+
let errorMessage;
|
|
2983
|
+
if (!uuid) {
|
|
2984
|
+
labels = ["pepr.dev/controller", "admission"];
|
|
2985
|
+
errorMessage = `No pods found with admission labels`;
|
|
2986
|
+
} else {
|
|
2987
|
+
labels = ["app", `pepr-${uuid}`];
|
|
2988
|
+
errorMessage = `No pods found for module ${uuid}`;
|
|
2989
|
+
}
|
|
2990
|
+
return { labels, errorMessage };
|
|
2991
|
+
}
|
|
2992
|
+
function getK8sLogFromKubeConfig() {
|
|
2993
|
+
const kc = new import_client_node4.KubeConfig();
|
|
2994
|
+
kc.loadFromDefault();
|
|
2995
|
+
return new import_client_node4.Log(kc);
|
|
2996
|
+
}
|
|
2997
|
+
function createLogStream() {
|
|
2998
|
+
const logStream = new import_stream.default.PassThrough();
|
|
2999
|
+
logStream.on("data", async (chunk) => {
|
|
3000
|
+
const lines = chunk.toString().split("\n");
|
|
3001
|
+
const respMsg = `"msg":"Check response"`;
|
|
3002
|
+
for (const line of lines) {
|
|
3003
|
+
if (!line.includes(respMsg)) continue;
|
|
3004
|
+
processLogLine(line);
|
|
3005
|
+
}
|
|
3006
|
+
});
|
|
3007
|
+
return logStream;
|
|
3008
|
+
}
|
|
3009
|
+
function processLogLine(line) {
|
|
3010
|
+
try {
|
|
3011
|
+
const payload = JSON.parse(line.trim());
|
|
3012
|
+
const isMutate = payload.res.patchType || payload.res.warnings;
|
|
3013
|
+
const name2 = `${payload.namespace}${payload.name}`;
|
|
3014
|
+
const uid = payload.res.uid;
|
|
3015
|
+
if (isMutate) {
|
|
3016
|
+
processMutateLog(payload, name2, uid);
|
|
3017
|
+
} else {
|
|
3018
|
+
processValidateLog(payload, name2, uid);
|
|
3019
|
+
}
|
|
3020
|
+
} catch {
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
function processMutateLog(payload, name2, uid) {
|
|
3024
|
+
const plainPatch = payload.res.patch !== void 0 && payload.res.patch !== null ? atob(payload.res.patch) : "";
|
|
3025
|
+
const patch = plainPatch !== "" && JSON.stringify(JSON.parse(plainPatch), null, 2);
|
|
3026
|
+
const patchType = payload.res.patchType || payload.res.warnings || "";
|
|
3027
|
+
const allowOrDeny = payload.res.allowed ? "\u{1F500}" : "\u{1F6AB}";
|
|
3028
|
+
console.log(`
|
|
3029
|
+
${allowOrDeny} MUTATE ${name2} (${uid})`);
|
|
3030
|
+
if (patchType.length > 0) {
|
|
3031
|
+
console.log(`
|
|
3032
|
+
\x1B[1;34m${patch}\x1B[0m`);
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
function processValidateLog(payload, name2, uid) {
|
|
3036
|
+
const failures = Array.isArray(payload.res) ? payload.res : [payload.res];
|
|
3037
|
+
const filteredFailures = failures.filter((r) => !r.allowed).map((r) => r.status?.message || "");
|
|
3038
|
+
console.log(`
|
|
3039
|
+
${filteredFailures.length > 0 ? "\u274C" : "\u2705"} VALIDATE ${name2} (${uid})`);
|
|
3040
|
+
if (filteredFailures.length > 0) {
|
|
3041
|
+
console.log(`\x1B[1;31m${filteredFailures}\x1B[0m`);
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
2937
3044
|
|
|
2938
3045
|
// src/cli/init/index.ts
|
|
2939
|
-
var
|
|
2940
|
-
var
|
|
3046
|
+
var import_child_process5 = require("child_process");
|
|
3047
|
+
var import_path4 = require("path");
|
|
2941
3048
|
var import_prompts4 = __toESM(require("prompts"));
|
|
2942
3049
|
|
|
2943
3050
|
// src/cli/init/walkthrough.ts
|
|
2944
|
-
var
|
|
3051
|
+
var import_fs11 = require("fs");
|
|
2945
3052
|
var import_prompts3 = __toESM(require("prompts"));
|
|
2946
3053
|
|
|
2947
3054
|
// src/lib/errors.ts
|
|
@@ -2969,7 +3076,7 @@ async function setName(name2) {
|
|
|
2969
3076
|
validate: async (val) => {
|
|
2970
3077
|
try {
|
|
2971
3078
|
const name3 = sanitizeName(val);
|
|
2972
|
-
await
|
|
3079
|
+
await import_fs11.promises.access(name3, import_fs11.promises.constants.F_OK);
|
|
2973
3080
|
return "A directory with this name already exists";
|
|
2974
3081
|
} catch (e) {
|
|
2975
3082
|
return val.length > 2 || "The name must be at least 3 characters long";
|
|
@@ -3083,19 +3190,19 @@ function init_default(program2) {
|
|
|
3083
3190
|
console.log("Creating new Pepr module...");
|
|
3084
3191
|
try {
|
|
3085
3192
|
await createDir(dirName);
|
|
3086
|
-
await createDir((0,
|
|
3087
|
-
await createDir((0,
|
|
3088
|
-
await write((0,
|
|
3089
|
-
await write((0,
|
|
3090
|
-
await write((0,
|
|
3091
|
-
await write((0,
|
|
3092
|
-
await write((0,
|
|
3093
|
-
await write((0,
|
|
3094
|
-
await write((0,
|
|
3095
|
-
await write((0,
|
|
3096
|
-
await write((0,
|
|
3097
|
-
await write((0,
|
|
3098
|
-
await write((0,
|
|
3193
|
+
await createDir((0, import_path4.resolve)(dirName, ".vscode"));
|
|
3194
|
+
await createDir((0, import_path4.resolve)(dirName, "capabilities"));
|
|
3195
|
+
await write((0, import_path4.resolve)(dirName, gitignore.path), gitignore.data);
|
|
3196
|
+
await write((0, import_path4.resolve)(dirName, eslint.path), eslint.data);
|
|
3197
|
+
await write((0, import_path4.resolve)(dirName, prettier.path), prettier.data);
|
|
3198
|
+
await write((0, import_path4.resolve)(dirName, packageJSON2.path), packageJSON2.data);
|
|
3199
|
+
await write((0, import_path4.resolve)(dirName, readme.path), readme.data);
|
|
3200
|
+
await write((0, import_path4.resolve)(dirName, tsConfig.path), tsConfig.data);
|
|
3201
|
+
await write((0, import_path4.resolve)(dirName, peprTS3.path), peprTS3.data);
|
|
3202
|
+
await write((0, import_path4.resolve)(dirName, ".vscode", snippet.path), snippet.data);
|
|
3203
|
+
await write((0, import_path4.resolve)(dirName, ".vscode", codeSettings.path), codeSettings.data);
|
|
3204
|
+
await write((0, import_path4.resolve)(dirName, "capabilities", samplesYaml.path), samplesYaml.data);
|
|
3205
|
+
await write((0, import_path4.resolve)(dirName, "capabilities", helloPepr.path), helloPepr.data);
|
|
3099
3206
|
if (!opts.skipPostInit) {
|
|
3100
3207
|
doPostInitActions(dirName);
|
|
3101
3208
|
}
|
|
@@ -3112,14 +3219,14 @@ function init_default(program2) {
|
|
|
3112
3219
|
}
|
|
3113
3220
|
var doPostInitActions = (dirName) => {
|
|
3114
3221
|
process.chdir(dirName);
|
|
3115
|
-
(0,
|
|
3222
|
+
(0, import_child_process5.execSync)("npm install", {
|
|
3116
3223
|
stdio: "inherit"
|
|
3117
3224
|
});
|
|
3118
|
-
(0,
|
|
3225
|
+
(0, import_child_process5.execSync)("git init --initial-branch=main", {
|
|
3119
3226
|
stdio: "inherit"
|
|
3120
3227
|
});
|
|
3121
3228
|
try {
|
|
3122
|
-
(0,
|
|
3229
|
+
(0, import_child_process5.execSync)("code .", {
|
|
3123
3230
|
stdio: "inherit"
|
|
3124
3231
|
});
|
|
3125
3232
|
} catch (e) {
|
|
@@ -3163,9 +3270,9 @@ var RootCmd = class extends import_commander2.Command {
|
|
|
3163
3270
|
};
|
|
3164
3271
|
|
|
3165
3272
|
// src/cli/update.ts
|
|
3166
|
-
var
|
|
3167
|
-
var
|
|
3168
|
-
var
|
|
3273
|
+
var import_child_process6 = require("child_process");
|
|
3274
|
+
var import_fs12 = __toESM(require("fs"));
|
|
3275
|
+
var import_path5 = require("path");
|
|
3169
3276
|
var import_prompts5 = __toESM(require("prompts"));
|
|
3170
3277
|
function update_default(program2) {
|
|
3171
3278
|
program2.command("update").description("Update this Pepr module. Not recommended for prod as it may change files.").option("--skip-template-update", "Skip updating the template files").action(async (opts) => {
|
|
@@ -3181,11 +3288,11 @@ function update_default(program2) {
|
|
|
3181
3288
|
}
|
|
3182
3289
|
console.log("Updating the Pepr module...");
|
|
3183
3290
|
try {
|
|
3184
|
-
(0,
|
|
3291
|
+
(0, import_child_process6.execSync)("npm install pepr@latest", {
|
|
3185
3292
|
stdio: "inherit"
|
|
3186
3293
|
});
|
|
3187
3294
|
if (!opts.skipTemplateUpdate) {
|
|
3188
|
-
(0,
|
|
3295
|
+
(0, import_child_process6.execSync)("npx pepr update-templates", {
|
|
3189
3296
|
stdio: "inherit"
|
|
3190
3297
|
});
|
|
3191
3298
|
}
|
|
@@ -3199,17 +3306,17 @@ function update_default(program2) {
|
|
|
3199
3306
|
console.log("Updating Pepr config and template tiles...");
|
|
3200
3307
|
try {
|
|
3201
3308
|
if (!opts.skipTemplateUpdate) {
|
|
3202
|
-
await write((0,
|
|
3203
|
-
await write((0,
|
|
3204
|
-
await write((0,
|
|
3205
|
-
await write((0,
|
|
3206
|
-
const samplePath = (0,
|
|
3207
|
-
if (
|
|
3208
|
-
|
|
3309
|
+
await write((0, import_path5.resolve)(prettier.path), prettier.data);
|
|
3310
|
+
await write((0, import_path5.resolve)(tsConfig.path), tsConfig.data);
|
|
3311
|
+
await write((0, import_path5.resolve)(".vscode", snippet.path), snippet.data);
|
|
3312
|
+
await write((0, import_path5.resolve)(".vscode", codeSettings.path), codeSettings.data);
|
|
3313
|
+
const samplePath = (0, import_path5.resolve)("capabilities", samplesYaml.path);
|
|
3314
|
+
if (import_fs12.default.existsSync(samplePath)) {
|
|
3315
|
+
import_fs12.default.unlinkSync(samplePath);
|
|
3209
3316
|
await write(samplePath, samplesYaml.data);
|
|
3210
3317
|
}
|
|
3211
|
-
const tsPath = (0,
|
|
3212
|
-
if (
|
|
3318
|
+
const tsPath = (0, import_path5.resolve)("capabilities", helloPepr.path);
|
|
3319
|
+
if (import_fs12.default.existsSync(tsPath)) {
|
|
3213
3320
|
await write(tsPath, helloPepr.data);
|
|
3214
3321
|
}
|
|
3215
3322
|
}
|
|
@@ -3221,7 +3328,7 @@ function update_default(program2) {
|
|
|
3221
3328
|
}
|
|
3222
3329
|
|
|
3223
3330
|
// src/cli/kfc.ts
|
|
3224
|
-
var
|
|
3331
|
+
var import_child_process7 = require("child_process");
|
|
3225
3332
|
var import_prompts6 = __toESM(require("prompts"));
|
|
3226
3333
|
function kfc_default(program2) {
|
|
3227
3334
|
program2.command("kfc [args...]").description("Execute Kubernetes Fluent Client commands").action(async (args) => {
|
|
@@ -3238,7 +3345,7 @@ function kfc_default(program2) {
|
|
|
3238
3345
|
args.push("--help");
|
|
3239
3346
|
}
|
|
3240
3347
|
const argsString = args.join(" ");
|
|
3241
|
-
(0,
|
|
3348
|
+
(0, import_child_process7.execSync)(`kubernetes-fluent-client ${argsString}`, {
|
|
3242
3349
|
stdio: "inherit"
|
|
3243
3350
|
});
|
|
3244
3351
|
} catch (e) {
|