pepr 0.42.0 → 0.42.1

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 (55) hide show
  1. package/dist/cli/build.d.ts +1 -0
  2. package/dist/cli/build.d.ts.map +1 -1
  3. package/dist/cli/build.helpers.d.ts +66 -0
  4. package/dist/cli/build.helpers.d.ts.map +1 -1
  5. package/dist/cli/monitor.d.ts +23 -0
  6. package/dist/cli/monitor.d.ts.map +1 -1
  7. package/dist/cli.js +341 -283
  8. package/dist/controller.js +1 -1
  9. package/dist/lib/assets/destroy.d.ts.map +1 -1
  10. package/dist/lib/assets/helm.d.ts +1 -1
  11. package/dist/lib/assets/helm.d.ts.map +1 -1
  12. package/dist/lib/assets/index.d.ts.map +1 -1
  13. package/dist/lib/assets/pods.d.ts +5 -19
  14. package/dist/lib/assets/pods.d.ts.map +1 -1
  15. package/dist/lib/assets/webhooks.d.ts.map +1 -1
  16. package/dist/lib/assets/yaml.d.ts.map +1 -1
  17. package/dist/lib/capability.d.ts.map +1 -1
  18. package/dist/lib/controller/index.d.ts.map +1 -1
  19. package/dist/lib/controller/store.d.ts +0 -1
  20. package/dist/lib/controller/store.d.ts.map +1 -1
  21. package/dist/lib/controller/storeCache.d.ts +1 -0
  22. package/dist/lib/controller/storeCache.d.ts.map +1 -1
  23. package/dist/lib/mutate-request.d.ts +2 -2
  24. package/dist/lib/mutate-request.d.ts.map +1 -1
  25. package/dist/lib/queue.d.ts.map +1 -1
  26. package/dist/lib/storage.d.ts +4 -4
  27. package/dist/lib/storage.d.ts.map +1 -1
  28. package/dist/lib/utils.d.ts.map +1 -1
  29. package/dist/lib/validate-processor.d.ts +4 -1
  30. package/dist/lib/validate-processor.d.ts.map +1 -1
  31. package/dist/lib/watch-processor.d.ts.map +1 -1
  32. package/dist/lib.js +136 -109
  33. package/dist/lib.js.map +3 -3
  34. package/package.json +1 -1
  35. package/src/cli/build.helpers.ts +180 -0
  36. package/src/cli/build.ts +85 -133
  37. package/src/cli/monitor.ts +108 -65
  38. package/src/lib/assets/deploy.ts +6 -6
  39. package/src/lib/assets/destroy.ts +1 -1
  40. package/src/lib/assets/helm.ts +6 -6
  41. package/src/lib/assets/index.ts +22 -22
  42. package/src/lib/assets/pods.ts +10 -5
  43. package/src/lib/assets/webhooks.ts +1 -1
  44. package/src/lib/assets/yaml.ts +12 -9
  45. package/src/lib/capability.ts +21 -10
  46. package/src/lib/controller/index.ts +9 -7
  47. package/src/lib/controller/store.ts +23 -10
  48. package/src/lib/controller/storeCache.ts +10 -1
  49. package/src/lib/mutate-request.ts +11 -11
  50. package/src/lib/queue.ts +12 -4
  51. package/src/lib/storage.ts +33 -24
  52. package/src/lib/utils.ts +5 -5
  53. package/src/lib/validate-processor.ts +47 -39
  54. package/src/lib/watch-processor.ts +11 -7
  55. package/src/sdk/cosign.ts +4 -4
@@ -1,92 +1,51 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
- import { Log as K8sLog, KubeConfig } from "@kubernetes/client-node";
4
+ import { Log as K8sLog, KubeConfig, KubernetesListObject } from "@kubernetes/client-node";
5
5
  import { K8s, kind } from "kubernetes-fluent-client";
6
6
  import stream from "stream";
7
7
  import { ResponseItem } from "../lib/types";
8
8
  import { RootCmd } from "./root";
9
9
 
