pepr 0.42.3 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) 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 +0 -7
  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 +742 -710
  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.map +1 -1
  27. package/dist/lib/enums.d.ts +4 -0
  28. package/dist/lib/enums.d.ts.map +1 -1
  29. package/dist/lib/processors/mutate-processor.d.ts +2 -0
  30. package/dist/lib/processors/mutate-processor.d.ts.map +1 -1
  31. package/dist/lib/processors/validate-processor.d.ts.map +1 -1
  32. package/dist/lib/telemetry/timeUtils.d.ts +2 -0
  33. package/dist/lib/telemetry/timeUtils.d.ts.map +1 -0
  34. package/dist/lib/telemetry/webhookTimeouts.d.ts +9 -0
  35. package/dist/lib/telemetry/webhookTimeouts.d.ts.map +1 -0
  36. package/dist/lib.js +79 -22
  37. package/dist/lib.js.map +4 -4
  38. package/package.json +5 -2
  39. package/src/cli/build.helpers.ts +7 -22
  40. package/src/cli/build.ts +72 -16
  41. package/src/cli/deploy.ts +7 -6
  42. package/src/cli/dev.ts +9 -6
  43. package/src/fixtures/loader.ts +2 -2
  44. package/src/lib/assets/assets.ts +66 -53
  45. package/src/lib/assets/deploy.ts +32 -30
  46. package/src/lib/assets/helm.ts +22 -4
  47. package/src/lib/assets/index.ts +33 -9
  48. package/src/lib/assets/pods.ts +2 -2
  49. package/src/lib/assets/webhooks.ts +21 -6
  50. package/src/lib/assets/yaml/generateAllYaml.ts +50 -0
  51. package/src/lib/assets/yaml/generateZarfYaml.ts +38 -0
  52. package/src/lib/assets/{yaml.ts → yaml/overridesFile.ts} +8 -120
  53. package/src/lib/core/module.ts +2 -1
  54. package/src/lib/enums.ts +6 -0
  55. package/src/lib/processors/mutate-processor.ts +15 -7
  56. package/src/lib/processors/validate-processor.ts +13 -4
  57. package/src/lib/telemetry/timeUtils.ts +1 -0
  58. package/src/lib/telemetry/webhookTimeouts.ts +34 -0
  59. package/dist/lib/assets/yaml.d.ts +0 -32
  60. package/dist/lib/assets/yaml.d.ts.map +0 -1
package/package.json CHANGED
@@ -15,7 +15,7 @@
15
15
  "!src/**/*.test.ts",
16
16
  "!dist/**/*.test.d.ts*"
17
17
  ],
18
- "version": "0.42.3",
18
+ "version": "0.44.0",
19
19
  "main": "dist/lib.js",
20
20
  "types": "dist/lib.d.ts",
