pepr 0.13.4 → 0.14.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/README.md +23 -4
- package/dist/cli.js +375 -204
- package/dist/controller.js +1 -1
- package/dist/lib/assets/deploy.d.ts.map +1 -1
- package/dist/lib/assets/destroy.d.ts +2 -0
- package/dist/lib/assets/destroy.d.ts.map +1 -0
- package/dist/lib/assets/index.d.ts +6 -5
- package/dist/lib/assets/index.d.ts.map +1 -1
- package/dist/lib/assets/networking.d.ts +6 -5
- package/dist/lib/assets/networking.d.ts.map +1 -1
- package/dist/lib/assets/pods.d.ts +84 -4
- package/dist/lib/assets/pods.d.ts.map +1 -1
- package/dist/lib/assets/rbac.d.ts +6 -4
- package/dist/lib/assets/rbac.d.ts.map +1 -1
- package/dist/lib/assets/store.d.ts +7 -0
- package/dist/lib/assets/store.d.ts.map +1 -0
- package/dist/lib/assets/webhooks.d.ts +2 -2
- package/dist/lib/assets/webhooks.d.ts.map +1 -1
- package/dist/lib/assets/yaml.d.ts.map +1 -1
- package/dist/lib/capability.d.ts +21 -4
- package/dist/lib/capability.d.ts.map +1 -1
- package/dist/lib/controller/index.d.ts +10 -0
- package/dist/lib/controller/index.d.ts.map +1 -0
- package/dist/lib/controller/store.d.ts +7 -0
- package/dist/lib/controller/store.d.ts.map +1 -0
- package/dist/lib/filter.d.ts +2 -2
- package/dist/lib/filter.d.ts.map +1 -1
- package/dist/lib/{k8s/types.d.ts → k8s.d.ts} +14 -25
- package/dist/lib/k8s.d.ts.map +1 -0
- package/dist/lib/metrics.d.ts +12 -12
- package/dist/lib/metrics.d.ts.map +1 -1
- package/dist/lib/module.d.ts +25 -4
- package/dist/lib/module.d.ts.map +1 -1
- package/dist/lib/mutate-processor.d.ts +3 -3
- package/dist/lib/mutate-processor.d.ts.map +1 -1
- package/dist/lib/mutate-request.d.ts +11 -10
- package/dist/lib/mutate-request.d.ts.map +1 -1
- package/dist/lib/storage.d.ts +56 -0
- package/dist/lib/storage.d.ts.map +1 -0
- package/dist/lib/tls.d.ts.map +1 -0
- package/dist/lib/types.d.ts +28 -48
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/validate-processor.d.ts +2 -2
- package/dist/lib/validate-processor.d.ts.map +1 -1
- package/dist/lib/validate-request.d.ts +9 -8
- package/dist/lib/validate-request.d.ts.map +1 -1
- package/dist/lib/watch-processor.d.ts +3 -0
- package/dist/lib/watch-processor.d.ts.map +1 -0
- package/dist/lib.d.ts +3 -7
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +484 -807
- package/dist/lib.js.map +4 -4
- package/package.json +13 -17
- package/src/lib/assets/deploy.ts +69 -127
- package/src/lib/assets/destroy.ts +33 -0
- package/src/lib/assets/index.ts +8 -14
- package/src/lib/assets/networking.ts +28 -5
- package/src/lib/assets/pods.ts +130 -11
- package/src/lib/assets/rbac.ts +42 -4
- package/src/lib/assets/store.ts +49 -0
- package/src/lib/assets/webhooks.ts +2 -2
- package/src/lib/assets/yaml.ts +13 -3
- package/src/lib/capability.ts +69 -14
- package/src/lib/{controller.ts → controller/index.ts} +25 -23
- package/src/lib/controller/store.ts +197 -0
- package/src/lib/filter.ts +2 -2
- package/src/lib/{k8s/types.ts → k8s.ts} +15 -26
- package/src/lib/metrics.ts +22 -38
- package/src/lib/module.ts +47 -10
- package/src/lib/mutate-processor.ts +6 -6
- package/src/lib/mutate-request.ts +18 -26
- package/src/lib/storage.ts +128 -0
- package/src/lib/types.ts +30 -53
- package/src/lib/validate-processor.ts +5 -4
- package/src/lib/validate-request.ts +15 -19
- package/src/lib/watch-processor.ts +55 -0
- package/src/lib.ts +4 -8
- package/src/templates/capabilities/hello-pepr.ts +54 -5
- package/src/templates/package.json +1 -0
- package/dist/lib/controller.d.ts +0 -10
- package/dist/lib/controller.d.ts.map +0 -1
- package/dist/lib/fetch.d.ts +0 -23
- package/dist/lib/fetch.d.ts.map +0 -1
- package/dist/lib/k8s/index.d.ts +0 -7
- package/dist/lib/k8s/index.d.ts.map +0 -1
- package/dist/lib/k8s/kinds.d.ts +0 -12
- package/dist/lib/k8s/kinds.d.ts.map +0 -1
- package/dist/lib/k8s/tls.d.ts.map +0 -1
- package/dist/lib/k8s/types.d.ts.map +0 -1
- package/dist/lib/k8s/upstream.d.ts +0 -4
- package/dist/lib/k8s/upstream.d.ts.map +0 -1
- package/jest.config.json +0 -4
- package/journey/before.ts +0 -21
- package/journey/k8s.ts +0 -100
- package/journey/pepr-build.ts +0 -69
- package/journey/pepr-deploy.ts +0 -174
- package/journey/pepr-dev.ts +0 -155
- package/journey/pepr-format.ts +0 -13
- package/journey/pepr-init.ts +0 -12
- package/src/lib/fetch.ts +0 -76
- package/src/lib/k8s/index.ts +0 -14
- package/src/lib/k8s/kinds.ts +0 -531
- package/src/lib/k8s/upstream.ts +0 -53
- /package/dist/lib/{k8s/tls.d.ts → tls.d.ts} +0 -0
- /package/src/lib/{k8s/tls.ts → tls.ts} +0 -0
package/dist/cli.js
CHANGED
|
@@ -112,10 +112,10 @@ var import_path = require("path");
|
|
|
112
112
|
// src/lib/assets/index.ts
|
|
113
113
|
var import_crypto3 = __toESM(require("crypto"));
|
|
114
114
|
|
|
115
|
-
// src/lib/
|
|
115
|
+
// src/lib/tls.ts
|
|
116
116
|
var import_node_forge = __toESM(require("node-forge"));
|
|
117
117
|
var caName = "Pepr Ephemeral CA";
|
|
118
|
-
function genTLS(
|
|
118
|
+
function genTLS(name2) {
|
|
119
119
|
const caKeys = import_node_forge.default.pki.rsa.generateKeyPair(2048);
|
|
120
120
|
const caCert = genCert(caKeys, caName, [{ name: "commonName", value: caName }]);
|
|
121
121
|
caCert.setExtensions([
|
|
@@ -133,7 +133,7 @@ function genTLS(name) {
|
|
|
133
133
|
}
|
|
134
134
|
]);
|
|
135
135
|
const serverKeys = import_node_forge.default.pki.rsa.generateKeyPair(2048);
|
|
136
|
-
const serverCert = genCert(serverKeys,
|
|
136
|
+
const serverCert = genCert(serverKeys, name2, caCert.subject.attributes);
|
|
137
137
|
caCert.sign(caKeys.privateKey, import_node_forge.default.md.sha256.create());
|
|
138
138
|
serverCert.sign(caKeys.privateKey, import_node_forge.default.md.sha256.create());
|
|
139
139
|
const pem = {
|
|
@@ -146,7 +146,7 @@ function genTLS(name) {
|
|
|
146
146
|
const crt = Buffer.from(pem.crt).toString("base64");
|
|
147
147
|
return { ca, key, crt, pem };
|
|
148
148
|
}
|
|
149
|
-
function genCert(key,
|
|
149
|
+
function genCert(key, name2, issuer) {
|
|
150
150
|
const crt = import_node_forge.default.pki.createCertificate();
|
|
151
151
|
crt.publicKey = key.publicKey;
|
|
152
152
|
crt.serialNumber = "01";
|
|
@@ -160,7 +160,7 @@ function genCert(key, name, issuer) {
|
|
|
160
160
|
{
|
|
161
161
|
type: 2,
|
|
162
162
|
// DNS
|
|
163
|
-
value:
|
|
163
|
+
value: name2
|
|
164
164
|
}
|
|
165
165
|
]
|
|
166
166
|
}
|
|
@@ -170,9 +170,9 @@ function genCert(key, name, issuer) {
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
// src/lib/assets/deploy.ts
|
|
173
|
-
var import_client_node = require("@kubernetes/client-node");
|
|
174
173
|
var import_crypto = __toESM(require("crypto"));
|
|
175
174
|
var import_fs = require("fs");
|
|
175
|
+
var import_kubernetes_fluent_client2 = require("kubernetes-fluent-client");
|
|
176
176
|
|
|
177
177
|
// src/lib/logger.ts
|
|
178
178
|
var import_pino = require("pino");
|
|
@@ -193,12 +193,12 @@ if (process.env.LOG_LEVEL) {
|
|
|
193
193
|
var logger_default = Log;
|
|
194
194
|
|
|
195
195
|
// src/lib/assets/networking.ts
|
|
196
|
-
function apiTokenSecret(
|
|
196
|
+
function apiTokenSecret(name2, apiToken) {
|
|
197
197
|
return {
|
|
198
198
|
apiVersion: "v1",
|
|
199
199
|
kind: "Secret",
|
|
200
200
|
metadata: {
|
|
201
|
-
name: `${
|
|
201
|
+
name: `${name2}-api-token`,
|
|
202
202
|
namespace: "pepr-system"
|
|
203
203
|
},
|
|
204
204
|
type: "Opaque",
|
|
@@ -207,12 +207,12 @@ function apiTokenSecret(name, apiToken) {
|
|
|
207
207
|
}
|
|
208
208
|
};
|
|
209
209
|
}
|
|
210
|
-
function tlsSecret(
|
|
210
|
+
function tlsSecret(name2, tls) {
|
|
211
211
|
return {
|
|
212
212
|
apiVersion: "v1",
|
|
213
213
|
kind: "Secret",
|
|
214
214
|
metadata: {
|
|
215
|
-
name: `${
|
|
215
|
+
name: `${name2}-tls`,
|
|
216
216
|
namespace: "pepr-system"
|
|
217
217
|
},
|
|
218
218
|
type: "kubernetes.io/tls",
|
|
@@ -222,17 +222,38 @@ function tlsSecret(name, tls) {
|
|
|
222
222
|
}
|
|
223
223
|
};
|
|
224
224
|
}
|
|
225
|
-
function service(
|
|
225
|
+
function service(name2) {
|
|
226
|
+
return {
|
|
227
|
+
apiVersion: "v1",
|
|
228
|
+
kind: "Service",
|
|
229
|
+
metadata: {
|
|
230
|
+
name: name2,
|
|
231
|
+
namespace: "pepr-system"
|
|
232
|
+
},
|
|
233
|
+
spec: {
|
|
234
|
+
selector: {
|
|
235
|
+
app: name2
|
|
236
|
+
},
|
|
237
|
+
ports: [
|
|
238
|
+
{
|
|
239
|
+
port: 443,
|
|
240
|
+
targetPort: 3e3
|
|
241
|
+
}
|
|
242
|
+
]
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
function watcherService(name2) {
|
|
226
247
|
return {
|
|
227
248
|
apiVersion: "v1",
|
|
228
249
|
kind: "Service",
|
|
229
250
|
metadata: {
|
|
230
|
-
name
|
|
251
|
+
name: `${name2}-watcher`,
|
|
231
252
|
namespace: "pepr-system"
|
|
232
253
|
},
|
|
233
254
|
spec: {
|
|
234
255
|
selector: {
|
|
235
|
-
app:
|
|
256
|
+
app: `${name2}-watcher`
|
|
236
257
|
},
|
|
237
258
|
ports: [
|
|
238
259
|
{
|
|
@@ -251,21 +272,32 @@ var namespace = {
|
|
|
251
272
|
kind: "Namespace",
|
|
252
273
|
metadata: { name: "pepr-system" }
|
|
253
274
|
};
|
|
254
|
-
function
|
|
255
|
-
const { name, image } = assets;
|
|
256
|
-
const app =
|
|
275
|
+
function watcher(assets, hash) {
|
|
276
|
+
const { name: name2, image, capabilities, config } = assets;
|
|
277
|
+
const app = `${name2}-watcher`;
|
|
278
|
+
const bindings = [];
|
|
279
|
+
for (const capability of capabilities) {
|
|
280
|
+
const watchers = capability.bindings.filter((binding) => binding.isWatch);
|
|
281
|
+
bindings.push(...watchers);
|
|
282
|
+
}
|
|
283
|
+
if (bindings.length < 1) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
257
286
|
return {
|
|
258
287
|
apiVersion: "apps/v1",
|
|
259
288
|
kind: "Deployment",
|
|
260
289
|
metadata: {
|
|
261
|
-
name,
|
|
290
|
+
name: app,
|
|
262
291
|
namespace: "pepr-system",
|
|
263
292
|
labels: {
|
|
264
293
|
app
|
|
265
294
|
}
|
|
266
295
|
},
|
|
267
296
|
spec: {
|
|
268
|
-
replicas:
|
|
297
|
+
replicas: 1,
|
|
298
|
+
strategy: {
|
|
299
|
+
type: "Recreate"
|
|
300
|
+
},
|
|
269
301
|
selector: {
|
|
270
302
|
matchLabels: {
|
|
271
303
|
app
|
|
@@ -278,11 +310,10 @@ function deployment(assets, hash) {
|
|
|
278
310
|
}
|
|
279
311
|
},
|
|
280
312
|
spec: {
|
|
281
|
-
|
|
282
|
-
serviceAccountName: name,
|
|
313
|
+
serviceAccountName: name2,
|
|
283
314
|
containers: [
|
|
284
315
|
{
|
|
285
|
-
name: "
|
|
316
|
+
name: "watcher",
|
|
286
317
|
image,
|
|
287
318
|
imagePullPolicy: "IfNotPresent",
|
|
288
319
|
command: ["node", "/app/node_modules/pepr/dist/controller.js", hash],
|
|
@@ -305,10 +336,106 @@ function deployment(assets, hash) {
|
|
|
305
336
|
containerPort: 3e3
|
|
306
337
|
}
|
|
307
338
|
],
|
|
339
|
+
resources: {
|
|
340
|
+
requests: {
|
|
341
|
+
memory: "64Mi",
|
|
342
|
+
cpu: "100m"
|
|
343
|
+
},
|
|
344
|
+
limits: {
|
|
345
|
+
memory: "256Mi",
|
|
346
|
+
cpu: "500m"
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
volumeMounts: [
|
|
350
|
+
{
|
|
351
|
+
name: "tls-certs",
|
|
352
|
+
mountPath: "/etc/certs",
|
|
353
|
+
readOnly: true
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
name: "module",
|
|
357
|
+
mountPath: `/app/load`,
|
|
358
|
+
readOnly: true
|
|
359
|
+
}
|
|
360
|
+
],
|
|
308
361
|
env: [
|
|
362
|
+
{ name: "PEPR_WATCH_MODE", value: "true" },
|
|
363
|
+
{ name: "PEPR_PRETTY_LOG", value: "false" },
|
|
364
|
+
{ name: "LOG_LEVEL", value: config.logLevel || "debug" }
|
|
365
|
+
]
|
|
366
|
+
}
|
|
367
|
+
],
|
|
368
|
+
volumes: [
|
|
369
|
+
{
|
|
370
|
+
name: "tls-certs",
|
|
371
|
+
secret: {
|
|
372
|
+
secretName: `${name2}-tls`
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
name: "module",
|
|
377
|
+
secret: {
|
|
378
|
+
secretName: `${name2}-module`
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
]
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
function deployment(assets, hash) {
|
|
388
|
+
const { name: name2, image, config } = assets;
|
|
389
|
+
const app = name2;
|
|
390
|
+
return {
|
|
391
|
+
apiVersion: "apps/v1",
|
|
392
|
+
kind: "Deployment",
|
|
393
|
+
metadata: {
|
|
394
|
+
name: name2,
|
|
395
|
+
namespace: "pepr-system",
|
|
396
|
+
labels: {
|
|
397
|
+
app
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
spec: {
|
|
401
|
+
replicas: 2,
|
|
402
|
+
selector: {
|
|
403
|
+
matchLabels: {
|
|
404
|
+
app
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
template: {
|
|
408
|
+
metadata: {
|
|
409
|
+
labels: {
|
|
410
|
+
app
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
spec: {
|
|
414
|
+
priorityClassName: "system-node-critical",
|
|
415
|
+
serviceAccountName: name2,
|
|
416
|
+
containers: [
|
|
417
|
+
{
|
|
418
|
+
name: "server",
|
|
419
|
+
image,
|
|
420
|
+
imagePullPolicy: "IfNotPresent",
|
|
421
|
+
command: ["node", "/app/node_modules/pepr/dist/controller.js", hash],
|
|
422
|
+
readinessProbe: {
|
|
423
|
+
httpGet: {
|
|
424
|
+
path: "/healthz",
|
|
425
|
+
port: 3e3,
|
|
426
|
+
scheme: "HTTPS"
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
livenessProbe: {
|
|
430
|
+
httpGet: {
|
|
431
|
+
path: "/healthz",
|
|
432
|
+
port: 3e3,
|
|
433
|
+
scheme: "HTTPS"
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
ports: [
|
|
309
437
|
{
|
|
310
|
-
|
|
311
|
-
value: "false"
|
|
438
|
+
containerPort: 3e3
|
|
312
439
|
}
|
|
313
440
|
],
|
|
314
441
|
resources: {
|
|
@@ -321,6 +448,10 @@ function deployment(assets, hash) {
|
|
|
321
448
|
cpu: "500m"
|
|
322
449
|
}
|
|
323
450
|
},
|
|
451
|
+
env: [
|
|
452
|
+
{ name: "PEPR_PRETTY_LOG", value: "false" },
|
|
453
|
+
{ name: "LOG_LEVEL", value: config.logLevel || "debug" }
|
|
454
|
+
],
|
|
324
455
|
volumeMounts: [
|
|
325
456
|
{
|
|
326
457
|
name: "tls-certs",
|
|
@@ -344,19 +475,19 @@ function deployment(assets, hash) {
|
|
|
344
475
|
{
|
|
345
476
|
name: "tls-certs",
|
|
346
477
|
secret: {
|
|
347
|
-
secretName: `${
|
|
478
|
+
secretName: `${name2}-tls`
|
|
348
479
|
}
|
|
349
480
|
},
|
|
350
481
|
{
|
|
351
482
|
name: "api-token",
|
|
352
483
|
secret: {
|
|
353
|
-
secretName: `${
|
|
484
|
+
secretName: `${name2}-api-token`
|
|
354
485
|
}
|
|
355
486
|
},
|
|
356
487
|
{
|
|
357
488
|
name: "module",
|
|
358
489
|
secret: {
|
|
359
|
-
secretName: `${
|
|
490
|
+
secretName: `${name2}-module`
|
|
360
491
|
}
|
|
361
492
|
}
|
|
362
493
|
]
|
|
@@ -365,14 +496,14 @@ function deployment(assets, hash) {
|
|
|
365
496
|
}
|
|
366
497
|
};
|
|
367
498
|
}
|
|
368
|
-
function moduleSecret(
|
|
499
|
+
function moduleSecret(name2, data, hash) {
|
|
369
500
|
const compressed = (0, import_zlib.gzipSync)(data);
|
|
370
501
|
const path = `module-${hash}.js.gz`;
|
|
371
502
|
return {
|
|
372
503
|
apiVersion: "v1",
|
|
373
504
|
kind: "Secret",
|
|
374
505
|
metadata: {
|
|
375
|
-
name: `${
|
|
506
|
+
name: `${name2}-module`,
|
|
376
507
|
namespace: "pepr-system"
|
|
377
508
|
},
|
|
378
509
|
type: "Opaque",
|
|
@@ -383,11 +514,11 @@ function moduleSecret(name, data, hash) {
|
|
|
383
514
|
}
|
|
384
515
|
|
|
385
516
|
// src/lib/assets/rbac.ts
|
|
386
|
-
function clusterRole(
|
|
517
|
+
function clusterRole(name2) {
|
|
387
518
|
return {
|
|
388
519
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
389
520
|
kind: "ClusterRole",
|
|
390
|
-
metadata: { name },
|
|
521
|
+
metadata: { name: name2 },
|
|
391
522
|
rules: [
|
|
392
523
|
{
|
|
393
524
|
// @todo: make this configurable
|
|
@@ -398,35 +529,125 @@ function clusterRole(name) {
|
|
|
398
529
|
]
|
|
399
530
|
};
|
|
400
531
|
}
|
|
401
|
-
function clusterRoleBinding(
|
|
532
|
+
function clusterRoleBinding(name2) {
|
|
402
533
|
return {
|
|
403
534
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
404
535
|
kind: "ClusterRoleBinding",
|
|
405
|
-
metadata: { name },
|
|
536
|
+
metadata: { name: name2 },
|
|
406
537
|
roleRef: {
|
|
407
538
|
apiGroup: "rbac.authorization.k8s.io",
|
|
408
539
|
kind: "ClusterRole",
|
|
409
|
-
name
|
|
540
|
+
name: name2
|
|
410
541
|
},
|
|
411
542
|
subjects: [
|
|
412
543
|
{
|
|
413
544
|
kind: "ServiceAccount",
|
|
414
|
-
name,
|
|
545
|
+
name: name2,
|
|
415
546
|
namespace: "pepr-system"
|
|
416
547
|
}
|
|
417
548
|
]
|
|
418
549
|
};
|
|
419
550
|
}
|
|
420
|
-
function serviceAccount(
|
|
551
|
+
function serviceAccount(name2) {
|
|
421
552
|
return {
|
|
422
553
|
apiVersion: "v1",
|
|
423
554
|
kind: "ServiceAccount",
|
|
424
555
|
metadata: {
|
|
425
|
-
name,
|
|
556
|
+
name: name2,
|
|
426
557
|
namespace: "pepr-system"
|
|
427
558
|
}
|
|
428
559
|
};
|
|
429
560
|
}
|
|
561
|
+
function storeRole(name2) {
|
|
562
|
+
name2 = `${name2}-store`;
|
|
563
|
+
return {
|
|
564
|
+
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
565
|
+
kind: "Role",
|
|
566
|
+
metadata: { name: name2, namespace: "pepr-system" },
|
|
567
|
+
rules: [
|
|
568
|
+
{
|
|
569
|
+
apiGroups: ["pepr.dev/*"],
|
|
570
|
+
resources: ["peprstores"],
|
|
571
|
+
resourceNames: [""],
|
|
572
|
+
verbs: ["create", "get", "patch", "watch"]
|
|
573
|
+
}
|
|
574
|
+
]
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
function storeRoleBinding(name2) {
|
|
578
|
+
name2 = `${name2}-store`;
|
|
579
|
+
return {
|
|
580
|
+
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
581
|
+
kind: "RoleBinding",
|
|
582
|
+
metadata: { name: name2, namespace: "pepr-system" },
|
|
583
|
+
roleRef: {
|
|
584
|
+
apiGroup: "rbac.authorization.k8s.io",
|
|
585
|
+
kind: "Role",
|
|
586
|
+
name: name2
|
|
587
|
+
},
|
|
588
|
+
subjects: [
|
|
589
|
+
{
|
|
590
|
+
kind: "ServiceAccount",
|
|
591
|
+
name: name2,
|
|
592
|
+
namespace: "pepr-system"
|
|
593
|
+
}
|
|
594
|
+
]
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// src/lib/k8s.ts
|
|
599
|
+
var import_kubernetes_fluent_client = require("kubernetes-fluent-client");
|
|
600
|
+
var PeprStore = class extends import_kubernetes_fluent_client.GenericKind {
|
|
601
|
+
};
|
|
602
|
+
var peprStoreGVK = {
|
|
603
|
+
kind: "PeprStore",
|
|
604
|
+
version: "v1",
|
|
605
|
+
group: "pepr.dev"
|
|
606
|
+
};
|
|
607
|
+
(0, import_kubernetes_fluent_client.RegisterKind)(PeprStore, peprStoreGVK);
|
|
608
|
+
|
|
609
|
+
// src/lib/assets/store.ts
|
|
610
|
+
var { group, version, kind } = peprStoreGVK;
|
|
611
|
+
var singular = kind.toLocaleLowerCase();
|
|
612
|
+
var plural = `${singular}s`;
|
|
613
|
+
var name = `${plural}.${group}`;
|
|
614
|
+
var peprStoreCRD = {
|
|
615
|
+
apiVersion: "apiextensions.k8s.io/v1",
|
|
616
|
+
kind: "CustomResourceDefinition",
|
|
617
|
+
metadata: {
|
|
618
|
+
name
|
|
619
|
+
},
|
|
620
|
+
spec: {
|
|
621
|
+
group,
|
|
622
|
+
versions: [
|
|
623
|
+
{
|
|
624
|
+
// typescript doesn't know this is really already set, which is kind of annoying
|
|
625
|
+
name: version || "v1",
|
|
626
|
+
served: true,
|
|
627
|
+
storage: true,
|
|
628
|
+
schema: {
|
|
629
|
+
openAPIV3Schema: {
|
|
630
|
+
type: "object",
|
|
631
|
+
properties: {
|
|
632
|
+
data: {
|
|
633
|
+
type: "object",
|
|
634
|
+
additionalProperties: {
|
|
635
|
+
type: "string"
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
],
|
|
643
|
+
scope: "Namespaced",
|
|
644
|
+
names: {
|
|
645
|
+
plural,
|
|
646
|
+
singular,
|
|
647
|
+
kind
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
};
|
|
430
651
|
|
|
431
652
|
// src/lib/assets/webhooks.ts
|
|
432
653
|
var import_ramda = require("ramda");
|
|
@@ -442,7 +663,7 @@ async function generateWebhookRules(assets, isMutateWebhook) {
|
|
|
442
663
|
for (const capability of capabilities) {
|
|
443
664
|
console.info(`Module ${config.uuid} has capability: ${capability.name}`);
|
|
444
665
|
for (const binding of capability.bindings) {
|
|
445
|
-
const { event, kind, isMutate, isValidate } = binding;
|
|
666
|
+
const { event, kind: kind3, isMutate, isValidate } = binding;
|
|
446
667
|
if (isMutateWebhook && !isMutate) {
|
|
447
668
|
continue;
|
|
448
669
|
}
|
|
@@ -455,10 +676,10 @@ async function generateWebhookRules(assets, isMutateWebhook) {
|
|
|
455
676
|
} else {
|
|
456
677
|
operations.push(event);
|
|
457
678
|
}
|
|
458
|
-
const resource =
|
|
679
|
+
const resource = kind3.plural || `${kind3.kind.toLowerCase()}s`;
|
|
459
680
|
rules.push({
|
|
460
|
-
apiGroups: [
|
|
461
|
-
apiVersions: [
|
|
681
|
+
apiGroups: [kind3.group],
|
|
682
|
+
apiVersions: [kind3.version || "*"],
|
|
462
683
|
operations,
|
|
463
684
|
resources: [resource]
|
|
464
685
|
});
|
|
@@ -468,7 +689,7 @@ async function generateWebhookRules(assets, isMutateWebhook) {
|
|
|
468
689
|
}
|
|
469
690
|
async function webhookConfig(assets, mutateOrValidate, timeoutSeconds = 10) {
|
|
470
691
|
const ignore = [peprIgnoreLabel];
|
|
471
|
-
const { name, tls, config, apiToken, host } = assets;
|
|
692
|
+
const { name: name2, tls, config, apiToken, host } = assets;
|
|
472
693
|
const ignoreNS = (0, import_ramda.concat)(peprIgnoreNamespaces, config.alwaysIgnore.namespaces || []);
|
|
473
694
|
if (ignoreNS) {
|
|
474
695
|
ignore.push({
|
|
@@ -485,7 +706,7 @@ async function webhookConfig(assets, mutateOrValidate, timeoutSeconds = 10) {
|
|
|
485
706
|
clientConfig.url = `https://${host}:3000${apiPath}`;
|
|
486
707
|
} else {
|
|
487
708
|
clientConfig.service = {
|
|
488
|
-
name,
|
|
709
|
+
name: name2,
|
|
489
710
|
namespace: "pepr-system",
|
|
490
711
|
path: apiPath
|
|
491
712
|
};
|
|
@@ -498,10 +719,10 @@ async function webhookConfig(assets, mutateOrValidate, timeoutSeconds = 10) {
|
|
|
498
719
|
return {
|
|
499
720
|
apiVersion: "admissionregistration.k8s.io/v1",
|
|
500
721
|
kind: isMutate ? "MutatingWebhookConfiguration" : "ValidatingWebhookConfiguration",
|
|
501
|
-
metadata: { name },
|
|
722
|
+
metadata: { name: name2 },
|
|
502
723
|
webhooks: [
|
|
503
724
|
{
|
|
504
|
-
name: `${
|
|
725
|
+
name: `${name2}.pepr.dev`,
|
|
505
726
|
admissionReviewVersions: ["v1", "v1beta1"],
|
|
506
727
|
clientConfig,
|
|
507
728
|
failurePolicy: "Ignore",
|
|
@@ -524,137 +745,82 @@ async function webhookConfig(assets, mutateOrValidate, timeoutSeconds = 10) {
|
|
|
524
745
|
// src/lib/assets/deploy.ts
|
|
525
746
|
async function deploy(assets, webhookTimeout) {
|
|
526
747
|
logger_default.info("Establishing connection to Kubernetes");
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
kubeConfig.loadFromDefault();
|
|
531
|
-
const coreV1Api = kubeConfig.makeApiClient(import_client_node.CoreV1Api);
|
|
532
|
-
const admissionApi = kubeConfig.makeApiClient(import_client_node.AdmissionregistrationV1Api);
|
|
533
|
-
try {
|
|
534
|
-
logger_default.info("Checking for namespace");
|
|
535
|
-
await coreV1Api.readNamespace(peprNS);
|
|
536
|
-
} catch (e) {
|
|
537
|
-
logger_default.debug(e instanceof import_client_node.HttpError ? e.body : e);
|
|
538
|
-
logger_default.info("Creating namespace");
|
|
539
|
-
await coreV1Api.createNamespace(namespace);
|
|
540
|
-
}
|
|
748
|
+
const { name: name2, host, path } = assets;
|
|
749
|
+
logger_default.info("Applying pepr-system namespace");
|
|
750
|
+
await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.Namespace).Apply(namespace);
|
|
541
751
|
const mutateWebhook = await webhookConfig(assets, "mutate", webhookTimeout);
|
|
542
752
|
if (mutateWebhook) {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
logger_default.info("Removing and re-creating mutating webhook");
|
|
549
|
-
await admissionApi.deleteMutatingWebhookConfiguration(mutateWebhook.metadata?.name ?? "");
|
|
550
|
-
await admissionApi.createMutatingWebhookConfiguration(mutateWebhook);
|
|
551
|
-
}
|
|
753
|
+
logger_default.info("Applying mutating webhook");
|
|
754
|
+
await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.MutatingWebhookConfiguration).Apply(mutateWebhook);
|
|
755
|
+
} else {
|
|
756
|
+
logger_default.info("Mutating webhook not needed, removing if it exists");
|
|
757
|
+
await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.MutatingWebhookConfiguration).Delete(name2);
|
|
552
758
|
}
|
|
553
759
|
const validateWebhook = await webhookConfig(assets, "validate", webhookTimeout);
|
|
554
760
|
if (validateWebhook) {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
logger_default.info("Removing and re-creating validating webhook");
|
|
561
|
-
await admissionApi.deleteValidatingWebhookConfiguration(validateWebhook.metadata?.name ?? "");
|
|
562
|
-
await admissionApi.createValidatingWebhookConfiguration(validateWebhook);
|
|
563
|
-
}
|
|
761
|
+
logger_default.info("Applying validating webhook");
|
|
762
|
+
await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.ValidatingWebhookConfiguration).Apply(validateWebhook);
|
|
763
|
+
} else {
|
|
764
|
+
logger_default.info("Validating webhook not needed, removing if it exists");
|
|
765
|
+
await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.ValidatingWebhookConfiguration).Delete(name2);
|
|
564
766
|
}
|
|
767
|
+
logger_default.info("Applying the Pepr Store CRD if it doesn't exist");
|
|
768
|
+
await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.CustomResourceDefinition).Apply(peprStoreCRD);
|
|
565
769
|
if (host) {
|
|
566
770
|
return;
|
|
567
771
|
}
|
|
568
|
-
if (!path) {
|
|
569
|
-
throw new Error("No code provided");
|
|
570
|
-
}
|
|
571
772
|
const code = await import_fs.promises.readFile(path);
|
|
572
773
|
const hash = import_crypto.default.createHash("sha256").update(code).digest("hex");
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
const crb = clusterRoleBinding(name);
|
|
576
|
-
try {
|
|
577
|
-
logger_default.info("Creating cluster role binding");
|
|
578
|
-
await rbacApi.createClusterRoleBinding(crb);
|
|
579
|
-
} catch (e) {
|
|
580
|
-
logger_default.debug(e instanceof import_client_node.HttpError ? e.body : e);
|
|
581
|
-
logger_default.info("Removing and re-creating cluster role binding");
|
|
582
|
-
await rbacApi.deleteClusterRoleBinding(crb.metadata?.name ?? "");
|
|
583
|
-
await rbacApi.createClusterRoleBinding(crb);
|
|
584
|
-
}
|
|
585
|
-
const cr = clusterRole(name);
|
|
586
|
-
try {
|
|
587
|
-
logger_default.info("Creating cluster role");
|
|
588
|
-
await rbacApi.createClusterRole(cr);
|
|
589
|
-
} catch (e) {
|
|
590
|
-
logger_default.debug(e instanceof import_client_node.HttpError ? e.body : e);
|
|
591
|
-
logger_default.info("Removing and re-creating the cluster role");
|
|
592
|
-
try {
|
|
593
|
-
await rbacApi.deleteClusterRole(cr.metadata?.name ?? "");
|
|
594
|
-
await rbacApi.createClusterRole(cr);
|
|
595
|
-
} catch (e2) {
|
|
596
|
-
logger_default.debug(e2 instanceof import_client_node.HttpError ? e2.body : e2);
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
const sa = serviceAccount(name);
|
|
600
|
-
try {
|
|
601
|
-
logger_default.info("Creating service account");
|
|
602
|
-
await coreV1Api.createNamespacedServiceAccount(peprNS, sa);
|
|
603
|
-
} catch (e) {
|
|
604
|
-
logger_default.debug(e instanceof import_client_node.HttpError ? e.body : e);
|
|
605
|
-
logger_default.info("Removing and re-creating service account");
|
|
606
|
-
await coreV1Api.deleteNamespacedServiceAccount(sa.metadata?.name ?? "", peprNS);
|
|
607
|
-
await coreV1Api.createNamespacedServiceAccount(peprNS, sa);
|
|
608
|
-
}
|
|
609
|
-
const mod = moduleSecret(name, code, hash);
|
|
610
|
-
try {
|
|
611
|
-
logger_default.info("Creating module secret");
|
|
612
|
-
await coreV1Api.createNamespacedSecret(peprNS, mod);
|
|
613
|
-
} catch (e) {
|
|
614
|
-
logger_default.debug(e instanceof import_client_node.HttpError ? e.body : e);
|
|
615
|
-
logger_default.info("Removing and re-creating module secret");
|
|
616
|
-
await coreV1Api.deleteNamespacedSecret(mod.metadata?.name ?? "", peprNS);
|
|
617
|
-
await coreV1Api.createNamespacedSecret(peprNS, mod);
|
|
618
|
-
}
|
|
619
|
-
const svc = service(name);
|
|
620
|
-
try {
|
|
621
|
-
logger_default.info("Creating service");
|
|
622
|
-
await coreV1Api.createNamespacedService(peprNS, svc);
|
|
623
|
-
} catch (e) {
|
|
624
|
-
logger_default.debug(e instanceof import_client_node.HttpError ? e.body : e);
|
|
625
|
-
logger_default.info("Removing and re-creating service");
|
|
626
|
-
await coreV1Api.deleteNamespacedService(svc.metadata?.name ?? "", peprNS);
|
|
627
|
-
await coreV1Api.createNamespacedService(peprNS, svc);
|
|
628
|
-
}
|
|
629
|
-
const tls = tlsSecret(name, assets.tls);
|
|
630
|
-
try {
|
|
631
|
-
logger_default.info("Creating TLS secret");
|
|
632
|
-
await coreV1Api.createNamespacedSecret(peprNS, tls);
|
|
633
|
-
} catch (e) {
|
|
634
|
-
logger_default.debug(e instanceof import_client_node.HttpError ? e.body : e);
|
|
635
|
-
logger_default.info("Removing and re-creating TLS secret");
|
|
636
|
-
await coreV1Api.deleteNamespacedSecret(tls.metadata?.name ?? "", peprNS);
|
|
637
|
-
await coreV1Api.createNamespacedSecret(peprNS, tls);
|
|
638
|
-
}
|
|
639
|
-
const apiToken = apiTokenSecret(name, assets.apiToken);
|
|
640
|
-
try {
|
|
641
|
-
logger_default.info("Creating API token secret");
|
|
642
|
-
await coreV1Api.createNamespacedSecret(peprNS, apiToken);
|
|
643
|
-
} catch (e) {
|
|
644
|
-
logger_default.debug(e instanceof import_client_node.HttpError ? e.body : e);
|
|
645
|
-
logger_default.info("Removing and re-creating API token secret");
|
|
646
|
-
await coreV1Api.deleteNamespacedSecret(apiToken.metadata?.name ?? "", peprNS);
|
|
647
|
-
await coreV1Api.createNamespacedSecret(peprNS, apiToken);
|
|
774
|
+
if (code.length < 1) {
|
|
775
|
+
throw new Error("No code provided");
|
|
648
776
|
}
|
|
777
|
+
await setupRBAC(name2);
|
|
778
|
+
await setupController(assets, code, hash);
|
|
779
|
+
await setupWatcher(assets, hash);
|
|
780
|
+
}
|
|
781
|
+
async function setupRBAC(name2) {
|
|
782
|
+
logger_default.info("Applying cluster role binding");
|
|
783
|
+
const crb = clusterRoleBinding(name2);
|
|
784
|
+
await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.ClusterRoleBinding).Apply(crb);
|
|
785
|
+
logger_default.info("Applying cluster role");
|
|
786
|
+
const cr = clusterRole(name2);
|
|
787
|
+
await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.ClusterRole).Apply(cr);
|
|
788
|
+
logger_default.info("Applying service account");
|
|
789
|
+
const sa = serviceAccount(name2);
|
|
790
|
+
await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.ServiceAccount).Apply(sa);
|
|
791
|
+
logger_default.info("Applying store role");
|
|
792
|
+
const role = storeRole(name2);
|
|
793
|
+
await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.Role).Apply(role);
|
|
794
|
+
logger_default.info("Applying store role binding");
|
|
795
|
+
const roleBinding = storeRoleBinding(name2);
|
|
796
|
+
await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.RoleBinding).Apply(roleBinding);
|
|
797
|
+
}
|
|
798
|
+
async function setupController(assets, code, hash) {
|
|
799
|
+
const { name: name2 } = assets;
|
|
800
|
+
logger_default.info("Applying module secret");
|
|
801
|
+
const mod = moduleSecret(name2, code, hash);
|
|
802
|
+
await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.Secret).Apply(mod);
|
|
803
|
+
logger_default.info("Applying controller service");
|
|
804
|
+
const svc = service(name2);
|
|
805
|
+
await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.Service).Apply(svc);
|
|
806
|
+
logger_default.info("Applying TLS secret");
|
|
807
|
+
const tls = tlsSecret(name2, assets.tls);
|
|
808
|
+
await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.Secret).Apply(tls);
|
|
809
|
+
logger_default.info("Applying API token secret");
|
|
810
|
+
const apiToken = apiTokenSecret(name2, assets.apiToken);
|
|
811
|
+
await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.Secret).Apply(apiToken);
|
|
812
|
+
logger_default.info("Applying deployment");
|
|
649
813
|
const dep = deployment(assets, hash);
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
logger_default.info("
|
|
656
|
-
await
|
|
657
|
-
|
|
814
|
+
await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.Deployment).Apply(dep);
|
|
815
|
+
}
|
|
816
|
+
async function setupWatcher(assets, hash) {
|
|
817
|
+
const watchDeployment = watcher(assets, hash);
|
|
818
|
+
if (watchDeployment) {
|
|
819
|
+
logger_default.info("Applying watcher deployment");
|
|
820
|
+
await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.Deployment).Apply(watchDeployment);
|
|
821
|
+
logger_default.info("Applying watcher service");
|
|
822
|
+
const watchSvc = watcherService(assets.name);
|
|
823
|
+
await (0, import_kubernetes_fluent_client2.K8s)(import_kubernetes_fluent_client2.kind.Service).Apply(watchSvc);
|
|
658
824
|
}
|
|
659
825
|
}
|
|
660
826
|
|
|
@@ -683,14 +849,14 @@ function loadCapabilities(path) {
|
|
|
683
849
|
}
|
|
684
850
|
|
|
685
851
|
// src/lib/assets/yaml.ts
|
|
686
|
-
var
|
|
852
|
+
var import_client_node = require("@kubernetes/client-node");
|
|
687
853
|
var import_crypto2 = __toESM(require("crypto"));
|
|
688
854
|
var import_fs2 = require("fs");
|
|
689
|
-
function zarfYaml({ name, image, config }, path) {
|
|
855
|
+
function zarfYaml({ name: name2, image, config }, path) {
|
|
690
856
|
const zarfCfg = {
|
|
691
857
|
kind: "ZarfPackageConfig",
|
|
692
858
|
metadata: {
|
|
693
|
-
name,
|
|
859
|
+
name: name2,
|
|
694
860
|
description: `Pepr Module: ${config.description}`,
|
|
695
861
|
url: "https://github.com/defenseunicorns/pepr",
|
|
696
862
|
version: `${config.appVersion || "0.0.1"}`
|
|
@@ -710,24 +876,29 @@ function zarfYaml({ name, image, config }, path) {
|
|
|
710
876
|
}
|
|
711
877
|
]
|
|
712
878
|
};
|
|
713
|
-
return (0,
|
|
879
|
+
return (0, import_client_node.dumpYaml)(zarfCfg, { noRefs: true });
|
|
714
880
|
}
|
|
715
881
|
async function allYaml(assets) {
|
|
716
|
-
const { name, tls, apiToken, path } = assets;
|
|
882
|
+
const { name: name2, tls, apiToken, path } = assets;
|
|
717
883
|
const code = await import_fs2.promises.readFile(path);
|
|
718
884
|
const hash = import_crypto2.default.createHash("sha256").update(code).digest("hex");
|
|
719
885
|
const mutateWebhook = await webhookConfig(assets, "mutate");
|
|
720
886
|
const validateWebhook = await webhookConfig(assets, "validate");
|
|
887
|
+
const watchDeployment = watcher(assets, hash);
|
|
721
888
|
const resources = [
|
|
722
889
|
namespace,
|
|
723
|
-
clusterRole(
|
|
724
|
-
clusterRoleBinding(
|
|
725
|
-
serviceAccount(
|
|
726
|
-
apiTokenSecret(
|
|
727
|
-
tlsSecret(
|
|
890
|
+
clusterRole(name2),
|
|
891
|
+
clusterRoleBinding(name2),
|
|
892
|
+
serviceAccount(name2),
|
|
893
|
+
apiTokenSecret(name2, apiToken),
|
|
894
|
+
tlsSecret(name2, tls),
|
|
728
895
|
deployment(assets, hash),
|
|
729
|
-
service(
|
|
730
|
-
|
|
896
|
+
service(name2),
|
|
897
|
+
watcherService(name2),
|
|
898
|
+
moduleSecret(name2, code, hash),
|
|
899
|
+
peprStoreCRD,
|
|
900
|
+
storeRole(name2),
|
|
901
|
+
storeRoleBinding(name2)
|
|
731
902
|
];
|
|
732
903
|
if (mutateWebhook) {
|
|
733
904
|
resources.push(mutateWebhook);
|
|
@@ -735,7 +906,10 @@ async function allYaml(assets) {
|
|
|
735
906
|
if (validateWebhook) {
|
|
736
907
|
resources.push(validateWebhook);
|
|
737
908
|
}
|
|
738
|
-
|
|
909
|
+
if (watchDeployment) {
|
|
910
|
+
resources.push(watchDeployment);
|
|
911
|
+
}
|
|
912
|
+
return resources.map((r) => (0, import_client_node.dumpYaml)(r, { noRefs: true })).join("---\n");
|
|
739
913
|
}
|
|
740
914
|
|
|
741
915
|
// src/lib/assets/index.ts
|
|
@@ -744,9 +918,6 @@ var Assets = class {
|
|
|
744
918
|
this.config = config;
|
|
745
919
|
this.path = path;
|
|
746
920
|
this.host = host;
|
|
747
|
-
this.deploy = this.deploy.bind(this);
|
|
748
|
-
this.zarfYaml = this.zarfYaml.bind(this);
|
|
749
|
-
this.allYaml = this.allYaml.bind(this);
|
|
750
921
|
this.name = `pepr-${config.uuid}`;
|
|
751
922
|
this.image = `ghcr.io/defenseunicorns/pepr/controller:v${config.peprVersion}`;
|
|
752
923
|
this.tls = genTLS(this.host || `${this.name}.pepr-system.svc`);
|
|
@@ -757,21 +928,19 @@ var Assets = class {
|
|
|
757
928
|
apiToken;
|
|
758
929
|
capabilities;
|
|
759
930
|
image;
|
|
760
|
-
async
|
|
931
|
+
deploy = async (webhookTimeout) => {
|
|
761
932
|
this.capabilities = await loadCapabilities(this.path);
|
|
762
933
|
await deploy(this, webhookTimeout);
|
|
763
|
-
}
|
|
764
|
-
zarfYaml(path)
|
|
765
|
-
|
|
766
|
-
}
|
|
767
|
-
async allYaml() {
|
|
934
|
+
};
|
|
935
|
+
zarfYaml = (path) => zarfYaml(this, path);
|
|
936
|
+
allYaml = async () => {
|
|
768
937
|
this.capabilities = await loadCapabilities(this.path);
|
|
769
938
|
return allYaml(this);
|
|
770
|
-
}
|
|
939
|
+
};
|
|
771
940
|
};
|
|
772
941
|
|
|
773
942
|
// src/cli/init/templates.ts
|
|
774
|
-
var
|
|
943
|
+
var import_client_node2 = require("@kubernetes/client-node");
|
|
775
944
|
var import_util = require("util");
|
|
776
945
|
var import_uuid = require("uuid");
|
|
777
946
|
|
|
@@ -976,8 +1145,8 @@ var hello_pepr_samples_default = [
|
|
|
976
1145
|
var gitIgnore = "# Ignore node_modules and Pepr build artifacts\nnode_modules\ndist\ninsecure*\n";
|
|
977
1146
|
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';
|
|
978
1147
|
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';
|
|
979
|
-
var helloPeprTS = 'import {\n Capability,\n Log,\n PeprMutateRequest,\n RegisterKind,\n a,\n fetch,\n fetchStatus,\n} from "pepr";\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\nconst { When } = 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 * 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\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate & Validate Actions (CM Example 2) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This combines 2 different types of actions: \'Mutate\', and \'Validate\'. 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 and finally validate that the ConfigMap has the label\n * `pepr`.\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\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://api.chucknorris.io/jokes/random?category=dev");\n * const joke = await fetch("https://api.chucknorris.io/jokes/random?category=dev") as TheChuckNorrisJoke;\n * ```\n *\n * Alternatively, you can drop the type completely:\n *\n * ```ts\n * fetch("https://api.chucknorris.io/jokes/random?category=dev")\n * ```\n */\ninterface TheChuckNorrisJoke {\n icon_url: string;\n id: string;\n url: string;\n value: string;\n}\n\nWhen(a.ConfigMap)\n .IsCreated()\n .WithLabel("chuck-norris")\n .Mutate(async change => {\n // Try/catch is not needed as a response object will always be returned\n const response = await fetch<TheChuckNorrisJoke>(\n "https://api.chucknorris.io/jokes/random?category=dev",\n );\n\n // Instead, check the `response.ok` field\n if (response.ok) {\n // Add the Chuck Norris joke to the configmap\n change.Raw.data["chuck-says"] = response.data.value;\n return;\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';
|
|
980
|
-
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" }, version: "0.
|
|
1148
|
+
var helloPeprTS = 'import {\n Capability,\n K8s,\n Log,\n PeprMutateRequest,\n RegisterKind,\n a,\n fetch,\n fetchStatus,\n kind,\n} from "pepr";\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 // 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 // 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 });\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://api.chucknorris.io/jokes/random?category=dev");\n * const joke = await fetch("https://api.chucknorris.io/jokes/random?category=dev") as TheChuckNorrisJoke;\n * ```\n *\n * Alternatively, you can drop the type completely:\n *\n * ```ts\n * fetch("https://api.chucknorris.io/jokes/random?category=dev")\n * ```\n */\ninterface TheChuckNorrisJoke {\n icon_url: string;\n id: string;\n url: string;\n value: string;\n}\n\nWhen(a.ConfigMap)\n .IsCreated()\n .WithLabel("chuck-norris")\n .Mutate(async change => {\n // Try/catch is not needed as a response object will always be returned\n const response = await fetch<TheChuckNorrisJoke>(\n "https://api.chucknorris.io/jokes/random?category=dev",\n );\n\n // Instead, check the `response.ok` field\n if (response.ok) {\n // Add the Chuck Norris joke to the configmap\n change.Raw.data["chuck-says"] = response.data.value;\n return;\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';
|
|
1149
|
+
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" }, version: "0.14.0", main: "dist/lib.js", types: "dist/lib.d.ts", scripts: { "gen-data-json": "node hack/build-template-data.js", prebuild: "rm -fr dist/* && npm run gen-data-json", build: "tsc && node build.mjs", test: "npm run test:unit && npm run test:journey", "test:unit": "npm run gen-data-json && jest src --coverage", "test:journey": "npm run test:journey:k3d && npm run test:journey:build && npm run test:journey:image && npm run test:journey:run", "test:journey:k3d": "k3d cluster delete pepr-dev && k3d cluster create pepr-dev --k3s-arg '--debug@server:0'", "test:journey:build": "npm run build && npm pack", "test:journey:image": "docker buildx build --tag pepr:dev . && k3d image import pepr:dev -c pepr-dev", "test:journey:run": "jest journey/entrypoint.test.ts", "format:check": "eslint src && prettier src --check", "format:fix": "eslint src --fix && prettier src --write" }, dependencies: { express: "4.18.2", "fast-json-patch": "3.1.1", "kubernetes-fluent-client": "1.4.1", pino: "8.15.3", "pino-pretty": "10.2.0", "prom-client": "14.2.0", ramda: "0.29.0" }, devDependencies: { "@jest/globals": "29.7.0", "@types/eslint": "8.44.3", "@types/express": "4.17.18", "@types/node": "18.x.x", "@types/node-forge": "1.3.6", "@types/prompts": "2.4.5", "@types/ramda": "0.29.5", "@types/uuid": "9.0.4", jest: "29.7.0", nock: "13.3.3", "ts-jest": "29.1.1" }, peerDependencies: { "@typescript-eslint/eslint-plugin": "6.7.3", "@typescript-eslint/parser": "6.7.3", commander: "11.0.0", esbuild: "0.19.4", eslint: "8.50.0", "node-forge": "1.3.1", prettier: "3.0.3", prompts: "2.4.2", typescript: "5.2.2", uuid: "9.0.1" } };
|
|
981
1150
|
|
|
982
1151
|
// src/templates/pepr.code-snippets.json
|
|
983
1152
|
var pepr_code_snippets_default = {
|
|
@@ -1036,8 +1205,8 @@ var tsconfig_module_default = {
|
|
|
1036
1205
|
|
|
1037
1206
|
// src/cli/init/utils.ts
|
|
1038
1207
|
var import_fs3 = require("fs");
|
|
1039
|
-
function sanitizeName(
|
|
1040
|
-
let sanitized =
|
|
1208
|
+
function sanitizeName(name2) {
|
|
1209
|
+
let sanitized = name2.toLowerCase().replace(/[^a-z0-9-]+/gi, "-");
|
|
1041
1210
|
sanitized = sanitized.replace(/^-+|-+$/g, "");
|
|
1042
1211
|
sanitized = sanitized.replace(/--+/g, "-");
|
|
1043
1212
|
return sanitized;
|
|
@@ -1061,13 +1230,13 @@ function write(path, data) {
|
|
|
1061
1230
|
}
|
|
1062
1231
|
|
|
1063
1232
|
// src/cli/init/templates.ts
|
|
1064
|
-
var { dependencies, devDependencies, peerDependencies, scripts, version } = packageJSON;
|
|
1233
|
+
var { dependencies, devDependencies, peerDependencies, scripts, version: version2 } = packageJSON;
|
|
1065
1234
|
function genPkgJSON(opts, pgkVerOverride) {
|
|
1066
1235
|
const uuid = (0, import_uuid.v5)(opts.name, (0, import_uuid.v4)());
|
|
1067
|
-
const
|
|
1236
|
+
const name2 = sanitizeName(opts.name);
|
|
1068
1237
|
const { typescript } = peerDependencies;
|
|
1069
1238
|
const data = {
|
|
1070
|
-
name,
|
|
1239
|
+
name: name2,
|
|
1071
1240
|
version: "0.0.1",
|
|
1072
1241
|
description: opts.description,
|
|
1073
1242
|
keywords: ["pepr", "k8s", "policy-engine", "pepr-module", "security"],
|
|
@@ -1087,7 +1256,7 @@ function genPkgJSON(opts, pgkVerOverride) {
|
|
|
1087
1256
|
"k3d-setup": scripts["test:journey:k3d"]
|
|
1088
1257
|
},
|
|
1089
1258
|
dependencies: {
|
|
1090
|
-
pepr: pgkVerOverride ||
|
|
1259
|
+
pepr: pgkVerOverride || version2
|
|
1091
1260
|
},
|
|
1092
1261
|
devDependencies: {
|
|
1093
1262
|
typescript
|
|
@@ -1119,7 +1288,7 @@ var gitignore = {
|
|
|
1119
1288
|
};
|
|
1120
1289
|
var samplesYaml = {
|
|
1121
1290
|
path: "hello-pepr.samples.yaml",
|
|
1122
|
-
data: hello_pepr_samples_default.map((r) => (0,
|
|
1291
|
+
data: hello_pepr_samples_default.map((r) => (0, import_client_node2.dumpYaml)(r, { noRefs: true })).join("---\n")
|
|
1123
1292
|
};
|
|
1124
1293
|
var snippet = {
|
|
1125
1294
|
path: "pepr.code-snippets",
|
|
@@ -1230,6 +1399,7 @@ function build_default(program2) {
|
|
|
1230
1399
|
}
|
|
1231
1400
|
var externalLibs = Object.keys(dependencies);
|
|
1232
1401
|
externalLibs.push("pepr");
|
|
1402
|
+
externalLibs.push("@kubernetes/client-node");
|
|
1233
1403
|
async function loadModule(entryPoint = peprTS2) {
|
|
1234
1404
|
const cfgPath = (0, import_path.resolve)(".", "package.json");
|
|
1235
1405
|
const input = (0, import_path.resolve)(".", entryPoint);
|
|
@@ -1245,16 +1415,16 @@ async function loadModule(entryPoint = peprTS2) {
|
|
|
1245
1415
|
const moduleText = await import_fs5.promises.readFile(cfgPath, { encoding: "utf-8" });
|
|
1246
1416
|
const cfg = JSON.parse(moduleText);
|
|
1247
1417
|
const { uuid } = cfg.pepr;
|
|
1248
|
-
const
|
|
1249
|
-
cfg.pepr.peprVersion =
|
|
1418
|
+
const name2 = `pepr-${uuid}.js`;
|
|
1419
|
+
cfg.pepr.peprVersion = version2;
|
|
1250
1420
|
if (!uuid) {
|
|
1251
1421
|
throw new Error("Could not load the uuid in package.json");
|
|
1252
1422
|
}
|
|
1253
1423
|
return {
|
|
1254
1424
|
cfg,
|
|
1255
1425
|
input,
|
|
1256
|
-
name,
|
|
1257
|
-
path: (0, import_path.resolve)("dist",
|
|
1426
|
+
name: name2,
|
|
1427
|
+
path: (0, import_path.resolve)("dist", name2),
|
|
1258
1428
|
uuid
|
|
1259
1429
|
};
|
|
1260
1430
|
}
|
|
@@ -1419,6 +1589,7 @@ function dev_default(program2) {
|
|
|
1419
1589
|
env: {
|
|
1420
1590
|
...process.env,
|
|
1421
1591
|
LOG_LEVEL: "debug",
|
|
1592
|
+
PEPR_MODE: "dev",
|
|
1422
1593
|
PEPR_API_TOKEN: webhook.apiToken,
|
|
1423
1594
|
PEPR_PRETTY_LOGS: "true",
|
|
1424
1595
|
SSL_KEY_PATH: "insecure-tls.key",
|
|
@@ -1471,8 +1642,8 @@ function walkthrough() {
|
|
|
1471
1642
|
message: "Enter a name for the new Pepr module. This will create a new directory based on the name.\n",
|
|
1472
1643
|
validate: async (val) => {
|
|
1473
1644
|
try {
|
|
1474
|
-
const
|
|
1475
|
-
await import_fs7.promises.access(
|
|
1645
|
+
const name2 = sanitizeName(val);
|
|
1646
|
+
await import_fs7.promises.access(name2, import_fs7.promises.constants.F_OK);
|
|
1476
1647
|
return "A directory with this name already exists";
|
|
1477
1648
|
} catch (e) {
|
|
1478
1649
|
return val.length > 2 || "The name must be at least 3 characters long";
|
|
@@ -1595,8 +1766,8 @@ function init_default(program2) {
|
|
|
1595
1766
|
var import_commander = require("commander");
|
|
1596
1767
|
var RootCmd = class extends import_commander.Command {
|
|
1597
1768
|
// eslint-disable-next-line class-methods-use-this
|
|
1598
|
-
createCommand(
|
|
1599
|
-
const cmd = new import_commander.Command(
|
|
1769
|
+
createCommand(name2) {
|
|
1770
|
+
const cmd = new import_commander.Command(name2);
|
|
1600
1771
|
cmd.option("-l, --log-level [level]", "Log level: debug, info, warn, error", "info");
|
|
1601
1772
|
cmd.hook("preAction", (run) => {
|
|
1602
1773
|
logger_default.level = run.opts().logLevel;
|
|
@@ -1668,7 +1839,7 @@ if (process.env.npm_lifecycle_event !== "npx") {
|
|
|
1668
1839
|
console.warn("Pepr should be run via `npx pepr <command>` instead of `pepr <command>`.");
|
|
1669
1840
|
}
|
|
1670
1841
|
var program = new RootCmd();
|
|
1671
|
-
program.version(
|
|
1842
|
+
program.version(version2).description(`Pepr (v${version2}) - Type safe K8s middleware for humans`).action(() => {
|
|
1672
1843
|
if (program.args.length < 1) {
|
|
1673
1844
|
console.log(banner);
|
|
1674
1845
|
program.help();
|