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