pepr 0.42.3 → 0.44.0-nightly.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/cli/build.d.ts +25 -11
  2. package/dist/cli/build.d.ts.map +1 -1
  3. package/dist/cli/build.helpers.d.ts +13 -14
  4. package/dist/cli/build.helpers.d.ts.map +1 -1
  5. package/dist/cli/deploy.d.ts.map +1 -1
  6. package/dist/cli/dev.d.ts.map +1 -1
  7. package/dist/cli/init/templates.d.ts +7 -5
  8. package/dist/cli/init/templates.d.ts.map +1 -1
  9. package/dist/cli.js +786 -739
  10. package/dist/controller.js +1 -1
  11. package/dist/fixtures/loader.d.ts.map +1 -1
  12. package/dist/lib/assets/assets.d.ts +15 -12
  13. package/dist/lib/assets/assets.d.ts.map +1 -1
  14. package/dist/lib/assets/deploy.d.ts +1 -1
  15. package/dist/lib/assets/deploy.d.ts.map +1 -1
  16. package/dist/lib/assets/helm.d.ts.map +1 -1
  17. package/dist/lib/assets/index.d.ts.map +1 -1
  18. package/dist/lib/assets/webhooks.d.ts +4 -1
  19. package/dist/lib/assets/webhooks.d.ts.map +1 -1
  20. package/dist/lib/assets/yaml/generateAllYaml.d.ts +9 -0
  21. package/dist/lib/assets/yaml/generateAllYaml.d.ts.map +1 -0
  22. package/dist/lib/assets/yaml/generateZarfYaml.d.ts +5 -0
  23. package/dist/lib/assets/yaml/generateZarfYaml.d.ts.map +1 -0
  24. package/dist/lib/assets/yaml/overridesFile.d.ts +15 -0
  25. package/dist/lib/assets/yaml/overridesFile.d.ts.map +1 -0
  26. package/dist/lib/core/module.d.ts +23 -20
  27. package/dist/lib/core/module.d.ts.map +1 -1
  28. package/dist/lib/enums.d.ts +4 -0
  29. package/dist/lib/enums.d.ts.map +1 -1
  30. package/dist/lib/processors/mutate-processor.d.ts +2 -0
  31. package/dist/lib/processors/mutate-processor.d.ts.map +1 -1
  32. package/dist/lib/processors/validate-processor.d.ts.map +1 -1
  33. package/dist/lib/telemetry/timeUtils.d.ts +2 -0
  34. package/dist/lib/telemetry/timeUtils.d.ts.map +1 -0
  35. package/dist/lib/telemetry/webhookTimeouts.d.ts +9 -0
  36. package/dist/lib/telemetry/webhookTimeouts.d.ts.map +1 -0
  37. package/dist/lib.js +79 -22
  38. package/dist/lib.js.map +4 -4
  39. package/package.json +7 -4
  40. package/src/cli/build.helpers.ts +35 -35
  41. package/src/cli/build.ts +126 -70
  42. package/src/cli/deploy.ts +7 -6
  43. package/src/cli/dev.ts +9 -6
  44. package/src/cli/init/templates.ts +6 -5
  45. package/src/fixtures/loader.ts +2 -2
  46. package/src/lib/assets/assets.ts +66 -53
  47. package/src/lib/assets/deploy.ts +32 -30
  48. package/src/lib/assets/helm.ts +22 -4
  49. package/src/lib/assets/index.ts +33 -9
  50. package/src/lib/assets/pods.ts +2 -2
  51. package/src/lib/assets/webhooks.ts +21 -6
  52. package/src/lib/assets/yaml/generateAllYaml.ts +50 -0
  53. package/src/lib/assets/yaml/generateZarfYaml.ts +38 -0
  54. package/src/lib/assets/{yaml.ts → yaml/overridesFile.ts} +8 -120
  55. package/src/lib/core/module.ts +26 -21
  56. package/src/lib/enums.ts +6 -0
  57. package/src/lib/processors/mutate-processor.ts +15 -7
  58. package/src/lib/processors/validate-processor.ts +13 -4
  59. package/src/lib/telemetry/timeUtils.ts +1 -0
  60. package/src/lib/telemetry/webhookTimeouts.ts +34 -0
  61. package/dist/lib/assets/yaml.d.ts +0 -32
  62. package/dist/lib/assets/yaml.d.ts.map +0 -1