10
- export default function (program: RootCmd) {
10
+ interface LogPayload {
11
+ namespace: string;
12
+ name: string;
13
+ res: {
14
+ uid: string;
15
+ allowed?: boolean;
16
+ patch?: string;
17
+ patchType?: string;
18
+ warnings?: string;
19
+ status?: {
20
+ message: string;
21
+ };
22
+ };
23
+ }
24
+
25
+ export default function (program: RootCmd): void {
11
26
  program
12
27
  .command("monitor [module-uuid]")
13
28
  .description("Monitor a Pepr Module")
14
29
  .action(async uuid => {
15
- let labels: string[];
16
- let errorMessage: string;
17
-
18
- if (!uuid) {
19
- labels = ["pepr.dev/controller", "admission"];
20
- errorMessage = `No pods found with admission labels`;
21
- } else {
22
- labels = ["app", `pepr-${uuid}`];
23
- errorMessage = `No pods found for module ${uuid}`;
24
- }
30
+ const { labels, errorMessage } = getLabelsAndErrorMessage(uuid);
25
31
 
26
32
  // Get the logs for the `app=pepr-${module}` or `pepr.dev/controller=admission` pod selector
27
- const pods = await K8s(kind.Pod)
33
+ const pods: KubernetesListObject<kind.Pod> = await K8s(kind.Pod)
28
34
  .InNamespace("pepr-system")
29
35
  .WithLabel(labels[0], labels[1])
30
36
  .Get();
31
37
 
32
- const podNames = pods.items.flatMap(pod => pod.metadata!.name) as string[];
38
+ // Pods will ways have a metadata and name fields
39
+ const podNames: string[] = pods.items.flatMap(pod => pod.metadata!.name || "");
33
40
 
34
41
  if (podNames.length < 1) {
35
42
  console.error(errorMessage);
36
43
  process.exit(1);
37
44
  }
38
45
 
39
- const kc = new KubeConfig();
40
- kc.loadFromDefault();
41
-
42
- const log = new K8sLog(kc);
43
-
44
- const logStream = new stream.PassThrough();
45
- logStream.on("data", async chunk => {
46
- const respMsg = `"msg":"Check response"`;
47
- // Split the chunk into lines
48
- const lines = await chunk.toString().split("\n");
49
-
50
- for (const line of lines) {
51
- // Check for `"msg":"Hello Pepr"`
52
- if (!line.includes(respMsg)) continue;
53
- try {
54
- const payload = JSON.parse(line.trim());
55
- const isMutate = payload.res.patchType || payload.res.warnings;
56
-
57
- const name = `${payload.namespace}${payload.name}`;
58
- const uid = payload.res.uid;
59
-
60
- if (isMutate) {
61
- const plainPatch =
62
- payload.res?.patch !== undefined && payload.res?.patch !== null
63
- ? atob(payload.res.patch)
64
- : "";
65
-
66
- const patch = plainPatch !== "" && JSON.stringify(JSON.parse(plainPatch), null, 2);
67
- const patchType = payload.res.patchType || payload.res.warnings || "";
68
- const allowOrDeny = payload.res.allowed ? "🔀" : "🚫";
69
- console.log(`\n${allowOrDeny} MUTATE ${name} (${uid})`);
70
- patchType.length > 0 && console.log(`\n\u001b[1;34m${patch}\u001b[0m`);
71
- } else {
72
- const failures = Array.isArray(payload.res) ? payload.res : [payload.res];
73
-
74
- const filteredFailures = failures
75
- .filter((r: ResponseItem) => !r.allowed)
76
- .map((r: ResponseItem) => r.status.message);
77
-
78
- console.log(
79
- `\n${filteredFailures.length > 0 ? "❌" : "✅"} VALIDATE ${name} (${uid})`,
80
- );
81
- console.log(
82
- filteredFailures.length > 0 ? `\u001b[1;31m${filteredFailures}\u001b[0m` : "",
83
- );
84
- }
85
- } catch {
86
- // Do nothing
87
- }
88
- }
89
- });
46
+ const log = getK8sLogFromKubeConfig();
47
+
48
+ const logStream = createLogStream();
90
49
 
91
50
  for (const podName of podNames) {
92
51
  await log.log("pepr-system", podName, "server", logStream, {
@@ -97,3 +56,87 @@ export default function (program: RootCmd) {
97
56
  }
98
57
  });
99
58
  }
59
+
60
+ export function getLabelsAndErrorMessage(uuid?: string): {
61
+ labels: string[];
62
+ errorMessage: string;
63
+ } {
64
+ let labels: string[];
65
+ let errorMessage: string;
66
+
67
+ if (!uuid) {
68
+ labels = ["pepr.dev/controller", "admission"];
69
+ errorMessage = `No pods found with admission labels`;
70
+ } else {
71
+ labels = ["app", `pepr-${uuid}`];
72
+ errorMessage = `No pods found for module ${uuid}`;
73
+ }
74
+
75
+ return { labels, errorMessage };
76
+ }
77
+
78
+ export function getK8sLogFromKubeConfig(): K8sLog {
79
+ const kc = new KubeConfig();
80
+ kc.loadFromDefault();
81
+ return new K8sLog(kc);
82
+ }
83
+
84
+ function createLogStream(): stream.PassThrough {
85
+ const logStream = new stream.PassThrough();
86
+
87
+ logStream.on("data", async chunk => {
88
+ const lines = chunk.toString().split("\n");
89
+ const respMsg = `"msg":"Check response"`;
90
+
91
+ for (const line of lines) {
92
+ if (!line.includes(respMsg)) continue;
93
+ processLogLine(line);
94
+ }
95
+ });
96
+
97
+ return logStream;
98
+ }
99
+
100
+ function processLogLine(line: string): void {
101
+ try {
102
+ const payload: LogPayload = JSON.parse(line.trim());
103
+ const isMutate = payload.res.patchType || payload.res.warnings;
104
+ const name = `${payload.namespace}${payload.name}`;
105
+ const uid = payload.res.uid;
106
+
107
+ if (isMutate) {
108
+ processMutateLog(payload, name, uid);
109
+ } else {
110
+ processValidateLog(payload, name, uid);
111
+ }
112
+ } catch {
113
+ // Do nothing
114
+ }
115
+ }
116
+
117
+ export function processMutateLog(payload: LogPayload, name: string, uid: string): void {
118
+ const plainPatch =
119
+ payload.res.patch !== undefined && payload.res.patch !== null ? atob(payload.res.patch) : "";
120
+
121
+ const patch = plainPatch !== "" && JSON.stringify(JSON.parse(plainPatch), null, 2);
122
+ const patchType = payload.res.patchType || payload.res.warnings || "";
123
+ const allowOrDeny = payload.res.allowed ? "🔀" : "🚫";
124
+
125
+ console.log(`\n${allowOrDeny} MUTATE ${name} (${uid})`);
126
+ if (patchType.length > 0) {
127
+ console.log(`\n\u001b[1;34m${patch}\u001b[0m`);
128
+ }
129
+ }
130
+
131
+ export function processValidateLog(payload: LogPayload, name: string, uid: string): void {
132
+ const failures = Array.isArray(payload.res) ? payload.res : [payload.res];
133
+
134
+ const filteredFailures = failures
135
+ .filter((r: ResponseItem) => !r.allowed)
136
+ .map((r: ResponseItem) => r.status?.message || "");
137
+
138
+ console.log(`\n${filteredFailures.length > 0 ? "❌" : "✅"} VALIDATE ${name} (${uid})`);
139
+ if (filteredFailures.length > 0) {
140
+ console.log(`\u001b[1;31m${filteredFailures}\u001b[0m`);
141
+ }
142
+ }
@@ -9,7 +9,7 @@ import { V1PolicyRule as PolicyRule } from "@kubernetes/client-node";
9
9
  import { Assets } from ".";
10
10
  import Log from "../telemetry/logger";
11
11
  import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking";
12
- import { deployment, moduleSecret, namespace, watcher } from "./pods";
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
15
  import { webhookConfig } from "./webhooks";
@@ -19,7 +19,7 @@ export async function deployImagePullSecret(imagePullSecret: ImagePullSecret, na
19
19
  try {
20
20
  await K8s(kind.Namespace).Get("pepr-system");
21
21
  } catch {
22
- await K8s(kind.Namespace).Apply(namespace());
22
+ await K8s(kind.Namespace).Apply(getNamespace());
23
23
  }
24
24
 
25
25
  try {
@@ -48,7 +48,7 @@ export async function deploy(assets: Assets, force: boolean, webhookTimeout?: nu
48
48
  const { name, host, path } = assets;
49
49
 
50
50
  Log.info("Applying pepr-system namespace");
51
- await K8s(kind.Namespace).Apply(namespace(assets.config.customLabels?.namespace));
51
+ await K8s(kind.Namespace).Apply(getNamespace(assets.config.customLabels?.namespace));
52
52
 
53
53
  // Create the mutating webhook configuration if it is needed
54
54
  const mutateWebhook = await webhookConfig(assets, "mutate", webhookTimeout);
@@ -123,7 +123,7 @@ async function setupController(assets: Assets, code: Buffer, hash: string, force
123
123
  const { name } = assets;
124
124
 
125
125
  Log.info("Applying module secret");
126
- const mod = moduleSecret(name, code, hash);
126
+ const mod = getModuleSecret(name, code, hash);
127
127
  await K8s(kind.Secret).Apply(mod, { force });
128
128
 
129
129
  Log.info("Applying controller service");
@@ -139,14 +139,14 @@ async function setupController(assets: Assets, code: Buffer, hash: string, force
139
139
  await K8s(kind.Secret).Apply(apiToken, { force });
140
140
 
141
141
  Log.info("Applying deployment");
142
- const dep = deployment(assets, hash, assets.buildTimestamp);
142
+ const dep = getDeployment(assets, hash, assets.buildTimestamp);
143
143
  await K8s(kind.Deployment).Apply(dep, { force });
144
144
  }
145
145
 
146
146
  // Setup the watcher deployment and service
147
147
  async function setupWatcher(assets: Assets, hash: string, force: boolean) {
148
148
  // If the module has a watcher, deploy it
149
- const watchDeployment = watcher(assets, hash, assets.buildTimestamp);
149
+ const watchDeployment = getWatcher(assets, hash, assets.buildTimestamp);
150
150
  if (watchDeployment) {
151
151
  Log.info("Applying watcher deployment");
152
152
  await K8s(kind.Deployment).Apply(watchDeployment, { force });
@@ -6,7 +6,7 @@ import { K8s, kind } from "kubernetes-fluent-client";
6
6
  import Log from "../telemetry/logger";
7
7
  import { peprStoreCRD } from "./store";
8
8
 
9
- export async function destroyModule(name: string) {
9
+ export async function destroyModule(name: string): Promise<void> {
10
10
  const namespace = "pepr-system";
11
11
 
12
12
  Log.info("Destroying Pepr module");
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
- export function clusterRoleTemplate() {
4
+ export function clusterRoleTemplate(): string {
5
5
  return `
6
6
  apiVersion: rbac.authorization.k8s.io/v1
7
7
  kind: ClusterRole
@@ -15,7 +15,7 @@ export function clusterRoleTemplate() {
15
15
  `;
16
16
  }
17
17
 
18
- export function nsTemplate() {
18
+ export function namespaceTemplate(): string {
19
19
  return `
20
20
  apiVersion: v1
21
21
  kind: Namespace
@@ -32,7 +32,7 @@ export function nsTemplate() {
32
32
  `;
33
33
  }
34
34
 
35
- export function chartYaml(name: string, description?: string) {
35
+ export function chartYaml(name: string, description?: string): string {
36
36
  return `
37
37
  apiVersion: v2
38
38
  name: ${name}
@@ -61,7 +61,7 @@ export function chartYaml(name: string, description?: string) {
61
61
  `;
62
62
  }
63
63
 
64
- export function watcherDeployTemplate(buildTimestamp: string) {
64
+ export function watcherDeployTemplate(buildTimestamp: string): string {
65
65
  return `
66
66
  apiVersion: apps/v1
67
67
  kind: Deployment
@@ -142,7 +142,7 @@ export function watcherDeployTemplate(buildTimestamp: string) {
142
142
  `;
143
143
  }
144
144
 
145
- export function admissionDeployTemplate(buildTimestamp: string) {
145
+ export function admissionDeployTemplate(buildTimestamp: string): string {
146
146
  return `
147
147
  apiVersion: apps/v1
148
148
  kind: Deployment
@@ -228,7 +228,7 @@ export function admissionDeployTemplate(buildTimestamp: string) {
228
228
  `;
229
229
  }
230
230
 
231
- export function serviceMonitorTemplate(name: string) {
231
+ export function serviceMonitorTemplate(name: string): string {
232
232
  return `
233
233
  {{- if .Values.${name}.serviceMonitor.enabled }}
234
234
  apiVersion: monitoring.coreos.com/v1
@@ -16,7 +16,7 @@ import { dedent } from "../helpers";
16
16
  import { resolve } from "path";
17
17
  import {
18
18
  chartYaml,
19
- nsTemplate,
19
+ namespaceTemplate,
20
20
  admissionDeployTemplate,
21
21
  watcherDeployTemplate,
22
22
  clusterRoleTemplate,
@@ -25,7 +25,7 @@ import {
25
25
  import { promises as fs } from "fs";
26
26
  import { webhookConfig } from "./webhooks";
27
27
  import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking";
28
- import { watcher, moduleSecret } from "./pods";
28
+ import { getWatcher, getModuleSecret } from "./pods";
29
29
 
30
30
  import { clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "./rbac";
31
31
  import { createDirectoryIfNotExists } from "../filesystemService";
@@ -51,7 +51,7 @@ function createWebhookYaml(
51
51
  );
52
52
  }
53
53
 
54
- function helmLayout(basePath: string, unique: string) {
54
+ function helmLayout(basePath: string, unique: string): Record<string, Record<string, string>> {
55
55
  const helm: Record<string, Record<string, string>> = {
56
56
  dirs: {
57
57
  chart: resolve(`${basePath}/${unique}-chart`),
@@ -119,20 +119,20 @@ export class Assets {
119
119
  this.apiToken = crypto.randomBytes(32).toString("hex");
120
120
  }
121
121
 
122
- setHash = (hash: string) => {
122
+ setHash = (hash: string): void => {
123
123
  this.hash = hash;
124
124
  };
125
125
 
126
- deploy = async (force: boolean, webhookTimeout?: number) => {
126
+ deploy = async (force: boolean, webhookTimeout?: number): Promise<void> => {
127
127
  this.capabilities = await loadCapabilities(this.path);
128
128
  await deploy(this, force, webhookTimeout);
129
129
  };
130
130
 
131
- zarfYaml = (path: string) => zarfYaml(this, path);
131
+ zarfYaml = (path: string): string => zarfYaml(this, path);
132
132
 
133
- zarfYamlChart = (path: string) => zarfYamlChart(this, path);
133
+ zarfYamlChart = (path: string): string => zarfYamlChart(this, path);
134
134
 
135
- allYaml = async (imagePullSecret?: string) => {
135
+ allYaml = async (imagePullSecret?: string): Promise<string> => {
136
136
  this.capabilities = await loadCapabilities(this.path);
137
137
  // give error if namespaces are not respected
138
138
  for (const capability of this.capabilities) {
@@ -143,7 +143,7 @@ export class Assets {
143
143
  };
144
144
 
145
145
  /* eslint max-statements: ["warn", 21] */
146
- generateHelmChart = async (basePath: string) => {
146
+ generateHelmChart = async (basePath: string): Promise<void> => {
147
147
  const helm = helmLayout(basePath, this.config.uuid);
148
148
 
149
149
  try {
@@ -156,18 +156,18 @@ export class Assets {
156
156
  const code = await fs.readFile(this.path);
157
157
 
158
158
  const pairs: [string, () => string][] = [
159
- [helm.files.chartYaml, () => dedent(chartYaml(this.config.uuid, this.config.description || ""))],
160
- [helm.files.namespaceYaml, () => dedent(nsTemplate())],
161
- [helm.files.watcherServiceYaml, () => toYaml(watcherService(this.name))],
162
- [helm.files.admissionServiceYaml, () => toYaml(service(this.name))],
163
- [helm.files.tlsSecretYaml, () => toYaml(tlsSecret(this.name, this.tls))],
164
- [helm.files.apiTokenSecretYaml, () => toYaml(apiTokenSecret(this.name, this.apiToken))],
165
- [helm.files.storeRoleYaml, () => toYaml(storeRole(this.name))],
166
- [helm.files.storeRoleBindingYaml, () => toYaml(storeRoleBinding(this.name))],
167
- [helm.files.clusterRoleYaml, () => dedent(clusterRoleTemplate())],
168
- [helm.files.clusterRoleBindingYaml, () => toYaml(clusterRoleBinding(this.name))],
169
- [helm.files.serviceAccountYaml, () => toYaml(serviceAccount(this.name))],
170
- [helm.files.moduleSecretYaml, () => toYaml(moduleSecret(this.name, code, this.hash))],
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
171
  ];
172
172
  await Promise.all(pairs.map(async ([file, content]) => await fs.writeFile(file, content())));
173
173
 
@@ -191,7 +191,7 @@ export class Assets {
191
191
  await fs.writeFile(helm.files.validationWebhookYaml, createWebhookYaml(this, validateWebhook));
192
192
  }
193
193
 
194
- const watchDeployment = watcher(this, this.hash, this.buildTimestamp);
194
+ const watchDeployment = getWatcher(this, this.hash, this.buildTimestamp);
195
195
  if (watchDeployment) {
196
196
  await fs.writeFile(helm.files.watcherDeploymentYaml, dedent(watcherDeployTemplate(this.buildTimestamp)));
197
197
  await fs.writeFile(helm.files.watcherServiceMonitorYaml, dedent(serviceMonitorTemplate("watcher")));
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
- import { V1EnvVar } from "@kubernetes/client-node";
4
+ 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";
@@ -10,7 +10,7 @@ import { ModuleConfig } from "../module";
10
10
  import { Binding } from "../types";
11
11
 
12
12
  /** Generate the pepr-system namespace */
13
- export function namespace(namespaceLabels?: Record<string, string>) {
13
+ export function getNamespace(namespaceLabels?: Record<string, string>): KubernetesObject {
14
14
  if (namespaceLabels) {
15
15
  return {
16
16
  apiVersion: "v1",
@@ -31,7 +31,12 @@ export function namespace(namespaceLabels?: Record<string, string>) {
31
31
  }
32
32
  }
33
33
 
34
- export function watcher(assets: Assets, hash: string, buildTimestamp: string, imagePullSecret?: string) {
34
+ export function getWatcher(
35
+ assets: Assets,
36
+ hash: string,
37
+ buildTimestamp: string,
38
+ imagePullSecret?: string,
39
+ ): kind.Deployment | null {
35
40
  const { name, image, capabilities, config } = assets;
36
41
 
37
42
  let hasSchedule = false;
@@ -186,7 +191,7 @@ export function watcher(assets: Assets, hash: string, buildTimestamp: string, im
186
191
  return deploy;
187
192
  }
188
193
 
189
- export function deployment(
194
+ export function getDeployment(
190
195
  assets: Assets,
191
196
  hash: string,
192
197
  buildTimestamp: string,
@@ -336,7 +341,7 @@ export function deployment(
336
341
  return deploy;
337
342
  }
338
343
 
339
- export function moduleSecret(name: string, data: Buffer, hash: string): kind.Secret {
344
+ export function getModuleSecret(name: string, data: Buffer, hash: string): kind.Secret {
340
345
  // Compress the data
341
346
  const compressed = gzipSync(data);
342
347
  const path = `module-${hash}.js.gz`;
@@ -20,7 +20,7 @@ const peprIgnoreLabel: V1LabelSelectorRequirement = {
20
20
 
21
21
  const peprIgnoreNamespaces: string[] = ["kube-system", "pepr-system"];
22
22
 
23
- export async function generateWebhookRules(assets: Assets, isMutateWebhook: boolean) {
23
+ export async function generateWebhookRules(assets: Assets, isMutateWebhook: boolean): Promise<V1RuleWithOperations[]> {
24
24
  const { config, capabilities } = assets;
25
25
  const rules: V1RuleWithOperations[] = [];
26
26
 
@@ -6,13 +6,16 @@ import crypto from "crypto";
6
6
  import { promises as fs } from "fs";
7
7
  import { Assets } from ".";
8
8
  import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking";
9
- import { deployment, moduleSecret, namespace, watcher } from "./pods";
9
+ import { getDeployment, getModuleSecret, getNamespace, getWatcher } from "./pods";
10
10
  import { clusterRole, clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "./rbac";
11
11
  import { webhookConfig } from "./webhooks";
12
12
  import { genEnv } from "./pods";
13
13
 
14
14
  // Helm Chart overrides file (values.yaml) generated from assets
15
- export async function overridesFile({ hash, name, image, config, apiToken, capabilities }: Assets, path: string) {
15
+ export async function overridesFile(
16
+ { hash, name, image, config, apiToken, capabilities }: Assets,
17
+ path: string,
18
+ ): Promise<void> {
16
19
  const rbacOverrides = clusterRole(name, capabilities, config.rbacMode, config.rbac).rules;
17
20
 
18
21
  const overrides = {
@@ -166,7 +169,7 @@ export async function overridesFile({ hash, name, image, config, apiToken, capab
166
169
 
167
170
  await fs.writeFile(path, dumpYaml(overrides, { noRefs: true, forceQuotes: true }));
168
171
  }
169
- export function zarfYaml({ name, image, config }: Assets, path: string) {
172
+ export function zarfYaml({ name, image, config }: Assets, path: string): string {
170
173
  const zarfCfg = {
171
174
  kind: "ZarfPackageConfig",
172
175
  metadata: {
@@ -194,7 +197,7 @@ export function zarfYaml({ name, image, config }: Assets, path: string) {
194
197
  return dumpYaml(zarfCfg, { noRefs: true });
195
198
  }
196
199
 
197
- export function zarfYamlChart({ name, image, config }: Assets, path: string) {
200
+ export function zarfYamlChart({ name, image, config }: Assets, path: string): string {
198
201
  const zarfCfg = {
199
202
  kind: "ZarfPackageConfig",
200
203
  metadata: {
@@ -223,7 +226,7 @@ export function zarfYamlChart({ name, image, config }: Assets, path: string) {
223
226
  return dumpYaml(zarfCfg, { noRefs: true });
224
227
  }
225
228
 
226
- export async function allYaml(assets: Assets, imagePullSecret?: string) {
229
+ export async function allYaml(assets: Assets, imagePullSecret?: string): Promise<string> {
227
230
  const { name, tls, apiToken, path, config } = assets;
228
231
  const code = await fs.readFile(path);
229
232
 
@@ -232,19 +235,19 @@ export async function allYaml(assets: Assets, imagePullSecret?: string) {
232
235
 
233
236
  const mutateWebhook = await webhookConfig(assets, "mutate", assets.config.webhookTimeout);
234
237
  const validateWebhook = await webhookConfig(assets, "validate", assets.config.webhookTimeout);
235
- const watchDeployment = watcher(assets, assets.hash, assets.buildTimestamp, imagePullSecret);
238
+ const watchDeployment = getWatcher(assets, assets.hash, assets.buildTimestamp, imagePullSecret);
236
239
 
237
240
  const resources = [
238
- namespace(assets.config.customLabels?.namespace),
241
+ getNamespace(assets.config.customLabels?.namespace),
239
242
  clusterRole(name, assets.capabilities, config.rbacMode, config.rbac),
240
243
  clusterRoleBinding(name),
241
244
  serviceAccount(name),
242
245
  apiTokenSecret(name, apiToken),
243
246
  tlsSecret(name, tls),
244
- deployment(assets, assets.hash, assets.buildTimestamp, imagePullSecret),
247
+ getDeployment(assets, assets.hash, assets.buildTimestamp, imagePullSecret),
245
248
  service(name),
246
249
  watcherService(name),
247
- moduleSecret(name, code, assets.hash),
250
+ getModuleSecret(name, code, assets.hash),
248
251
  storeRole(name),
249
252
  storeRoleBinding(name),
250
253
  ];
@@ -71,7 +71,7 @@ export class Capability implements CapabilityExport {
71
71
  }
72
72
  };
73
73
 
74
- public getScheduleStore() {
74
+ public getScheduleStore(): Storage {
75
75
  return this.#scheduleStore;
76
76
  }
77
77
 
@@ -111,19 +111,19 @@ export class Capability implements CapabilityExport {
111
111
  onReady: this.#scheduleStore.onReady,
112
112
  };
113
113
 
114
- get bindings() {
114
+ get bindings(): Binding[] {
115
115
  return this.#bindings;
116
116
  }
117
117
 
118
- get name() {
118
+ get name(): string {
119
119
  return this.#name;
120
120
  }
121
121
 
122
- get description() {
122
+ get description(): string {
123
123
  return this.#description;
124
124
  }
125
125
 
126
- get namespaces() {
126
+ get namespaces(): string[] {
127
127
  return this.#namespaces || [];
128
128
  }
129
129
 
@@ -207,8 +207,19 @@ export class Capability implements CapabilityExport {
207
207
  const bindings = this.#bindings;
208
208
  const prefix = `${this.#name}: ${model.name}`;
209
209
  const commonChain = { WithLabel, WithAnnotation, WithDeletionTimestamp, Mutate, Validate, Watch, Reconcile, Alias };
210
- const isNotEmpty = (value: object) => Object.keys(value).length > 0;
211
- const log = (message: string, cbString: string) => {
210
+
211
+ type CommonChainType = typeof commonChain;
212
+ type ExtendedCommonChainType = CommonChainType & {
213
+ Alias: (alias: string) => CommonChainType;
214
+ InNamespace: (...namespaces: string[]) => BindingWithName<T>;
215
+ InNamespaceRegex: (...namespaces: RegExp[]) => BindingWithName<T>;
216
+ WithName: (name: string) => BindingFilter<T>;
217
+ WithNameRegex: (regexName: RegExp) => BindingFilter<T>;
218
+ WithDeletionTimestamp: () => BindingFilter<T>;
219
+ };
220
+
221
+ const isNotEmpty = (value: object): boolean => Object.keys(value).length > 0;
222
+ const log = (message: string, cbString: string): void => {
212
223
  const filteredObj = pickBy(isNotEmpty, binding.filters);
213
224
 
214
225
  Log.info(`${message} configured for ${binding.event}`, prefix);
@@ -329,7 +340,7 @@ export class Capability implements CapabilityExport {
329
340
  isWatch: true,
330
341
  isFinalize: true,
331
342
  event: Event.UPDATE,
332
- finalizeCallback: async (update: InstanceType<T>, logger = aliasLogger) => {
343
+ finalizeCallback: async (update: InstanceType<T>, logger = aliasLogger): Promise<boolean | void> => {
333
344
  Log.info(`Executing finalize action with alias: ${binding.alias || "no alias provided"}`);
334
345
  return await finalizeCallback(update, logger);
335
346
  },
@@ -380,13 +391,13 @@ export class Capability implements CapabilityExport {
380
391
  return commonChain;
381
392
  }
382
393
 
383
- function Alias(alias: string) {
394
+ function Alias(alias: string): CommonChainType {
384
395
  Log.debug(`Adding prefix alias ${alias}`, prefix);
385
396
  binding.alias = alias;
386
397
  return commonChain;
387
398
  }
388
399
 
389
- function bindEvent(event: Event) {
400
+ function bindEvent(event: Event): ExtendedCommonChainType {
390
401
  binding.event = event;
391
402
  return {
392
403
  ...commonChain,