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
@@ -78,7 +78,7 @@ export class Controller {
78
78
  }
79
79
 
80
80
  /** Start the webhook server */
81
- startServer = (port: number) => {
81
+ startServer = (port: number): void => {
82
82
  if (this.#running) {
83
83
  throw new Error("Cannot start Pepr module: Pepr module was not instantiated with deferStart=true");
84
84
  }
@@ -133,7 +133,7 @@ export class Controller {
133
133
  });
134
134
  };
135
135
 
136
- #bindEndpoints = () => {
136
+ #bindEndpoints = (): void => {
137
137
  // Health check endpoint
138
138
  this.#app.get("/healthz", Controller.#healthz);
139
139
 
@@ -162,7 +162,7 @@ export class Controller {
162
162
  * @param next The next middleware function
163
163
  * @returns
164
164
  */
165
- #validateToken = (req: express.Request, res: express.Response, next: NextFunction) => {
165
+ #validateToken = (req: express.Request, res: express.Response, next: NextFunction): void => {
166
166
  // Validate the token
167
167
  const { token } = req.params;
168
168
  if (token !== this.#token) {
@@ -183,7 +183,7 @@ export class Controller {
183
183
  * @param req the incoming request
184
184
  * @param res the outgoing response
185
185
  */
186
- #metrics = async (req: express.Request, res: express.Response) => {
186
+ #metrics = async (req: express.Request, res: express.Response): Promise<void> => {
187
187
  try {
188
188
  // https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md#basic-info
189
189
  res.set("Content-Type", "text/plain; version=0.0.4");
@@ -200,7 +200,9 @@ export class Controller {
200
200
  * @param admissionKind the type of admission request
201
201
  * @returns the request handler
202
202
  */
203
- #admissionReq = (admissionKind: "Mutate" | "Validate") => {
203
+ #admissionReq = (
204
+ admissionKind: "Mutate" | "Validate",
205
+ ): ((req: express.Request, res: express.Response) => Promise<void>) => {
204
206
  // Create the admission request handler
205
207
  return async (req: express.Request, res: express.Response) => {
206
208
  // Start the metrics timer
@@ -259,7 +261,7 @@ export class Controller {
259
261
  * @param res the outgoing response
260
262
  * @param next the next middleware function
261
263
  */
262
- static #logger(req: express.Request, res: express.Response, next: express.NextFunction) {
264
+ static #logger(req: express.Request, res: express.Response, next: express.NextFunction): void {
263
265
  const startTime = Date.now();
264
266
 
265
267
  res.on("finish", () => {
@@ -288,7 +290,7 @@ export class Controller {
288
290
  * @param req the incoming request
289
291
  * @param res the outgoing response
290
292
  */
291
- static #healthz(req: express.Request, res: express.Response) {
293
+ static #healthz(req: express.Request, res: express.Response): void {
292
294
  try {
293
295
  res.send("OK");
294
296
  } catch (err) {
@@ -12,7 +12,8 @@ import { DataOp, DataSender, DataStore, Storage } from "../storage";
12
12
  import { fillStoreCache, sendUpdatesAndFlushCache } from "./storeCache";
13
13
 
14
14
  const namespace = "pepr-system";
15
- export const debounceBackoff = 1000;
15
+ const debounceBackoffReceive = 1000;
16
+ const debounceBackoffSend = 4000;
16
17
 
17
18
  export class StoreController {
18
19
  #name: string;
@@ -25,7 +26,7 @@ export class StoreController {
25
26
 
26
27
  this.#name = name;
27
28
 
28
- const setStorageInstance = (registrationFunction: () => Storage, name: string) => {
29
+ const setStorageInstance = (registrationFunction: () => Storage, name: string): void => {
29
30
  const scheduleStore = registrationFunction();
30
31
 
31
32
  // Bind the store sender to the capability
@@ -61,13 +62,22 @@ export class StoreController {
61
62
  );
62
63
  }
63
64
 
64
- #setupWatch = () => {
65
+ #setupWatch = (): void => {
65
66
  const watcher = K8s(Store, { name: this.#name, namespace }).Watch(this.#receive);
66
67
  watcher.start().catch(e => Log.error(e, "Error starting Pepr store watch"));
67
68
  };
68
69
 
69
- #migrateAndSetupWatch = async (store: Store) => {
70
+ #migrateAndSetupWatch = async (store: Store): Promise<void> => {
70
71
  Log.debug(redactedStore(store), "Pepr Store migration");
72
+ // Add cacheID label to store
73
+ await K8s(Store, { namespace, name: this.#name }).Patch([
74
+ {
75
+ op: "add",
76
+ path: "/metadata/labels/pepr.dev-cacheID",
77
+ value: `${Date.now()}`,
78
+ },
79
+ ]);
80
+
71
81
  const data: DataStore = store.data || {};
72
82
  let storeCache: Record<string, Operation> = {};
73
83
 
@@ -96,11 +106,11 @@ export class StoreController {
96
106
  this.#setupWatch();
97
107
  };
98
108
 
99
- #receive = (store: Store) => {
109
+ #receive = (store: Store): void => {
100
110
  Log.debug(redactedStore(store), "Pepr Store update");
101
111
 
102
112
  // Wrap the update in a debounced function
103
- const debounced = () => {
113
+ const debounced = (): void => {
104
114
  // Base64 decode the data
105
115
  const data: DataStore = store.data || {};
106
116
 
@@ -134,10 +144,10 @@ export class StoreController {
134
144
 
135
145
  // Debounce the update to 1 second to avoid multiple rapid calls
136
146
  clearTimeout(this.#sendDebounce);
137
- this.#sendDebounce = setTimeout(debounced, this.#onReady ? 0 : debounceBackoff);
147
+ this.#sendDebounce = setTimeout(debounced, this.#onReady ? 0 : debounceBackoffReceive);
138
148
  };
139
149
 
140
- #send = (capabilityName: string) => {
150
+ #send = (capabilityName: string): DataSender => {
141
151
  let storeCache: Record<string, Operation> = {};
142
152
 
143
153
  // Create a sender function for the capability to add/remove data from the store
@@ -151,12 +161,12 @@ export class StoreController {
151
161
  Log.debug(redactedPatch(storeCache), "Sending updates to Pepr store");
152
162
  void sendUpdatesAndFlushCache(storeCache, namespace, this.#name);
153
163
  }
154
- }, debounceBackoff);
164
+ }, debounceBackoffSend);
155
165
 
156
166
  return sender;
157
167
  };
158
168
 
159
- #createStoreResource = async (e: unknown) => {
169
+ #createStoreResource = async (e: unknown): Promise<void> => {
160
170
  Log.info(`Pepr store not found, creating...`);
161
171
  Log.debug(e);
162
172
 
@@ -165,6 +175,9 @@ export class StoreController {
165
175
  metadata: {
166
176
  name: this.#name,
167
177
  namespace,
178
+ labels: {
179
+ "pepr.dev-cacheID": `${Date.now()}`,
180
+ },
168
181
  },
169
182
  data: {
170
183
  // JSON Patch will die if the data is empty, so we need to add a placeholder
@@ -11,7 +11,7 @@ export const sendUpdatesAndFlushCache = async (cache: Record<string, Operation>,
11
11
 
12
12
  try {
13
13
  if (payload.length > 0) {
14
- await K8s(Store, { namespace, name }).Patch(payload); // Send patch to cluster
14
+ await K8s(Store, { namespace, name }).Patch(updateCacheID(payload)); // Send patch to cluster
15
15
  Object.keys(cache).forEach(key => delete cache[key]);
16
16
  }
17
17
  } catch (err) {
@@ -61,3 +61,12 @@ export const fillStoreCache = (
61
61
  }
62
62
  return cache;
63
63
  };
64
+
65
+ export function updateCacheID(payload: Operation[]): Operation[] {
66
+ payload.push({
67
+ op: "replace",
68
+ path: "/metadata/labels/pepr.dev-cacheID",
69
+ value: `${Date.now()}`,
70
+ });
71
+ return payload;
72
+ }
@@ -11,19 +11,19 @@ export class PeprMutateRequest<T extends KubernetesObject> {
11
11
  Raw: T;
12
12
  #input: AdmissionRequest<T>;
13
13
 
14
- get PermitSideEffects() {
14
+ get PermitSideEffects(): boolean {
15
15
  return !this.#input.dryRun;
16
16
  }
17
17
 
18
- get IsDryRun() {
18
+ get IsDryRun(): boolean | undefined {
19
19
  return this.#input.dryRun;
20
20
  }
21
21
 
22
- get OldResource() {
22
+ get OldResource(): KubernetesObject | undefined {
23
23
  return this.#input.oldObject;
24
24
  }
25
25
 
26
- get Request() {
26
+ get Request(): AdmissionRequest<KubernetesObject> {
27
27
  return this.#input;
28
28
  }
29
29
 
@@ -42,11 +42,11 @@ export class PeprMutateRequest<T extends KubernetesObject> {
42
42
  }
43
43
  }
44
44
 
45
- Merge = (obj: DeepPartial<T>) => {
45
+ Merge = (obj: DeepPartial<T>): void => {
46
46
  this.Raw = mergeDeepRight(this.Raw, obj) as unknown as T;
47
47
  };
48
48
 
49
- SetLabel = (key: string, value: string) => {
49
+ SetLabel = (key: string, value: string): this => {
50
50
  const ref = this.Raw;
51
51
  ref.metadata = ref.metadata ?? {};
52
52
  ref.metadata.labels = ref.metadata.labels ?? {};
@@ -54,7 +54,7 @@ export class PeprMutateRequest<T extends KubernetesObject> {
54
54
  return this;
55
55
  };
56
56
 
57
- SetAnnotation = (key: string, value: string) => {
57
+ SetAnnotation = (key: string, value: string): this => {
58
58
  const ref = this.Raw;
59
59
  ref.metadata = ref.metadata ?? {};
60
60
  ref.metadata.annotations = ref.metadata.annotations ?? {};
@@ -62,25 +62,25 @@ export class PeprMutateRequest<T extends KubernetesObject> {
62
62
  return this;
63
63
  };
64
64
 
65
- RemoveLabel = (key: string) => {
65
+ RemoveLabel = (key: string): this => {
66
66
  if (this.Raw.metadata?.labels?.[key]) {
67
67
  delete this.Raw.metadata.labels[key];
68
68
  }
69
69
  return this;
70
70
  };
71
71
 
72
- RemoveAnnotation = (key: string) => {
72
+ RemoveAnnotation = (key: string): this => {
73
73
  if (this.Raw.metadata?.annotations?.[key]) {
74
74
  delete this.Raw.metadata.annotations[key];
75
75
  }
76
76
  return this;
77
77
  };
78
78
 
79
- HasLabel = (key: string) => {
79
+ HasLabel = (key: string): boolean => {
80
80
  return this.Raw.metadata?.labels?.[key] !== undefined;
81
81
  };
82
82
 
83
- HasAnnotation = (key: string) => {
83
+ HasAnnotation = (key: string): boolean => {
84
84
  return this.Raw.metadata?.annotations?.[key] !== undefined;
85
85
  };
86
86
  }
package/src/lib/queue.ts CHANGED
@@ -29,11 +29,19 @@ export class Queue<K extends KubernetesObject> {
29
29
  this.#uid = `${Date.now()}-${randomBytes(2).toString("hex")}`;
30
30
  }
31
31
 
32
- label() {
32
+ label(): { name: string; uid: string } {
33
33
  return { name: this.#name, uid: this.#uid };
34
34
  }
35
35
 
36
- stats() {
36
+ stats(): {
37
+ queue: {
38
+ name: string;
39
+ uid: string;
40
+ };
41
+ stats: {
42
+ length: number;
43
+ };
44
+ } {
37
45
  return {
38
46
  queue: this.label(),
39
47
  stats: {
@@ -51,7 +59,7 @@ export class Queue<K extends KubernetesObject> {
51
59
  * @param reconcile The callback to enqueue for reconcile
52
60
  * @returns A promise that resolves when the object is reconciled
53
61
  */
54
- enqueue(item: K, phase: WatchPhase, reconcile: WatchCallback) {
62
+ enqueue(item: K, phase: WatchPhase, reconcile: WatchCallback): Promise<void> {
55
63
  const note = {
56
64
  queue: this.label(),
57
65
  item: {
@@ -73,7 +81,7 @@ export class Queue<K extends KubernetesObject> {
73
81
  *
74
82
  * @returns A promise that resolves when the webapp is reconciled
75
83
  */
76
- async #dequeue() {
84
+ async #dequeue(): Promise<false | undefined> {
77
85
  // If there is a pending promise, do nothing
78
86
  if (this.#pendingPromise) {
79
87
  Log.debug("Pending promise, not dequeuing");
@@ -12,6 +12,11 @@ export type Unsubscribe = () => void;
12
12
  const MAX_WAIT_TIME = 15000;
13
13
  const STORE_VERSION_PREFIX = "v2";
14
14
 
15
+ interface WaitRecord {
16
+ timeout?: ReturnType<typeof setTimeout>;
17
+ unsubscribe?: () => void;
18
+ }
19
+
15
20
  export function v2StoreKey(key: string): string {
16
21
  return `${STORE_VERSION_PREFIX}-${pointer.escape(key)}`;
17
22
  }
@@ -58,13 +63,13 @@ export interface PeprStore {
58
63
  * Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
59
64
  * Resolves when the key/value show up in the store.
60
65
  */
61
- setItemAndWait(key: string, value: string): Promise<void>;
66
+ setItemAndWait(key: string, value: string): Promise<string>;
62
67
 
63
68
  /**
64
69
  * Remove the value of the key.
65
70
  * Resolves when the key does not show up in the store.
66
71
  */
67
- removeItemAndWait(key: string): Promise<void>;
72
+ removeItemAndWait(key: string): Promise<string>;
68
73
  }
69
74
 
70
75
  /**
@@ -128,22 +133,24 @@ export class Storage implements PeprStore {
128
133
  * @param value - The value of the key
129
134
  * @returns
130
135
  */
131
- setItemAndWait = (key: string, value: string): Promise<void> => {
136
+ setItemAndWait = (key: string, value: string): Promise<string> => {
132
137
  this.#dispatchUpdate("add", [v2StoreKey(key)], value);
138
+ const record: WaitRecord = {};
133
139
 
134
- return new Promise<void>((resolve, reject) => {
135
- const unsubscribe = this.subscribe(data => {
140
+ return new Promise<string>((resolve, reject) => {
141
+ // If promise has not resolved before MAX_WAIT_TIME reject
142
+ record.timeout = setTimeout(() => {
143
+ record.unsubscribe!();
144
+ return reject(`MAX_WAIT_TIME elapsed: Key ${key} not seen in ${MAX_WAIT_TIME / 1000}s`);
145
+ }, MAX_WAIT_TIME);
146
+
147
+ record.unsubscribe = this.subscribe(data => {
136
148
  if (data[`${v2UnescapedStoreKey(key)}`] === value) {
137
- unsubscribe();
138
- resolve();
149
+ record.unsubscribe!();
150
+ clearTimeout(record.timeout);
151
+ resolve("ok");
139
152
  }
140
153
  });
141
-
142
- // If promise has not resolved before MAX_WAIT_TIME reject
143
- setTimeout(() => {
144
- unsubscribe();
145
- return reject();
146
- }, MAX_WAIT_TIME);
147
154
  });
148
155
  };
149
156
 
@@ -154,21 +161,23 @@ export class Storage implements PeprStore {
154
161
  * @param key - The key to add into the store
155
162
  * @returns
156
163
  */
157
- removeItemAndWait = (key: string): Promise<void> => {
164
+ removeItemAndWait = (key: string): Promise<string> => {
158
165
  this.#dispatchUpdate("remove", [v2StoreKey(key)]);
159
- return new Promise<void>((resolve, reject) => {
160
- const unsubscribe = this.subscribe(data => {
166
+ const record: WaitRecord = {};
167
+ return new Promise<string>((resolve, reject) => {
168
+ // If promise has not resolved before MAX_WAIT_TIME reject
169
+ record.timeout = setTimeout(() => {
170
+ record.unsubscribe!();
171
+ return reject(`MAX_WAIT_TIME elapsed: Key ${key} still seen after ${MAX_WAIT_TIME / 1000}s`);
172
+ }, MAX_WAIT_TIME);
173
+
174
+ record.unsubscribe = this.subscribe(data => {
161
175
  if (!Object.hasOwn(data, `${v2UnescapedStoreKey(key)}`)) {
162
- unsubscribe();
163
- resolve();
176
+ record.unsubscribe!();
177
+ clearTimeout(record.timeout);
178
+ resolve("ok");
164
179
  }
165
180
  });
166
-
167
- // If promise has not resolved before MAX_WAIT_TIME reject
168
- setTimeout(() => {
169
- unsubscribe();
170
- return reject();
171
- }, MAX_WAIT_TIME);
172
181
  });
173
182
  };
174
183
 
package/src/lib/utils.ts CHANGED
@@ -4,14 +4,14 @@
4
4
  import Log from "./telemetry/logger";
5
5
 
6
6
  /** Test if a string is ascii or not */
7
- export const isAscii = /^[\s\x20-\x7E]*$/;
7
+ export const isAscii: RegExp = /^[\s\x20-\x7E]*$/;
8
8
 
9
9
  /**
10
10
  * Encode all ascii values in a map to base64
11
11
  * @param obj The object to encode
12
12
  * @param skip A list of keys to skip encoding
13
13
  */
14
- export function convertToBase64Map(obj: { data?: Record<string, string> }, skip: string[]) {
14
+ export function convertToBase64Map(obj: { data?: Record<string, string> }, skip: string[]): void {
15
15
  obj.data = obj.data ?? {};
16
16
  for (const key in obj.data) {
17
17
  const value = obj.data[key];
@@ -25,7 +25,7 @@ export function convertToBase64Map(obj: { data?: Record<string, string> }, skip:
25
25
  * @param obj The object to decode
26
26
  * @returns A list of keys that were skipped
27
27
  */
28
- export function convertFromBase64Map(obj: { data?: Record<string, string> }) {
28
+ export function convertFromBase64Map(obj: { data?: Record<string, string> }): string[] {
29
29
  const skip: string[] = [];
30
30
 
31
31
  obj.data = obj.data ?? {};
@@ -47,11 +47,11 @@ export function convertFromBase64Map(obj: { data?: Record<string, string> }) {
47
47
  }
48
48
 
49
49
  /** Decode a base64 string */
50
- export function base64Decode(data: string) {
50
+ export function base64Decode(data: string): string {
51
51
  return Buffer.from(data, "base64").toString("utf-8");
52
52
  }
53
53
 
54
54
  /** Encode a string to base64 */
55
- export function base64Encode(data: string) {
55
+ export function base64Encode(data: string): string {
56
56
  return Buffer.from(data).toString("base64");
57
57
  }
@@ -1,17 +1,56 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
- import { kind } from "kubernetes-fluent-client";
5
-
4
+ import { kind, KubernetesObject } from "kubernetes-fluent-client";
6
5
  import { Capability } from "./capability";
7
6
  import { shouldSkipRequest } from "./filter/filter";
8
7
  import { ValidateResponse } from "./k8s";
9
- import { AdmissionRequest } from "./types";
8
+ import { AdmissionRequest, Binding } from "./types";
10
9
  import Log from "./telemetry/logger";
11
10
  import { convertFromBase64Map } from "./utils";
12
11
  import { PeprValidateRequest } from "./validate-request";
13
12
  import { ModuleConfig } from "./module";
14
13
 
14
+ export async function processRequest(
15
+ binding: Binding,
16
+ actionMetadata: Record<string, string>,
17
+ peprValidateRequest: PeprValidateRequest<KubernetesObject>,
18
+ ): Promise<ValidateResponse> {
19
+ const label = binding.validateCallback!.name;
20
+ Log.info(actionMetadata, `Processing validation action (${label})`);
21
+
22
+ const valResp: ValidateResponse = {
23
+ uid: peprValidateRequest.Request.uid,
24
+ allowed: true, // Assume it's allowed until a validation check fails
25
+ };
26
+
27
+ try {
28
+ // Run the validation callback, if it fails set allowed to false
29
+ const callbackResp = await binding.validateCallback!(peprValidateRequest);
30
+ valResp.allowed = callbackResp.allowed;
31
+
32
+ // If the validation callback returned a status code or message, set it in the Response
33
+ if (callbackResp.statusCode || callbackResp.statusMessage) {
34
+ valResp.status = {
35
+ code: callbackResp.statusCode || 400,
36
+ message: callbackResp.statusMessage || `Validation failed for ${name}`,
37
+ };
38
+ }
39
+
40
+ Log.info(actionMetadata, `Validation action complete (${label}): ${callbackResp.allowed ? "allowed" : "denied"}`);
41
+ return valResp;
42
+ } catch (e) {
43
+ // If any validation throws an error, note the failure in the Response
44
+ Log.error(actionMetadata, `Action failed: ${JSON.stringify(e)}`);
45
+ valResp.allowed = false;
46
+ valResp.status = {
47
+ code: 500,
48
+ message: `Action failed with error: ${JSON.stringify(e)}`,
49
+ };
50
+ return valResp;
51
+ }
52
+ }
53
+
15
54
  export async function validateProcessor(
16
55
  config: ModuleConfig,
17
56
  capabilities: Capability[],
@@ -32,52 +71,21 @@ export async function validateProcessor(
32
71
  for (const { name, bindings, namespaces } of capabilities) {
33
72
  const actionMetadata = { ...reqMetadata, name };
34
73
 
35
- for (const action of bindings) {
74
+ for (const binding of bindings) {
36
75
  // Skip this action if it's not a validation action
37
- if (!action.validateCallback) {
76
+ if (!binding.validateCallback) {
38
77
  continue;
39
78
  }
40
79
 
41
- const localResponse: ValidateResponse = {
42
- uid: req.uid,
43
- allowed: true, // Assume it's allowed until a validation check fails
44
- };
45
-
46
80
  // Continue to the next action without doing anything if this one should be skipped
47
- const shouldSkip = shouldSkipRequest(action, req, namespaces, config?.alwaysIgnore?.namespaces);
81
+ const shouldSkip = shouldSkipRequest(binding, req, namespaces, config?.alwaysIgnore?.namespaces);
48
82
  if (shouldSkip !== "") {
49
83
  Log.debug(shouldSkip);
50
84
  continue;
51
85
  }
52
86
 
53
- const label = action.validateCallback.name;
54
- Log.info(actionMetadata, `Processing validation action (${label})`);
55
-
56
- try {
57
- // Run the validation callback, if it fails set allowed to false
58
- const resp = await action.validateCallback(wrapped);
59
- localResponse.allowed = resp.allowed;
60
-
61
- // If the validation callback returned a status code or message, set it in the Response
62
- if (resp.statusCode || resp.statusMessage) {
63
- localResponse.status = {
64
- code: resp.statusCode || 400,
65
- message: resp.statusMessage || `Validation failed for ${name}`,
66
- };
67
- }
68
-
69
- Log.info(actionMetadata, `Validation action complete (${label}): ${resp.allowed ? "allowed" : "denied"}`);
70
- } catch (e) {
71
- // If any validation throws an error, note the failure in the Response
72
- Log.error(actionMetadata, `Action failed: ${JSON.stringify(e)}`);
73
- localResponse.allowed = false;
74
- localResponse.status = {
75
- code: 500,
76
- message: `Action failed with error: ${JSON.stringify(e)}`,
77
- };
78
- return [localResponse];
79
- }
80
- response.push(localResponse);
87
+ const resp = await processRequest(binding, actionMetadata, wrapped);
88
+ response.push(resp);
81
89
  }
82
90
  }
83
91
 
@@ -20,7 +20,7 @@ const queues: Record<string, Queue<KubernetesObject>> = {};
20
20
  * @param obj The object to derive a key from
21
21
  * @returns The key to a Queue in the list of queues
22
22
  */
23
- export function queueKey(obj: KubernetesObject) {
23
+ export function queueKey(obj: KubernetesObject): string {
24
24
  const options = ["kind", "kindNs", "kindNsName", "global"];
25
25
  const d3fault = "kind";
26
26
 
@@ -40,7 +40,7 @@ export function queueKey(obj: KubernetesObject) {
40
40
  return lookup[strat];
41
41
  }
42
42
 
43
- export function getOrCreateQueue(obj: KubernetesObject) {
43
+ export function getOrCreateQueue(obj: KubernetesObject): Queue<KubernetesObject> {
44
44
  const key = queueKey(obj);
45
45
  if (!queues[key]) {
46
46
  queues[key] = new Queue<KubernetesObject>(key);
@@ -74,7 +74,7 @@ const eventToPhaseMap = {
74
74
  *
75
75
  * @param capabilities The capabilities to load watches for
76
76
  */
77
- export function setupWatch(capabilities: Capability[], ignoredNamespaces?: string[]) {
77
+ export function setupWatch(capabilities: Capability[], ignoredNamespaces?: string[]): void {
78
78
  capabilities.map(capability =>
79
79
  capability.bindings
80
80
  .filter(binding => binding.isWatch)
@@ -88,14 +88,18 @@ export function setupWatch(capabilities: Capability[], ignoredNamespaces?: strin
88
88
  * @param binding the binding to watch
89
89
  * @param capabilityNamespaces list of namespaces to filter on
90
90
  */
91
- async function runBinding(binding: Binding, capabilityNamespaces: string[], ignoredNamespaces?: string[]) {
91
+ async function runBinding(
92
+ binding: Binding,
93
+ capabilityNamespaces: string[],
94
+ ignoredNamespaces?: string[],
95
+ ): Promise<void> {
92
96
  // Get the phases to match, fallback to any
93
97
  const phaseMatch: WatchPhase[] = eventToPhaseMap[binding.event] || eventToPhaseMap[Event.ANY];
94
98
 
95
99
  // The watch callback is run when an object is received or dequeued
96
100
  Log.debug({ watchCfg }, "Effective WatchConfig");
97
101
 
98
- const watchCallback = async (kubernetesObject: KubernetesObject, phase: WatchPhase) => {
102
+ const watchCallback = async (kubernetesObject: KubernetesObject, phase: WatchPhase): Promise<void> => {
99
103
  // First, filter the object based on the phase
100
104
  if (phaseMatch.includes(phase)) {
101
105
  try {
@@ -117,7 +121,7 @@ async function runBinding(binding: Binding, capabilityNamespaces: string[], igno
117
121
  }
118
122
  };
119
123
 
120
- const handleFinalizerRemoval = async (kubernetesObject: KubernetesObject) => {
124
+ const handleFinalizerRemoval = async (kubernetesObject: KubernetesObject): Promise<void> => {
121
125
  if (!kubernetesObject.metadata?.deletionTimestamp) {
122
126
  return;
123
127
  }
@@ -191,7 +195,7 @@ async function runBinding(binding: Binding, capabilityNamespaces: string[], igno
191
195
  }
192
196
  }
193
197
 
194
- export function logEvent(event: WatchEvent, message: string = "", obj?: KubernetesObject) {
198
+ export function logEvent(event: WatchEvent, message: string = "", obj?: KubernetesObject): void {
195
199
  const logMessage = `Watch event ${event} received${message ? `. ${message}.` : "."}`;
196
200
  if (obj) {
197
201
  Log.debug(obj, logMessage);
package/src/sdk/cosign.ts CHANGED
@@ -212,15 +212,15 @@ export async function verifyImage(
212
212
  url: `https://${X.iref.host}/v2/${X.iref.name}/manifests/${X.iref.tag}`,
213
213
  };
214
214
 
215
- const supportsMediaType = async (url: string, mediaType: string) => {
215
+ const supportsMediaType = async (url: string, mediaType: string): Promise<boolean> => {
216
216
  return (await head(url, mediaType, { ca: tlsCrts }))["content-type"] === mediaType;
217
217
  };
218
218
 
219
- const canOciV1Manifest = async (manifestUrl: string) => {
219
+ const canOciV1Manifest = async (manifestUrl: string): Promise<boolean> => {
220
220
  return supportsMediaType(manifestUrl, MediaTypeOciV1.Manifest);
221
221
  };
222
222
 
223
- const canDockerV2Manifest = async (manifestUrl: string) => {
223
+ const canDockerV2Manifest = async (manifestUrl: string): Promise<boolean> => {
224
224
  return supportsMediaType(manifestUrl, MediaTypeDockerV2.Manifest);
225
225
  };
226
226
 
@@ -228,7 +228,7 @@ export async function verifyImage(
228
228
  const manifestResp =
229
229
  await canOciV1Manifest(X.manifest.url) ? await get(X.manifest.url, MediaTypeOciV1.Manifest, {ca: tlsCrts}) :
230
230
  await canDockerV2Manifest(X.manifest.url) ? await get(X.manifest.url, MediaTypeDockerV2.Manifest, {ca: tlsCrts}) :
231
- (() => { throw "Can't pull image manifest with supported MediaType." })();
231
+ (():never => { throw "Can't pull image manifest with supported MediaType." })();
232
232
  X.manifest.content = manifestResp.body;
233
233
 
234
234
  X.manifest.digest = `sha256:${crypto