pepr 0.42.1 → 0.42.3

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 (138) hide show
  1. package/dist/cli/build.helpers.d.ts +1 -1
  2. package/dist/cli/build.helpers.d.ts.map +1 -1
  3. package/dist/cli/deploy.d.ts +15 -0
  4. package/dist/cli/deploy.d.ts.map +1 -1
  5. package/dist/cli/dev.d.ts.map +1 -1
  6. package/dist/cli/format.d.ts.map +1 -1
  7. package/dist/cli/format.helpers.d.ts +3 -0
  8. package/dist/cli/format.helpers.d.ts.map +1 -0
  9. package/dist/cli/init/enums.d.ts +10 -0
  10. package/dist/cli/init/enums.d.ts.map +1 -0
  11. package/dist/cli/init/index.d.ts.map +1 -1
  12. package/dist/cli/init/templates.d.ts +15 -11
  13. package/dist/cli/init/templates.d.ts.map +1 -1
  14. package/dist/cli/init/utils.d.ts.map +1 -1
  15. package/dist/cli/init/walkthrough.d.ts +3 -2
  16. package/dist/cli/init/walkthrough.d.ts.map +1 -1
  17. package/dist/cli/kfc.d.ts.map +1 -1
  18. package/dist/cli/root.d.ts.map +1 -1
  19. package/dist/cli/update.d.ts.map +1 -1
  20. package/dist/cli/uuid.d.ts.map +1 -1
  21. package/dist/cli.js +1073 -1220
  22. package/dist/controller.js +1 -195
  23. package/dist/fixtures/loader.d.ts.map +1 -1
  24. package/dist/lib/assets/assets.d.ts +27 -0
  25. package/dist/lib/assets/assets.d.ts.map +1 -0
  26. package/dist/lib/assets/deploy.d.ts +1 -1
  27. package/dist/lib/assets/deploy.d.ts.map +1 -1
  28. package/dist/lib/assets/index.d.ts +5 -24
  29. package/dist/lib/assets/index.d.ts.map +1 -1
  30. package/dist/lib/assets/pods.d.ts +2 -4
  31. package/dist/lib/assets/pods.d.ts.map +1 -1
  32. package/dist/lib/assets/rbac.d.ts.map +1 -1
  33. package/dist/lib/assets/webhooks.d.ts +1 -1
  34. package/dist/lib/assets/webhooks.d.ts.map +1 -1
  35. package/dist/lib/assets/yaml.d.ts +31 -5
  36. package/dist/lib/assets/yaml.d.ts.map +1 -1
  37. package/dist/lib/controller/index.d.ts +2 -2
  38. package/dist/lib/controller/index.d.ts.map +1 -1
  39. package/dist/lib/controller/store.d.ts +1 -1
  40. package/dist/lib/controller/store.d.ts.map +1 -1
  41. package/dist/lib/controller/storeCache.d.ts +1 -1
  42. package/dist/lib/controller/storeCache.d.ts.map +1 -1
  43. package/dist/lib/{capability.d.ts → core/capability.d.ts} +1 -1
  44. package/dist/lib/core/capability.d.ts.map +1 -0
  45. package/dist/lib/{module.d.ts → core/module.d.ts} +2 -2
  46. package/dist/lib/core/module.d.ts.map +1 -0
  47. package/dist/lib/core/queue.d.ts.map +1 -0
  48. package/dist/lib/{schedule.d.ts → core/schedule.d.ts} +0 -1
  49. package/dist/lib/core/schedule.d.ts.map +1 -0
  50. package/dist/lib/core/storage.d.ts.map +1 -0
  51. package/dist/lib/deploymentChecks.d.ts.map +1 -1
  52. package/dist/lib/errors.d.ts +0 -5
  53. package/dist/lib/errors.d.ts.map +1 -1
  54. package/dist/lib/filesystemService.d.ts.map +1 -1
  55. package/dist/lib/filter/adjudicators/adjudicators.d.ts +5 -4
  56. package/dist/lib/filter/adjudicators/adjudicators.d.ts.map +1 -1
  57. package/dist/lib/filter/filter.d.ts +33 -1
  58. package/dist/lib/filter/filter.d.ts.map +1 -1
  59. package/dist/lib/finalizer.d.ts.map +1 -1
  60. package/dist/lib/helpers.d.ts +4 -9
  61. package/dist/lib/helpers.d.ts.map +1 -1
  62. package/dist/lib/included-files.d.ts.map +1 -1
  63. package/dist/lib/mutate-request.d.ts.map +1 -1
  64. package/dist/lib/processors/mutate-processor.d.ts +28 -0
  65. package/dist/lib/processors/mutate-processor.d.ts.map +1 -0
  66. package/dist/lib/{validate-processor.d.ts → processors/validate-processor.d.ts} +5 -5
  67. package/dist/lib/processors/validate-processor.d.ts.map +1 -0
  68. package/dist/lib/{watch-processor.d.ts → processors/watch-processor.d.ts} +2 -2
  69. package/dist/lib/processors/watch-processor.d.ts.map +1 -0
  70. package/dist/lib/telemetry/logger.d.ts.map +1 -1
  71. package/dist/lib/telemetry/metrics.d.ts.map +1 -1
  72. package/dist/lib/validate-request.d.ts +2 -2
  73. package/dist/lib/validate-request.d.ts.map +1 -1
  74. package/dist/lib.d.ts +2 -2
  75. package/dist/lib.d.ts.map +1 -1
  76. package/dist/lib.js +383 -243
  77. package/dist/lib.js.map +4 -4
  78. package/dist/sdk/heredoc.d.ts.map +1 -1
  79. package/package.json +9 -9
  80. package/src/cli/build.helpers.ts +1 -1
  81. package/src/cli/build.ts +1 -1
  82. package/src/cli/deploy.ts +114 -75
  83. package/src/cli/dev.ts +3 -3
  84. package/src/cli/format.helpers.ts +27 -0
  85. package/src/cli/format.ts +4 -18
  86. package/src/cli/init/enums.ts +9 -0
  87. package/src/cli/init/index.ts +4 -3
  88. package/src/cli/init/templates.ts +30 -2
  89. package/src/cli/init/utils.ts +3 -3
  90. package/src/cli/init/walkthrough.ts +7 -8
  91. package/src/cli/kfc.ts +1 -1
  92. package/src/cli/root.ts +1 -1
  93. package/src/cli/update.ts +1 -1
  94. package/src/cli/uuid.ts +1 -1
  95. package/src/fixtures/loader.ts +2 -2
  96. package/src/lib/assets/assets.ts +176 -0
  97. package/src/lib/assets/deploy.ts +6 -6
  98. package/src/lib/assets/index.ts +10 -144
  99. package/src/lib/assets/pods.ts +2 -2
  100. package/src/lib/assets/webhooks.ts +32 -56
  101. package/src/lib/assets/yaml.ts +47 -25
  102. package/src/lib/controller/index.ts +4 -4
  103. package/src/lib/controller/store.ts +2 -2
  104. package/src/lib/controller/storeCache.ts +6 -2
  105. package/src/lib/{capability.ts → core/capability.ts} +4 -4
  106. package/src/lib/{module.ts → core/module.ts} +10 -10
  107. package/src/lib/{queue.ts → core/queue.ts} +1 -1
  108. package/src/lib/deploymentChecks.ts +2 -2
  109. package/src/lib/errors.ts +3 -8
  110. package/src/lib/filesystemService.ts +1 -1
  111. package/src/lib/filter/adjudicators/adjudicators.ts +40 -9
  112. package/src/lib/filter/filter.ts +204 -111
  113. package/src/lib/finalizer.ts +2 -2
  114. package/src/lib/helpers.ts +20 -133
  115. package/src/lib/included-files.ts +1 -1
  116. package/src/lib/processors/mutate-processor.ts +225 -0
  117. package/src/lib/{validate-processor.ts → processors/validate-processor.ts} +8 -8
  118. package/src/lib/{watch-processor.ts → processors/watch-processor.ts} +8 -8
  119. package/src/lib/telemetry/logger.ts +3 -1
  120. package/src/lib/tls.ts +5 -1
  121. package/src/lib/validate-request.ts +4 -4
  122. package/src/lib.ts +2 -2
  123. package/src/runtime/controller.ts +2 -2
  124. package/src/sdk/heredoc.ts +1 -1
  125. package/dist/lib/capability.d.ts.map +0 -1
  126. package/dist/lib/module.d.ts.map +0 -1
  127. package/dist/lib/mutate-processor.d.ts +0 -6
  128. package/dist/lib/mutate-processor.d.ts.map +0 -1
  129. package/dist/lib/queue.d.ts.map +0 -1
  130. package/dist/lib/schedule.d.ts.map +0 -1
  131. package/dist/lib/storage.d.ts.map +0 -1
  132. package/dist/lib/validate-processor.d.ts.map +0 -1
  133. package/dist/lib/watch-processor.d.ts.map +0 -1
  134. package/src/lib/mutate-processor.ts +0 -165
  135. /package/dist/lib/{queue.d.ts → core/queue.d.ts} +0 -0
  136. /package/dist/lib/{storage.d.ts → core/storage.d.ts} +0 -0
  137. /package/src/lib/{schedule.ts → core/schedule.ts} +0 -0
  138. /package/src/lib/{storage.ts → core/storage.ts} +0 -0