@@ -11,92 +11,97 @@ import {
11
11
  serviceMonitorTemplate,
12
12
  watcherDeployTemplate,
13
13
  } from "./helm";
14
+ import {
15
+ V1Deployment,
16
+ V1MutatingWebhookConfiguration,
17
+ V1ValidatingWebhookConfiguration,
18
+ } from "@kubernetes/client-node/dist/gen";
14
19
  import { createDirectoryIfNotExists } from "../filesystemService";
15
- import { deploy } from "./deploy";
20
+ import { overridesFile } from "./yaml/overridesFile";
16
21
  import { getDeployment, getModuleSecret, getWatcher } from "./pods";
17
22
  import { helmLayout, createWebhookYaml, toYaml } from "./index";
18
23
  import { loadCapabilities } from "./loader";
19
24
  import { namespaceComplianceValidator, dedent } from "../helpers";
25
+ import { promises as fs } from "fs";
20
26
  import { storeRole, storeRoleBinding, clusterRoleBinding, serviceAccount } from "./rbac";
21
27
  import { watcherService, service, tlsSecret, apiTokenSecret } from "./networking";
22
- import { webhookConfig } from "./webhooks";
23
- import { generateZarfYaml, generateZarfYamlChart, generateAllYaml, overridesFile } from "./yaml";
24
- import { promises as fs } from "fs";
25
- import { V1MutatingWebhookConfiguration, V1ValidatingWebhookConfiguration } from "@kubernetes/client-node/dist/gen";
28
+ import { WebhookType } from "../enums";
26
29
 
