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.
Files changed (56) 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 +1 -8
  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 +3 -0
  8. package/dist/cli/init/templates.d.ts.map +1 -1
  9. package/dist/cli.js +1292 -1247
  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 +30 -0
  13. package/dist/lib/assets/assets.d.ts.map +1 -0
  14. package/dist/lib/assets/deploy.d.ts +2 -2
  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 +4 -23
  18. package/dist/lib/assets/index.d.ts.map +1 -1
  19. package/dist/lib/assets/pods.d.ts +1 -1
  20. package/dist/lib/assets/pods.d.ts.map +1 -1
  21. package/dist/lib/assets/webhooks.d.ts +5 -2
  22. package/dist/lib/assets/webhooks.d.ts.map +1 -1
  23. package/dist/lib/assets/yaml/generateAllYaml.d.ts +9 -0
  24. package/dist/lib/assets/yaml/generateAllYaml.d.ts.map +1 -0
  25. package/dist/lib/assets/yaml/generateZarfYaml.d.ts +5 -0
  26. package/dist/lib/assets/yaml/generateZarfYaml.d.ts.map +1 -0
  27. package/dist/lib/assets/yaml/overridesFile.d.ts +15 -0
  28. package/dist/lib/assets/yaml/overridesFile.d.ts.map +1 -0
  29. package/dist/lib/core/module.d.ts.map +1 -1
  30. package/dist/lib/enums.d.ts +4 -0
  31. package/dist/lib/enums.d.ts.map +1 -1
  32. package/dist/lib/processors/mutate-processor.d.ts.map +1 -1
  33. package/dist/lib/processors/validate-processor.d.ts.map +1 -1
  34. package/dist/lib.js +37 -18
  35. package/dist/lib.js.map +4 -4
  36. package/package.json +5 -2
  37. package/src/cli/build.helpers.ts +8 -23
  38. package/src/cli/build.ts +73 -17
  39. package/src/cli/deploy.ts +8 -7
  40. package/src/cli/dev.ts +8 -7
  41. package/src/fixtures/loader.ts +2 -2
  42. package/src/lib/assets/assets.ts +189 -0
  43. package/src/lib/assets/deploy.ts +33 -31
  44. package/src/lib/assets/helm.ts +22 -4
  45. package/src/lib/assets/index.ts +40 -150
  46. package/src/lib/assets/pods.ts +3 -3
  47. package/src/lib/assets/webhooks.ts +23 -17
  48. package/src/lib/assets/yaml/generateAllYaml.ts +50 -0
  49. package/src/lib/assets/yaml/generateZarfYaml.ts +38 -0
  50. package/src/lib/assets/{yaml.ts → yaml/overridesFile.ts} +19 -109
  51. package/src/lib/core/module.ts +2 -1
  52. package/src/lib/enums.ts +6 -0
  53. package/src/lib/processors/mutate-processor.ts +2 -1
  54. package/src/lib/processors/validate-processor.ts +7 -1
  55. package/dist/lib/assets/yaml.d.ts +0 -6
  56. package/dist/lib/assets/yaml.d.ts.map +0 -1
@@ -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:
@@ -1,57 +1,59 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
- import crypto from "crypto";
5
4
  import { dumpYaml } from "@kubernetes/client-node";
6
5
  import { kind } from "kubernetes-fluent-client";
7
- import { ModuleConfig } from "../core/module";
8
- import { TLSOut, genTLS } from "../tls";
9
- import { CapabilityExport } from "../types";
10
- import { WebhookIgnore } from "../k8s";
11
- import { deploy } from "./deploy";
12
- import { loadCapabilities } from "./loader";
13
- import { allYaml, zarfYaml, overridesFile, zarfYamlChart } from "./yaml";
14
- import { namespaceComplianceValidator, replaceString } from "../helpers";
15
- import { dedent } from "../helpers";
6
+ import { replaceString } from "../helpers";
16
7
  import { resolve } from "path";