@@ -0,0 +1,176 @@
1
+ import crypto from "crypto";
2
+ import { CapabilityExport } from "../types";
3
+ import { ModuleConfig } from "../core/module";
4
+ import { TLSOut, genTLS } from "../tls";
5
+ import { WebhookIgnore } from "../k8s";
6
+ import {
7
+ chartYaml,
8
+ namespaceTemplate,
9
+ clusterRoleTemplate,
10
+ admissionDeployTemplate,
11
+ serviceMonitorTemplate,
12
+ watcherDeployTemplate,
13
+ } from "./helm";
14
+ import { createDirectoryIfNotExists } from "../filesystemService";
15
+ import { deploy } from "./deploy";
16
+ import { getDeployment, getModuleSecret, getWatcher } from "./pods";
17
+ import { helmLayout, createWebhookYaml, toYaml } from "./index";
18
+ import { loadCapabilities } from "./loader";
19
+ import { namespaceComplianceValidator, dedent } from "../helpers";
20
+ import { storeRole, storeRoleBinding, clusterRoleBinding, serviceAccount } from "./rbac";
21
+ 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";
26
+
27
+ export class Assets {
28
+ readonly name: string;
29
+ readonly tls: TLSOut;
30
+ readonly apiToken: string;
31
+ readonly alwaysIgnore!: WebhookIgnore;
32
+ capabilities!: CapabilityExport[];
33
+
34
+ image: string;
35
+ buildTimestamp: string;
36
+ hash: string;
37
+
38
+ constructor(
39
+ readonly config: ModuleConfig,
40
+ readonly path: string,
41
+ readonly host?: string,
42
+ ) {
43
+ this.name = `pepr-${config.uuid}`;
44
+ this.buildTimestamp = `${Date.now()}`;
45
+ this.alwaysIgnore = config.alwaysIgnore;
46
+ this.image = `ghcr.io/defenseunicorns/pepr/controller:v${config.peprVersion}`;
47
+ this.hash = "";
48
+ // Generate the ephemeral tls things
49
+ this.tls = genTLS(this.host || `${this.name}.pepr-system.svc`);
50
+
51
+ // Generate the api token for the controller / webhook
52
+ this.apiToken = crypto.randomBytes(32).toString("hex");
53
+ }
54
+
55
+ setHash = (hash: string): void => {
56
+ this.hash = hash;
57
+ };
58
+
59
+ deploy = async (force: boolean, webhookTimeout?: number): Promise<void> => {
60
+ this.capabilities = await loadCapabilities(this.path);
61
+ await deploy(this, force, webhookTimeout);
62
+ };
63
+
64
+ zarfYaml = (path: string): string => generateZarfYaml(this.name, this.image, this.config, path);
65
+
66
+ zarfYamlChart = (path: string): string => generateZarfYamlChart(this.name, this.image, this.config, path);
67
+
68
+ allYaml = async (imagePullSecret?: string): Promise<string> => {
69
+ this.capabilities = await loadCapabilities(this.path);
70
+ // give error if namespaces are not respected
71
+ for (const capability of this.capabilities) {
72
+ namespaceComplianceValidator(capability, this.alwaysIgnore?.namespaces);
73
+ }
74
+
75
+ const webhooks = {
76
+ mutate: await webhookConfig(this, "mutate", this.config.webhookTimeout),
77
+ validate: await webhookConfig(this, "validate", this.config.webhookTimeout),
78
+ };
79
+
80
+ const code = await fs.readFile(this.path);
81
+
82
+ // Generate a hash of the code
83
+ this.hash = crypto.createHash("sha256").update(code).digest("hex");
84
+
85
+ const deployments = {
86
+ default: getDeployment(this, this.hash, this.buildTimestamp, imagePullSecret),
87
+ watch: getWatcher(this, this.hash, this.buildTimestamp, imagePullSecret),
88
+ };
89
+
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);
100
+ };
101
+
102
+ writeWebhookFiles = async (
103
+ validateWebhook: V1MutatingWebhookConfiguration | V1ValidatingWebhookConfiguration | null,
104
+ mutateWebhook: V1MutatingWebhookConfiguration | V1ValidatingWebhookConfiguration | null,
105
+ helm: Record<string, Record<string, string>>,
106
+ ): Promise<void> => {
107
+ if (validateWebhook || mutateWebhook) {
108
+ await fs.writeFile(helm.files.admissionDeploymentYaml, dedent(admissionDeployTemplate(this.buildTimestamp)));
109
+ await fs.writeFile(helm.files.admissionServiceMonitorYaml, dedent(serviceMonitorTemplate("admission")));
110
+ }
111
+
112
+ if (mutateWebhook) {
113
+ await fs.writeFile(helm.files.mutationWebhookYaml, createWebhookYaml(this.name, this.config, mutateWebhook));
114
+ }
115
+
116
+ if (validateWebhook) {
117
+ await fs.writeFile(helm.files.validationWebhookYaml, createWebhookYaml(this.name, this.config, validateWebhook));
118
+ }
119
+ };
120
+
121
+ generateHelmChart = async (basePath: string): Promise<void> => {
122
+ const helm = helmLayout(basePath, this.config.uuid);
123
+
124
+ try {
125
+ await Promise.all(
126
+ Object.values(helm.dirs)
127
+ .sort((l, r) => l.split("/").length - r.split("/").length)
128
+ .map(async dir => await createDirectoryIfNotExists(dir)),
129
+ );
130
+
131
+ const code = await fs.readFile(this.path);
132
+
133
+ const pairs: [string, () => string][] = [
134
+ [helm.files.chartYaml, (): string => dedent(chartYaml(this.config.uuid, this.config.description || ""))],
135
+ [helm.files.namespaceYaml, (): string => dedent(namespaceTemplate())],
136
+ [helm.files.watcherServiceYaml, (): string => toYaml(watcherService(this.name))],
137
+ [helm.files.admissionServiceYaml, (): string => toYaml(service(this.name))],
138
+ [helm.files.tlsSecretYaml, (): string => toYaml(tlsSecret(this.name, this.tls))],
139
+ [helm.files.apiTokenSecretYaml, (): string => toYaml(apiTokenSecret(this.name, this.apiToken))],
140
+ [helm.files.storeRoleYaml, (): string => toYaml(storeRole(this.name))],
141
+ [helm.files.storeRoleBindingYaml, (): string => toYaml(storeRoleBinding(this.name))],
142
+ [helm.files.clusterRoleYaml, (): string => dedent(clusterRoleTemplate())],
143
+ [helm.files.clusterRoleBindingYaml, (): string => toYaml(clusterRoleBinding(this.name))],
144
+ [helm.files.serviceAccountYaml, (): string => toYaml(serviceAccount(this.name))],
145
+ [helm.files.moduleSecretYaml, (): string => toYaml(getModuleSecret(this.name, code, this.hash))],
146
+ ];
147
+ await Promise.all(pairs.map(async ([file, content]) => await fs.writeFile(file, content())));
148
+
149
+ const overrideData = {
150
+ hash: this.hash,
151
+ name: this.name,
152
+ image: this.image,
153
+ config: this.config,
154
+ apiToken: this.apiToken,
155
+ capabilities: this.capabilities,
156
+ };
157
+ await overridesFile(overrideData, helm.files.valuesYaml);
158
+
159
+ const [mutateWebhook, validateWebhook] = await Promise.all([
160
+ webhookConfig(this, "mutate", this.config.webhookTimeout),
161
+ webhookConfig(this, "validate", this.config.webhookTimeout),
162
+ ]);
163
+
164
+ await this.writeWebhookFiles(validateWebhook, mutateWebhook, helm);
165
+
166
+ const watchDeployment = getWatcher(this, this.hash, this.buildTimestamp);
167
+ if (watchDeployment) {
168
+ await fs.writeFile(helm.files.watcherDeploymentYaml, dedent(watcherDeployTemplate(this.buildTimestamp)));
169
+ await fs.writeFile(helm.files.watcherServiceMonitorYaml, dedent(serviceMonitorTemplate("watcher")));
170
+ }
171
+ } catch (err) {
172
+ console.error(`Error generating helm chart: ${err.message}`);
173
+ process.exit(1);
174
+ }
175
+ };
176
+ }
@@ -6,7 +6,7 @@ import { promises as fs } from "fs";
6
6
  import { K8s, kind } from "kubernetes-fluent-client";
