pepr 0.42.3 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/build.d.ts +25 -11
- package/dist/cli/build.d.ts.map +1 -1
- package/dist/cli/build.helpers.d.ts +0 -7
- package/dist/cli/build.helpers.d.ts.map +1 -1
- package/dist/cli/deploy.d.ts.map +1 -1
- package/dist/cli/dev.d.ts.map +1 -1
- package/dist/cli/init/templates.d.ts +3 -0
- package/dist/cli/init/templates.d.ts.map +1 -1
- package/dist/cli.js +742 -710
- package/dist/controller.js +1 -1
- package/dist/fixtures/loader.d.ts.map +1 -1
- package/dist/lib/assets/assets.d.ts +15 -12
- package/dist/lib/assets/assets.d.ts.map +1 -1
- package/dist/lib/assets/deploy.d.ts +1 -1
- package/dist/lib/assets/deploy.d.ts.map +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/webhooks.d.ts +4 -1
- package/dist/lib/assets/webhooks.d.ts.map +1 -1
- package/dist/lib/assets/yaml/generateAllYaml.d.ts +9 -0
- package/dist/lib/assets/yaml/generateAllYaml.d.ts.map +1 -0
- package/dist/lib/assets/yaml/generateZarfYaml.d.ts +5 -0
- package/dist/lib/assets/yaml/generateZarfYaml.d.ts.map +1 -0
- package/dist/lib/assets/yaml/overridesFile.d.ts +15 -0
- package/dist/lib/assets/yaml/overridesFile.d.ts.map +1 -0
- package/dist/lib/core/module.d.ts.map +1 -1
- package/dist/lib/enums.d.ts +4 -0
- package/dist/lib/enums.d.ts.map +1 -1
- package/dist/lib/processors/mutate-processor.d.ts +2 -0
- package/dist/lib/processors/mutate-processor.d.ts.map +1 -1
- package/dist/lib/processors/validate-processor.d.ts.map +1 -1
- package/dist/lib/telemetry/timeUtils.d.ts +2 -0
- package/dist/lib/telemetry/timeUtils.d.ts.map +1 -0
- package/dist/lib/telemetry/webhookTimeouts.d.ts +9 -0
- package/dist/lib/telemetry/webhookTimeouts.d.ts.map +1 -0
- package/dist/lib.js +79 -22
- package/dist/lib.js.map +4 -4
- package/package.json +5 -2
- package/src/cli/build.helpers.ts +7 -22
- package/src/cli/build.ts +72 -16
- package/src/cli/deploy.ts +7 -6
- package/src/cli/dev.ts +9 -6
- package/src/fixtures/loader.ts +2 -2
- package/src/lib/assets/assets.ts +66 -53
- package/src/lib/assets/deploy.ts +32 -30
- package/src/lib/assets/helm.ts +22 -4
- package/src/lib/assets/index.ts +33 -9
- package/src/lib/assets/pods.ts +2 -2
- package/src/lib/assets/webhooks.ts +21 -6
- package/src/lib/assets/yaml/generateAllYaml.ts +50 -0
- package/src/lib/assets/yaml/generateZarfYaml.ts +38 -0
- package/src/lib/assets/{yaml.ts → yaml/overridesFile.ts} +8 -120
- package/src/lib/core/module.ts +2 -1
- package/src/lib/enums.ts +6 -0
- package/src/lib/processors/mutate-processor.ts +15 -7
- package/src/lib/processors/validate-processor.ts +13 -4
- package/src/lib/telemetry/timeUtils.ts +1 -0
- package/src/lib/telemetry/webhookTimeouts.ts +34 -0
- package/dist/lib/assets/yaml.d.ts +0 -32
- package/dist/lib/assets/yaml.d.ts.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -92,7 +92,7 @@ var import_fs9 = require("fs");
|
|
|
92
92
|
var import_path3 = require("path");
|
|
93
93
|
|
|
94
94
|
// src/lib/assets/assets.ts
|
|
95
|
-
var
|
|
95
|
+
var import_crypto = __toESM(require("crypto"));
|
|
96
96
|
|
|
97
97
|
// src/lib/tls.ts
|
|
98
98
|
var import_node_forge = __toESM(require("node-forge"));
|
|
@@ -247,8 +247,13 @@ function watcherDeployTemplate(buildTimestamp) {
|
|
|
247
247
|
- name: watcher
|
|
248
248
|
image: {{ .Values.watcher.image }}
|
|
249
249
|
imagePullPolicy: IfNotPresent
|
|
250
|
-
|
|
251
|
-
|
|
250
|
+
{{- if gt (len .Values.imagePullSecrets) 0 }}
|
|
251
|
+
imagePullSecrets:
|
|
252
|
+
{{- range .Values.imagePullSecrets }}
|
|
253
|
+
- name: {{ . }}
|
|
254
|
+
{{- end }}
|
|
255
|
+
{{- end }}
|
|
256
|
+
args:
|
|
252
257
|
- /app/node_modules/pepr/dist/controller.js
|
|
253
258
|
- {{ .Values.hash }}
|
|
254
259
|
readinessProbe:
|
|
@@ -263,6 +268,10 @@ function watcherDeployTemplate(buildTimestamp) {
|
|
|
263
268
|
{{- toYaml .Values.watcher.env | nindent 12 }}
|
|
264
269
|
- name: PEPR_WATCH_MODE
|
|
265
270
|
value: "true"
|
|
271
|
+
{{- if .Values.additionalIgnoredNamespaces }}
|
|
272
|
+
- name: PEPR_ADDITIONAL_IGNORED_NAMESPACES
|
|
273
|
+
value: "{{ join ", " .Values.additionalIgnoredNamespaces }}"
|
|
274
|
+
{{- end }}
|
|
266
275
|
envFrom:
|
|
267
276
|
{{- toYaml .Values.watcher.envFrom | nindent 12 }}
|
|
268
277
|
securityContext:
|
|
@@ -326,8 +335,13 @@ function admissionDeployTemplate(buildTimestamp) {
|
|
|
326
335
|
- name: server
|
|
327
336
|
image: {{ .Values.admission.image }}
|
|
328
337
|
imagePullPolicy: IfNotPresent
|
|
329
|
-
|
|
330
|
-
|
|
338
|
+
{{- if gt (len .Values.imagePullSecrets) 0 }}
|
|
339
|
+
imagePullSecrets:
|
|
340
|
+
{{- range .Values.imagePullSecrets }}
|
|
341
|
+
- name: {{ . }}
|
|
342
|
+
{{- end }}
|
|
343
|
+
{{- end }}
|
|
344
|
+
args:
|
|
331
345
|
- /app/node_modules/pepr/dist/controller.js
|
|
332
346
|
- {{ .Values.hash }}
|
|
333
347
|
readinessProbe:
|
|
@@ -342,6 +356,10 @@ function admissionDeployTemplate(buildTimestamp) {
|
|
|
342
356
|
{{- toYaml .Values.admission.env | nindent 12 }}
|
|
343
357
|
- name: PEPR_WATCH_MODE
|
|
344
358
|
value: "false"
|
|
359
|
+
{{- if .Values.additionalIgnoredNamespaces }}
|
|
360
|
+
- name: PEPR_ADDITIONAL_IGNORED_NAMESPACES
|
|
361
|
+
value: "{{ join ", " .Values.additionalIgnoredNamespaces }}"
|
|
362
|
+
{{- end }}
|
|
345
363
|
envFrom:
|
|
346
364
|
{{- toYaml .Values.admission.envFrom | nindent 12 }}
|
|
347
365
|
securityContext:
|
|
@@ -415,10 +433,8 @@ async function createDirectoryIfNotExists(path) {
|
|
|
415
433
|
}
|
|
416
434
|
}
|
|
417
435
|
|
|
418
|
-
// src/lib/assets/
|
|
419
|
-
var
|
|
420
|
-
var import_fs2 = require("fs");
|
|
421
|
-
var import_kubernetes_fluent_client3 = require("kubernetes-fluent-client");
|
|
436
|
+
// src/lib/assets/pods.ts
|
|
437
|
+
var import_zlib = require("zlib");
|
|
422
438
|
|
|
423
439
|
// src/lib/telemetry/logger.ts
|
|
424
440
|
var import_pino = require("pino");
|
|
@@ -440,90 +456,6 @@ if (process.env.LOG_LEVEL) {
|
|
|
440
456
|
}
|
|
441
457
|
var logger_default = Log;
|
|
442
458
|
|
|
443
|
-
// src/lib/assets/networking.ts
|
|
444
|
-
function apiTokenSecret(name2, apiToken) {
|
|
445
|
-
return {
|
|
446
|
-
apiVersion: "v1",
|
|
447
|
-
kind: "Secret",
|
|
448
|
-
metadata: {
|
|
449
|
-
name: `${name2}-api-token`,
|
|
450
|
-
namespace: "pepr-system"
|
|
451
|
-
},
|
|
452
|
-
type: "Opaque",
|
|
453
|
-
data: {
|
|
454
|
-
value: Buffer.from(apiToken).toString("base64")
|
|
455
|
-
}
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
function tlsSecret(name2, tls) {
|
|
459
|
-
return {
|
|
460
|
-
apiVersion: "v1",
|
|
461
|
-
kind: "Secret",
|
|
462
|
-
metadata: {
|
|
463
|
-
name: `${name2}-tls`,
|
|
464
|
-
namespace: "pepr-system"
|
|
465
|
-
},
|
|
466
|
-
type: "kubernetes.io/tls",
|
|
467
|
-
data: {
|
|
468
|
-
"tls.crt": tls.crt,
|
|
469
|
-
"tls.key": tls.key
|
|
470
|
-
}
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
function service(name2) {
|
|
474
|
-
return {
|
|
475
|
-
apiVersion: "v1",
|
|
476
|
-
kind: "Service",
|
|
477
|
-
metadata: {
|
|
478
|
-
name: name2,
|
|
479
|
-
namespace: "pepr-system",
|
|
480
|
-
labels: {
|
|
481
|
-
"pepr.dev/controller": "admission"
|
|
482
|
-
}
|
|
483
|
-
},
|
|
484
|
-
spec: {
|
|
485
|
-
selector: {
|
|
486
|
-
app: name2,
|
|
487
|
-
"pepr.dev/controller": "admission"
|
|
488
|
-
},
|
|
489
|
-
ports: [
|
|
490
|
-
{
|
|
491
|
-
port: 443,
|
|
492
|
-
targetPort: 3e3
|
|
493
|
-
}
|
|
494
|
-
]
|
|
495
|
-
}
|
|
496
|
-
};
|
|
497
|
-
}
|
|
498
|
-
function watcherService(name2) {
|
|
499
|
-
return {
|
|
500
|
-
apiVersion: "v1",
|
|
501
|
-
kind: "Service",
|
|
502
|
-
metadata: {
|
|
503
|
-
name: `${name2}-watcher`,
|
|
504
|
-
namespace: "pepr-system",
|
|
505
|
-
labels: {
|
|
506
|
-
"pepr.dev/controller": "watcher"
|
|
507
|
-
}
|
|
508
|
-
},
|
|
509
|
-
spec: {
|
|
510
|
-
selector: {
|
|
511
|
-
app: `${name2}-watcher`,
|
|
512
|
-
"pepr.dev/controller": "watcher"
|
|
513
|
-
},
|
|
514
|
-
ports: [
|
|
515
|
-
{
|
|
516
|
-
port: 443,
|
|
517
|
-
targetPort: 3e3
|
|
518
|
-
}
|
|
519
|
-
]
|
|
520
|
-
}
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// src/lib/assets/pods.ts
|
|
525
|
-
var import_zlib = require("zlib");
|
|
526
|
-
|
|
527
459
|
// src/sdk/sdk.ts
|
|
528
460
|
var import_kubernetes_fluent_client = require("kubernetes-fluent-client");
|
|
529
461
|
function sanitizeResourceName(name2) {
|
|
@@ -727,7 +659,7 @@ function getWatcher(assets, hash, buildTimestamp, imagePullSecret) {
|
|
|
727
659
|
if (bindings.length < 1 && !hasSchedule) {
|
|
728
660
|
return null;
|
|
729
661
|
}
|
|
730
|
-
const
|
|
662
|
+
const deploy = {
|
|
731
663
|
apiVersion: "apps/v1",
|
|
732
664
|
kind: "Deployment",
|
|
733
665
|
metadata: {
|
|
@@ -777,7 +709,7 @@ function getWatcher(assets, hash, buildTimestamp, imagePullSecret) {
|
|
|
777
709
|
name: "watcher",
|
|
778
710
|
image,
|
|
779
711
|
imagePullPolicy: "IfNotPresent",
|
|
780
|
-
|
|
712
|
+
args: ["/app/node_modules/pepr/dist/controller.js", hash],
|
|
781
713
|
readinessProbe: {
|
|
782
714
|
httpGet: {
|
|
783
715
|
path: "/healthz",
|
|
@@ -852,14 +784,14 @@ function getWatcher(assets, hash, buildTimestamp, imagePullSecret) {
|
|
|
852
784
|
}
|
|
853
785
|
};
|
|
854
786
|
if (imagePullSecret) {
|
|
855
|
-
|
|
787
|
+
deploy.spec.template.spec.imagePullSecrets = [{ name: imagePullSecret }];
|
|
856
788
|
}
|
|
857
|
-
return
|
|
789
|
+
return deploy;
|
|
858
790
|
}
|
|
859
791
|
function getDeployment(assets, hash, buildTimestamp, imagePullSecret) {
|
|
860
792
|
const { name: name2, image, config } = assets;
|
|
861
793
|
const app = name2;
|
|
862
|
-
const
|
|
794
|
+
const deploy = {
|
|
863
795
|
apiVersion: "apps/v1",
|
|
864
796
|
kind: "Deployment",
|
|
865
797
|
metadata: {
|
|
@@ -907,7 +839,7 @@ function getDeployment(assets, hash, buildTimestamp, imagePullSecret) {
|
|
|
907
839
|
name: "server",
|
|
908
840
|
image,
|
|
909
841
|
imagePullPolicy: "IfNotPresent",
|
|
910
|
-
|
|
842
|
+
args: ["/app/node_modules/pepr/dist/controller.js", hash],
|
|
911
843
|
readinessProbe: {
|
|
912
844
|
httpGet: {
|
|
913
845
|
path: "/healthz",
|
|
@@ -993,9 +925,9 @@ function getDeployment(assets, hash, buildTimestamp, imagePullSecret) {
|
|
|
993
925
|
}
|
|
994
926
|
};
|
|
995
927
|
if (imagePullSecret) {
|
|
996
|
-
|
|
928
|
+
deploy.spec.template.spec.imagePullSecrets = [{ name: imagePullSecret }];
|
|
997
929
|
}
|
|
998
|
-
return
|
|
930
|
+
return deploy;
|
|
999
931
|
}
|
|
1000
932
|
function getModuleSecret(name2, data, hash) {
|
|
1001
933
|
const compressed = (0, import_zlib.gzipSync)(data);
|
|
@@ -1036,6 +968,9 @@ function genEnv(config, watchMode = false, ignoreWatchMode = false) {
|
|
|
1036
968
|
return ignoreWatchMode ? Object.entries({ ...noWatchDef, ...cfg }).map(([name2, value]) => ({ name: name2, value })) : Object.entries({ ...def, ...cfg }).map(([name2, value]) => ({ name: name2, value }));
|
|
1037
969
|
}
|
|
1038
970
|
|
|
971
|
+
// src/lib/assets/yaml/overridesFile.ts
|
|
972
|
+
var import_client_node = require("@kubernetes/client-node");
|
|
973
|
+
|
|
1039
974
|
// src/lib/assets/rbac.ts
|
|
1040
975
|
function clusterRole(name2, capabilities, rbacMode = "admin", customRbac) {
|
|
1041
976
|
const rbacMap = createRBACMap(capabilities);
|
|
@@ -1138,371 +1073,51 @@ function storeRoleBinding(name2) {
|
|
|
1138
1073
|
};
|
|
1139
1074
|
}
|
|
1140
1075
|
|
|
1141
|
-
// src/lib/
|
|
1142
|
-
var
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
var peprStoreCRD = {
|
|
1158
|
-
apiVersion: "apiextensions.k8s.io/v1",
|
|
1159
|
-
kind: "CustomResourceDefinition",
|
|
1160
|
-
metadata: {
|
|
1161
|
-
name
|
|
1162
|
-
},
|
|
1163
|
-
spec: {
|
|
1164
|
-
group,
|
|
1165
|
-
versions: [
|
|
1166
|
-
{
|
|
1167
|
-
// typescript doesn't know this is really already set, which is kind of annoying
|
|
1168
|
-
name: version || "v1",
|
|
1169
|
-
served: true,
|
|
1170
|
-
storage: true,
|
|
1171
|
-
schema: {
|
|
1172
|
-
openAPIV3Schema: {
|
|
1173
|
-
type: "object",
|
|
1174
|
-
properties: {
|
|
1175
|
-
data: {
|
|
1176
|
-
type: "object",
|
|
1177
|
-
additionalProperties: {
|
|
1178
|
-
type: "string"
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
}
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1076
|
+
// src/lib/assets/yaml/overridesFile.ts
|
|
1077
|
+
var import_fs2 = require("fs");
|
|
1078
|
+
async function overridesFile({ hash, name: name2, image, config, apiToken, capabilities }, path, imagePullSecrets) {
|
|
1079
|
+
const rbacOverrides = clusterRole(name2, capabilities, config.rbacMode, config.rbac).rules;
|
|
1080
|
+
const overrides = {
|
|
1081
|
+
imagePullSecrets,
|
|
1082
|
+
additionalIgnoredNamespaces: [],
|
|
1083
|
+
rbac: rbacOverrides,
|
|
1084
|
+
secrets: {
|
|
1085
|
+
apiToken: Buffer.from(apiToken).toString("base64")
|
|
1086
|
+
},
|
|
1087
|
+
hash,
|
|
1088
|
+
namespace: {
|
|
1089
|
+
annotations: {},
|
|
1090
|
+
labels: {
|
|
1091
|
+
"pepr.dev": ""
|
|
1184
1092
|
}
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
async function generateWebhookRules(assets, isMutateWebhook) {
|
|
1214
|
-
const { config, capabilities } = assets;
|
|
1215
|
-
const rules = capabilities.flatMap((capability) => {
|
|
1216
|
-
console.info(`Module ${config.uuid} has capability: ${capability.name}`);
|
|
1217
|
-
return capability.bindings.map((binding) => validateRule(binding, isMutateWebhook)).filter((rule) => !!rule);
|
|
1218
|
-
});
|
|
1219
|
-
return (0, import_ramda.uniqWith)(import_ramda.equals, rules);
|
|
1220
|
-
}
|
|
1221
|
-
async function webhookConfig(assets, mutateOrValidate, timeoutSeconds = 10) {
|
|
1222
|
-
const ignore = [];
|
|
1223
|
-
const { name: name2, tls, config, apiToken, host } = assets;
|
|
1224
|
-
const ignoreNS = (0, import_ramda.concat)(peprIgnoreNamespaces, config?.alwaysIgnore?.namespaces || []);
|
|
1225
|
-
if (ignoreNS) {
|
|
1226
|
-
ignore.push({
|
|
1227
|
-
key: "kubernetes.io/metadata.name",
|
|
1228
|
-
operator: "NotIn",
|
|
1229
|
-
values: ignoreNS
|
|
1230
|
-
});
|
|
1231
|
-
}
|
|
1232
|
-
const clientConfig = {
|
|
1233
|
-
caBundle: tls.ca
|
|
1234
|
-
};
|
|
1235
|
-
const apiPath = `/${mutateOrValidate}/${apiToken}`;
|
|
1236
|
-
if (host) {
|
|
1237
|
-
clientConfig.url = `https://${host}:3000${apiPath}`;
|
|
1238
|
-
} else {
|
|
1239
|
-
clientConfig.service = {
|
|
1240
|
-
name: name2,
|
|
1241
|
-
namespace: "pepr-system",
|
|
1242
|
-
path: apiPath
|
|
1243
|
-
};
|
|
1244
|
-
}
|
|
1245
|
-
const isMutate = mutateOrValidate === "mutate";
|
|
1246
|
-
const rules = await generateWebhookRules(assets, isMutate);
|
|
1247
|
-
if (rules.length < 1) {
|
|
1248
|
-
return null;
|
|
1249
|
-
}
|
|
1250
|
-
return {
|
|
1251
|
-
apiVersion: "admissionregistration.k8s.io/v1",
|
|
1252
|
-
kind: isMutate ? "MutatingWebhookConfiguration" : "ValidatingWebhookConfiguration",
|
|
1253
|
-
metadata: { name: name2 },
|
|
1254
|
-
webhooks: [
|
|
1255
|
-
{
|
|
1256
|
-
name: `${name2}.pepr.dev`,
|
|
1257
|
-
admissionReviewVersions: ["v1", "v1beta1"],
|
|
1258
|
-
clientConfig,
|
|
1259
|
-
failurePolicy: config.onError === "reject" ? "Fail" : "Ignore",
|
|
1260
|
-
matchPolicy: "Equivalent",
|
|
1261
|
-
timeoutSeconds,
|
|
1262
|
-
namespaceSelector: {
|
|
1263
|
-
matchExpressions: ignore
|
|
1264
|
-
},
|
|
1265
|
-
rules,
|
|
1266
|
-
// @todo: track side effects state
|
|
1267
|
-
sideEffects: "None"
|
|
1268
|
-
}
|
|
1269
|
-
]
|
|
1270
|
-
};
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
// src/lib/assets/deploy.ts
|
|
1274
|
-
async function deployImagePullSecret(imagePullSecret, name2) {
|
|
1275
|
-
try {
|
|
1276
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Namespace).Get("pepr-system");
|
|
1277
|
-
} catch {
|
|
1278
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Namespace).Apply(getNamespace());
|
|
1279
|
-
}
|
|
1280
|
-
try {
|
|
1281
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(
|
|
1282
|
-
{
|
|
1283
|
-
apiVersion: "v1",
|
|
1284
|
-
kind: "Secret",
|
|
1285
|
-
metadata: {
|
|
1286
|
-
name: name2,
|
|
1287
|
-
namespace: "pepr-system"
|
|
1288
|
-
},
|
|
1289
|
-
type: "kubernetes.io/dockerconfigjson",
|
|
1290
|
-
data: {
|
|
1291
|
-
".dockerconfigjson": Buffer.from(JSON.stringify(imagePullSecret)).toString("base64")
|
|
1292
|
-
}
|
|
1293
|
-
},
|
|
1294
|
-
{ force: true }
|
|
1295
|
-
);
|
|
1296
|
-
} catch (e) {
|
|
1297
|
-
logger_default.error(e);
|
|
1298
|
-
}
|
|
1299
|
-
}
|
|
1300
|
-
async function deploy(assets, force, webhookTimeout) {
|
|
1301
|
-
logger_default.info("Establishing connection to Kubernetes");
|
|
1302
|
-
const { name: name2, host, path } = assets;
|
|
1303
|
-
logger_default.info("Applying pepr-system namespace");
|
|
1304
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Namespace).Apply(getNamespace(assets.config.customLabels?.namespace));
|
|
1305
|
-
const mutateWebhook = await webhookConfig(assets, "mutate", webhookTimeout);
|
|
1306
|
-
if (mutateWebhook) {
|
|
1307
|
-
logger_default.info("Applying mutating webhook");
|
|
1308
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.MutatingWebhookConfiguration).Apply(mutateWebhook, { force });
|
|
1309
|
-
} else {
|
|
1310
|
-
logger_default.info("Mutating webhook not needed, removing if it exists");
|
|
1311
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.MutatingWebhookConfiguration).Delete(name2);
|
|
1312
|
-
}
|
|
1313
|
-
const validateWebhook = await webhookConfig(assets, "validate", webhookTimeout);
|
|
1314
|
-
if (validateWebhook) {
|
|
1315
|
-
logger_default.info("Applying validating webhook");
|
|
1316
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ValidatingWebhookConfiguration).Apply(validateWebhook, { force });
|
|
1317
|
-
} else {
|
|
1318
|
-
logger_default.info("Validating webhook not needed, removing if it exists");
|
|
1319
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ValidatingWebhookConfiguration).Delete(name2);
|
|
1320
|
-
}
|
|
1321
|
-
logger_default.info("Applying the Pepr Store CRD if it doesn't exist");
|
|
1322
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.CustomResourceDefinition).Apply(peprStoreCRD, { force });
|
|
1323
|
-
if (host) {
|
|
1324
|
-
return;
|
|
1325
|
-
}
|
|
1326
|
-
const code = await import_fs2.promises.readFile(path);
|
|
1327
|
-
const hash = import_crypto.default.createHash("sha256").update(code).digest("hex");
|
|
1328
|
-
if (code.length < 1) {
|
|
1329
|
-
throw new Error("No code provided");
|
|
1330
|
-
}
|
|
1331
|
-
await setupRBAC(name2, assets.capabilities, force, assets.config);
|
|
1332
|
-
await setupController(assets, code, hash, force);
|
|
1333
|
-
await setupWatcher(assets, hash, force);
|
|
1334
|
-
}
|
|
1335
|
-
async function setupRBAC(name2, capabilities, force, config) {
|
|
1336
|
-
const { rbacMode, rbac } = config;
|
|
1337
|
-
logger_default.info("Applying cluster role binding");
|
|
1338
|
-
const crb = clusterRoleBinding(name2);
|
|
1339
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ClusterRoleBinding).Apply(crb, { force });
|
|
1340
|
-
logger_default.info("Applying cluster role");
|
|
1341
|
-
const cr = clusterRole(name2, capabilities, rbacMode, rbac);
|
|
1342
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ClusterRole).Apply(cr, { force });
|
|
1343
|
-
logger_default.info("Applying service account");
|
|
1344
|
-
const sa = serviceAccount(name2);
|
|
1345
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ServiceAccount).Apply(sa, { force });
|
|
1346
|
-
logger_default.info("Applying store role");
|
|
1347
|
-
const role = storeRole(name2);
|
|
1348
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Role).Apply(role, { force });
|
|
1349
|
-
logger_default.info("Applying store role binding");
|
|
1350
|
-
const roleBinding = storeRoleBinding(name2);
|
|
1351
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.RoleBinding).Apply(roleBinding, { force });
|
|
1352
|
-
}
|
|
1353
|
-
async function setupController(assets, code, hash, force) {
|
|
1354
|
-
const { name: name2 } = assets;
|
|
1355
|
-
logger_default.info("Applying module secret");
|
|
1356
|
-
const mod = getModuleSecret(name2, code, hash);
|
|
1357
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(mod, { force });
|
|
1358
|
-
logger_default.info("Applying controller service");
|
|
1359
|
-
const svc = service(name2);
|
|
1360
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Service).Apply(svc, { force });
|
|
1361
|
-
logger_default.info("Applying TLS secret");
|
|
1362
|
-
const tls = tlsSecret(name2, assets.tls);
|
|
1363
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(tls, { force });
|
|
1364
|
-
logger_default.info("Applying API token secret");
|
|
1365
|
-
const apiToken = apiTokenSecret(name2, assets.apiToken);
|
|
1366
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(apiToken, { force });
|
|
1367
|
-
logger_default.info("Applying deployment");
|
|
1368
|
-
const dep = getDeployment(assets, hash, assets.buildTimestamp);
|
|
1369
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Deployment).Apply(dep, { force });
|
|
1370
|
-
}
|
|
1371
|
-
async function setupWatcher(assets, hash, force) {
|
|
1372
|
-
const watchDeployment = getWatcher(assets, hash, assets.buildTimestamp);
|
|
1373
|
-
if (watchDeployment) {
|
|
1374
|
-
logger_default.info("Applying watcher deployment");
|
|
1375
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Deployment).Apply(watchDeployment, { force });
|
|
1376
|
-
logger_default.info("Applying watcher service");
|
|
1377
|
-
const watchSvc = watcherService(assets.name);
|
|
1378
|
-
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Service).Apply(watchSvc, { force });
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
// src/lib/assets/index.ts
|
|
1383
|
-
var import_client_node = require("@kubernetes/client-node");
|
|
1384
|
-
var import_path = require("path");
|
|
1385
|
-
function toYaml(obj) {
|
|
1386
|
-
return (0, import_client_node.dumpYaml)(obj, { noRefs: true });
|
|
1387
|
-
}
|
|
1388
|
-
function createWebhookYaml(name2, config, webhookConfiguration) {
|
|
1389
|
-
const yaml = toYaml(webhookConfiguration);
|
|
1390
|
-
return replaceString(
|
|
1391
|
-
replaceString(
|
|
1392
|
-
replaceString(yaml, name2, "{{ .Values.uuid }}"),
|
|
1393
|
-
config.onError === "reject" ? "Fail" : "Ignore",
|
|
1394
|
-
"{{ .Values.admission.failurePolicy }}"
|
|
1395
|
-
),
|
|
1396
|
-
`${config.webhookTimeout}` || "10",
|
|
1397
|
-
"{{ .Values.admission.webhookTimeout }}"
|
|
1398
|
-
);
|
|
1399
|
-
}
|
|
1400
|
-
function helmLayout(basePath, unique) {
|
|
1401
|
-
const helm = {
|
|
1402
|
-
dirs: {
|
|
1403
|
-
chart: (0, import_path.resolve)(`${basePath}/${unique}-chart`)
|
|
1404
|
-
},
|
|
1405
|
-
files: {}
|
|
1406
|
-
};
|
|
1407
|
-
helm.dirs = {
|
|
1408
|
-
...helm.dirs,
|
|
1409
|
-
charts: `${helm.dirs.chart}/charts`,
|
|
1410
|
-
tmpls: `${helm.dirs.chart}/templates`
|
|
1411
|
-
};
|
|
1412
|
-
helm.files = {
|
|
1413
|
-
...helm.files,
|
|
1414
|
-
valuesYaml: `${helm.dirs.chart}/values.yaml`,
|
|
1415
|
-
chartYaml: `${helm.dirs.chart}/Chart.yaml`,
|
|
1416
|
-
namespaceYaml: `${helm.dirs.tmpls}/namespace.yaml`,
|
|
1417
|
-
watcherServiceYaml: `${helm.dirs.tmpls}/watcher-service.yaml`,
|
|
1418
|
-
admissionServiceYaml: `${helm.dirs.tmpls}/admission-service.yaml`,
|
|
1419
|
-
mutationWebhookYaml: `${helm.dirs.tmpls}/mutation-webhook.yaml`,
|
|
1420
|
-
validationWebhookYaml: `${helm.dirs.tmpls}/validation-webhook.yaml`,
|
|
1421
|
-
admissionDeploymentYaml: `${helm.dirs.tmpls}/admission-deployment.yaml`,
|
|
1422
|
-
admissionServiceMonitorYaml: `${helm.dirs.tmpls}/admission-service-monitor.yaml`,
|
|
1423
|
-
watcherDeploymentYaml: `${helm.dirs.tmpls}/watcher-deployment.yaml`,
|
|
1424
|
-
watcherServiceMonitorYaml: `${helm.dirs.tmpls}/watcher-service-monitor.yaml`,
|
|
1425
|
-
tlsSecretYaml: `${helm.dirs.tmpls}/tls-secret.yaml`,
|
|
1426
|
-
apiTokenSecretYaml: `${helm.dirs.tmpls}/api-token-secret.yaml`,
|
|
1427
|
-
moduleSecretYaml: `${helm.dirs.tmpls}/module-secret.yaml`,
|
|
1428
|
-
storeRoleYaml: `${helm.dirs.tmpls}/store-role.yaml`,
|
|
1429
|
-
storeRoleBindingYaml: `${helm.dirs.tmpls}/store-role-binding.yaml`,
|
|
1430
|
-
clusterRoleYaml: `${helm.dirs.tmpls}/cluster-role.yaml`,
|
|
1431
|
-
clusterRoleBindingYaml: `${helm.dirs.tmpls}/cluster-role-binding.yaml`,
|
|
1432
|
-
serviceAccountYaml: `${helm.dirs.tmpls}/service-account.yaml`
|
|
1433
|
-
};
|
|
1434
|
-
return helm;
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
|
-
// src/lib/assets/loader.ts
|
|
1438
|
-
var import_child_process = require("child_process");
|
|
1439
|
-
function loadCapabilities(path) {
|
|
1440
|
-
return new Promise((resolve6, reject) => {
|
|
1441
|
-
const program2 = (0, import_child_process.fork)(path, {
|
|
1442
|
-
env: {
|
|
1443
|
-
...process.env,
|
|
1444
|
-
LOG_LEVEL: "warn",
|
|
1445
|
-
PEPR_MODE: "build",
|
|
1446
|
-
NODE_OPTIONS: "--disable-warning=DEP0040"
|
|
1447
|
-
}
|
|
1448
|
-
});
|
|
1449
|
-
program2.on("message", (message) => {
|
|
1450
|
-
const capabilities = message.valueOf();
|
|
1451
|
-
for (const capability of capabilities) {
|
|
1452
|
-
console.info(`Registered Pepr Capability "${capability.name}"`);
|
|
1453
|
-
}
|
|
1454
|
-
resolve6(capabilities);
|
|
1455
|
-
});
|
|
1456
|
-
program2.on("error", (error) => {
|
|
1457
|
-
reject(error);
|
|
1458
|
-
});
|
|
1459
|
-
});
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
// src/lib/assets/yaml.ts
|
|
1463
|
-
var import_client_node2 = require("@kubernetes/client-node");
|
|
1464
|
-
var import_fs3 = require("fs");
|
|
1465
|
-
async function overridesFile({ hash, name: name2, image, config, apiToken, capabilities }, path) {
|
|
1466
|
-
const rbacOverrides = clusterRole(name2, capabilities, config.rbacMode, config.rbac).rules;
|
|
1467
|
-
const overrides = {
|
|
1468
|
-
rbac: rbacOverrides,
|
|
1469
|
-
secrets: {
|
|
1470
|
-
apiToken: Buffer.from(apiToken).toString("base64")
|
|
1471
|
-
},
|
|
1472
|
-
hash,
|
|
1473
|
-
namespace: {
|
|
1474
|
-
annotations: {},
|
|
1475
|
-
labels: {
|
|
1476
|
-
"pepr.dev": ""
|
|
1477
|
-
}
|
|
1478
|
-
},
|
|
1479
|
-
uuid: name2,
|
|
1480
|
-
admission: {
|
|
1481
|
-
terminationGracePeriodSeconds: 5,
|
|
1482
|
-
failurePolicy: config.onError === "reject" ? "Fail" : "Ignore",
|
|
1483
|
-
webhookTimeout: config.webhookTimeout,
|
|
1484
|
-
env: genEnv(config, false, true),
|
|
1485
|
-
envFrom: [],
|
|
1486
|
-
image,
|
|
1487
|
-
annotations: {
|
|
1488
|
-
"pepr.dev/description": `${config.description}` || ""
|
|
1489
|
-
},
|
|
1490
|
-
labels: {
|
|
1491
|
-
app: name2,
|
|
1492
|
-
"pepr.dev/controller": "admission",
|
|
1493
|
-
"pepr.dev/uuid": config.uuid
|
|
1494
|
-
},
|
|
1495
|
-
securityContext: {
|
|
1496
|
-
runAsUser: 65532,
|
|
1497
|
-
runAsGroup: 65532,
|
|
1498
|
-
runAsNonRoot: true,
|
|
1499
|
-
fsGroup: 65532
|
|
1500
|
-
},
|
|
1501
|
-
readinessProbe: {
|
|
1502
|
-
httpGet: {
|
|
1503
|
-
path: "/healthz",
|
|
1504
|
-
port: 3e3,
|
|
1505
|
-
scheme: "HTTPS"
|
|
1093
|
+
},
|
|
1094
|
+
uuid: name2,
|
|
1095
|
+
admission: {
|
|
1096
|
+
terminationGracePeriodSeconds: 5,
|
|
1097
|
+
failurePolicy: config.onError === "reject" ? "Fail" : "Ignore",
|
|
1098
|
+
webhookTimeout: config.webhookTimeout,
|
|
1099
|
+
env: genEnv(config, false, true),
|
|
1100
|
+
envFrom: [],
|
|
1101
|
+
image,
|
|
1102
|
+
annotations: {
|
|
1103
|
+
"pepr.dev/description": `${config.description}` || ""
|
|
1104
|
+
},
|
|
1105
|
+
labels: {
|
|
1106
|
+
app: name2,
|
|
1107
|
+
"pepr.dev/controller": "admission",
|
|
1108
|
+
"pepr.dev/uuid": config.uuid
|
|
1109
|
+
},
|
|
1110
|
+
securityContext: {
|
|
1111
|
+
runAsUser: 65532,
|
|
1112
|
+
runAsGroup: 65532,
|
|
1113
|
+
runAsNonRoot: true,
|
|
1114
|
+
fsGroup: 65532
|
|
1115
|
+
},
|
|
1116
|
+
readinessProbe: {
|
|
1117
|
+
httpGet: {
|
|
1118
|
+
path: "/healthz",
|
|
1119
|
+
port: 3e3,
|
|
1120
|
+
scheme: "HTTPS"
|
|
1506
1121
|
},
|
|
1507
1122
|
initialDelaySeconds: 10
|
|
1508
1123
|
},
|
|
@@ -1612,167 +1227,260 @@ async function overridesFile({ hash, name: name2, image, config, apiToken, capab
|
|
|
1612
1227
|
}
|
|
1613
1228
|
}
|
|
1614
1229
|
};
|
|
1615
|
-
await
|
|
1230
|
+
await import_fs2.promises.writeFile(path, (0, import_client_node.dumpYaml)(overrides, { noRefs: true, forceQuotes: true }));
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
// src/lib/assets/index.ts
|
|
1234
|
+
var import_client_node2 = require("@kubernetes/client-node");
|
|
1235
|
+
var import_path = require("path");
|
|
1236
|
+
function toYaml(obj) {
|
|
1237
|
+
return (0, import_client_node2.dumpYaml)(obj, { noRefs: true });
|
|
1238
|
+
}
|
|
1239
|
+
function createWebhookYaml(name2, config, webhookConfiguration) {
|
|
1240
|
+
const yaml = toYaml(webhookConfiguration);
|
|
1241
|
+
const replacements = [
|
|
1242
|
+
{ search: name2, replace: "{{ .Values.uuid }}" },
|
|
1243
|
+
{
|
|
1244
|
+
search: config.onError === "reject" ? "Fail" : "Ignore",
|
|
1245
|
+
replace: "{{ .Values.admission.failurePolicy }}"
|
|
1246
|
+
},
|
|
1247
|
+
{
|
|
1248
|
+
search: `${config.webhookTimeout}` || "10",
|
|
1249
|
+
replace: "{{ .Values.admission.webhookTimeout }}"
|
|
1250
|
+
},
|
|
1251
|
+
{
|
|
1252
|
+
search: `
|
|
1253
|
+
- key: kubernetes.io/metadata.name
|
|
1254
|
+
operator: NotIn
|
|
1255
|
+
values:
|
|
1256
|
+
- kube-system
|
|
1257
|
+
- pepr-system
|
|
1258
|
+
`,
|
|
1259
|
+
replace: `
|
|
1260
|
+
- key: kubernetes.io/metadata.name
|
|
1261
|
+
operator: NotIn
|
|
1262
|
+
values:
|
|
1263
|
+
- kube-system
|
|
1264
|
+
- pepr-system
|
|
1265
|
+
{{- range .Values.additionalIgnoredNamespaces }}
|
|
1266
|
+
- {{ . }}
|
|
1267
|
+
{{- end }}
|
|
1268
|
+
`
|
|
1269
|
+
}
|
|
1270
|
+
];
|
|
1271
|
+
return replacements.reduce((updatedYaml, { search, replace }) => replaceString(updatedYaml, search, replace), yaml);
|
|
1272
|
+
}
|
|
1273
|
+
function helmLayout(basePath, unique) {
|
|
1274
|
+
const helm = {
|
|
1275
|
+
dirs: {
|
|
1276
|
+
chart: (0, import_path.resolve)(`${basePath}/${unique}-chart`)
|
|
1277
|
+
},
|
|
1278
|
+
files: {}
|
|
1279
|
+
};
|
|
1280
|
+
helm.dirs = {
|
|
1281
|
+
...helm.dirs,
|
|
1282
|
+
charts: `${helm.dirs.chart}/charts`,
|
|
1283
|
+
tmpls: `${helm.dirs.chart}/templates`
|
|
1284
|
+
};
|
|
1285
|
+
helm.files = {
|
|
1286
|
+
...helm.files,
|
|
1287
|
+
valuesYaml: `${helm.dirs.chart}/values.yaml`,
|
|
1288
|
+
chartYaml: `${helm.dirs.chart}/Chart.yaml`,
|
|
1289
|
+
namespaceYaml: `${helm.dirs.tmpls}/namespace.yaml`,
|
|
1290
|
+
watcherServiceYaml: `${helm.dirs.tmpls}/watcher-service.yaml`,
|
|
1291
|
+
admissionServiceYaml: `${helm.dirs.tmpls}/admission-service.yaml`,
|
|
1292
|
+
mutationWebhookYaml: `${helm.dirs.tmpls}/mutation-webhook.yaml`,
|
|
1293
|
+
validationWebhookYaml: `${helm.dirs.tmpls}/validation-webhook.yaml`,
|
|
1294
|
+
admissionDeploymentYaml: `${helm.dirs.tmpls}/admission-deployment.yaml`,
|
|
1295
|
+
admissionServiceMonitorYaml: `${helm.dirs.tmpls}/admission-service-monitor.yaml`,
|
|
1296
|
+
watcherDeploymentYaml: `${helm.dirs.tmpls}/watcher-deployment.yaml`,
|
|
1297
|
+
watcherServiceMonitorYaml: `${helm.dirs.tmpls}/watcher-service-monitor.yaml`,
|
|
1298
|
+
tlsSecretYaml: `${helm.dirs.tmpls}/tls-secret.yaml`,
|
|
1299
|
+
apiTokenSecretYaml: `${helm.dirs.tmpls}/api-token-secret.yaml`,
|
|
1300
|
+
moduleSecretYaml: `${helm.dirs.tmpls}/module-secret.yaml`,
|
|
1301
|
+
storeRoleYaml: `${helm.dirs.tmpls}/store-role.yaml`,
|
|
1302
|
+
storeRoleBindingYaml: `${helm.dirs.tmpls}/store-role-binding.yaml`,
|
|
1303
|
+
clusterRoleYaml: `${helm.dirs.tmpls}/cluster-role.yaml`,
|
|
1304
|
+
clusterRoleBindingYaml: `${helm.dirs.tmpls}/cluster-role-binding.yaml`,
|
|
1305
|
+
serviceAccountYaml: `${helm.dirs.tmpls}/service-account.yaml`
|
|
1306
|
+
};
|
|
1307
|
+
return helm;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
// src/lib/assets/loader.ts
|
|
1311
|
+
var import_child_process = require("child_process");
|
|
1312
|
+
function loadCapabilities(path) {
|
|
1313
|
+
return new Promise((resolve6, reject) => {
|
|
1314
|
+
const program2 = (0, import_child_process.fork)(path, {
|
|
1315
|
+
env: {
|
|
1316
|
+
...process.env,
|
|
1317
|
+
LOG_LEVEL: "warn",
|
|
1318
|
+
PEPR_MODE: "build",
|
|
1319
|
+
NODE_OPTIONS: "--disable-warning=DEP0040"
|
|
1320
|
+
}
|
|
1321
|
+
});
|
|
1322
|
+
program2.on("message", (message) => {
|
|
1323
|
+
const capabilities = message.valueOf();
|
|
1324
|
+
for (const capability of capabilities) {
|
|
1325
|
+
console.info(`Registered Pepr Capability "${capability.name}"`);
|
|
1326
|
+
}
|
|
1327
|
+
resolve6(capabilities);
|
|
1328
|
+
});
|
|
1329
|
+
program2.on("error", (error) => {
|
|
1330
|
+
reject(error);
|
|
1331
|
+
});
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
// src/lib/assets/assets.ts
|
|
1336
|
+
var import_fs3 = require("fs");
|
|
1337
|
+
|
|
1338
|
+
// src/lib/assets/networking.ts
|
|
1339
|
+
function apiTokenSecret(name2, apiToken) {
|
|
1340
|
+
return {
|
|
1341
|
+
apiVersion: "v1",
|
|
1342
|
+
kind: "Secret",
|
|
1343
|
+
metadata: {
|
|
1344
|
+
name: `${name2}-api-token`,
|
|
1345
|
+
namespace: "pepr-system"
|
|
1346
|
+
},
|
|
1347
|
+
type: "Opaque",
|
|
1348
|
+
data: {
|
|
1349
|
+
value: Buffer.from(apiToken).toString("base64")
|
|
1350
|
+
}
|
|
1351
|
+
};
|
|
1616
1352
|
}
|
|
1617
|
-
function
|
|
1618
|
-
|
|
1619
|
-
|
|
1353
|
+
function tlsSecret(name2, tls) {
|
|
1354
|
+
return {
|
|
1355
|
+
apiVersion: "v1",
|
|
1356
|
+
kind: "Secret",
|
|
1620
1357
|
metadata: {
|
|
1621
|
-
name: name2
|
|
1622
|
-
|
|
1623
|
-
url: "https://github.com/defenseunicorns/pepr",
|
|
1624
|
-
version: `${config.appVersion || "0.0.1"}`
|
|
1358
|
+
name: `${name2}-tls`,
|
|
1359
|
+
namespace: "pepr-system"
|
|
1625
1360
|
},
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
{
|
|
1632
|
-
name: "module",
|
|
1633
|
-
namespace: "pepr-system",
|
|
1634
|
-
files: [path]
|
|
1635
|
-
}
|
|
1636
|
-
],
|
|
1637
|
-
images: [image]
|
|
1638
|
-
}
|
|
1639
|
-
]
|
|
1361
|
+
type: "kubernetes.io/tls",
|
|
1362
|
+
data: {
|
|
1363
|
+
"tls.crt": tls.crt,
|
|
1364
|
+
"tls.key": tls.key
|
|
1365
|
+
}
|
|
1640
1366
|
};
|
|
1641
|
-
return (0, import_client_node2.dumpYaml)(zarfCfg, { noRefs: true });
|
|
1642
1367
|
}
|
|
1643
|
-
function
|
|
1644
|
-
|
|
1645
|
-
|
|
1368
|
+
function service(name2) {
|
|
1369
|
+
return {
|
|
1370
|
+
apiVersion: "v1",
|
|
1371
|
+
kind: "Service",
|
|
1646
1372
|
metadata: {
|
|
1647
1373
|
name: name2,
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
},
|
|
1652
|
-
components: [
|
|
1653
|
-
{
|
|
1654
|
-
name: "module",
|
|
1655
|
-
required: true,
|
|
1656
|
-
charts: [
|
|
1657
|
-
{
|
|
1658
|
-
name: "module",
|
|
1659
|
-
namespace: "pepr-system",
|
|
1660
|
-
version: `${config.appVersion || "0.0.1"}`,
|
|
1661
|
-
localPath: path
|
|
1662
|
-
}
|
|
1663
|
-
],
|
|
1664
|
-
images: [image]
|
|
1374
|
+
namespace: "pepr-system",
|
|
1375
|
+
labels: {
|
|
1376
|
+
"pepr.dev/controller": "admission"
|
|
1665
1377
|
}
|
|
1666
|
-
|
|
1378
|
+
},
|
|
1379
|
+
spec: {
|
|
1380
|
+
selector: {
|
|
1381
|
+
app: name2,
|
|
1382
|
+
"pepr.dev/controller": "admission"
|
|
1383
|
+
},
|
|
1384
|
+
ports: [
|
|
1385
|
+
{
|
|
1386
|
+
port: 443,
|
|
1387
|
+
targetPort: 3e3
|
|
1388
|
+
}
|
|
1389
|
+
]
|
|
1390
|
+
}
|
|
1667
1391
|
};
|
|
1668
|
-
return (0, import_client_node2.dumpYaml)(zarfCfg, { noRefs: true });
|
|
1669
1392
|
}
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
resources.push(deployments.watch);
|
|
1695
|
-
}
|
|
1696
|
-
return resources.map((r) => (0, import_client_node2.dumpYaml)(r, { noRefs: true })).join("---\n");
|
|
1393
|
+
function watcherService(name2) {
|
|
1394
|
+
return {
|
|
1395
|
+
apiVersion: "v1",
|
|
1396
|
+
kind: "Service",
|
|
1397
|
+
metadata: {
|
|
1398
|
+
name: `${name2}-watcher`,
|
|
1399
|
+
namespace: "pepr-system",
|
|
1400
|
+
labels: {
|
|
1401
|
+
"pepr.dev/controller": "watcher"
|
|
1402
|
+
}
|
|
1403
|
+
},
|
|
1404
|
+
spec: {
|
|
1405
|
+
selector: {
|
|
1406
|
+
app: `${name2}-watcher`,
|
|
1407
|
+
"pepr.dev/controller": "watcher"
|
|
1408
|
+
},
|
|
1409
|
+
ports: [
|
|
1410
|
+
{
|
|
1411
|
+
port: 443,
|
|
1412
|
+
targetPort: 3e3
|
|
1413
|
+
}
|
|
1414
|
+
]
|
|
1415
|
+
}
|
|
1416
|
+
};
|
|
1697
1417
|
}
|
|
1698
1418
|
|
|
1699
1419
|
// src/lib/assets/assets.ts
|
|
1700
|
-
var import_fs4 = require("fs");
|
|
1701
1420
|
var Assets = class {
|
|
1702
|
-
constructor(config, path, host) {
|
|
1703
|
-
this.config = config;
|
|
1704
|
-
this.path = path;
|
|
1705
|
-
this.host = host;
|
|
1706
|
-
this.name = `pepr-${config.uuid}`;
|
|
1707
|
-
this.buildTimestamp = `${Date.now()}`;
|
|
1708
|
-
this.alwaysIgnore = config.alwaysIgnore;
|
|
1709
|
-
this.image = `ghcr.io/defenseunicorns/pepr/controller:v${config.peprVersion}`;
|
|
1710
|
-
this.hash = "";
|
|
1711
|
-
this.tls = genTLS(this.host || `${this.name}.pepr-system.svc`);
|
|
1712
|
-
this.apiToken = import_crypto2.default.randomBytes(32).toString("hex");
|
|
1713
|
-
}
|
|
1714
1421
|
name;
|
|
1715
1422
|
tls;
|
|
1716
1423
|
apiToken;
|
|
1424
|
+
config;
|
|
1425
|
+
path;
|
|
1717
1426
|
alwaysIgnore;
|
|
1427
|
+
imagePullSecrets;
|
|
1718
1428
|
capabilities;
|
|
1719
1429
|
image;
|
|
1720
1430
|
buildTimestamp;
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
this.
|
|
1724
|
-
|
|
1725
|
-
|
|
1431
|
+
host;
|
|
1432
|
+
constructor(config, path, imagePullSecrets, host) {
|
|
1433
|
+
this.name = `pepr-${config.uuid}`;
|
|
1434
|
+
this.imagePullSecrets = imagePullSecrets;
|
|
1435
|
+
this.buildTimestamp = `${Date.now()}`;
|
|
1436
|
+
this.config = config;
|
|
1437
|
+
this.path = path;
|
|
1438
|
+
this.host = host;
|
|
1439
|
+
this.alwaysIgnore = config.alwaysIgnore;
|
|
1440
|
+
this.image = `ghcr.io/defenseunicorns/pepr/controller:v${config.peprVersion}`;
|
|
1441
|
+
this.tls = genTLS(host || `${this.name}.pepr-system.svc`);
|
|
1442
|
+
this.apiToken = import_crypto.default.randomBytes(32).toString("hex");
|
|
1443
|
+
}
|
|
1444
|
+
async deploy(deployFunction, force, webhookTimeout) {
|
|
1726
1445
|
this.capabilities = await loadCapabilities(this.path);
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1446
|
+
const timeout = typeof webhookTimeout === "number" ? webhookTimeout : 10;
|
|
1447
|
+
await deployFunction(this, force, timeout);
|
|
1448
|
+
}
|
|
1449
|
+
zarfYaml = (zarfYamlGenerator, path) => zarfYamlGenerator(this, path, "manifests");
|
|
1450
|
+
zarfYamlChart = (zarfYamlGenerator, path) => zarfYamlGenerator(this, path, "charts");
|
|
1451
|
+
allYaml = async (yamlGenerationFunction, imagePullSecret) => {
|
|
1732
1452
|
this.capabilities = await loadCapabilities(this.path);
|
|
1733
1453
|
for (const capability of this.capabilities) {
|
|
1734
1454
|
namespaceComplianceValidator(capability, this.alwaysIgnore?.namespaces);
|
|
1735
1455
|
}
|
|
1736
|
-
const
|
|
1737
|
-
|
|
1738
|
-
validate: await webhookConfig(this, "validate", this.config.webhookTimeout)
|
|
1739
|
-
};
|
|
1740
|
-
const code = await import_fs4.promises.readFile(this.path);
|
|
1741
|
-
this.hash = import_crypto2.default.createHash("sha256").update(code).digest("hex");
|
|
1456
|
+
const code = await import_fs3.promises.readFile(this.path);
|
|
1457
|
+
const moduleHash = import_crypto.default.createHash("sha256").update(code).digest("hex");
|
|
1742
1458
|
const deployments = {
|
|
1743
|
-
default: getDeployment(this,
|
|
1744
|
-
watch: getWatcher(this,
|
|
1745
|
-
};
|
|
1746
|
-
const assetsInputs = {
|
|
1747
|
-
apiToken: this.apiToken,
|
|
1748
|
-
capabilities: this.capabilities,
|
|
1749
|
-
config: this.config,
|
|
1750
|
-
hash: this.hash,
|
|
1751
|
-
name: this.name,
|
|
1752
|
-
path: this.path,
|
|
1753
|
-
tls: this.tls
|
|
1459
|
+
default: getDeployment(this, moduleHash, this.buildTimestamp, imagePullSecret),
|
|
1460
|
+
watch: getWatcher(this, moduleHash, this.buildTimestamp, imagePullSecret)
|
|
1754
1461
|
};
|
|
1755
|
-
return
|
|
1462
|
+
return yamlGenerationFunction(this, deployments);
|
|
1756
1463
|
};
|
|
1757
1464
|
writeWebhookFiles = async (validateWebhook, mutateWebhook, helm) => {
|
|
1758
1465
|
if (validateWebhook || mutateWebhook) {
|
|
1759
|
-
await
|
|
1760
|
-
await
|
|
1466
|
+
await import_fs3.promises.writeFile(helm.files.admissionDeploymentYaml, dedent(admissionDeployTemplate(this.buildTimestamp)));
|
|
1467
|
+
await import_fs3.promises.writeFile(helm.files.admissionServiceMonitorYaml, dedent(serviceMonitorTemplate("admission")));
|
|
1761
1468
|
}
|
|
1762
1469
|
if (mutateWebhook) {
|
|
1763
|
-
await
|
|
1470
|
+
await import_fs3.promises.writeFile(helm.files.mutationWebhookYaml, createWebhookYaml(this.name, this.config, mutateWebhook));
|
|
1764
1471
|
}
|
|
1765
1472
|
if (validateWebhook) {
|
|
1766
|
-
await
|
|
1473
|
+
await import_fs3.promises.writeFile(helm.files.validationWebhookYaml, createWebhookYaml(this.name, this.config, validateWebhook));
|
|
1767
1474
|
}
|
|
1768
1475
|
};
|
|
1769
|
-
generateHelmChart = async (basePath) => {
|
|
1476
|
+
generateHelmChart = async (webhookGeneratorFunction, basePath) => {
|
|
1770
1477
|
const helm = helmLayout(basePath, this.config.uuid);
|
|
1771
1478
|
try {
|
|
1772
1479
|
await Promise.all(
|
|
1773
1480
|
Object.values(helm.dirs).sort((l, r) => l.split("/").length - r.split("/").length).map(async (dir) => await createDirectoryIfNotExists(dir))
|
|
1774
1481
|
);
|
|
1775
|
-
const code = await
|
|
1482
|
+
const code = await import_fs3.promises.readFile(this.path);
|
|
1483
|
+
const moduleHash = import_crypto.default.createHash("sha256").update(code).digest("hex");
|
|
1776
1484
|
const pairs = [
|
|
1777
1485
|
[helm.files.chartYaml, () => dedent(chartYaml(this.config.uuid, this.config.description || ""))],
|
|
1778
1486
|
[helm.files.namespaceYaml, () => dedent(namespaceTemplate())],
|
|
@@ -1785,27 +1493,27 @@ var Assets = class {
|
|
|
1785
1493
|
[helm.files.clusterRoleYaml, () => dedent(clusterRoleTemplate())],
|
|
1786
1494
|
[helm.files.clusterRoleBindingYaml, () => toYaml(clusterRoleBinding(this.name))],
|
|
1787
1495
|
[helm.files.serviceAccountYaml, () => toYaml(serviceAccount(this.name))],
|
|
1788
|
-
[helm.files.moduleSecretYaml, () => toYaml(getModuleSecret(this.name, code,
|
|
1496
|
+
[helm.files.moduleSecretYaml, () => toYaml(getModuleSecret(this.name, code, moduleHash))]
|
|
1789
1497
|
];
|
|
1790
|
-
await Promise.all(pairs.map(async ([file, content]) => await
|
|
1498
|
+
await Promise.all(pairs.map(async ([file, content]) => await import_fs3.promises.writeFile(file, content())));
|
|
1791
1499
|
const overrideData = {
|
|
1792
|
-
hash:
|
|
1500
|
+
hash: moduleHash,
|
|
1793
1501
|
name: this.name,
|
|
1794
1502
|
image: this.image,
|
|
1795
1503
|
config: this.config,
|
|
1796
1504
|
apiToken: this.apiToken,
|
|
1797
1505
|
capabilities: this.capabilities
|
|
1798
1506
|
};
|
|
1799
|
-
await overridesFile(overrideData, helm.files.valuesYaml);
|
|
1800
|
-
const
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
await this.writeWebhookFiles(
|
|
1805
|
-
const watchDeployment = getWatcher(this,
|
|
1507
|
+
await overridesFile(overrideData, helm.files.valuesYaml, this.imagePullSecrets);
|
|
1508
|
+
const webhooks = {
|
|
1509
|
+
mutate: await webhookGeneratorFunction(this, "mutate" /* MUTATE */, this.config.webhookTimeout),
|
|
1510
|
+
validate: await webhookGeneratorFunction(this, "validate" /* VALIDATE */, this.config.webhookTimeout)
|
|
1511
|
+
};
|
|
1512
|
+
await this.writeWebhookFiles(webhooks.validate, webhooks.mutate, helm);
|
|
1513
|
+
const watchDeployment = getWatcher(this, moduleHash, this.buildTimestamp);
|
|
1806
1514
|
if (watchDeployment) {
|
|
1807
|
-
await
|
|
1808
|
-
await
|
|
1515
|
+
await import_fs3.promises.writeFile(helm.files.watcherDeploymentYaml, dedent(watcherDeployTemplate(this.buildTimestamp)));
|
|
1516
|
+
await import_fs3.promises.writeFile(helm.files.watcherServiceMonitorYaml, dedent(serviceMonitorTemplate("watcher")));
|
|
1809
1517
|
}
|
|
1810
1518
|
} catch (err) {
|
|
1811
1519
|
console.error(`Error generating helm chart: ${err.message}`);
|
|
@@ -2021,7 +1729,7 @@ var gitIgnore = "# Ignore node_modules and Pepr build artifacts\nnode_modules\nd
|
|
|
2021
1729
|
var readmeMd = '# Pepr Module\n\nThis is a Pepr Module. [Pepr](https://github.com/defenseunicorns/pepr) is a type-safe Kubernetes middleware system.\n\nThe `capabilities` directory contains all the capabilities for this module. By default,\na capability is a single typescript file in the format of `capability-name.ts` that is\nimported in the root `pepr.ts` file as `import { HelloPepr } from "./capabilities/hello-pepr";`.\nBecause this is typescript, you can organize this however you choose, e.g. creating a sub-folder\nper-capability or common logic in shared files or folders.\n\nExample Structure:\n\n```\nModule Root\n\u251C\u2500\u2500 package.json\n\u251C\u2500\u2500 pepr.ts\n\u2514\u2500\u2500 capabilities\n \u251C\u2500\u2500 example-one.ts\n \u251C\u2500\u2500 example-three.ts\n \u2514\u2500\u2500 example-two.ts\n```\n';
|
|
2022
1730
|
var peprTS = 'import { PeprModule } from "pepr";\n// cfg loads your pepr configuration from package.json\nimport cfg from "./package.json";\n\n// HelloPepr is a demo capability that is included with Pepr. Comment or delete the line below to remove it.\nimport { HelloPepr } from "./capabilities/hello-pepr";\n\n/**\n * This is the main entrypoint for this Pepr module. It is run when the module is started.\n * This is where you register your Pepr configurations and capabilities.\n */\nnew PeprModule(cfg, [\n // "HelloPepr" is a demo capability that is included with Pepr. Comment or delete the line below to remove it.\n HelloPepr,\n\n // Your additional capabilities go here\n]);\n';
|
|
2023
1731
|
var helloPeprTS = 'import {\n Capability,\n K8s,\n Log,\n PeprMutateRequest,\n RegisterKind,\n a,\n fetch,\n fetchStatus,\n kind,\n} from "pepr";\nimport { MockAgent, setGlobalDispatcher } from "undici";\n\n/**\n * The HelloPepr Capability is an example capability to demonstrate some general concepts of Pepr.\n * To test this capability you run `pepr dev`and then run the following command:\n * `kubectl apply -f capabilities/hello-pepr.samples.yaml`\n */\nexport const HelloPepr = new Capability({\n name: "hello-pepr",\n description: "A simple example capability to show how things work.",\n namespaces: ["pepr-demo", "pepr-demo-2"],\n});\n\n// Use the \'When\' function to create a new action, use \'Store\' to persist data\nconst { When, Store } = HelloPepr;\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (Namespace) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action removes the label `remove-me` when a Namespace is created.\n * Note we don\'t need to specify the namespace here, because we\'ve already specified\n * it in the Capability definition above.\n */\nWhen(a.Namespace)\n .IsCreated()\n .Mutate(ns => ns.RemoveLabel("remove-me"));\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Watch Action with K8s SSA (Namespace) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action watches for the `pepr-demo-2` namespace to be created, then creates a ConfigMap with\n * the name `pepr-ssa-demo` and adds the namespace UID to the ConfigMap data. Because Pepr uses\n * server-side apply for this operation, the ConfigMap will be created or updated if it already exists.\n */\nWhen(a.Namespace)\n .IsCreated()\n .WithName("pepr-demo-2")\n .Watch(async ns => {\n Log.info("Namespace pepr-demo-2 was created.");\n\n try {\n // Apply the ConfigMap using K8s server-side apply\n await K8s(kind.ConfigMap).Apply({\n metadata: {\n name: "pepr-ssa-demo",\n namespace: "pepr-demo-2",\n },\n data: {\n "ns-uid": ns.metadata.uid,\n },\n });\n } catch (error) {\n // You can use the Log object to log messages to the Pepr controller pod\n Log.error(error, "Failed to apply ConfigMap using server-side apply.");\n }\n\n // You can share data between actions using the Store, including between different types of actions\n Store.setItem("watch-data", "This data was stored by a Watch Action.");\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 1) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This is a single action. They can be in the same file or put imported from other files.\n * In this example, when a ConfigMap is created with the name `example-1`, then add a label and annotation.\n *\n * Equivalent to manually running:\n * `kubectl label configmap example-1 pepr=was-here`\n * `kubectl annotate configmap example-1 pepr.dev=annotations-work-too`\n */\nWhen(a.ConfigMap)\n .IsCreated()\n .WithName("example-1")\n .Mutate(request => {\n request\n .SetLabel("pepr", "was-here")\n .SetAnnotation("pepr.dev", "annotations-work-too");\n\n // Use the Store to persist data between requests and Pepr controller pods\n Store.setItem("example-1", "was-here");\n\n // This data is written asynchronously and can be read back via `Store.getItem()` or `Store.subscribe()`\n Store.setItem("example-1-data", JSON.stringify(request.Raw.data));\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate & Validate Actions (CM Example 2) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This combines 3 different types of actions: \'Mutate\', \'Validate\', and \'Watch\'. The order\n * of the actions is required, but each action is optional. In this example, when a ConfigMap is created\n * with the name `example-2`, then add a label and annotation, validate that the ConfigMap has the label\n * `pepr`, and log the request.\n */\nWhen(a.ConfigMap)\n .IsCreated()\n .WithName("example-2")\n .Mutate(request => {\n // This Mutate Action will mutate the request before it is persisted to the cluster\n\n // Use `request.Merge()` to merge the new data with the existing data\n request.Merge({\n metadata: {\n labels: {\n pepr: "was-here",\n },\n annotations: {\n "pepr.dev": "annotations-work-too",\n },\n },\n });\n })\n .Validate(request => {\n // This Validate Action will validate the request before it is persisted to the cluster\n\n // Approve the request if the ConfigMap has the label \'pepr\'\n if (request.HasLabel("pepr")) {\n return request.Approve();\n }\n\n // Otherwise, deny the request with an error message (optional)\n return request.Deny("ConfigMap must have label \'pepr\'");\n })\n .Watch((cm, phase) => {\n // This Watch Action will watch the ConfigMap after it has been persisted to the cluster\n Log.info(cm, `ConfigMap was ${phase} with the name example-2`);\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 2a) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action shows a simple validation that will deny any ConfigMap that has the\n * annotation `evil`. Note that the `Deny()` function takes an optional second parameter that is a\n * user-defined status code to return.\n */\nWhen(a.ConfigMap)\n .IsCreated()\n .Validate(request => {\n if (request.HasAnnotation("evil")) {\n return request.Deny("No evil CM annotations allowed.", 400);\n }\n\n return request.Approve();\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 3) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action combines different styles. Unlike the previous actions, this one will look\n * for any ConfigMap in the `pepr-demo` namespace that has the label `change=by-label` during either\n * CREATE or UPDATE. Note that all conditions added such as `WithName()`, `WithLabel()`, `InNamespace()`,\n * are ANDs so all conditions must be true for the request to be processed.\n */\nWhen(a.ConfigMap)\n .IsCreatedOrUpdated()\n .WithLabel("change", "by-label")\n .Mutate(request => {\n // The K8s object e are going to mutate\n const cm = request.Raw;\n\n // Get the username and uid of the K8s request\n const { username, uid } = request.Request.userInfo;\n\n // Store some data about the request in the configmap\n cm.data["username"] = username;\n cm.data["uid"] = uid;\n\n // You can still mix other ways of making changes too\n request.SetAnnotation("pepr.dev", "making-waves");\n });\n\n// This action validates the label `change=by-label` is deleted\nWhen(a.ConfigMap)\n .IsDeleted()\n .WithLabel("change", "by-label")\n .Validate(request => {\n // Log and then always approve the request\n Log.info("CM with label \'change=by-label\' was deleted.");\n return request.Approve();\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 4) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action show how you can use the `Mutate()` function without an inline function.\n * This is useful if you want to keep your actions small and focused on a single task,\n * or if you want to reuse the same function in multiple actions.\n */\nWhen(a.ConfigMap).IsCreated().WithName("example-4").Mutate(example4Cb);\n\n// This function uses the complete type definition, but is not required.\nfunction example4Cb(cm: PeprMutateRequest<a.ConfigMap>) {\n cm.SetLabel("pepr.dev/first", "true");\n cm.SetLabel("pepr.dev/second", "true");\n cm.SetLabel("pepr.dev/third", "true");\n}\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 4a) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This is the same as Example 4, except this only operates on a CM in the `pepr-demo-2` namespace.\n * Note because the Capability defines namespaces, the namespace specified here must be one of those.\n * Alternatively, you can remove the namespace from the Capability definition and specify it here.\n */\nWhen(a.ConfigMap)\n .IsCreated()\n .InNamespace("pepr-demo-2")\n .WithName("example-4a")\n .Mutate(example4Cb);\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 5) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action is a bit more complex. It will look for any ConfigMap in the `pepr-demo`\n * namespace that has the label `chuck-norris` during CREATE. When it finds one, it will fetch a\n * random Chuck Norris joke from the API and add it to the ConfigMap. This is a great example of how\n * you can use Pepr to make changes to your K8s objects based on external data.\n *\n * Note the use of the `async` keyword. This is required for any action that uses `await` or `fetch()`.\n *\n * Also note we are passing a type to the `fetch()` function. This is optional, but it will help you\n * avoid mistakes when working with the data returned from the API. You can also use the `as` keyword to\n * cast the data returned from the API.\n *\n * These are equivalent:\n * ```ts\n * const joke = await fetch<TheChuckNorrisJoke>("https://icanhazdadjoke.com/");\n * const joke = await fetch("https://icanhazdadjoke.com/") as TheChuckNorrisJoke;\n * ```\n *\n * Alternatively, you can drop the type completely:\n *\n * ```ts\n * fetch("https://icanhazdadjoke.com")\n * ```\n */\ninterface TheChuckNorrisJoke {\n id: string;\n joke: string;\n status: number;\n}\n\nWhen(a.ConfigMap)\n .IsCreatedOrUpdated()\n .WithLabel("chuck-norris")\n .Mutate(cm => cm.SetLabel("got-jokes", "true"))\n .Watch(async cm => {\n const jokeURL = "https://icanhazdadjoke.com";\n\n const mockAgent: MockAgent = new MockAgent();\n setGlobalDispatcher(mockAgent);\n const mockClient = mockAgent.get(jokeURL);\n mockClient.intercept({ path: "/", method: "GET" }).reply(\n 200,\n {\n id: "R7UfaahVfFd",\n joke: "Funny joke goes here.",\n status: 200,\n },\n {\n headers: {\n "Content-Type": "application/json; charset=utf-8",\n },\n },\n );\n\n // Try/catch is not needed as a response object will always be returned\n const response = await fetch<TheChuckNorrisJoke>(jokeURL, {\n headers: {\n Accept: "application/json",\n },\n });\n\n // Instead, check the `response.ok` field\n if (response.ok) {\n const { joke } = response.data;\n // Add Joke to the Store\n await Store.setItemAndWait(jokeURL, joke);\n // Add the Chuck Norris joke to the configmap\n try {\n await K8s(kind.ConfigMap).Apply({\n metadata: {\n name: cm.metadata.name,\n namespace: cm.metadata.namespace,\n },\n data: {\n "chuck-says": Store.getItem(jokeURL),\n },\n });\n } catch (error) {\n Log.error(error, "Failed to apply ConfigMap using server-side apply.", {\n cm,\n });\n }\n }\n\n // You can also assert on different HTTP response codes\n if (response.status === fetchStatus.NOT_FOUND) {\n // Do something else\n return;\n }\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (Secret Base64 Handling) *\n * ---------------------------------------------------------------------------------------------------\n *\n * The K8s JS client provides incomplete support for base64 encoding/decoding handling for secrets,\n * unlike the GO client. To make this less painful, Pepr automatically handles base64 encoding/decoding\n * secret data before and after the action is executed.\n */\nWhen(a.Secret)\n .IsCreated()\n .WithName("secret-1")\n .Mutate(request => {\n const secret = request.Raw;\n\n // This will be encoded at the end of all processing back to base64: "Y2hhbmdlLXdpdGhvdXQtZW5jb2Rpbmc="\n secret.data.magic = "change-without-encoding";\n\n // You can modify the data directly, and it will be encoded at the end of all processing\n secret.data.example += " - modified by Pepr";\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (Untyped Custom Resource) *\n * ---------------------------------------------------------------------------------------------------\n *\n * Out of the box, Pepr supports all the standard Kubernetes objects. However, you can also create\n * your own types. This is useful if you are working with an Operator that creates custom resources.\n * There are two ways to do this, the first is to use the `When()` function with a `GenericKind`,\n * the second is to create a new class that extends `GenericKind` and use the `RegisterKind()` function.\n *\n * This example shows how to use the `When()` function with a `GenericKind`. Note that you\n * must specify the `group`, `version`, and `kind` of the object (if applicable). This is how Pepr knows\n * if the action should be triggered or not. Since we are using a `GenericKind`,\n * Pepr will not be able to provide any intellisense for the object, so you will need to refer to the\n * Kubernetes API documentation for the object you are working with.\n *\n * You will need to wait for the CRD in `hello-pepr.samples.yaml` to be created, then you can apply\n *\n * ```yaml\n * apiVersion: pepr.dev/v1\n * kind: Unicorn\n * metadata:\n * name: example-1\n * namespace: pepr-demo\n * spec:\n * message: replace-me\n * counter: 0\n * ```\n */\nWhen(a.GenericKind, {\n group: "pepr.dev",\n version: "v1",\n kind: "Unicorn",\n})\n .IsCreated()\n .WithName("example-1")\n .Mutate(request => {\n request.Merge({\n spec: {\n message: "Hello Pepr without type data!",\n counter: Math.random(),\n },\n });\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (Typed Custom Resource) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This example shows how to use the `RegisterKind()` function to create a new type. This is useful\n * if you are working with an Operator that creates custom resources and you want to have intellisense\n * for the object. Note that you must specify the `group`, `version`, and `kind` of the object (if applicable)\n * as this is how Pepr knows if the action should be triggered or not.\n *\n * Once you register a new Kind with Pepr, you can use the `When()` function with the new Kind. Ideally,\n * you should register custom Kinds at the top of your Capability file or Pepr Module so they are available\n * to all actions, but we are putting it here for demonstration purposes.\n *\n * You will need to wait for the CRD in `hello-pepr.samples.yaml` to be created, then you can apply\n *\n * ```yaml\n * apiVersion: pepr.dev/v1\n * kind: Unicorn\n * metadata:\n * name: example-2\n * namespace: pepr-demo\n * spec:\n * message: replace-me\n * counter: 0\n * ```*\n */\nclass UnicornKind extends a.GenericKind {\n spec: {\n /**\n * JSDoc comments can be added to explain more details about the field.\n *\n * @example\n * ```ts\n * request.Raw.spec.message = "Hello Pepr!";\n * ```\n * */\n message: string;\n counter: number;\n };\n}\n\nRegisterKind(UnicornKind, {\n group: "pepr.dev",\n version: "v1",\n kind: "Unicorn",\n});\n\nWhen(UnicornKind)\n .IsCreated()\n .WithName("example-2")\n .Mutate(request => {\n request.Merge({\n spec: {\n message: "Hello Pepr with type data!",\n counter: Math.random(),\n },\n });\n });\n\n/**\n * A callback function that is called once the Pepr Store is fully loaded.\n */\nStore.onReady(data => {\n Log.info(data, "Pepr Store Ready");\n});\n';
|
|
2024
|
-
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.
|
|
1732
|
+
var packageJSON = { name: "pepr", description: "Kubernetes application engine", author: "Defense Unicorns", homepage: "https://github.com/defenseunicorns/pepr", license: "Apache-2.0", bin: "dist/cli.js", repository: "defenseunicorns/pepr", engines: { node: ">=18.0.0" }, files: ["/dist", "/src", "!src/**/*.test.ts", "!dist/**/*.test.d.ts*"], version: "0.44.0", main: "dist/lib.js", types: "dist/lib.d.ts", scripts: { ci: "npm ci", "gen-data-json": "node hack/build-template-data.js", prebuild: "rm -fr dist/* && npm run gen-data-json", version: "node scripts/set-version.js", build: "tsc && node build.mjs && npm pack", "build:image": "npm run build && docker buildx build --output type=docker --tag pepr:dev .", test: "npm run test:unit && npm run test:journey", "test:unit": "npm run gen-data-json && jest src --coverage --detectOpenHandles --coverageDirectory=./coverage --testPathIgnorePatterns='cosign.e2e.test.ts'", "test:integration": "npm run test:integration:prep && npm run test:integration:run", "test:integration:prep": "./integration/prep.sh", "test:integration:run": "jest --maxWorkers=4 integration", "test:journey": "npm run test:journey:k3d && npm run build && npm run test:journey:image && npm run test:journey:run", "test:journey:prep": "if [ ! -d ./pepr-upgrade-test ]; then git clone https://github.com/defenseunicorns/pepr-upgrade-test.git ; fi", "test:journey-wasm": "npm run test:journey:k3d && npm run build && npm run test:journey:image && npm run test:journey:run-wasm", "test:journey:k3d": "k3d cluster delete pepr-dev && k3d cluster create pepr-dev --k3s-arg '--debug@server:0' --wait && kubectl rollout status deployment -n kube-system", "test:journey:image": "docker buildx build --output type=docker --tag pepr:dev . && k3d image import pepr:dev -c pepr-dev", "test:journey:run": "jest --detectOpenHandles journey/entrypoint.test.ts && npm run test:journey:prep && npm run test:journey:upgrade", "test:journey:run-wasm": "jest --detectOpenHandles journey/entrypoint-wasm.test.ts", "test:journey:upgrade": "npm run test:journey:k3d && npm run test:journey:image && jest --detectOpenHandles journey/pepr-upgrade.test.ts", "format:check": "eslint src && prettier src --check", "format:fix": "eslint src --fix && prettier src --write", prepare: `if [ "$NODE_ENV" != 'production' ]; then husky; fi` }, dependencies: { "@types/ramda": "0.30.2", express: "4.21.2", "fast-json-patch": "3.1.1", "follow-redirects": "1.15.9", "http-status-codes": "^2.3.0", "json-pointer": "^0.6.2", "kubernetes-fluent-client": "3.3.8", pino: "9.6.0", "pino-pretty": "13.0.0", "prom-client": "15.1.3", ramda: "0.30.1", sigstore: "3.0.0" }, devDependencies: { "@commitlint/cli": "19.6.1", "@commitlint/config-conventional": "19.6.0", "@fast-check/jest": "^2.0.1", "@jest/globals": "29.7.0", "@types/eslint": "9.6.1", "@types/express": "5.0.0", "@types/follow-redirects": "1.14.4", "@types/json-pointer": "^1.0.34", "@types/node": "22.x.x", "@types/node-forge": "1.3.11", "@types/uuid": "10.0.0", "fast-check": "^3.19.0", husky: "^9.1.6", jest: "29.7.0", "js-yaml": "^4.1.0", "ts-jest": "29.2.5", undici: "^7.0.1" }, peerDependencies: { "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", "@types/prompts": "2.4.9", eslint: "8.57.0", commander: "12.1.0", esbuild: "0.24.0", "node-forge": "1.3.1", prettier: "3.4.2", prompts: "2.4.2", typescript: "^5.3.3", uuid: "11.0.3" } };
|
|
2025
1733
|
|
|
2026
1734
|
// src/templates/pepr.code-snippets.json
|
|
2027
1735
|
var pepr_code_snippets_default = {
|
|
@@ -2080,7 +1788,7 @@ var tsconfig_module_default = {
|
|
|
2080
1788
|
};
|
|
2081
1789
|
|
|
2082
1790
|
// src/cli/init/utils.ts
|
|
2083
|
-
var
|
|
1791
|
+
var import_fs4 = require("fs");
|
|
2084
1792
|
function sanitizeName(name2) {
|
|
2085
1793
|
if (typeof name2 !== "string") {
|
|
2086
1794
|
throw TypeError(
|
|
@@ -2094,7 +1802,7 @@ function sanitizeName(name2) {
|
|
|
2094
1802
|
}
|
|
2095
1803
|
async function createDir(dir) {
|
|
2096
1804
|
try {
|
|
2097
|
-
await
|
|
1805
|
+
await import_fs4.promises.mkdir(dir);
|
|
2098
1806
|
} catch (err) {
|
|
2099
1807
|
if (err && err.code === "EEXIST") {
|
|
2100
1808
|
throw new Error(`Directory ${dir} already exists`);
|
|
@@ -2107,11 +1815,11 @@ function write(path, data) {
|
|
|
2107
1815
|
if (typeof data !== "string") {
|
|
2108
1816
|
data = JSON.stringify(data, null, 2);
|
|
2109
1817
|
}
|
|
2110
|
-
return
|
|
1818
|
+
return import_fs4.promises.writeFile(path, data);
|
|
2111
1819
|
}
|
|
2112
1820
|
|
|
2113
1821
|
// src/cli/init/templates.ts
|
|
2114
|
-
var { dependencies, devDependencies, peerDependencies, scripts, version
|
|
1822
|
+
var { dependencies, devDependencies, peerDependencies, scripts, version } = packageJSON;
|
|
2115
1823
|
function genPkgJSON(opts, pgkVerOverride) {
|
|
2116
1824
|
const uuid = (0, import_uuid.v5)(opts.name, (0, import_uuid.v4)());
|
|
2117
1825
|
const name2 = sanitizeName(opts.name);
|
|
@@ -2147,7 +1855,7 @@ function genPkgJSON(opts, pgkVerOverride) {
|
|
|
2147
1855
|
"k3d-setup": scripts["test:journey:k3d"]
|
|
2148
1856
|
},
|
|
2149
1857
|
dependencies: {
|
|
2150
|
-
pepr: pgkVerOverride ||
|
|
1858
|
+
pepr: pgkVerOverride || version,
|
|
2151
1859
|
undici: "^7.0.1"
|
|
2152
1860
|
},
|
|
2153
1861
|
devDependencies: {
|
|
@@ -2210,85 +1918,244 @@ var import_commander = require("commander");
|
|
|
2210
1918
|
var import_eslint = require("eslint");
|
|
2211
1919
|
|
|
2212
1920
|
// src/cli/format.helpers.ts
|
|
2213
|
-
var
|
|
1921
|
+
var import_fs5 = require("fs");
|
|
2214
1922
|
var import_prettier = require("prettier");
|
|
2215
1923
|
async function formatWithPrettier(results, validateOnly) {
|
|
2216
1924
|
let hasFailure = false;
|
|
2217
1925
|
for (const { filePath } of results) {
|
|
2218
|
-
const content = await
|
|
1926
|
+
const content = await import_fs5.promises.readFile(filePath, "utf8");
|
|
2219
1927
|
const cfg = await (0, import_prettier.resolveConfig)(filePath);
|
|
2220
1928
|
const formatted = await (0, import_prettier.format)(content, { filepath: filePath, ...cfg });
|
|
2221
1929
|
if (validateOnly && formatted !== content) {
|
|
2222
1930
|
hasFailure = true;
|
|
2223
1931
|
console.error(`File ${filePath} is not formatted correctly`);
|
|
2224
1932
|
} else {
|
|
2225
|
-
await
|
|
1933
|
+
await import_fs5.promises.writeFile(filePath, formatted);
|
|
2226
1934
|
}
|
|
2227
1935
|
}
|
|
2228
1936
|
return hasFailure;
|
|
2229
1937
|
}
|
|
2230
1938
|
|
|
2231
|
-
// src/cli/format.ts
|
|
2232
|
-
function format_default(program2) {
|
|
2233
|
-
program2.command("format").description("Lint and format this Pepr module").option("-v, --validate-only", "Do not modify files, only validate formatting").action(async (opts) => {
|
|
2234
|
-
const success = await peprFormat(opts.validateOnly);
|
|
2235
|
-
if (success) {
|
|
2236
|
-
console.info("\u2705 Module formatted");
|
|
2237
|
-
} else {
|
|
2238
|
-
process.exit(1);
|
|
2239
|
-
}
|
|
2240
|
-
});
|
|
2241
|
-
}
|
|
2242
|
-
async function peprFormat(validateOnly) {
|
|
2243
|
-
{
|
|
2244
|
-
try {
|
|
2245
|
-
const eslint2 = new import_eslint.ESLint();
|
|
2246
|
-
const results = await eslint2.lintFiles(["./**/*.ts"]);
|
|
2247
|
-
let hasFailure = false;
|
|
2248
|
-
results.forEach(async (result) => {
|
|
2249
|
-
const errorCount = result.fatalErrorCount + result.errorCount;
|
|
2250
|
-
if (errorCount > 0) {
|
|
2251
|
-
hasFailure = true;
|
|
2252
|
-
}
|
|
2253
|
-
});
|
|
2254
|
-
const formatter = await eslint2.loadFormatter("stylish");
|
|
2255
|
-
const resultText = await formatter.format(results, {});
|
|
2256
|
-
if (resultText) {
|
|
2257
|
-
console.log(resultText);
|
|
2258
|
-
}
|
|
2259
|
-
if (!validateOnly) {
|
|
2260
|
-
await import_eslint.ESLint.outputFixes(results);
|
|
2261
|
-
}
|
|
2262
|
-
hasFailure = await formatWithPrettier(results, validateOnly);
|
|
2263
|
-
return !hasFailure;
|
|
2264
|
-
} catch (e) {
|
|
2265
|
-
console.error(`Error formatting module:`, e);
|
|
2266
|
-
return false;
|
|
2267
|
-
}
|
|
2268
|
-
}
|
|
1939
|
+
// src/cli/format.ts
|
|
1940
|
+
function format_default(program2) {
|
|
1941
|
+
program2.command("format").description("Lint and format this Pepr module").option("-v, --validate-only", "Do not modify files, only validate formatting").action(async (opts) => {
|
|
1942
|
+
const success = await peprFormat(opts.validateOnly);
|
|
1943
|
+
if (success) {
|
|
1944
|
+
console.info("\u2705 Module formatted");
|
|
1945
|
+
} else {
|
|
1946
|
+
process.exit(1);
|
|
1947
|
+
}
|
|
1948
|
+
});
|
|
1949
|
+
}
|
|
1950
|
+
async function peprFormat(validateOnly) {
|
|
1951
|
+
{
|
|
1952
|
+
try {
|
|
1953
|
+
const eslint2 = new import_eslint.ESLint();
|
|
1954
|
+
const results = await eslint2.lintFiles(["./**/*.ts"]);
|
|
1955
|
+
let hasFailure = false;
|
|
1956
|
+
results.forEach(async (result) => {
|
|
1957
|
+
const errorCount = result.fatalErrorCount + result.errorCount;
|
|
1958
|
+
if (errorCount > 0) {
|
|
1959
|
+
hasFailure = true;
|
|
1960
|
+
}
|
|
1961
|
+
});
|
|
1962
|
+
const formatter = await eslint2.loadFormatter("stylish");
|
|
1963
|
+
const resultText = await formatter.format(results, {});
|
|
1964
|
+
if (resultText) {
|
|
1965
|
+
console.log(resultText);
|
|
1966
|
+
}
|
|
1967
|
+
if (!validateOnly) {
|
|
1968
|
+
await import_eslint.ESLint.outputFixes(results);
|
|
1969
|
+
}
|
|
1970
|
+
hasFailure = await formatWithPrettier(results, validateOnly);
|
|
1971
|
+
return !hasFailure;
|
|
1972
|
+
} catch (e) {
|
|
1973
|
+
console.error(`Error formatting module:`, e);
|
|
1974
|
+
return false;
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
// src/lib/included-files.ts
|
|
1980
|
+
var import_fs6 = require("fs");
|
|
1981
|
+
async function createDockerfile(version3, description, includedFiles) {
|
|
1982
|
+
const file = `
|
|
1983
|
+
# Use an official Node.js runtime as the base image
|
|
1984
|
+
FROM ghcr.io/defenseunicorns/pepr/controller:v${version3}
|
|
1985
|
+
|
|
1986
|
+
LABEL description="${description}"
|
|
1987
|
+
|
|
1988
|
+
# Add the included files to the image
|
|
1989
|
+
${includedFiles.map((f) => `ADD ${f} ${f}`).join("\n")}
|
|
1990
|
+
|
|
1991
|
+
`;
|
|
1992
|
+
await import_fs6.promises.writeFile("Dockerfile.controller", file, { encoding: "utf-8" });
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
// src/cli/build.helpers.ts
|
|
1996
|
+
var import_child_process2 = require("child_process");
|
|
1997
|
+
var import_esbuild = require("esbuild");
|
|
1998
|
+
var import_path2 = require("path");
|
|
1999
|
+
var import_fs8 = require("fs");
|
|
2000
|
+
|
|
2001
|
+
// src/lib/assets/yaml/generateAllYaml.ts
|
|
2002
|
+
var import_crypto2 = __toESM(require("crypto"));
|
|
2003
|
+
var import_client_node4 = require("@kubernetes/client-node");
|
|
2004
|
+
var import_fs7 = require("fs");
|
|
2005
|
+
|
|
2006
|
+
// src/lib/assets/webhooks.ts
|
|
2007
|
+
var import_ramda = require("ramda");
|
|
2008
|
+
var peprIgnoreNamespaces = ["kube-system", "pepr-system"];
|
|
2009
|
+
var validateRule = (binding, isMutateWebhook) => {
|
|
2010
|
+
const { event, kind: kind8, isMutate, isValidate } = binding;
|
|
2011
|
+
if (isMutateWebhook && !isMutate || !isMutateWebhook && !isValidate) {
|
|
2012
|
+
return void 0;
|
|
2013
|
+
}
|
|
2014
|
+
const operations = event === "CREATEORUPDATE" /* CREATE_OR_UPDATE */ ? ["CREATE" /* CREATE */, "UPDATE" /* UPDATE */] : [event];
|
|
2015
|
+
const resource = kind8.plural || `${kind8.kind.toLowerCase()}s`;
|
|
2016
|
+
const ruleObject = {
|
|
2017
|
+
apiGroups: [kind8.group],
|
|
2018
|
+
apiVersions: [kind8.version || "*"],
|
|
2019
|
+
operations,
|
|
2020
|
+
resources: [resource, ...resource === "pods" ? ["pods/ephemeralcontainers"] : []]
|
|
2021
|
+
};
|
|
2022
|
+
return ruleObject;
|
|
2023
|
+
};
|
|
2024
|
+
function resolveIgnoreNamespaces(ignoredNSConfig = []) {
|
|
2025
|
+
const ignoredNSEnv = process.env.PEPR_ADDITIONAL_IGNORED_NAMESPACES;
|
|
2026
|
+
if (!ignoredNSEnv) {
|
|
2027
|
+
return ignoredNSConfig;
|
|
2028
|
+
}
|
|
2029
|
+
const namespaces = ignoredNSEnv.split(",").map((ns) => ns.trim());
|
|
2030
|
+
if (ignoredNSConfig) {
|
|
2031
|
+
namespaces.push(...ignoredNSConfig);
|
|
2032
|
+
}
|
|
2033
|
+
return namespaces.filter((ns) => ns.length > 0);
|
|
2034
|
+
}
|
|
2035
|
+
async function generateWebhookRules(assets, isMutateWebhook) {
|
|
2036
|
+
const { config, capabilities } = assets;
|
|
2037
|
+
const rules = capabilities.flatMap((capability) => {
|
|
2038
|
+
console.info(`Module ${config.uuid} has capability: ${capability.name}`);
|
|
2039
|
+
return capability.bindings.map((binding) => validateRule(binding, isMutateWebhook)).filter((rule) => !!rule);
|
|
2040
|
+
});
|
|
2041
|
+
return (0, import_ramda.uniqWith)(import_ramda.equals, rules);
|
|
2042
|
+
}
|
|
2043
|
+
async function webhookConfigGenerator(assets, mutateOrValidate, timeoutSeconds = 10) {
|
|
2044
|
+
const ignore = [];
|
|
2045
|
+
const { name: name2, tls, config, apiToken, host } = assets;
|
|
2046
|
+
const ignoreNS = (0, import_ramda.concat)(peprIgnoreNamespaces, resolveIgnoreNamespaces(config?.alwaysIgnore?.namespaces));
|
|
2047
|
+
if (ignoreNS) {
|
|
2048
|
+
ignore.push({
|
|
2049
|
+
key: "kubernetes.io/metadata.name",
|
|
2050
|
+
operator: "NotIn",
|
|
2051
|
+
values: ignoreNS
|
|
2052
|
+
});
|
|
2053
|
+
}
|
|
2054
|
+
const clientConfig = {
|
|
2055
|
+
caBundle: tls.ca
|
|
2056
|
+
};
|
|
2057
|
+
const apiPath = `/${mutateOrValidate}/${apiToken}`;
|
|
2058
|
+
if (host) {
|
|
2059
|
+
clientConfig.url = `https://${host}:3000${apiPath}`;
|
|
2060
|
+
} else {
|
|
2061
|
+
clientConfig.service = {
|
|
2062
|
+
name: name2,
|
|
2063
|
+
namespace: "pepr-system",
|
|
2064
|
+
path: apiPath
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2067
|
+
const isMutate = mutateOrValidate === "mutate" /* MUTATE */;
|
|
2068
|
+
const rules = await generateWebhookRules(assets, isMutate);
|
|
2069
|
+
if (rules.length < 1) {
|
|
2070
|
+
return null;
|
|
2071
|
+
}
|
|
2072
|
+
return {
|
|
2073
|
+
apiVersion: "admissionregistration.k8s.io/v1",
|
|
2074
|
+
kind: isMutate ? "MutatingWebhookConfiguration" : "ValidatingWebhookConfiguration",
|
|
2075
|
+
metadata: { name: name2 },
|
|
2076
|
+
webhooks: [
|
|
2077
|
+
{
|
|
2078
|
+
name: `${name2}.pepr.dev`,
|
|
2079
|
+
admissionReviewVersions: ["v1", "v1beta1"],
|
|
2080
|
+
clientConfig,
|
|
2081
|
+
failurePolicy: config.onError === "reject" ? "Fail" : "Ignore",
|
|
2082
|
+
matchPolicy: "Equivalent",
|
|
2083
|
+
timeoutSeconds,
|
|
2084
|
+
namespaceSelector: {
|
|
2085
|
+
matchExpressions: ignore
|
|
2086
|
+
},
|
|
2087
|
+
rules,
|
|
2088
|
+
// @todo: track side effects state
|
|
2089
|
+
sideEffects: "None"
|
|
2090
|
+
}
|
|
2091
|
+
]
|
|
2092
|
+
};
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
// src/lib/assets/yaml/generateAllYaml.ts
|
|
2096
|
+
async function generateAllYaml(assets, deployments) {
|
|
2097
|
+
const { name: name2, tls, apiToken, path, config } = assets;
|
|
2098
|
+
const code = await import_fs7.promises.readFile(path);
|
|
2099
|
+
const hash = import_crypto2.default.createHash("sha256").update(code).digest("hex");
|
|
2100
|
+
const resources = [
|
|
2101
|
+
getNamespace(assets.config.customLabels?.namespace),
|
|
2102
|
+
clusterRole(name2, assets.capabilities, config.rbacMode, config.rbac),
|
|
2103
|
+
clusterRoleBinding(name2),
|
|
2104
|
+
serviceAccount(name2),
|
|
2105
|
+
apiTokenSecret(name2, apiToken),
|
|
2106
|
+
tlsSecret(name2, tls),
|
|
2107
|
+
deployments.default,
|
|
2108
|
+
service(name2),
|
|
2109
|
+
watcherService(name2),
|
|
2110
|
+
getModuleSecret(name2, code, hash),
|
|
2111
|
+
storeRole(name2),
|
|
2112
|
+
storeRoleBinding(name2)
|
|
2113
|
+
];
|
|
2114
|
+
const webhooks = {
|
|
2115
|
+
mutate: await webhookConfigGenerator(assets, "mutate" /* MUTATE */, assets.config.webhookTimeout),
|
|
2116
|
+
validate: await webhookConfigGenerator(assets, "validate" /* VALIDATE */, assets.config.webhookTimeout)
|
|
2117
|
+
};
|
|
2118
|
+
const additionalResources = [webhooks.mutate, webhooks.validate, deployments.watch].filter(
|
|
2119
|
+
(resource) => resource !== null && resource !== void 0
|
|
2120
|
+
);
|
|
2121
|
+
resources.push(...additionalResources);
|
|
2122
|
+
return resources.map((resource) => (0, import_client_node4.dumpYaml)(resource, { noRefs: true })).join("---\n");
|
|
2269
2123
|
}
|
|
2270
2124
|
|
|
2271
|
-
// src/lib/
|
|
2272
|
-
var
|
|
2273
|
-
|
|
2274
|
-
const
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2125
|
+
// src/lib/assets/yaml/generateZarfYaml.ts
|
|
2126
|
+
var import_client_node5 = require("@kubernetes/client-node");
|
|
2127
|
+
function generateZarfYamlGeneric(assets, path, type) {
|
|
2128
|
+
const manifestSettings = {
|
|
2129
|
+
name: "module",
|
|
2130
|
+
namespace: "pepr-system",
|
|
2131
|
+
files: [path]
|
|
2132
|
+
};
|
|
2133
|
+
const chartSettings = {
|
|
2134
|
+
name: "module",
|
|
2135
|
+
namespace: "pepr-system",
|
|
2136
|
+
version: `${assets.config.appVersion || "0.0.1"}`,
|
|
2137
|
+
localPath: path
|
|
2138
|
+
};
|
|
2139
|
+
const component = {
|
|
2140
|
+
name: "module",
|
|
2141
|
+
required: true,
|
|
2142
|
+
images: [assets.image],
|
|
2143
|
+
[type]: [type === "manifests" ? manifestSettings : chartSettings]
|
|
2144
|
+
};
|
|
2145
|
+
const zarfCfg = {
|
|
2146
|
+
kind: "ZarfPackageConfig",
|
|
2147
|
+
metadata: {
|
|
2148
|
+
name: assets.name,
|
|
2149
|
+
description: `Pepr Module: ${assets.config.description}`,
|
|
2150
|
+
url: "https://github.com/defenseunicorns/pepr",
|
|
2151
|
+
version: `${assets.config.appVersion || "0.0.1"}`
|
|
2152
|
+
},
|
|
2153
|
+
components: [component]
|
|
2154
|
+
};
|
|
2155
|
+
return (0, import_client_node5.dumpYaml)(zarfCfg, { noRefs: true });
|
|
2285
2156
|
}
|
|
2286
2157
|
|
|
2287
2158
|
// src/cli/build.helpers.ts
|
|
2288
|
-
var import_child_process2 = require("child_process");
|
|
2289
|
-
var import_esbuild = require("esbuild");
|
|
2290
|
-
var import_path2 = require("path");
|
|
2291
|
-
var import_fs8 = require("fs");
|
|
2292
2159
|
function determineRbacMode(opts, cfg) {
|
|
2293
2160
|
if (opts.rbacMode) {
|
|
2294
2161
|
return opts.rbacMode;
|
|
@@ -2323,17 +2190,6 @@ function validImagePullSecret(imagePullSecretName) {
|
|
|
2323
2190
|
}
|
|
2324
2191
|
}
|
|
2325
2192
|
}
|
|
2326
|
-
function handleCustomImage(customImage, registry) {
|
|
2327
|
-
let defaultImage = "";
|
|
2328
|
-
if (customImage) {
|
|
2329
|
-
if (registry) {
|
|
2330
|
-
console.error(`Custom Image and registry cannot be used together.`);
|
|
2331
|
-
process.exit(1);
|
|
2332
|
-
}
|
|
2333
|
-
defaultImage = customImage;
|
|
2334
|
-
}
|
|
2335
|
-
return defaultImage;
|
|
2336
|
-
}
|
|
2337
2193
|
async function handleCustomImageBuild(includedFiles, peprVersion, description, image) {
|
|
2338
2194
|
if (includedFiles.length > 0) {
|
|
2339
2195
|
await createDockerfile(peprVersion, description, includedFiles);
|
|
@@ -2372,17 +2228,17 @@ async function generateYamlAndWriteToDisk(obj) {
|
|
|
2372
2228
|
const yamlFile = `pepr-module-${uuid}.yaml`;
|
|
2373
2229
|
const chartPath = `${uuid}-chart`;
|
|
2374
2230
|
const yamlPath = (0, import_path2.resolve)(outputDir2, yamlFile);
|
|
2375
|
-
const yaml = await assets.allYaml(imagePullSecret);
|
|
2231
|
+
const yaml = await assets.allYaml(generateAllYaml, imagePullSecret);
|
|
2376
2232
|
const zarfPath = (0, import_path2.resolve)(outputDir2, "zarf.yaml");
|
|
2377
2233
|
let localZarf = "";
|
|
2378
2234
|
if (zarf === "chart") {
|
|
2379
|
-
localZarf = assets.zarfYamlChart(chartPath);
|
|
2235
|
+
localZarf = assets.zarfYamlChart(generateZarfYamlGeneric, chartPath);
|
|
2380
2236
|
} else {
|
|
2381
|
-
localZarf = assets.zarfYaml(yamlFile);
|
|
2237
|
+
localZarf = assets.zarfYaml(generateZarfYamlGeneric, yamlFile);
|
|
2382
2238
|
}
|
|
2383
2239
|
await import_fs8.promises.writeFile(yamlPath, yaml);
|
|
2384
2240
|
await import_fs8.promises.writeFile(zarfPath, localZarf);
|
|
2385
|
-
await assets.generateHelmChart(outputDir2);
|
|
2241
|
+
await assets.generateHelmChart(webhookConfigGenerator, outputDir2);
|
|
2386
2242
|
console.info(`\u2705 K8s resource for the module saved to ${yamlPath}`);
|
|
2387
2243
|
}
|
|
2388
2244
|
|
|
@@ -2393,27 +2249,34 @@ function build_default(program2) {
|
|
|
2393
2249
|
program2.command("build").description("Build a Pepr Module for deployment").option("-e, --entry-point [file]", "Specify the entry point file to build with.", peprTS2).option(
|
|
2394
2250
|
"-n, --no-embed",
|
|
2395
2251
|
"Disables embedding of deployment files into output module. Useful when creating library modules intended solely for reuse/distribution via NPM."
|
|
2396
|
-
).
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
"
|
|
2401
|
-
|
|
2252
|
+
).addOption(
|
|
2253
|
+
new import_commander.Option(
|
|
2254
|
+
"-i, --custom-image <custom-image>",
|
|
2255
|
+
"Specify a custom image (including version) for Admission and Watch Deployments. Example: 'docker.io/username/custom-pepr-controller:v1.0.0'"
|
|
2256
|
+
).conflicts(["version", "registryInfo", "registry"])
|
|
2257
|
+
).addOption(
|
|
2258
|
+
new import_commander.Option(
|
|
2259
|
+
"-r, --registry-info [<registry>/<username>]",
|
|
2260
|
+
"Provide the image registry and username for building and pushing a custom WASM container. Requires authentication. Builds and pushes 'registry/username/custom-pepr-controller:<current-version>'."
|
|
2261
|
+
).conflicts(["customImage", "version", "registry"])
|
|
2402
2262
|
).option("-o, --output-dir <output directory>", "Define where to place build output").option(
|
|
2403
2263
|
"--timeout <timeout>",
|
|
2404
2264
|
"How long the API server should wait for a webhook to respond before treating the call as a failure",
|
|
2405
2265
|
parseTimeout
|
|
2406
|
-
).
|
|
2407
|
-
|
|
2408
|
-
|
|
2266
|
+
).addOption(
|
|
2267
|
+
new import_commander.Option(
|
|
2268
|
+
"-v, --version <version>",
|
|
2269
|
+
"The version of the Pepr image to use in the deployment manifests. Example: '0.27.3'."
|
|
2270
|
+
).conflicts(["customImage", "registryInfo"])
|
|
2409
2271
|
).option(
|
|
2410
2272
|
"--withPullSecret <imagePullSecret>",
|
|
2411
|
-
"Image Pull Secret: Use image pull secret for controller Deployment."
|
|
2273
|
+
"Image Pull Secret: Use image pull secret for controller Deployment.",
|
|
2274
|
+
""
|
|
2412
2275
|
).addOption(
|
|
2413
2276
|
new import_commander.Option(
|
|
2414
2277
|
"--registry <GitHub|Iron Bank>",
|
|
2415
2278
|
"Container registry: Choose container registry for deployment manifests. Can't be used with --custom-image."
|
|
2416
|
-
).choices(["GitHub", "Iron Bank"])
|
|
2279
|
+
).conflicts(["customImage", "registryInfo"]).choices(["GitHub", "Iron Bank"])
|
|
2417
2280
|
).addOption(
|
|
2418
2281
|
new import_commander.Option(
|
|
2419
2282
|
"-z, --zarf [manifest|chart]",
|
|
@@ -2429,7 +2292,7 @@ function build_default(program2) {
|
|
|
2429
2292
|
if (buildModuleResult?.cfg && buildModuleResult.path && buildModuleResult.uuid) {
|
|
2430
2293
|
const { cfg, path, uuid } = buildModuleResult;
|
|
2431
2294
|
const { includedFiles } = cfg.pepr;
|
|
2432
|
-
let image =
|
|
2295
|
+
let image = opts.customImage || "";
|
|
2433
2296
|
if (opts.timeout !== void 0) {
|
|
2434
2297
|
cfg.pepr.webhookTimeout = opts.timeout;
|
|
2435
2298
|
}
|
|
@@ -2445,10 +2308,14 @@ function build_default(program2) {
|
|
|
2445
2308
|
...cfg.pepr,
|
|
2446
2309
|
appVersion: cfg.version,
|
|
2447
2310
|
description: cfg.description,
|
|
2311
|
+
alwaysIgnore: {
|
|
2312
|
+
namespaces: cfg.pepr.alwaysIgnore?.namespaces
|
|
2313
|
+
},
|
|
2448
2314
|
// Can override the rbacMode with the CLI option
|
|
2449
2315
|
rbacMode: determineRbacMode(opts, cfg)
|
|
2450
2316
|
},
|
|
2451
|
-
path
|
|
2317
|
+
path,
|
|
2318
|
+
opts.withPullSecret === "" ? [] : [opts.withPullSecret]
|
|
2452
2319
|
);
|
|
2453
2320
|
image = checkIronBankImage(opts.registry, image, cfg.pepr.peprVersion);
|
|
2454
2321
|
image !== "" ? assets.image = image : null;
|
|
@@ -2484,7 +2351,7 @@ async function loadModule(entryPoint = peprTS2) {
|
|
|
2484
2351
|
const cfg = JSON.parse(moduleText);
|
|
2485
2352
|
const { uuid } = cfg.pepr;
|
|
2486
2353
|
const name2 = `pepr-${uuid}.js`;
|
|
2487
|
-
cfg.pepr.peprVersion =
|
|
2354
|
+
cfg.pepr.peprVersion = version;
|
|
2488
2355
|
if (!uuid) {
|
|
2489
2356
|
throw new Error("Could not load the uuid in package.json");
|
|
2490
2357
|
}
|
|
@@ -2590,6 +2457,169 @@ async function checkFormat() {
|
|
|
2590
2457
|
// src/cli/deploy.ts
|
|
2591
2458
|
var import_prompts = __toESM(require("prompts"));
|
|
2592
2459
|
|
|
2460
|
+
// src/lib/assets/deploy.ts
|
|
2461
|
+
var import_crypto3 = __toESM(require("crypto"));
|
|
2462
|
+
var import_fs10 = require("fs");
|
|
2463
|
+
var import_kubernetes_fluent_client3 = require("kubernetes-fluent-client");
|
|
2464
|
+
|
|
2465
|
+
// src/lib/k8s.ts
|
|
2466
|
+
var import_kubernetes_fluent_client2 = require("kubernetes-fluent-client");
|
|
2467
|
+
var Store = class extends import_kubernetes_fluent_client2.GenericKind {
|
|
2468
|
+
};
|
|
2469
|
+
var peprStoreGVK = {
|
|
2470
|
+
kind: "PeprStore",
|
|
2471
|
+
version: "v1",
|
|
2472
|
+
group: "pepr.dev"
|
|
2473
|
+
};
|
|
2474
|
+
(0, import_kubernetes_fluent_client2.RegisterKind)(Store, peprStoreGVK);
|
|
2475
|
+
|
|
2476
|
+
// src/lib/assets/store.ts
|
|
2477
|
+
var { group, version: version2, kind: kind2 } = peprStoreGVK;
|
|
2478
|
+
var singular = kind2.toLocaleLowerCase();
|
|
2479
|
+
var plural = `${singular}s`;
|
|
2480
|
+
var name = `${plural}.${group}`;
|
|
2481
|
+
var peprStoreCRD = {
|
|
2482
|
+
apiVersion: "apiextensions.k8s.io/v1",
|
|
2483
|
+
kind: "CustomResourceDefinition",
|
|
2484
|
+
metadata: {
|
|
2485
|
+
name
|
|
2486
|
+
},
|
|
2487
|
+
spec: {
|
|
2488
|
+
group,
|
|
2489
|
+
versions: [
|
|
2490
|
+
{
|
|
2491
|
+
// typescript doesn't know this is really already set, which is kind of annoying
|
|
2492
|
+
name: version2 || "v1",
|
|
2493
|
+
served: true,
|
|
2494
|
+
storage: true,
|
|
2495
|
+
schema: {
|
|
2496
|
+
openAPIV3Schema: {
|
|
2497
|
+
type: "object",
|
|
2498
|
+
properties: {
|
|
2499
|
+
data: {
|
|
2500
|
+
type: "object",
|
|
2501
|
+
additionalProperties: {
|
|
2502
|
+
type: "string"
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
],
|
|
2510
|
+
scope: "Namespaced",
|
|
2511
|
+
names: {
|
|
2512
|
+
plural,
|
|
2513
|
+
singular,
|
|
2514
|
+
kind: kind2
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
};
|
|
2518
|
+
|
|
2519
|
+
// src/lib/assets/deploy.ts
|
|
2520
|
+
async function deployImagePullSecret(imagePullSecret, name2) {
|
|
2521
|
+
try {
|
|
2522
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Namespace).Get("pepr-system");
|
|
2523
|
+
} catch {
|
|
2524
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Namespace).Apply(getNamespace());
|
|
2525
|
+
}
|
|
2526
|
+
try {
|
|
2527
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(
|
|
2528
|
+
{
|
|
2529
|
+
apiVersion: "v1",
|
|
2530
|
+
kind: "Secret",
|
|
2531
|
+
metadata: {
|
|
2532
|
+
name: name2,
|
|
2533
|
+
namespace: "pepr-system"
|
|
2534
|
+
},
|
|
2535
|
+
type: "kubernetes.io/dockerconfigjson",
|
|
2536
|
+
data: {
|
|
2537
|
+
".dockerconfigjson": Buffer.from(JSON.stringify(imagePullSecret)).toString("base64")
|
|
2538
|
+
}
|
|
2539
|
+
},
|
|
2540
|
+
{ force: true }
|
|
2541
|
+
);
|
|
2542
|
+
} catch (e) {
|
|
2543
|
+
logger_default.error(e);
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
async function handleWebhookConfiguration(assets, type, webhookTimeout, force) {
|
|
2547
|
+
const kindMap = {
|
|
2548
|
+
mutate: import_kubernetes_fluent_client3.kind.MutatingWebhookConfiguration,
|
|
2549
|
+
validate: import_kubernetes_fluent_client3.kind.ValidatingWebhookConfiguration
|
|
2550
|
+
};
|
|
2551
|
+
const webhookConfig = await webhookConfigGenerator(assets, type, webhookTimeout);
|
|
2552
|
+
if (webhookConfig) {
|
|
2553
|
+
logger_default.info(`Applying ${type} webhook`);
|
|
2554
|
+
await (0, import_kubernetes_fluent_client3.K8s)(kindMap[type]).Apply(webhookConfig, { force });
|
|
2555
|
+
} else {
|
|
2556
|
+
logger_default.info(`${type.charAt(0).toUpperCase() + type.slice(1)} webhook not needed, removing if it exists`);
|
|
2557
|
+
await (0, import_kubernetes_fluent_client3.K8s)(kindMap[type]).Delete(assets.name);
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
async function deployWebhook(assets, force, webhookTimeout) {
|
|
2561
|
+
logger_default.info("Establishing connection to Kubernetes");
|
|
2562
|
+
logger_default.info("Applying pepr-system namespace");
|
|
2563
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Namespace).Apply(getNamespace(assets.config.customLabels?.namespace));
|
|
2564
|
+
await handleWebhookConfiguration(assets, "mutate" /* MUTATE */, webhookTimeout, force);
|
|
2565
|
+
await handleWebhookConfiguration(assets, "validate" /* VALIDATE */, webhookTimeout, force);
|
|
2566
|
+
logger_default.info("Applying the Pepr Store CRD if it doesn't exist");
|
|
2567
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.CustomResourceDefinition).Apply(peprStoreCRD, { force });
|
|
2568
|
+
if (assets.host) return;
|
|
2569
|
+
const code = await import_fs10.promises.readFile(assets.path);
|
|
2570
|
+
if (!code.length) throw new Error("No code provided");
|
|
2571
|
+
const hash = import_crypto3.default.createHash("sha256").update(code).digest("hex");
|
|
2572
|
+
await setupRBAC(assets.name, assets.capabilities, force, assets.config);
|
|
2573
|
+
await setupController(assets, code, hash, force);
|
|
2574
|
+
await setupWatcher(assets, hash, force);
|
|
2575
|
+
}
|
|
2576
|
+
async function setupRBAC(name2, capabilities, force, config) {
|
|
2577
|
+
const { rbacMode, rbac } = config;
|
|
2578
|
+
logger_default.info("Applying cluster role binding");
|
|
2579
|
+
const crb = clusterRoleBinding(name2);
|
|
2580
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ClusterRoleBinding).Apply(crb, { force });
|
|
2581
|
+
logger_default.info("Applying cluster role");
|
|
2582
|
+
const cr = clusterRole(name2, capabilities, rbacMode, rbac);
|
|
2583
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ClusterRole).Apply(cr, { force });
|
|
2584
|
+
logger_default.info("Applying service account");
|
|
2585
|
+
const sa = serviceAccount(name2);
|
|
2586
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.ServiceAccount).Apply(sa, { force });
|
|
2587
|
+
logger_default.info("Applying store role");
|
|
2588
|
+
const role = storeRole(name2);
|
|
2589
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Role).Apply(role, { force });
|
|
2590
|
+
logger_default.info("Applying store role binding");
|
|
2591
|
+
const roleBinding = storeRoleBinding(name2);
|
|
2592
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.RoleBinding).Apply(roleBinding, { force });
|
|
2593
|
+
}
|
|
2594
|
+
async function setupController(assets, code, hash, force) {
|
|
2595
|
+
const { name: name2 } = assets;
|
|
2596
|
+
logger_default.info("Applying module secret");
|
|
2597
|
+
const mod = getModuleSecret(name2, code, hash);
|
|
2598
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(mod, { force });
|
|
2599
|
+
logger_default.info("Applying controller service");
|
|
2600
|
+
const svc = service(name2);
|
|
2601
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Service).Apply(svc, { force });
|
|
2602
|
+
logger_default.info("Applying TLS secret");
|
|
2603
|
+
const tls = tlsSecret(name2, assets.tls);
|
|
2604
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(tls, { force });
|
|
2605
|
+
logger_default.info("Applying API token secret");
|
|
2606
|
+
const apiToken = apiTokenSecret(name2, assets.apiToken);
|
|
2607
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Secret).Apply(apiToken, { force });
|
|
2608
|
+
logger_default.info("Applying deployment");
|
|
2609
|
+
const dep = getDeployment(assets, hash, assets.buildTimestamp);
|
|
2610
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Deployment).Apply(dep, { force });
|
|
2611
|
+
}
|
|
2612
|
+
async function setupWatcher(assets, hash, force) {
|
|
2613
|
+
const watchDeployment = getWatcher(assets, hash, assets.buildTimestamp);
|
|
2614
|
+
if (watchDeployment) {
|
|
2615
|
+
logger_default.info("Applying watcher deployment");
|
|
2616
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Deployment).Apply(watchDeployment, { force });
|
|
2617
|
+
logger_default.info("Applying watcher service");
|
|
2618
|
+
const watchSvc = watcherService(assets.name);
|
|
2619
|
+
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Service).Apply(watchSvc, { force });
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2593
2623
|
// src/lib/deploymentChecks.ts
|
|
2594
2624
|
var import_kubernetes_fluent_client4 = require("kubernetes-fluent-client");
|
|
2595
2625
|
async function checkDeploymentStatus(namespace) {
|
|
@@ -2703,11 +2733,12 @@ function deploy_default(program2) {
|
|
|
2703
2733
|
}
|
|
2704
2734
|
const webhook = new Assets(
|
|
2705
2735
|
{ ...builtModule.cfg.pepr, description: builtModule.cfg.description },
|
|
2706
|
-
builtModule.path
|
|
2736
|
+
builtModule.path,
|
|
2737
|
+
[]
|
|
2707
2738
|
);
|
|
2708
2739
|
webhook.image = opts.image ?? webhook.image;
|
|
2709
2740
|
try {
|
|
2710
|
-
await webhook.deploy(opts.force, builtModule.cfg.pepr.webhookTimeout ?? 10);
|
|
2741
|
+
await webhook.deploy(deployWebhook, opts.force, builtModule.cfg.pepr.webhookTimeout ?? 10);
|
|
2711
2742
|
validateCapabilityNames(webhook.capabilities);
|
|
2712
2743
|
await namespaceDeploymentsReady();
|
|
2713
2744
|
console.info(`\u2705 Module deployed successfully`);
|
|
@@ -2719,10 +2750,10 @@ function deploy_default(program2) {
|
|
|
2719
2750
|
}
|
|
2720
2751
|
|
|
2721
2752
|
// src/cli/dev.ts
|
|
2722
|
-
var import_child_process4 = require("child_process");
|
|
2723
|
-
var import_fs10 = require("fs");
|
|
2724
2753
|
var import_prompts2 = __toESM(require("prompts"));
|
|
2754
|
+
var import_child_process4 = require("child_process");
|
|
2725
2755
|
var import_kubernetes_fluent_client5 = require("kubernetes-fluent-client");
|
|
2756
|
+
var import_fs11 = require("fs");
|
|
2726
2757
|
function dev_default(program2) {
|
|
2727
2758
|
program2.command("dev").description("Setup a local webhook development environment").option("-h, --host [host]", "Host to listen on", "host.k3d.internal").option("--confirm", "Skip confirmation prompt").action(async (opts) => {
|
|
2728
2759
|
if (!opts.confirm) {
|
|
@@ -2742,10 +2773,11 @@ function dev_default(program2) {
|
|
|
2742
2773
|
description: cfg.description
|
|
2743
2774
|
},
|
|
2744
2775
|
path,
|
|
2776
|
+
[],
|
|
2745
2777
|
opts.host
|
|
2746
2778
|
);
|
|
2747
|
-
await
|
|
2748
|
-
await
|
|
2779
|
+
await import_fs11.promises.writeFile("insecure-tls.crt", webhook.tls.pem.crt);
|
|
2780
|
+
await import_fs11.promises.writeFile("insecure-tls.key", webhook.tls.pem.key);
|
|
2749
2781
|
try {
|
|
2750
2782
|
let program3;
|
|
2751
2783
|
const name2 = `pepr-${cfg.pepr.uuid}`;
|
|
@@ -2753,7 +2785,7 @@ function dev_default(program2) {
|
|
|
2753
2785
|
const store = `pepr-${cfg.pepr.uuid}-store`;
|
|
2754
2786
|
const runFork = async () => {
|
|
2755
2787
|
console.info(`Running module ${path}`);
|
|
2756
|
-
await webhook.deploy(false, 30);
|
|
2788
|
+
await webhook.deploy(deployWebhook, false, 30);
|
|
2757
2789
|
try {
|
|
2758
2790
|
validateCapabilityNames(webhook.capabilities);
|
|
2759
2791
|
} catch (e) {
|
|
@@ -2804,7 +2836,7 @@ function dev_default(program2) {
|
|
|
2804
2836
|
}
|
|
2805
2837
|
|
|
2806
2838
|
// src/cli/monitor.ts
|
|
2807
|
-
var
|
|
2839
|
+
var import_client_node6 = require("@kubernetes/client-node");
|
|
2808
2840
|
var import_kubernetes_fluent_client6 = require("kubernetes-fluent-client");
|
|
2809
2841
|
var import_stream = __toESM(require("stream"));
|
|
2810
2842
|
function monitor_default(program2) {
|
|
@@ -2840,9 +2872,9 @@ function getLabelsAndErrorMessage(uuid) {
|
|
|
2840
2872
|
return { labels, errorMessage };
|
|
2841
2873
|
}
|
|
2842
2874
|
function getK8sLogFromKubeConfig() {
|
|
2843
|
-
const kc = new
|
|
2875
|
+
const kc = new import_client_node6.KubeConfig();
|
|
2844
2876
|
kc.loadFromDefault();
|
|
2845
|
-
return new
|
|
2877
|
+
return new import_client_node6.Log(kc);
|
|
2846
2878
|
}
|
|
2847
2879
|
function createLogStream() {
|
|
2848
2880
|
const logStream = new import_stream.default.PassThrough();
|
|
@@ -2898,7 +2930,7 @@ var import_path4 = require("path");
|
|
|
2898
2930
|
var import_prompts4 = __toESM(require("prompts"));
|
|
2899
2931
|
|
|
2900
2932
|
// src/cli/init/walkthrough.ts
|
|
2901
|
-
var
|
|
2933
|
+
var import_fs12 = require("fs");
|
|
2902
2934
|
var import_prompts3 = __toESM(require("prompts"));
|
|
2903
2935
|
|
|
2904
2936
|
// src/cli/init/enums.ts
|
|
@@ -2929,7 +2961,7 @@ async function setName(name2) {
|
|
|
2929
2961
|
validate: async (val) => {
|
|
2930
2962
|
try {
|
|
2931
2963
|
const name3 = sanitizeName(val);
|
|
2932
|
-
await
|
|
2964
|
+
await import_fs12.promises.access(name3, import_fs12.promises.constants.F_OK);
|
|
2933
2965
|
return "A directory with this name already exists";
|
|
2934
2966
|
} catch (e) {
|
|
2935
2967
|
return val.length > 2 || "The name must be at least 3 characters long";
|
|
@@ -3097,9 +3129,9 @@ function uuid_default(program2) {
|
|
|
3097
3129
|
} else {
|
|
3098
3130
|
deployments = await (0, import_kubernetes_fluent_client7.K8s)(import_kubernetes_fluent_client7.kind.Deployment).InNamespace("pepr-system").WithLabel("pepr.dev/uuid", uuid).Get();
|
|
3099
3131
|
}
|
|
3100
|
-
deployments.items.map((
|
|
3101
|
-
const uuid2 =
|
|
3102
|
-
const description =
|
|
3132
|
+
deployments.items.map((deploy) => {
|
|
3133
|
+
const uuid2 = deploy.metadata?.labels?.["pepr.dev/uuid"] || "";
|
|
3134
|
+
const description = deploy.metadata?.annotations?.["pepr.dev/description"] || "";
|
|
3103
3135
|
if (uuid2 !== "") {
|
|
3104
3136
|
uuidTable[uuid2] = description;
|
|
3105
3137
|
}
|
|
@@ -3124,7 +3156,7 @@ var RootCmd = class extends import_commander2.Command {
|
|
|
3124
3156
|
|
|
3125
3157
|
// src/cli/update.ts
|
|
3126
3158
|
var import_child_process6 = require("child_process");
|
|
3127
|
-
var
|
|
3159
|
+
var import_fs13 = __toESM(require("fs"));
|
|
3128
3160
|
var import_path5 = require("path");
|
|
3129
3161
|
var import_prompts5 = __toESM(require("prompts"));
|
|
3130
3162
|
function update_default(program2) {
|
|
@@ -3164,12 +3196,12 @@ function update_default(program2) {
|
|
|
3164
3196
|
await write((0, import_path5.resolve)(".vscode", snippet.path), snippet.data);
|
|
3165
3197
|
await write((0, import_path5.resolve)(".vscode", codeSettings.path), codeSettings.data);
|
|
3166
3198
|
const samplePath = (0, import_path5.resolve)("capabilities", samplesYaml.path);
|
|
3167
|
-
if (
|
|
3168
|
-
|
|
3199
|
+
if (import_fs13.default.existsSync(samplePath)) {
|
|
3200
|
+
import_fs13.default.unlinkSync(samplePath);
|
|
3169
3201
|
await write(samplePath, samplesYaml.data);
|
|
3170
3202
|
}
|
|
3171
3203
|
const tsPath = (0, import_path5.resolve)("capabilities", helloPepr.path);
|
|
3172
|
-
if (
|
|
3204
|
+
if (import_fs13.default.existsSync(tsPath)) {
|
|
3173
3205
|
await write(tsPath, helloPepr.data);
|
|
3174
3206
|
}
|
|
3175
3207
|
}
|
|
@@ -3216,7 +3248,7 @@ var program = new RootCmd();
|
|
|
3216
3248
|
if (!process.env.PEPR_NODE_WARNINGS) {
|
|
3217
3249
|
process.removeAllListeners("warning");
|
|
3218
3250
|
}
|
|
3219
|
-
program.version(
|
|
3251
|
+
program.version(version).description(`Pepr (v${version}) - Type safe K8s middleware for humans`).action(() => {
|
|
3220
3252
|
if (program.args.length < 1) {
|
|
3221
3253
|
console.log(banner);
|
|
3222
3254
|
program.help();
|