27
30
  export class Assets {
28
31
  readonly name: string;
29
32
  readonly tls: TLSOut;
30
33
  readonly apiToken: string;
34
+ readonly config: ModuleConfig;
35
+ readonly path: string;
31
36
  readonly alwaysIgnore!: WebhookIgnore;
37
+ readonly imagePullSecrets: string[];
32
38
  capabilities!: CapabilityExport[];
33
-
34
39
  image: string;
35
40
  buildTimestamp: string;
36
- hash: string;
41
+ readonly host?: string;
37
42
 
38
- constructor(
39
- readonly config: ModuleConfig,
40
- readonly path: string,
41
- readonly host?: string,
42
- ) {
43
+ constructor(config: ModuleConfig, path: string, imagePullSecrets: string[], host?: string) {
43
44
  this.name = `pepr-${config.uuid}`;
45
+ this.imagePullSecrets = imagePullSecrets;
44
46
  this.buildTimestamp = `${Date.now()}`;
47
+ this.config = config;
48
+ this.path = path;
49
+ this.host = host;
45
50
  this.alwaysIgnore = config.alwaysIgnore;
46
51
  this.image = `ghcr.io/defenseunicorns/pepr/controller:v${config.peprVersion}`;
47
- this.hash = "";
52
+
48
53
  // Generate the ephemeral tls things
49
- this.tls = genTLS(this.host || `${this.name}.pepr-system.svc`);
54
+ this.tls = genTLS(host || `${this.name}.pepr-system.svc`);
50
55
 
51
56
  // Generate the api token for the controller / webhook
52
57
  this.apiToken = crypto.randomBytes(32).toString("hex");
53
58
  }
54
59
 
55
- setHash = (hash: string): void => {
56
- this.hash = hash;
57
- };
58
-
59
- deploy = async (force: boolean, webhookTimeout?: number): Promise<void> => {
60
+ async deploy(
61
+ deployFunction: (assets: Assets, force: boolean, webhookTimeout: number) => Promise<void>,
62
+ force: boolean,
63
+ webhookTimeout?: number,
64
+ ): Promise<void> {
60
65
  this.capabilities = await loadCapabilities(this.path);
61
- await deploy(this, force, webhookTimeout);
62
- };
63
66
 
64
- zarfYaml = (path: string): string => generateZarfYaml(this.name, this.image, this.config, path);
67
+ const timeout = typeof webhookTimeout === "number" ? webhookTimeout : 10;
65
68
 
66
- zarfYamlChart = (path: string): string => generateZarfYamlChart(this.name, this.image, this.config, path);
69
+ await deployFunction(this, force, timeout);
70
+ }
67
71
 
68
- allYaml = async (imagePullSecret?: string): Promise<string> => {
72
+ zarfYaml = (
73
+ zarfYamlGenerator: (assets: Assets, path: string, type: "manifests" | "charts") => string,
74
+ path: string,
75
+ ): string => zarfYamlGenerator(this, path, "manifests");
76
+
77
+ zarfYamlChart = (
78
+ zarfYamlGenerator: (assets: Assets, path: string, type: "manifests" | "charts") => string,
79
+ path: string,
80
+ ): string => zarfYamlGenerator(this, path, "charts");
81
+
82
+ allYaml = async (
83
+ yamlGenerationFunction: (
84
+ assyts: Assets,
85
+ deployments: { default: V1Deployment; watch: V1Deployment | null },
86
+ ) => Promise<string>,
87
+ imagePullSecret?: string,
88
+ ): Promise<string> => {
69
89
  this.capabilities = await loadCapabilities(this.path);
70
90
  // give error if namespaces are not respected
71
91
  for (const capability of this.capabilities) {
72
92
  namespaceComplianceValidator(capability, this.alwaysIgnore?.namespaces);
73
93
  }
74
94
 
75
- const webhooks = {
76
- mutate: await webhookConfig(this, "mutate", this.config.webhookTimeout),
77
- validate: await webhookConfig(this, "validate", this.config.webhookTimeout),
78
- };
79
-
80
95
  const code = await fs.readFile(this.path);
81
96
 
82
- // Generate a hash of the code
83
- this.hash = crypto.createHash("sha256").update(code).digest("hex");
97
+ const moduleHash = crypto.createHash("sha256").update(code).digest("hex");
84
98
 
85
99
  const deployments = {
86
- default: getDeployment(this, this.hash, this.buildTimestamp, imagePullSecret),
87
- watch: getWatcher(this, this.hash, this.buildTimestamp, imagePullSecret),
100
+ default: getDeployment(this, moduleHash, this.buildTimestamp, imagePullSecret),
101
+ watch: getWatcher(this, moduleHash, this.buildTimestamp, imagePullSecret),
88
102
  };
89
103
 
90
- const assetsInputs = {
91
- apiToken: this.apiToken,
92
- capabilities: this.capabilities,
93
- config: this.config,
94
- hash: this.hash,
95
- name: this.name,
96
- path: this.path,
97
- tls: this.tls,
98
- };
99
- return generateAllYaml(webhooks, deployments, assetsInputs);
104
+ return yamlGenerationFunction(this, deployments);
100
105
  };
101
106
 
102
107
  writeWebhookFiles = async (
@@ -118,7 +123,14 @@ export class Assets {
118
123
  }
119
124
  };
120
125
 
121
- generateHelmChart = async (basePath: string): Promise<void> => {
126
+ generateHelmChart = async (
127
+ webhookGeneratorFunction: (
128
+ assets: Assets,
129
+ mutateOrValidate: WebhookType,
130
+ timeoutSeconds: number | undefined,
131
+ ) => Promise<V1MutatingWebhookConfiguration | V1ValidatingWebhookConfiguration | null>,
132
+ basePath: string,
133
+ ): Promise<void> => {
122
134
  const helm = helmLayout(basePath, this.config.uuid);
123
135
 
124
136
  try {
@@ -129,6 +141,7 @@ export class Assets {
129
141
  );
130
142
 
131
143
  const code = await fs.readFile(this.path);
144
+ const moduleHash = crypto.createHash("sha256").update(code).digest("hex");
132
145
 
133
146
  const pairs: [string, () => string][] = [
134
147
  [helm.files.chartYaml, (): string => dedent(chartYaml(this.config.uuid, this.config.description || ""))],
@@ -142,28 +155,28 @@ export class Assets {
142
155
  [helm.files.clusterRoleYaml, (): string => dedent(clusterRoleTemplate())],
143
156
  [helm.files.clusterRoleBindingYaml, (): string => toYaml(clusterRoleBinding(this.name))],
144
157
  [helm.files.serviceAccountYaml, (): string => toYaml(serviceAccount(this.name))],
145
- [helm.files.moduleSecretYaml, (): string => toYaml(getModuleSecret(this.name, code, this.hash))],
158
+ [helm.files.moduleSecretYaml, (): string => toYaml(getModuleSecret(this.name, code, moduleHash))],
146
159
  ];
147
160
  await Promise.all(pairs.map(async ([file, content]) => await fs.writeFile(file, content())));
148
161
 
149
162
  const overrideData = {
150
- hash: this.hash,
163
+ hash: moduleHash,
151
164
  name: this.name,
152
165
  image: this.image,
153
166
  config: this.config,
154
167
  apiToken: this.apiToken,
155
168
  capabilities: this.capabilities,
156
169
  };
157
- await overridesFile(overrideData, helm.files.valuesYaml);
170
+ await overridesFile(overrideData, helm.files.valuesYaml, this.imagePullSecrets);
158
171
 
159
- const [mutateWebhook, validateWebhook] = await Promise.all([
160
- webhookConfig(this, "mutate", this.config.webhookTimeout),
161
- webhookConfig(this, "validate", this.config.webhookTimeout),
162
- ]);
172
+ const webhooks = {
173
+ mutate: await webhookGeneratorFunction(this, WebhookType.MUTATE, this.config.webhookTimeout),
174
+ validate: await webhookGeneratorFunction(this, WebhookType.VALIDATE, this.config.webhookTimeout),
175
+ };
163
176
 
164
- await this.writeWebhookFiles(validateWebhook, mutateWebhook, helm);
177
+ await this.writeWebhookFiles(webhooks.validate, webhooks.mutate, helm);
165
178
 
166
- const watchDeployment = getWatcher(this, this.hash, this.buildTimestamp);
179
+ const watchDeployment = getWatcher(this, moduleHash, this.buildTimestamp);
167
180
  if (watchDeployment) {
168
181
  await fs.writeFile(helm.files.watcherDeploymentYaml, dedent(watcherDeployTemplate(this.buildTimestamp)));
169
182
  await fs.writeFile(helm.files.watcherServiceMonitorYaml, dedent(serviceMonitorTemplate("watcher")));
@@ -12,8 +12,9 @@ import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking
12
12
  import { getDeployment, getModuleSecret, getNamespace, getWatcher } from "./pods";
13
13
  import { clusterRole, clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "./rbac";
14
14
  import { peprStoreCRD } from "./store";
15
- import { webhookConfig } from "./webhooks";
15
+ import { webhookConfigGenerator } from "./webhooks";
16
16
  import { CapabilityExport, ImagePullSecret } from "../types";
17
+ import { WebhookType } from "../enums";
17
18
 
18
19
  export async function deployImagePullSecret(imagePullSecret: ImagePullSecret, name: string): Promise<void> {
19
20
  try {
@@ -42,50 +43,51 @@ export async function deployImagePullSecret(imagePullSecret: ImagePullSecret, na
42
43
  Log.error(e);
43
44
  }
44
45
  }
45
- export async function deploy(assets: Assets, force: boolean, webhookTimeout?: number): Promise<void> {
46
- Log.info("Establishing connection to Kubernetes");
47
46
 
48
- const { name, host, path } = assets;
47
+ async function handleWebhookConfiguration(
48
+ assets: Assets,
49
+ type: WebhookType,
50
+ webhookTimeout: number,
51
+ force: boolean,
52
+ ): Promise<void> {
53
+ const kindMap = {
54
+ mutate: kind.MutatingWebhookConfiguration,
55
+ validate: kind.ValidatingWebhookConfiguration,
56
+ };
57
+
58
+ const webhookConfig = await webhookConfigGenerator(assets, type, webhookTimeout);
59
+
60
+ if (webhookConfig) {
61
+ Log.info(`Applying ${type} webhook`);
62
+ await K8s(kindMap[type]).Apply(webhookConfig, { force });
63
+ } else {
64
+ Log.info(`${type.charAt(0).toUpperCase() + type.slice(1)} webhook not needed, removing if it exists`);
65
+ await K8s(kindMap[type]).Delete(assets.name);
66
+ }
67
+ }
68
+
69
+ export async function deployWebhook(assets: Assets, force: boolean, webhookTimeout: number): Promise<void> {
70
+ Log.info("Establishing connection to Kubernetes");
49
71
 
50
72
  Log.info("Applying pepr-system namespace");
51
73
  await K8s(kind.Namespace).Apply(getNamespace(assets.config.customLabels?.namespace));
52
74
 
53
75
  // Create the mutating webhook configuration if it is needed
54
- const mutateWebhook = await webhookConfig(assets, "mutate", webhookTimeout);
55
- if (mutateWebhook) {
56
- Log.info("Applying mutating webhook");
57
- await K8s(kind.MutatingWebhookConfiguration).Apply(mutateWebhook, { force });
58
- } else {
59
- Log.info("Mutating webhook not needed, removing if it exists");
60
- await K8s(kind.MutatingWebhookConfiguration).Delete(name);
61
- }
76
+ await handleWebhookConfiguration(assets, WebhookType.MUTATE, webhookTimeout, force);
62
77
 
63
78
  // Create the validating webhook configuration if it is needed
64
- const validateWebhook = await webhookConfig(assets, "validate", webhookTimeout);
65
- if (validateWebhook) {
66
- Log.info("Applying validating webhook");
67
- await K8s(kind.ValidatingWebhookConfiguration).Apply(validateWebhook, { force });
68
- } else {
69
- Log.info("Validating webhook not needed, removing if it exists");
70
- await K8s(kind.ValidatingWebhookConfiguration).Delete(name);
71
- }
79
+ await handleWebhookConfiguration(assets, WebhookType.VALIDATE, webhookTimeout, force);
72
80
 
73
81
  Log.info("Applying the Pepr Store CRD if it doesn't exist");
74
82
  await K8s(kind.CustomResourceDefinition).Apply(peprStoreCRD, { force });
75
83
 
76
- // If a host is specified, we don't need to deploy the rest of the resources
77
- if (host) {
78
- return;
79
- }
84
+ if (assets.host) return; // Skip resource deployment if a host is already specified
80
85
 
81
- const code = await fs.readFile(path);
86
+ const code = await fs.readFile(assets.path);
87
+ if (!code.length) throw new Error("No code provided");
82
88
  const hash = crypto.createHash("sha256").update(code).digest("hex");
83
89
 
84
- if (code.length < 1) {
85
- throw new Error("No code provided");
86
- }
87
-
88
- await setupRBAC(name, assets.capabilities, force, assets.config);
90
+ await setupRBAC(assets.name, assets.capabilities, force, assets.config);
89
91
  await setupController(assets, code, hash, force);
90
92
  await setupWatcher(assets, hash, force);
91
93
  }
@@ -99,8 +99,13 @@ export function watcherDeployTemplate(buildTimestamp: string): string {
99
99
  - name: watcher
100
100
  image: {{ .Values.watcher.image }}
101
101
  imagePullPolicy: IfNotPresent
102
- command:
103
- - node
102
+ {{- if gt (len .Values.imagePullSecrets) 0 }}
103
+ imagePullSecrets:
104
+ {{- range .Values.imagePullSecrets }}
105
+ - name: {{ . }}
106
+ {{- end }}
107
+ {{- end }}
108
+ args:
104
109
  - /app/node_modules/pepr/dist/controller.js
105
110
  - {{ .Values.hash }}
106
111
  readinessProbe:
@@ -115,6 +120,10 @@ export function watcherDeployTemplate(buildTimestamp: string): string {
115
120
  {{- toYaml .Values.watcher.env | nindent 12 }}
116
121
  - name: PEPR_WATCH_MODE
117
122
  value: "true"
123
+ {{- if .Values.additionalIgnoredNamespaces }}
124
+ - name: PEPR_ADDITIONAL_IGNORED_NAMESPACES
125
+ value: "{{ join ", " .Values.additionalIgnoredNamespaces }}"
126
+ {{- end }}
118
127
  envFrom:
119
128
  {{- toYaml .Values.watcher.envFrom | nindent 12 }}
120
129
  securityContext:
@@ -179,8 +188,13 @@ export function admissionDeployTemplate(buildTimestamp: string): string {
179
188
  - name: server
180
189
  image: {{ .Values.admission.image }}
181
190
  imagePullPolicy: IfNotPresent
182
- command:
183
- - node
191
+ {{- if gt (len .Values.imagePullSecrets) 0 }}
192
+ imagePullSecrets:
193
+ {{- range .Values.imagePullSecrets }}
194
+ - name: {{ . }}
195
+ {{- end }}
196
+ {{- end }}
197
+ args:
184
198
  - /app/node_modules/pepr/dist/controller.js
185
199
  - {{ .Values.hash }}
186
200
  readinessProbe:
@@ -195,6 +209,10 @@ export function admissionDeployTemplate(buildTimestamp: string): string {
195
209
  {{- toYaml .Values.admission.env | nindent 12 }}
196
210
  - name: PEPR_WATCH_MODE
197
211
  value: "false"
212
+ {{- if .Values.additionalIgnoredNamespaces }}
213
+ - name: PEPR_ADDITIONAL_IGNORED_NAMESPACES
214
+ value: "{{ join ", " .Values.additionalIgnoredNamespaces }}"
215
+ {{- end }}
198
216
  envFrom:
199
217
  {{- toYaml .Values.admission.envFrom | nindent 12 }}
200
218
  securityContext:
@@ -12,21 +12,45 @@ export function toYaml(obj: any): string {
12
12
  return dumpYaml(obj, { noRefs: true });
13
13
  }
14
14
 
15
+ // Unit Test Me!!
15
16
  export function createWebhookYaml(
16
17
  name: string,
17
18
  config: ModuleConfig,
18
19
  webhookConfiguration: kind.MutatingWebhookConfiguration | kind.ValidatingWebhookConfiguration,
19
20
  ): string {
20
21
  const yaml = toYaml(webhookConfiguration);
21
- return replaceString(
22
- replaceString(
23
- replaceString(yaml, name, "{{ .Values.uuid }}"),
24
- config.onError === "reject" ? "Fail" : "Ignore",
25
- "{{ .Values.admission.failurePolicy }}",
26
- ),
27
- `${config.webhookTimeout}` || "10",
28
- "{{ .Values.admission.webhookTimeout }}",
29
- );
22
+ const replacements = [
23
+ { search: name, replace: "{{ .Values.uuid }}" },
24
+ {
25
+ search: config.onError === "reject" ? "Fail" : "Ignore",
26
+ replace: "{{ .Values.admission.failurePolicy }}",
27
+ },
28
+ {
29
+ search: `${config.webhookTimeout}` || "10",
30
+ replace: "{{ .Values.admission.webhookTimeout }}",
31
+ },
32
+ {
33
+ search: `
34
+ - key: kubernetes.io/metadata.name
35
+ operator: NotIn
36
+ values:
37
+ - kube-system
38
+ - pepr-system
39
+ `,
40
+ replace: `
41
+ - key: kubernetes.io/metadata.name
42
+ operator: NotIn
43
+ values:
44
+ - kube-system
45
+ - pepr-system
46
+ {{- range .Values.additionalIgnoredNamespaces }}
47
+ - {{ . }}
48
+ {{- end }}
49
+ `,
50
+ },
51
+ ];
52
+
53
+ return replacements.reduce((updatedYaml, { search, replace }) => replaceString(updatedYaml, search, replace), yaml);
30
54
  }
31
55
 
32
56
  export function helmLayout(basePath: string, unique: string): Record<string, Record<string, string>> {
@@ -109,7 +109,7 @@ export function getWatcher(
109
109
  name: "watcher",
110
110
  image,
111
111
  imagePullPolicy: "IfNotPresent",
112
- command: ["node", "/app/node_modules/pepr/dist/controller.js", hash],
112
+ args: ["/app/node_modules/pepr/dist/controller.js", hash],
113
113
  readinessProbe: {
114
114
  httpGet: {
115
115
  path: "/healthz",
@@ -248,7 +248,7 @@ export function getDeployment(
248
248
  name: "server",
249
249
  image,
250
250
  imagePullPolicy: "IfNotPresent",
251
- command: ["node", "/app/node_modules/pepr/dist/controller.js", hash],
251
+ args: ["/app/node_modules/pepr/dist/controller.js", hash],
252
252
  readinessProbe: {
253
253
  httpGet: {
254
254
  path: "/healthz",
@@ -10,10 +10,10 @@ import { kind } from "kubernetes-fluent-client";
10
10
  import { concat, equals, uniqWith } from "ramda";
11
11
 
12
12
  import { Assets } from "./assets";
13
- import { Event } from "../enums";
13
+ import { Event, WebhookType } from "../enums";
14
14
  import { Binding } from "../types";
15
15
 
16
- const peprIgnoreNamespaces: string[] = ["kube-system", "pepr-system"];
16
+ export const peprIgnoreNamespaces: string[] = ["kube-system", "pepr-system"];
17
17
 
18
18
  const validateRule = (binding: Binding, isMutateWebhook: boolean): V1RuleWithOperations | undefined => {
19
19
  const { event, kind, isMutate, isValidate } = binding;
@@ -39,6 +39,21 @@ const validateRule = (binding: Binding, isMutateWebhook: boolean): V1RuleWithOpe
39
39
  return ruleObject;
40
40
  };
41
41
 
42
+ export function resolveIgnoreNamespaces(ignoredNSConfig: string[] = []): string[] {
43
+ const ignoredNSEnv = process.env.PEPR_ADDITIONAL_IGNORED_NAMESPACES;
44
+ if (!ignoredNSEnv) {
45
+ return ignoredNSConfig;
46
+ }
47
+
48
+ const namespaces = ignoredNSEnv.split(",").map(ns => ns.trim());
49
+
50
+ // add alwaysIgnore.namespaces to the list
51
+ if (ignoredNSConfig) {
52
+ namespaces.push(...ignoredNSConfig);
53
+ }
54
+ return namespaces.filter(ns => ns.length > 0);
55
+ }
56
+
42
57
  export async function generateWebhookRules(assets: Assets, isMutateWebhook: boolean): Promise<V1RuleWithOperations[]> {
43
58
  const { config, capabilities } = assets;
44
59
 
@@ -53,15 +68,15 @@ export async function generateWebhookRules(assets: Assets, isMutateWebhook: bool
53
68
  return uniqWith(equals, rules);
54
69
  }
55
70
 
56
- export async function webhookConfig(
71
+ export async function webhookConfigGenerator(
57
72
  assets: Assets,
58
- mutateOrValidate: "mutate" | "validate",
73
+ mutateOrValidate: WebhookType,
59
74
  timeoutSeconds = 10,
60
75
  ): Promise<kind.MutatingWebhookConfiguration | kind.ValidatingWebhookConfiguration | null> {
61
76
  const ignore: V1LabelSelectorRequirement[] = [];
62
77
 
63
78
  const { name, tls, config, apiToken, host } = assets;
64
- const ignoreNS = concat(peprIgnoreNamespaces, config?.alwaysIgnore?.namespaces || []);
79
+ const ignoreNS = concat(peprIgnoreNamespaces, resolveIgnoreNamespaces(config?.alwaysIgnore?.namespaces));
65
80
 
66
81
  // Add any namespaces to ignore
67
82
  if (ignoreNS) {
@@ -91,7 +106,7 @@ export async function webhookConfig(
91
106
  };
92
107
  }
93
108
 
94
- const isMutate = mutateOrValidate === "mutate";
109
+ const isMutate = mutateOrValidate === WebhookType.MUTATE;
95
110
  const rules = await generateWebhookRules(assets, isMutate);
96
111
 
97
112
  // If there are no rules, return null
@@ -0,0 +1,50 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import crypto from "crypto";
5
+ import { Assets } from "../assets";
6
+ import { WebhookType } from "../../enums";
7
+ import { apiTokenSecret, service, tlsSecret, watcherService } from "../networking";
8
+ import { clusterRole, clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "../rbac";
9
+ import { dumpYaml, V1Deployment } from "@kubernetes/client-node";
10
+ import { getModuleSecret, getNamespace } from "../pods";
11
+ import { promises as fs } from "fs";
12
+ import { webhookConfigGenerator } from "../webhooks";
13
+
14
+ type deployments = { default: V1Deployment; watch: V1Deployment | null };
15
+
16
+ export async function generateAllYaml(assets: Assets, deployments: deployments): Promise<string> {
17
+ const { name, tls, apiToken, path, config } = assets;
18
+ const code = await fs.readFile(path);
19
+ const hash = crypto.createHash("sha256").update(code).digest("hex");
20
+
21
+ const resources = [
22
+ getNamespace(assets.config.customLabels?.namespace),
23
+ clusterRole(name, assets.capabilities, config.rbacMode, config.rbac),
24
+ clusterRoleBinding(name),
25
+ serviceAccount(name),
26
+ apiTokenSecret(name, apiToken),
27
+ tlsSecret(name, tls),
28
+ deployments.default,
29
+ service(name),
30
+ watcherService(name),
31
+ getModuleSecret(name, code, hash),
32
+ storeRole(name),
33
+ storeRoleBinding(name),
34
+ ];
35
+
36
+ const webhooks = {
37
+ mutate: await webhookConfigGenerator(assets, WebhookType.MUTATE, assets.config.webhookTimeout),
38
+ validate: await webhookConfigGenerator(assets, WebhookType.VALIDATE, assets.config.webhookTimeout),
39
+ };
40
+
41
+ // Add webhooks and watch deployment if they exist
42
+ const additionalResources = [webhooks.mutate, webhooks.validate, deployments.watch].filter(
43
+ resource => resource !== null && resource !== undefined,
44
+ );
45
+
46
+ resources.push(...additionalResources);
47
+
48
+ // Convert the resources to a single YAML string
49
+ return resources.map(resource => dumpYaml(resource, { noRefs: true })).join("---\n");
50
+ }
@@ -0,0 +1,38 @@
1
+ import { dumpYaml } from "@kubernetes/client-node";
2
+ import { Assets } from "../assets";
3
+
4
+ type ConfigType = "manifests" | "charts";
5
+
6
+ export function generateZarfYamlGeneric(assets: Assets, path: string, type: ConfigType): string {
7
+ const manifestSettings = {
8
+ name: "module",
9
+ namespace: "pepr-system",
10
+ files: [path],
11
+ };
12
+ const chartSettings = {
13
+ name: "module",
14
+ namespace: "pepr-system",
15
+ version: `${assets.config.appVersion || "0.0.1"}`,
16
+ localPath: path,
17
+ };
18
+
19
+ const component = {
20
+ name: "module",
21
+ required: true,
22
+ images: [assets.image],
23
+ [type]: [type === "manifests" ? manifestSettings : chartSettings],
24
+ };
25
+
26
+ const zarfCfg = {
27
+ kind: "ZarfPackageConfig",
28
+ metadata: {
29
+ name: assets.name,
30
+ description: `Pepr Module: ${assets.config.description}`,
31
+ url: "https://github.com/defenseunicorns/pepr",
32
+ version: `${assets.config.appVersion || "0.0.1"}`,
33
+ },
34
+ components: [component],
35
+ };
36
+
37
+ return dumpYaml(zarfCfg, { noRefs: true });
38
+ }