21
21
  "scripts": {
@@ -27,6 +27,9 @@
27
27
  "build:image": "npm run build && docker buildx build --output type=docker --tag pepr:dev .",
28
28
  "test": "npm run test:unit && npm run test:journey",
29
29
  "test:unit": "npm run gen-data-json && jest src --coverage --detectOpenHandles --coverageDirectory=./coverage --testPathIgnorePatterns='cosign.e2e.test.ts'",
30
+ "test:integration": "npm run test:integration:prep && npm run test:integration:run",
31
+ "test:integration:prep": "./integration/prep.sh",
32
+ "test:integration:run": "jest --maxWorkers=4 integration",
30
33
  "test:journey": "npm run test:journey:k3d && npm run build && npm run test:journey:image && npm run test:journey:run",
31
34
  "test:journey:prep": "if [ ! -d ./pepr-upgrade-test ]; then git clone https://github.com/defenseunicorns/pepr-upgrade-test.git ; fi",
32
35
  "test:journey-wasm": "npm run test:journey:k3d && npm run build && npm run test:journey:image && npm run test:journey:run-wasm",
@@ -46,7 +49,7 @@
46
49
  "follow-redirects": "1.15.9",
47
50
  "http-status-codes": "^2.3.0",
48
51
  "json-pointer": "^0.6.2",
49
- "kubernetes-fluent-client": "3.3.7",
52
+ "kubernetes-fluent-client": "3.3.8",
50
53
  "pino": "9.6.0",
51
54
  "pino-pretty": "13.0.0",
52
55
  "prom-client": "15.1.3",
@@ -8,6 +8,9 @@ import { BuildOptions, BuildResult, context, BuildContext } from "esbuild";
8
8
  import { Assets } from "../lib/assets/assets";
9
9
  import { resolve } from "path";
10
10
  import { promises as fs } from "fs";
11
+ import { generateAllYaml } from "../lib/assets/yaml/generateAllYaml";
12
+ import { webhookConfigGenerator } from "../lib/assets/webhooks";
13
+ import { generateZarfYamlGeneric } from "../lib/assets/yaml/generateZarfYaml";
11
14
 
12
15
  export type Reloader = (opts: BuildResult<BuildOptions>) => void | Promise<void>;
13
16
  /**
@@ -89,24 +92,6 @@ export function validImagePullSecret(imagePullSecretName: string): void {
89
92
  }
90
93
  }
91
94
 
92
- /**
93
- * Constraint to majke sure customImage and registry are not both used
94
- * @param customImage
95
- * @param registry
96
- * @returns
97
- */
98
- export function handleCustomImage(customImage: string, registry: string): string {
99
- let defaultImage = "";
100
- if (customImage) {
101
- if (registry) {
102
- console.error(`Custom Image and registry cannot be used together.`);
103
- process.exit(1);
104
- }
105
- defaultImage = customImage;
106
- }
107
- return defaultImage;
108
- }
109
-
110
95
  /**
111
96
  * Creates and pushes a custom image for WASM or any other included files
112
97
  * @param includedFiles
@@ -191,18 +176,18 @@ export async function generateYamlAndWriteToDisk(obj: {
191
176
  const yamlFile = `pepr-module-${uuid}.yaml`;
192
177
  const chartPath = `${uuid}-chart`;
193
178
  const yamlPath = resolve(outputDir, yamlFile);
194
- const yaml = await assets.allYaml(imagePullSecret);
179
+ const yaml = await assets.allYaml(generateAllYaml, imagePullSecret);
195
180
  const zarfPath = resolve(outputDir, "zarf.yaml");
196
181
 
197
182
  let localZarf = "";
198
183
  if (zarf === "chart") {
199
- localZarf = assets.zarfYamlChart(chartPath);
184
+ localZarf = assets.zarfYamlChart(generateZarfYamlGeneric, chartPath);
200
185
  } else {
201
- localZarf = assets.zarfYaml(yamlFile);
186
+ localZarf = assets.zarfYaml(generateZarfYamlGeneric, yamlFile);
202
187
  }
203
188
  await fs.writeFile(yamlPath, yaml);
204
189
  await fs.writeFile(zarfPath, localZarf);
205
190
 
206
- await assets.generateHelmChart(outputDir);
191
+ await assets.generateHelmChart(webhookConfigGenerator, outputDir);
207
192
  console.info(`✅ K8s resource for the module saved to ${yamlPath}`);
208
193
  }
package/src/cli/build.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
4
  import { execFileSync } from "child_process";
5
- import { BuildOptions, BuildResult, analyzeMetafile } from "esbuild";
5
+ import { BuildContext, BuildOptions, BuildResult, analyzeMetafile } from "esbuild";
6
6
  import { promises as fs } from "fs";
7
7
  import { basename, dirname, extname, resolve } from "path";
8
8
  import { Assets } from "../lib/assets/assets";
@@ -17,17 +17,55 @@ import {
17
17
  handleEmbedding,
18
18
  handleCustomOutputDir,
19
19
  handleValidCapabilityNames,
20
- handleCustomImage,
21
20
  handleCustomImageBuild,
22
21
  checkIronBankImage,
23
22
  validImagePullSecret,
24
23
  generateYamlAndWriteToDisk,
25
24
  } from "./build.helpers";
25
+ import { ModuleConfig } from "../lib/core/module";
26
26
 
27
27
  const peprTS = "pepr.ts";
28
28
  let outputDir: string = "dist";
29
29
  export type Reloader = (opts: BuildResult<BuildOptions>) => void | Promise<void>;
30
30
 
31
+ export type PeprNestedFields = Pick<
32
+ ModuleConfig,
33
+ | "uuid"
34
+ | "onError"
35
+ | "webhookTimeout"
36
+ | "customLabels"
37
+ | "alwaysIgnore"
38
+ | "env"
39
+ | "rbac"
40
+ | "rbacMode"
41
+ > & {
42
+ peprVersion: string;
43
+ };
44
+
45
+ export type PeprConfig = Omit<ModuleConfig, keyof PeprNestedFields> & {
46
+ pepr: PeprNestedFields & {
47
+ includedFiles: string[];
48
+ };
49
+ description: string;
50
+ version: string;
51
+ };
52
+
53
+ type LoadModuleReturn = {
54
+ cfg: PeprConfig;
55
+ entryPointPath: string;
56
+ modulePath: string;
57
+ name: string;
58
+ path: string;
59
+ uuid: string;
60
+ };
61
+
62
+ type BuildModuleReturn = {
63
+ ctx: BuildContext<BuildOptions>;
64
+ path: string;
65
+ cfg: PeprConfig;
66
+ uuid: string;
67
+ } | void;
68
+
31
69
  export default function (program: RootCmd): void {
32
70
  program
33
71
  .command("build")
@@ -37,34 +75,44 @@ export default function (program: RootCmd): void {
37
75
  "-n, --no-embed",
38
76
  "Disables embedding of deployment files into output module. Useful when creating library modules intended solely for reuse/distribution via NPM.",
39
77
  )
40
- .option(
41
- "-i, --custom-image <custom-image>",
42
- "Custom Image: Use custom image for Admission and Watch Deployments.",
78
+ .addOption(
79
+ new Option(
80
+ "-i, --custom-image <custom-image>",
81
+ "Specify a custom image (including version) for Admission and Watch Deployments. Example: 'docker.io/username/custom-pepr-controller:v1.0.0'",
82
+ ).conflicts(["version", "registryInfo", "registry"]),
43
83
  )
44
- .option(
45
- "-r, --registry-info [<registry>/<username>]",
46
- "Registry Info: Image registry and username. Note: You must be signed into the registry",
84
+ .addOption(
85
+ new Option(
86
+ "-r, --registry-info [<registry>/<username>]",
87
+ "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>'.",
88
+ ).conflicts(["customImage", "version", "registry"]),
47
89
  )
90
+
48
91
  .option("-o, --output-dir <output directory>", "Define where to place build output")
49
92
  .option(
50
93
  "--timeout <timeout>",
51
94
  "How long the API server should wait for a webhook to respond before treating the call as a failure",
52
95
  parseTimeout,
53
96
  )
54
- .option(
55
- "-v, --version <version>. Example: '0.27.3'",
56
- "The version of the Pepr image to use in the deployment manifests.",
97
+ .addOption(
98
+ new Option(
99
+ "-v, --version <version>",
100
+ "The version of the Pepr image to use in the deployment manifests. Example: '0.27.3'.",
101
+ ).conflicts(["customImage", "registryInfo"]),
57
102
  )
58
103
  .option(
59
104
  "--withPullSecret <imagePullSecret>",
60
105
  "Image Pull Secret: Use image pull secret for controller Deployment.",
106
+ "",
61
107
  )
62
108
 
63
109
  .addOption(
64
110
  new Option(
65
111
  "--registry <GitHub|Iron Bank>",
66
112
  "Container registry: Choose container registry for deployment manifests. Can't be used with --custom-image.",
67
- ).choices(["GitHub", "Iron Bank"]),
113
+ )
114
+ .conflicts(["customImage", "registryInfo"])
115
+ .choices(["GitHub", "Iron Bank"]),
68
116
  )
69
117
 
70
118
  .addOption(
@@ -91,7 +139,7 @@ export default function (program: RootCmd): void {
91
139
  // Files to include in controller image for WASM support
92
140
  const { includedFiles } = cfg.pepr;
93
141
 
94
- let image = handleCustomImage(opts.customImage, opts.registry);
142
+ let image = opts.customImage || "";
95
143
 
96
144
  // Check if there is a custom timeout defined
97
145
  if (opts.timeout !== undefined) {
@@ -120,10 +168,14 @@ export default function (program: RootCmd): void {
120
168
  ...cfg.pepr,
121
169
  appVersion: cfg.version,
122
170
  description: cfg.description,
171
+ alwaysIgnore: {
172
+ namespaces: cfg.pepr.alwaysIgnore?.namespaces,
173
+ },
123
174
  // Can override the rbacMode with the CLI option
124
175
  rbacMode: determineRbacMode(opts, cfg),
125
176
  },
126
177
  path,
178
+ opts.withPullSecret === "" ? [] : [opts.withPullSecret],
127
179
  );
128
180
 
129
181
  // If registry is set to Iron Bank, use Iron Bank image
@@ -156,7 +208,7 @@ externalLibs.push("pepr");
156
208
  // Add the kubernetes client to the list of external libraries as it is pulled in by kubernetes-fluent-client
157
209
  externalLibs.push("@kubernetes/client-node");
158
210
 
159
- export async function loadModule(entryPoint = peprTS) {
211
+ export async function loadModule(entryPoint = peprTS): Promise<LoadModuleReturn> {
160
212
  // Resolve path to the module / files
161
213
  const entryPointPath = resolve(".", entryPoint);
162
214
  const modulePath = dirname(entryPointPath);
@@ -197,7 +249,11 @@ export async function loadModule(entryPoint = peprTS) {
197
249
  };
198
250
  }
199
251
 
200
- export async function buildModule(reloader?: Reloader, entryPoint = peprTS, embed = true) {
252
+ export async function buildModule(
253
+ reloader?: Reloader,
254
+ entryPoint = peprTS,
255
+ embed = true,
256
+ ): Promise<BuildModuleReturn> {
201
257
  try {
202
258
  const { cfg, modulePath, path, uuid } = await loadModule(entryPoint);
203
259
 
@@ -314,7 +370,7 @@ function handleModuleBuildError(e: BuildModuleResult): void {
314
370
  }
315
371
  }
316
372
 
317
- export async function checkFormat() {
373
+ export async function checkFormat(): Promise<void> {
318
374
  const validFormat = await peprFormat(true);
319
375
 
320
376
  if (!validFormat) {
package/src/cli/deploy.ts CHANGED
@@ -4,13 +4,13 @@
4
4
  import prompt from "prompts";
5
5
 
6
6
  import { Assets } from "../lib/assets/assets";
7
- import { buildModule } from "./build";
8
- import { RootCmd } from "./root";
9
- import { validateCapabilityNames } from "../lib/helpers";
10
7
  import { ImagePullSecret } from "../lib/types";
11
- import { sanitizeName } from "./init/utils";
12
- import { deployImagePullSecret } from "../lib/assets/deploy";
8
+ import { RootCmd } from "./root";
9
+ import { buildModule } from "./build";
10
+ import { deployImagePullSecret, deployWebhook } from "../lib/assets/deploy";
13
11
  import { namespaceDeploymentsReady } from "../lib/deploymentChecks";
12
+ import { sanitizeName } from "./init/utils";
13
+ import { validateCapabilityNames } from "../lib/helpers";
14
14
 
15
15
  export interface ImagePullSecretDetails {
16
16
  pullSecret?: string;
@@ -128,11 +128,12 @@ export default function (program: RootCmd): void {
128
128
  const webhook = new Assets(
129
129
  { ...builtModule.cfg.pepr, description: builtModule.cfg.description },
130
130
  builtModule.path,
131
+ [],
131
132
  );
132
133
  webhook.image = opts.image ?? webhook.image;
133
134
 
134
135
  try {
135
- await webhook.deploy(opts.force, builtModule.cfg.pepr.webhookTimeout ?? 10);
136
+ await webhook.deploy(deployWebhook, opts.force, builtModule.cfg.pepr.webhookTimeout ?? 10);
136
137
 
137
138
  // wait for capabilities to be loaded and test names
138
139
  validateCapabilityNames(webhook.capabilities);
package/src/cli/dev.ts CHANGED
@@ -1,15 +1,17 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
- import { ChildProcess, fork } from "child_process";
5
- import { promises as fs } from "fs";
6
4
  import prompt from "prompts";
7
- import { validateCapabilityNames } from "../lib/helpers";
8
5
  import { Assets } from "../lib/assets/assets";
9
- import { buildModule, loadModule } from "./build";
10
- import { RootCmd } from "./root";
6
+ import { ChildProcess, fork } from "child_process";
11
7
  import { K8s, kind } from "kubernetes-fluent-client";
8
+ import { RootCmd } from "./root";
12
9
  import { Store } from "../lib/k8s";
10
+ import { buildModule, loadModule } from "./build";
11
+ import { deployWebhook } from "../lib/assets/deploy";
12
+ import { promises as fs } from "fs";
13
+ import { validateCapabilityNames } from "../lib/helpers";
14
+
13
15
  export default function (program: RootCmd): void {
14
16
  program
15
17
  .command("dev")
@@ -41,6 +43,7 @@ export default function (program: RootCmd): void {
41
43
  description: cfg.description,
42
44
  },
43
45
  path,
46
+ [],
44
47
  opts.host,
45
48
  );
46
49
 
@@ -59,7 +62,7 @@ export default function (program: RootCmd): void {
59
62
  console.info(`Running module ${path}`);
60
63
 
61
64
  // Deploy the webhook with a 30 second timeout for debugging, don't force
62
- await webhook.deploy(false, 30);
65
+ await webhook.deploy(deployWebhook, false, 30);
63
66
 
64
67
  try {
65
68
  // wait for capabilities to be loaded and test names
@@ -6,11 +6,11 @@ import admissionRequestDeletePod from "./data/admission-delete-pod.json";
6
6
  import admissionRequestCreateClusterRole from "./data/admission-create-clusterrole.json";
7
7
  import admissionRequestCreateDeployment from "./data/admission-create-deployment.json";
8
8
 
9
- export function AdmissionRequestCreateDeployment() {
9
+ export function AdmissionRequestCreateDeployment(): AdmissionRequest<kind.Deployment> {
10
10
  return cloneObject<kind.Deployment>(admissionRequestCreateDeployment);
11
11
  }
12
12
 
13
- export function AdmissionRequestCreatePod() {
13
+ export function AdmissionRequestCreatePod(): AdmissionRequest<kind.Pod> {
14
14
  return cloneObject<kind.Pod>(admissionRequestCreatePod);
15
15
  }
16
16
 
@@ -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
  }