17
- import {
18
- chartYaml,
19
- namespaceTemplate,
20
- admissionDeployTemplate,
21
- watcherDeployTemplate,
22
- clusterRoleTemplate,
23
- serviceMonitorTemplate,
24
- } from "./helm";
25
- import { promises as fs } from "fs";
26
- import { webhookConfig } from "./webhooks";
27
- import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking";
28
- import { getWatcher, getModuleSecret } from "./pods";
29
-
30
- import { clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "./rbac";
31
- import { createDirectoryIfNotExists } from "../filesystemService";
8
+ import { ModuleConfig } from "../core/module";
32
9
 
33
10
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
- function toYaml(obj: any): string {
11
+ export function toYaml(obj: any): string {
35
12
  return dumpYaml(obj, { noRefs: true });
36
13
  }
37
14
 
38
- function createWebhookYaml(
39
- assets: Assets,
15
+ // Unit Test Me!!
16
+ export function createWebhookYaml(
17
+ name: string,
18
+ config: ModuleConfig,
40
19
  webhookConfiguration: kind.MutatingWebhookConfiguration | kind.ValidatingWebhookConfiguration,
41
20
  ): string {
42
21
  const yaml = toYaml(webhookConfiguration);
43
- return replaceString(
44
- replaceString(
45
- replaceString(yaml, assets.name, "{{ .Values.uuid }}"),
46
- assets.config.onError === "reject" ? "Fail" : "Ignore",
47
- "{{ .Values.admission.failurePolicy }}",
48
- ),
49
- `${assets.config.webhookTimeout}` || "10",
50
- "{{ .Values.admission.webhookTimeout }}",
51
- );
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);
52
54
  }
53
55
 
54
- function helmLayout(basePath: string, unique: string): Record<string, Record<string, string>> {
56
+ export function helmLayout(basePath: string, unique: string): Record<string, Record<string, string>> {
55
57
  const helm: Record<string, Record<string, string>> = {
56
58
  dirs: {
57
59
  chart: resolve(`${basePath}/${unique}-chart`),
@@ -90,115 +92,3 @@ function helmLayout(basePath: string, unique: string): Record<string, Record<str
90
92
 
91
93
  return helm;
92
94
  }
93
-
94
- export class Assets {
95
- readonly name: string;
96
- readonly tls: TLSOut;
97
- readonly apiToken: string;
98
- readonly alwaysIgnore!: WebhookIgnore;
99
- capabilities!: CapabilityExport[];
100
-
101
- image: string;
102
- buildTimestamp: string;
103
- hash: string;
104
-
105
- constructor(
106
- readonly config: ModuleConfig,
107
- readonly path: string,
108
- readonly host?: string,
109
- ) {
110
- this.name = `pepr-${config.uuid}`;
111
- this.buildTimestamp = `${Date.now()}`;
112
- this.alwaysIgnore = config.alwaysIgnore;
113
- this.image = `ghcr.io/defenseunicorns/pepr/controller:v${config.peprVersion}`;
114
- this.hash = "";
115
- // Generate the ephemeral tls things
116
- this.tls = genTLS(this.host || `${this.name}.pepr-system.svc`);
117
-
118
- // Generate the api token for the controller / webhook
119
- this.apiToken = crypto.randomBytes(32).toString("hex");
120
- }
121
-
122
- setHash = (hash: string): void => {
123
- this.hash = hash;
124
- };
125
-
126
- deploy = async (force: boolean, webhookTimeout?: number): Promise<void> => {
127
- this.capabilities = await loadCapabilities(this.path);
128
- await deploy(this, force, webhookTimeout);
129
- };
130
-
131
- zarfYaml = (path: string): string => zarfYaml(this, path);
132
-
133
- zarfYamlChart = (path: string): string => zarfYamlChart(this, path);
134
-
135
- allYaml = async (imagePullSecret?: string): Promise<string> => {
136
- this.capabilities = await loadCapabilities(this.path);
137
- // give error if namespaces are not respected
138
- for (const capability of this.capabilities) {
139
- namespaceComplianceValidator(capability, this.alwaysIgnore?.namespaces);
140
- }
141
-
142
- return allYaml(this, imagePullSecret);
143
- };
144
-
145
- /* eslint max-statements: ["warn", 21] */
146
- generateHelmChart = async (basePath: string): Promise<void> => {
147
- const helm = helmLayout(basePath, this.config.uuid);
148
-
149
- try {
150
- await Promise.all(
151
- Object.values(helm.dirs)
152
- .sort((l, r) => l.split("/").length - r.split("/").length)
153
- .map(async dir => await createDirectoryIfNotExists(dir)),
154
- );
155
-
156
- const code = await fs.readFile(this.path);
157
-
158
- const pairs: [string, () => string][] = [
159
- [helm.files.chartYaml, (): string => dedent(chartYaml(this.config.uuid, this.config.description || ""))],
160
- [helm.files.namespaceYaml, (): string => dedent(namespaceTemplate())],
161
- [helm.files.watcherServiceYaml, (): string => toYaml(watcherService(this.name))],
162
- [helm.files.admissionServiceYaml, (): string => toYaml(service(this.name))],
163
- [helm.files.tlsSecretYaml, (): string => toYaml(tlsSecret(this.name, this.tls))],
164
- [helm.files.apiTokenSecretYaml, (): string => toYaml(apiTokenSecret(this.name, this.apiToken))],
165
- [helm.files.storeRoleYaml, (): string => toYaml(storeRole(this.name))],
166
- [helm.files.storeRoleBindingYaml, (): string => toYaml(storeRoleBinding(this.name))],
167
- [helm.files.clusterRoleYaml, (): string => dedent(clusterRoleTemplate())],
168
- [helm.files.clusterRoleBindingYaml, (): string => toYaml(clusterRoleBinding(this.name))],
169
- [helm.files.serviceAccountYaml, (): string => toYaml(serviceAccount(this.name))],
170
- [helm.files.moduleSecretYaml, (): string => toYaml(getModuleSecret(this.name, code, this.hash))],
171
- ];
172
- await Promise.all(pairs.map(async ([file, content]) => await fs.writeFile(file, content())));
173
-
174
- await overridesFile(this, helm.files.valuesYaml);
175
-
176
- const [mutateWebhook, validateWebhook] = await Promise.all([
177
- webhookConfig(this, "mutate", this.config.webhookTimeout),
178
- webhookConfig(this, "validate", this.config.webhookTimeout),
179
- ]);
180
-
181
- if (validateWebhook || mutateWebhook) {
182
- await fs.writeFile(helm.files.admissionDeploymentYaml, dedent(admissionDeployTemplate(this.buildTimestamp)));
183
- await fs.writeFile(helm.files.admissionServiceMonitorYaml, dedent(serviceMonitorTemplate("admission")));
184
- }
185
-
186
- if (mutateWebhook) {
187
- await fs.writeFile(helm.files.mutationWebhookYaml, createWebhookYaml(this, mutateWebhook));
188
- }
189
-
190
- if (validateWebhook) {
191
- await fs.writeFile(helm.files.validationWebhookYaml, createWebhookYaml(this, validateWebhook));
192
- }
193
-
194
- const watchDeployment = getWatcher(this, this.hash, this.buildTimestamp);
195
- if (watchDeployment) {
196
- await fs.writeFile(helm.files.watcherDeploymentYaml, dedent(watcherDeployTemplate(this.buildTimestamp)));
197
- await fs.writeFile(helm.files.watcherServiceMonitorYaml, dedent(serviceMonitorTemplate("watcher")));
198
- }
199
- } catch (err) {
200
- console.error(`Error generating helm chart: ${err.message}`);
201
- process.exit(1);
202
- }
203
- };
204
- }
@@ -5,7 +5,7 @@ import { KubernetesObject, V1EnvVar } from "@kubernetes/client-node";
5
5
  import { kind } from "kubernetes-fluent-client";
6
6
  import { gzipSync } from "zlib";
7
7
  import { secretOverLimit } from "../helpers";
8
- import { Assets } from ".";
8
+ import { Assets } from "./assets";
9
9
  import { ModuleConfig } from "../core/module";
10
10
  import { Binding } from "../types";
11
11
 
@@ -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",
@@ -9,17 +9,11 @@ import {
9
9
  import { kind } from "kubernetes-fluent-client";
10
10
  import { concat, equals, uniqWith } from "ramda";
11
11
 
12
- import { Assets } from ".";
13
- import { Event } from "../enums";
12
+ import { Assets } from "./assets";
13
+ import { Event, WebhookType } from "../enums";
14
14
  import { Binding } from "../types";
15
15
 
16
- const peprIgnoreLabel: V1LabelSelectorRequirement = {
17
- key: "pepr.dev",
18
- operator: "NotIn",
19
- values: ["ignore"],
20
- };
21
-
22
- const peprIgnoreNamespaces: string[] = ["kube-system", "pepr-system"];
16
+ export const peprIgnoreNamespaces: string[] = ["kube-system", "pepr-system"];
23
17
 
24
18
  const validateRule = (binding: Binding, isMutateWebhook: boolean): V1RuleWithOperations | undefined => {
25
19
  const { event, kind, isMutate, isValidate } = binding;
@@ -45,6 +39,21 @@ const validateRule = (binding: Binding, isMutateWebhook: boolean): V1RuleWithOpe
45
39
  return ruleObject;
46
40
  };
47
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
+
48
57
  export async function generateWebhookRules(assets: Assets, isMutateWebhook: boolean): Promise<V1RuleWithOperations[]> {
49
58
  const { config, capabilities } = assets;
50
59
 
@@ -59,15 +68,15 @@ export async function generateWebhookRules(assets: Assets, isMutateWebhook: bool
59
68
  return uniqWith(equals, rules);
60
69
  }
61
70
 
62
- export async function webhookConfig(
71
+ export async function webhookConfigGenerator(
63
72
  assets: Assets,
64
- mutateOrValidate: "mutate" | "validate",
73
+ mutateOrValidate: WebhookType,
65
74
  timeoutSeconds = 10,
66
75
  ): Promise<kind.MutatingWebhookConfiguration | kind.ValidatingWebhookConfiguration | null> {
67
- const ignore = [peprIgnoreLabel];
76
+ const ignore: V1LabelSelectorRequirement[] = [];
68
77
 
69
78
  const { name, tls, config, apiToken, host } = assets;
70
- const ignoreNS = concat(peprIgnoreNamespaces, config?.alwaysIgnore?.namespaces || []);
79
+ const ignoreNS = concat(peprIgnoreNamespaces, resolveIgnoreNamespaces(config?.alwaysIgnore?.namespaces));
71
80
 
72
81
  // Add any namespaces to ignore
73
82
  if (ignoreNS) {
@@ -97,7 +106,7 @@ export async function webhookConfig(
97
106
  };
98
107
  }
99
108
 
100
- const isMutate = mutateOrValidate === "mutate";
109
+ const isMutate = mutateOrValidate === WebhookType.MUTATE;
101
110
  const rules = await generateWebhookRules(assets, isMutate);
102
111
 
103
112
  // If there are no rules, return null
@@ -120,9 +129,6 @@ export async function webhookConfig(
120
129
  namespaceSelector: {
121
130
  matchExpressions: ignore,
122
131
  },
123
- objectSelector: {
124
- matchExpressions: ignore,
125
- },
126
132
  rules,
127
133
  // @todo: track side effects state
128
134
  sideEffects: "None",
@@ -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
+ }
@@ -1,24 +1,32 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
-
1
+ import { genEnv } from "../pods";
2
+ import { ModuleConfig } from "../../core/module";
3
+ import { CapabilityExport } from "../../types";
4
4
  import { dumpYaml } from "@kubernetes/client-node";
5
- import crypto from "crypto";
5
+ import { clusterRole } from "../rbac";
6
6
  import { promises as fs } from "fs";
7
- import { Assets } from ".";
8
- import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking";
9
- import { getDeployment, getModuleSecret, getNamespace, getWatcher } from "./pods";
10
- import { clusterRole, clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "./rbac";
11
- import { webhookConfig } from "./webhooks";
12
- import { genEnv } from "./pods";
13
7
 
8
+ type CommonOverrideValues = {
9
+ apiToken: string;
10
+ capabilities: CapabilityExport[];
11
+ config: ModuleConfig;
12
+ hash: string;
13
+ name: string;
14
+ };
15
+
16
+ type ChartOverrides = CommonOverrideValues & {
17
+ image: string;
18
+ };
14
19
  // Helm Chart overrides file (values.yaml) generated from assets
15
20
  export async function overridesFile(
16
- { hash, name, image, config, apiToken, capabilities }: Assets,
21
+ { hash, name, image, config, apiToken, capabilities }: ChartOverrides,
17
22
  path: string,
23
+ imagePullSecrets: string[],
18
24
  ): Promise<void> {
19
25
  const rbacOverrides = clusterRole(name, capabilities, config.rbacMode, config.rbac).rules;
20
26
 
21
27
  const overrides = {
28
+ imagePullSecrets,
29
+ additionalIgnoredNamespaces: [],
22
30
  rbac: rbacOverrides,
23
31
  secrets: {
24
32
  apiToken: Buffer.from(apiToken).toString("base64"),
@@ -169,101 +177,3 @@ export async function overridesFile(
169
177
 
170
178
  await fs.writeFile(path, dumpYaml(overrides, { noRefs: true, forceQuotes: true }));
171
179
  }
172
- export function zarfYaml({ name, image, config }: Assets, path: string): string {
173
- const zarfCfg = {
174
- kind: "ZarfPackageConfig",
175
- metadata: {
176
- name,
177
- description: `Pepr Module: ${config.description}`,
178
- url: "https://github.com/defenseunicorns/pepr",
179
- version: `${config.appVersion || "0.0.1"}`,
180
- },
181
- components: [
182
- {
183
- name: "module",
184
- required: true,
185
- manifests: [
186
- {
187
- name: "module",
188
- namespace: "pepr-system",
189
- files: [path],
190
- },
191
- ],
192
- images: [image],
193
- },
194
- ],
195
- };
196
-
197
- return dumpYaml(zarfCfg, { noRefs: true });
198
- }
199
-
200
- export function zarfYamlChart({ name, image, config }: Assets, path: string): string {
201
- const zarfCfg = {
202
- kind: "ZarfPackageConfig",
203
- metadata: {
204
- name,
205
- description: `Pepr Module: ${config.description}`,
206
- url: "https://github.com/defenseunicorns/pepr",
207
- version: `${config.appVersion || "0.0.1"}`,
208
- },
209
- components: [
210
- {
211
- name: "module",
212
- required: true,
213
- charts: [
214
- {
215
- name: "module",
216
- namespace: "pepr-system",
217
- version: `${config.appVersion || "0.0.1"}`,
218
- localPath: path,
219
- },
220
- ],
221
- images: [image],
222
- },
223
- ],
224
- };
225
-
226
- return dumpYaml(zarfCfg, { noRefs: true });
227
- }
228
-
229
- export async function allYaml(assets: Assets, imagePullSecret?: string): Promise<string> {
230
- const { name, tls, apiToken, path, config } = assets;
231
- const code = await fs.readFile(path);
232
-
233
- // Generate a hash of the code
234
- assets.hash = crypto.createHash("sha256").update(code).digest("hex");
235
-
236
- const mutateWebhook = await webhookConfig(assets, "mutate", assets.config.webhookTimeout);
237
- const validateWebhook = await webhookConfig(assets, "validate", assets.config.webhookTimeout);
238
- const watchDeployment = getWatcher(assets, assets.hash, assets.buildTimestamp, imagePullSecret);
239
-
240
- const resources = [
241
- getNamespace(assets.config.customLabels?.namespace),
242
- clusterRole(name, assets.capabilities, config.rbacMode, config.rbac),
243
- clusterRoleBinding(name),
244
- serviceAccount(name),
245
- apiTokenSecret(name, apiToken),
246
- tlsSecret(name, tls),
247
- getDeployment(assets, assets.hash, assets.buildTimestamp, imagePullSecret),
248
- service(name),
249
- watcherService(name),
250
- getModuleSecret(name, code, assets.hash),
251
- storeRole(name),
252
- storeRoleBinding(name),
253
- ];
254
-
255
- if (mutateWebhook) {
256
- resources.push(mutateWebhook);
257
- }
258
-
259
- if (validateWebhook) {
260
- resources.push(validateWebhook);
261
- }
262
-
263
- if (watchDeployment) {
264
- resources.push(watchDeployment);
265
- }
266
-
267
- // Convert the resources to a single YAML string
268
- return resources.map(r => dumpYaml(r, { noRefs: true })).join("---\n");
269
- }
@@ -9,6 +9,7 @@ import { CapabilityExport, AdmissionRequest } from "../types";
9
9
  import { setupWatch } from "../processors/watch-processor";
10
10
  import { Log } from "../../lib";
11
11
  import { V1PolicyRule as PolicyRule } from "@kubernetes/client-node";
12
+ import { resolveIgnoreNamespaces } from "../assets/webhooks";
12
13
 
13
14
  /** Custom Labels Type for package.json */
14
15
  export interface CustomLabels {
@@ -113,7 +114,7 @@ export class PeprModule {
113
114
  // Wait for the controller to be ready before setting up watches
114
115
  if (isWatchMode() || isDevMode()) {
115
116
  try {
116
- setupWatch(capabilities, pepr?.alwaysIgnore?.namespaces);
117
+ setupWatch(capabilities, resolveIgnoreNamespaces(pepr?.alwaysIgnore?.namespaces));
117
118
  } catch (e) {
118
119
  Log.error(e, "Error setting up watch");
119
120
  process.exit(1);
package/src/lib/enums.ts CHANGED
@@ -19,3 +19,9 @@ export enum Event {
19
19
  CREATE_OR_UPDATE = "CREATEORUPDATE",
20
20
  ANY = "*",
21
21
  }
22
+
23
+ // Supported webhook types for @kubernetes/client-node's V1MutatingWebhookConfiguration
24
+ export enum WebhookType {
25
+ MUTATE = "mutate",
26
+ VALIDATE = "validate",
27
+ }
@@ -14,6 +14,7 @@ import { ModuleConfig } from "../core/module";
14
14
  import { PeprMutateRequest } from "../mutate-request";
15
15
  import { base64Encode, convertFromBase64Map, convertToBase64Map } from "../utils";
16
16
  import { OnError } from "../../cli/init/enums";
17
+ import { resolveIgnoreNamespaces } from "../assets/webhooks";
17
18
 
18
19
  export interface Bindable {
19
20
  req: AdmissionRequest;
@@ -169,7 +170,7 @@ export async function mutateProcessor(
169
170
  bind.binding,
170
171
  bind.req,
171
172
  bind.namespaces,
172
- bind.config?.alwaysIgnore?.namespaces,
173
+ resolveIgnoreNamespaces(bind.config?.alwaysIgnore?.namespaces),
173
174
  );
174
175
  if (shouldSkip !== "") {
175
176
  Log.debug(shouldSkip);
@@ -10,6 +10,7 @@ import Log from "../telemetry/logger";
10
10
  import { convertFromBase64Map } from "../utils";
11
11
  import { PeprValidateRequest } from "../validate-request";
12
12
  import { ModuleConfig } from "../core/module";
13
+ import { resolveIgnoreNamespaces } from "../assets/webhooks";
13
14
 
14
15
  export async function processRequest(
15
16
  binding: Binding,
@@ -78,7 +79,12 @@ export async function validateProcessor(
78
79
  }
79
80
 
80
81
  // Continue to the next action without doing anything if this one should be skipped
81
- const shouldSkip = shouldSkipRequest(binding, req, namespaces, config?.alwaysIgnore?.namespaces);
82
+ const shouldSkip = shouldSkipRequest(
83
+ binding,
84
+ req,
85
+ namespaces,
86
+ resolveIgnoreNamespaces(config?.alwaysIgnore?.namespaces),
87
+ );
82
88
  if (shouldSkip !== "") {
83
89
  Log.debug(shouldSkip);
84
90
  continue;