7
7
  import { V1PolicyRule as PolicyRule } from "@kubernetes/client-node";
8
8
 
9
- import { Assets } from ".";
9
+ import { Assets } from "./assets";
10
10
  import Log from "../telemetry/logger";
11
11
  import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking";
12
12
  import { getDeployment, getModuleSecret, getNamespace, getWatcher } from "./pods";
@@ -15,7 +15,7 @@ import { peprStoreCRD } from "./store";
15
15
  import { webhookConfig } from "./webhooks";
16
16
  import { CapabilityExport, ImagePullSecret } from "../types";
17
17
 
18
- export async function deployImagePullSecret(imagePullSecret: ImagePullSecret, name: string) {
18
+ export async function deployImagePullSecret(imagePullSecret: ImagePullSecret, name: string): Promise<void> {
19
19
  try {
20
20
  await K8s(kind.Namespace).Get("pepr-system");
21
21
  } catch {
@@ -42,7 +42,7 @@ export async function deployImagePullSecret(imagePullSecret: ImagePullSecret, na
42
42
  Log.error(e);
43
43
  }
44
44
  }
45
- export async function deploy(assets: Assets, force: boolean, webhookTimeout?: number) {
45
+ export async function deploy(assets: Assets, force: boolean, webhookTimeout?: number): Promise<void> {
46
46
  Log.info("Establishing connection to Kubernetes");
47
47
 
48
48
  const { name, host, path } = assets;
@@ -95,7 +95,7 @@ async function setupRBAC(
95
95
  capabilities: CapabilityExport[],
96
96
  force: boolean,
97
97
  config: { rbacMode?: string; rbac?: PolicyRule[] },
98
- ) {
98
+ ): Promise<void> {
99
99
  const { rbacMode, rbac } = config;
100
100
 
101
101
  Log.info("Applying cluster role binding");
@@ -119,7 +119,7 @@ async function setupRBAC(
119
119
  await K8s(kind.RoleBinding).Apply(roleBinding, { force });
120
120
  }
121
121
 
122
- async function setupController(assets: Assets, code: Buffer, hash: string, force: boolean) {
122
+ async function setupController(assets: Assets, code: Buffer, hash: string, force: boolean): Promise<void> {
123
123
  const { name } = assets;
124
124
 
125
125
  Log.info("Applying module secret");
@@ -144,7 +144,7 @@ async function setupController(assets: Assets, code: Buffer, hash: string, force
144
144
  }
145
145
 
146
146
  // Setup the watcher deployment and service
147
- async function setupWatcher(assets: Assets, hash: string, force: boolean) {
147
+ async function setupWatcher(assets: Assets, hash: string, force: boolean): Promise<void> {
148
148
  // If the module has a watcher, deploy it
149
149
  const watchDeployment = getWatcher(assets, hash, assets.buildTimestamp);
150
150
  if (watchDeployment) {
@@ -1,57 +1,35 @@
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 "../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
+ export function createWebhookYaml(
16
+ name: string,
17
+ config: ModuleConfig,
40
18
  webhookConfiguration: kind.MutatingWebhookConfiguration | kind.ValidatingWebhookConfiguration,
41
19
  ): string {
42
20
  const yaml = toYaml(webhookConfiguration);
43
21
  return replaceString(
44
22
  replaceString(
45
- replaceString(yaml, assets.name, "{{ .Values.uuid }}"),
46
- assets.config.onError === "reject" ? "Fail" : "Ignore",
23
+ replaceString(yaml, name, "{{ .Values.uuid }}"),
24
+ config.onError === "reject" ? "Fail" : "Ignore",
47
25
  "{{ .Values.admission.failurePolicy }}",
48
26
  ),
49
- `${assets.config.webhookTimeout}` || "10",
27
+ `${config.webhookTimeout}` || "10",
50
28
  "{{ .Values.admission.webhookTimeout }}",
51
29
  );
52
30
  }
53
31
 
54
- function helmLayout(basePath: string, unique: string): Record<string, Record<string, string>> {
32
+ export function helmLayout(basePath: string, unique: string): Record<string, Record<string, string>> {
55
33
  const helm: Record<string, Record<string, string>> = {
56
34
  dirs: {
57
35
  chart: resolve(`${basePath}/${unique}-chart`),
@@ -90,115 +68,3 @@ function helmLayout(basePath: string, unique: string): Record<string, Record<str
90
68
 
91
69
  return helm;
92
70
  }
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,8 +5,8 @@ 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 ".";
9
- import { ModuleConfig } from "../module";
8
+ import { Assets } from "./assets";
9
+ import { ModuleConfig } from "../core/module";
10
10
  import { Binding } from "../types";
11
11
 
12
12
  /** Generate the pepr-system namespace */
@@ -9,68 +9,47 @@ import {
9
9
  import { kind } from "kubernetes-fluent-client";
10
10
  import { concat, equals, uniqWith } from "ramda";
11
11
 
12
- import { Assets } from ".";
12
+ import { Assets } from "./assets";
13
13
  import { Event } from "../enums";
14
-
15
- const peprIgnoreLabel: V1LabelSelectorRequirement = {
16
- key: "pepr.dev",
17
- operator: "NotIn",
18
- values: ["ignore"],
19
- };
14
+ import { Binding } from "../types";
20
15
 
21
16
  const peprIgnoreNamespaces: string[] = ["kube-system", "pepr-system"];
22
17
 
18
+ const validateRule = (binding: Binding, isMutateWebhook: boolean): V1RuleWithOperations | undefined => {
19
+ const { event, kind, isMutate, isValidate } = binding;
20
+
21
+ // Skip invalid bindings based on webhook type
22
+ if ((isMutateWebhook && !isMutate) || (!isMutateWebhook && !isValidate)) {
23
+ return undefined;
24
+ }
25
+
26
+ // Translate event to operations
27
+ const operations = event === Event.CREATE_OR_UPDATE ? [Event.CREATE, Event.UPDATE] : [event];
28
+
29
+ // Use the plural property if it exists, otherwise use lowercase kind + s
30
+ const resource = kind.plural || `${kind.kind.toLowerCase()}s`;
31
+
32
+ const ruleObject: V1RuleWithOperations = {
33
+ apiGroups: [kind.group],
34
+ apiVersions: [kind.version || "*"],
35
+ operations,
36
+ resources: [resource, ...(resource === "pods" ? ["pods/ephemeralcontainers"] : [])],
37
+ };
38
+
39
+ return ruleObject;
40
+ };
41
+
23
42
  export async function generateWebhookRules(assets: Assets, isMutateWebhook: boolean): Promise<V1RuleWithOperations[]> {
24
43
  const { config, capabilities } = assets;
25
- const rules: V1RuleWithOperations[] = [];
26
44
 
27
- // Iterate through the capabilities and generate the rules
28
- for (const capability of capabilities) {
45
+ const rules = capabilities.flatMap(capability => {
29
46
  console.info(`Module ${config.uuid} has capability: ${capability.name}`);
30
47
 
31
- // Read the bindings and generate the rules
32
- for (const binding of capability.bindings) {
33
- const { event, kind, isMutate, isValidate } = binding;
34
-
35
- // If the module doesn't have a callback for the event, skip it
36
- if (isMutateWebhook && !isMutate) {
37
- continue;
38
- }
39
-
40
- if (!isMutateWebhook && !isValidate) {
41
- continue;
42
- }
43
-
44
- const operations: string[] = [];
45
-
46
- // CreateOrUpdate is a Pepr-specific event that is translated to Create and Update
47
- if (event === Event.CREATE_OR_UPDATE) {
48
- operations.push(Event.CREATE, Event.UPDATE);
49
- } else {
50
- operations.push(event);
51
- }
52
-
53
- // Use the plural property if it exists, otherwise use lowercase kind + s
54
- const resource = kind.plural || `${kind.kind.toLowerCase()}s`;
55
-
56
- const ruleObject = {
57
- apiGroups: [kind.group],
58
- apiVersions: [kind.version || "*"],
59
- operations,
60
- resources: [resource],
61
- };
62
-
63
- // If the resource is pods, add ephemeralcontainers as well
64
- if (resource === "pods") {
65
- ruleObject.resources.push("pods/ephemeralcontainers");
66
- }
67
-
68
- // Add the rule to the rules array
69
- rules.push(ruleObject);
70
- }
71
- }
48
+ return capability.bindings
49
+ .map(binding => validateRule(binding, isMutateWebhook))
50
+ .filter((rule): rule is V1RuleWithOperations => !!rule);
51
+ });
72
52
 
73
- // Return the rules with duplicates removed
74
53
  return uniqWith(equals, rules);
75
54
  }
76
55
 
@@ -79,7 +58,7 @@ export async function webhookConfig(
79
58
  mutateOrValidate: "mutate" | "validate",
80
59
  timeoutSeconds = 10,
81
60
  ): Promise<kind.MutatingWebhookConfiguration | kind.ValidatingWebhookConfiguration | null> {
82
- const ignore = [peprIgnoreLabel];
61
+ const ignore: V1LabelSelectorRequirement[] = [];
83
62
 
84
63
  const { name, tls, config, apiToken, host } = assets;
85
64
  const ignoreNS = concat(peprIgnoreNamespaces, config?.alwaysIgnore?.namespaces || []);
@@ -135,9 +114,6 @@ export async function webhookConfig(
135
114
  namespaceSelector: {
136
115
  matchExpressions: ignore,
137
116
  },
138
- objectSelector: {
139
- matchExpressions: ignore,
140
- },
141
117
  rules,
142
118
  // @todo: track side effects state
143
119
  sideEffects: "None",
@@ -1,19 +1,41 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
- import { dumpYaml } from "@kubernetes/client-node";
5
- import crypto from "crypto";
4
+ import {
5
+ dumpYaml,
6
+ V1Deployment,
7
+ V1MutatingWebhookConfiguration,
8
+ V1ValidatingWebhookConfiguration,
9
+ } from "@kubernetes/client-node";
6
10
  import { promises as fs } from "fs";
7
- import { Assets } from ".";
8
11
  import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking";
9
- import { getDeployment, getModuleSecret, getNamespace, getWatcher } from "./pods";
12
+ import { getModuleSecret, getNamespace } from "./pods";
10
13
  import { clusterRole, clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "./rbac";
11
- import { webhookConfig } from "./webhooks";
12
14
  import { genEnv } from "./pods";
15
+ import { ModuleConfig } from "../core/module";
16
+ import { CapabilityExport } from "../types";
17
+ import { TLSOut } from "../tls";
18
+
19
+ type CommonOverrideValues = {
20
+ apiToken: string;
21
+ capabilities: CapabilityExport[];
22
+ config: ModuleConfig;
23
+ hash: string;
24
+ name: string;
25
+ };
26
+
27
+ type ChartOverrides = CommonOverrideValues & {
28
+ image: string;
29
+ };
30
+
31
+ type ResourceOverrides = CommonOverrideValues & {
32
+ path: string;
33
+ tls: TLSOut;
34
+ };
13
35
 
14
36
  // Helm Chart overrides file (values.yaml) generated from assets
15
37
  export async function overridesFile(
16
- { hash, name, image, config, apiToken, capabilities }: Assets,
38
+ { hash, name, image, config, apiToken, capabilities }: ChartOverrides,
17
39
  path: string,
18
40
  ): Promise<void> {
19
41
  const rbacOverrides = clusterRole(name, capabilities, config.rbacMode, config.rbac).rules;
@@ -169,7 +191,7 @@ export async function overridesFile(
169
191
 
170
192
  await fs.writeFile(path, dumpYaml(overrides, { noRefs: true, forceQuotes: true }));
171
193
  }
172
- export function zarfYaml({ name, image, config }: Assets, path: string): string {
194
+ export function generateZarfYaml(name: string, image: string, config: ModuleConfig, path: string): string {
173
195
  const zarfCfg = {
174
196
  kind: "ZarfPackageConfig",
175
197
  metadata: {
@@ -197,7 +219,7 @@ export function zarfYaml({ name, image, config }: Assets, path: string): string
197
219
  return dumpYaml(zarfCfg, { noRefs: true });
198
220
  }
199
221
 
200
- export function zarfYamlChart({ name, image, config }: Assets, path: string): string {
222
+ export function generateZarfYamlChart(name: string, image: string, config: ModuleConfig, path: string): string {
201
223
  const zarfCfg = {
202
224
  kind: "ZarfPackageConfig",
203
225
  metadata: {
@@ -226,16 +248,16 @@ export function zarfYamlChart({ name, image, config }: Assets, path: string): st
226
248
  return dumpYaml(zarfCfg, { noRefs: true });
227
249
  }
228
250
 
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");
251
+ type webhooks = { validate: V1ValidatingWebhookConfiguration | null; mutate: V1MutatingWebhookConfiguration | null };
252
+ type deployments = { default: V1Deployment; watch: V1Deployment | null };
235
253
 
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);
254
+ export async function generateAllYaml(
255
+ webhooks: webhooks,
256
+ deployments: deployments,
257
+ assets: ResourceOverrides,
258
+ ): Promise<string> {
259
+ const { name, tls, hash, apiToken, path, config } = assets;
260
+ const code = await fs.readFile(path);
239
261
 
240
262
  const resources = [
241
263
  getNamespace(assets.config.customLabels?.namespace),
@@ -244,24 +266,24 @@ export async function allYaml(assets: Assets, imagePullSecret?: string): Promise
244
266
  serviceAccount(name),
245
267
  apiTokenSecret(name, apiToken),
246
268
  tlsSecret(name, tls),
247
- getDeployment(assets, assets.hash, assets.buildTimestamp, imagePullSecret),
269
+ deployments.default,
248
270
  service(name),
249
271
  watcherService(name),
250
- getModuleSecret(name, code, assets.hash),
272
+ getModuleSecret(name, code, hash),
251
273
  storeRole(name),
252
274
  storeRoleBinding(name),
253
275
  ];
254
276
 
255
- if (mutateWebhook) {
256
- resources.push(mutateWebhook);
277
+ if (webhooks.mutate) {
278
+ resources.push(webhooks.mutate);
257
279
  }
258
280
 
259
- if (validateWebhook) {
260
- resources.push(validateWebhook);
281
+ if (webhooks.validate) {
282
+ resources.push(webhooks.validate);
261
283
  }
262
284
 
263
- if (watchDeployment) {
264
- resources.push(watchDeployment);
285
+ if (deployments.watch) {
286
+ resources.push(deployments.watch);
265
287
  }
266
288
 
267
289
  // Convert the resources to a single YAML string