pepr 0.40.1 → 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 (107) hide show
  1. package/README.md +11 -5
  2. package/dist/cli/build.d.ts +1 -0
  3. package/dist/cli/build.d.ts.map +1 -1
  4. package/dist/cli/build.helpers.d.ts +66 -0
  5. package/dist/cli/build.helpers.d.ts.map +1 -1
  6. package/dist/cli/deploy.d.ts.map +1 -1
  7. package/dist/cli/init/templates.d.ts +2 -2
  8. package/dist/cli/monitor.d.ts +23 -0
  9. package/dist/cli/monitor.d.ts.map +1 -1
  10. package/dist/cli.js +536 -429
  11. package/dist/controller.js +52 -27
  12. package/dist/lib/assets/destroy.d.ts.map +1 -1
  13. package/dist/lib/assets/helm.d.ts +1 -1
  14. package/dist/lib/assets/helm.d.ts.map +1 -1
  15. package/dist/lib/assets/index.d.ts.map +1 -1
  16. package/dist/lib/assets/pods.d.ts +5 -19
  17. package/dist/lib/assets/pods.d.ts.map +1 -1
  18. package/dist/lib/assets/webhooks.d.ts.map +1 -1
  19. package/dist/lib/assets/yaml.d.ts.map +1 -1
  20. package/dist/lib/capability.d.ts.map +1 -1
  21. package/dist/lib/controller/index.d.ts.map +1 -1
  22. package/dist/lib/controller/index.util.d.ts +10 -0
  23. package/dist/lib/controller/index.util.d.ts.map +1 -0
  24. package/dist/lib/controller/store.d.ts +0 -1
  25. package/dist/lib/controller/store.d.ts.map +1 -1
  26. package/dist/lib/controller/storeCache.d.ts +1 -0
  27. package/dist/lib/controller/storeCache.d.ts.map +1 -1
  28. package/dist/lib/deploymentChecks.d.ts +3 -0
  29. package/dist/lib/deploymentChecks.d.ts.map +1 -0
  30. package/dist/lib/enums.d.ts +5 -5
  31. package/dist/lib/enums.d.ts.map +1 -1
  32. package/dist/lib/filesystemService.d.ts +2 -0
  33. package/dist/lib/filesystemService.d.ts.map +1 -0
  34. package/dist/lib/filter/adjudicators/adjudicators.d.ts +73 -0
  35. package/dist/lib/filter/adjudicators/adjudicators.d.ts.map +1 -0
  36. package/dist/lib/filter/adjudicators/defaultTestObjects.d.ts +7 -0
  37. package/dist/lib/filter/adjudicators/defaultTestObjects.d.ts.map +1 -0
  38. package/dist/lib/helpers.d.ts +1 -4
  39. package/dist/lib/helpers.d.ts.map +1 -1
  40. package/dist/lib/mutate-request.d.ts +2 -2
  41. package/dist/lib/mutate-request.d.ts.map +1 -1
  42. package/dist/lib/queue.d.ts.map +1 -1
  43. package/dist/lib/schedule.d.ts.map +1 -1
  44. package/dist/lib/storage.d.ts +5 -5
  45. package/dist/lib/storage.d.ts.map +1 -1
  46. package/dist/lib/{logger.d.ts → telemetry/logger.d.ts} +1 -1
  47. package/dist/lib/telemetry/logger.d.ts.map +1 -0
  48. package/dist/lib/{metrics.d.ts → telemetry/metrics.d.ts} +3 -1
  49. package/dist/lib/telemetry/metrics.d.ts.map +1 -0
  50. package/dist/lib/types.d.ts +10 -9
  51. package/dist/lib/types.d.ts.map +1 -1
  52. package/dist/lib/utils.d.ts.map +1 -1
  53. package/dist/lib/validate-processor.d.ts +4 -1
  54. package/dist/lib/validate-processor.d.ts.map +1 -1
  55. package/dist/lib/watch-processor.d.ts.map +1 -1
  56. package/dist/lib.d.ts +1 -1
  57. package/dist/lib.d.ts.map +1 -1
  58. package/dist/lib.js +283 -231
  59. package/dist/lib.js.map +4 -4
  60. package/dist/sdk/sdk.d.ts +3 -4
  61. package/dist/sdk/sdk.d.ts.map +1 -1
  62. package/package.json +5 -5
  63. package/src/cli/build.helpers.ts +180 -0
  64. package/src/cli/build.ts +85 -132
  65. package/src/cli/deploy.ts +2 -1
  66. package/src/cli/init/templates.ts +1 -1
  67. package/src/cli/monitor.ts +108 -65
  68. package/src/lib/assets/deploy.ts +7 -7
  69. package/src/lib/assets/destroy.ts +2 -2
  70. package/src/lib/assets/helm.ts +6 -6
  71. package/src/lib/assets/index.ts +110 -89
  72. package/src/lib/assets/pods.ts +10 -5
  73. package/src/lib/assets/webhooks.ts +3 -3
  74. package/src/lib/assets/yaml.ts +12 -9
  75. package/src/lib/capability.ts +29 -19
  76. package/src/lib/controller/index.ts +41 -69
  77. package/src/lib/controller/index.util.ts +47 -0
  78. package/src/lib/controller/store.ts +24 -11
  79. package/src/lib/controller/storeCache.ts +11 -2
  80. package/src/lib/deploymentChecks.ts +43 -0
  81. package/src/lib/enums.ts +5 -5
  82. package/src/lib/filesystemService.ts +16 -0
  83. package/src/lib/filter/{adjudicators.ts → adjudicators/adjudicators.ts} +67 -35
  84. package/src/lib/filter/adjudicators/defaultTestObjects.ts +46 -0
  85. package/src/lib/filter/filter.ts +1 -1
  86. package/src/lib/finalizer.ts +1 -1
  87. package/src/lib/helpers.ts +31 -88
  88. package/src/lib/mutate-processor.ts +1 -1
  89. package/src/lib/mutate-request.ts +11 -11
  90. package/src/lib/queue.ts +13 -5
  91. package/src/lib/schedule.ts +8 -8
  92. package/src/lib/storage.ts +48 -39
  93. package/src/lib/{logger.ts → telemetry/logger.ts} +1 -1
  94. package/src/lib/{metrics.ts → telemetry/metrics.ts} +18 -17
  95. package/src/lib/types.ts +12 -9
  96. package/src/lib/utils.ts +6 -6
  97. package/src/lib/validate-processor.ts +48 -40
  98. package/src/lib/watch-processor.ts +19 -15
  99. package/src/lib.ts +1 -1
  100. package/src/runtime/controller.ts +1 -1
  101. package/src/sdk/cosign.ts +4 -4
  102. package/src/sdk/sdk.ts +6 -9
  103. package/src/templates/capabilities/hello-pepr.ts +19 -9
  104. package/dist/lib/filter/adjudicators.d.ts +0 -69
  105. package/dist/lib/filter/adjudicators.d.ts.map +0 -1
  106. package/dist/lib/logger.d.ts.map +0 -1
  107. package/dist/lib/metrics.d.ts.map +0 -1
@@ -12,15 +12,20 @@ export type Unsubscribe = () => void;
12
12
  const MAX_WAIT_TIME = 15000;
13
13
  const STORE_VERSION_PREFIX = "v2";
14
14
 
15
- export function v2StoreKey(key: string) {
15
+ interface WaitRecord {
16
+ timeout?: ReturnType<typeof setTimeout>;
17
+ unsubscribe?: () => void;
18
+ }
19
+
20
+ export function v2StoreKey(key: string): string {
16
21
  return `${STORE_VERSION_PREFIX}-${pointer.escape(key)}`;
17
22
  }
18
23
 
19
- export function v2UnescapedStoreKey(key: string) {
24
+ export function v2UnescapedStoreKey(key: string): string {
20
25
  return `${STORE_VERSION_PREFIX}-${key}`;
21
26
  }
22
27
 
23
- export function stripV2Prefix(key: string) {
28
+ export function stripV2Prefix(key: string): string {
24
29
  return key.replace(/^v2-/, "");
25
30
  }
26
31
  export interface PeprStore {
@@ -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
  /**
@@ -80,11 +85,11 @@ export class Storage implements PeprStore {
80
85
  #subscriberId = 0;
81
86
  #readyHandlers: DataReceiver[] = [];
82
87
 
83
- registerSender = (send: DataSender) => {
88
+ registerSender = (send: DataSender): void => {
84
89
  this.#send = send;
85
90
  };
86
91
 
87
- receive = (data: DataStore) => {
92
+ receive = (data: DataStore): void => {
88
93
  this.#store = data || {};
89
94
 
90
95
  this.#onReady();
@@ -96,7 +101,7 @@ export class Storage implements PeprStore {
96
101
  }
97
102
  };
98
103
 
99
- getItem = (key: string) => {
104
+ getItem = (key: string): string | null => {
100
105
  const result = this.#store[v2UnescapedStoreKey(key)] || null;
101
106
  if (result !== null && typeof result !== "function" && typeof result !== "object") {
102
107
  return result;
@@ -104,7 +109,7 @@ export class Storage implements PeprStore {
104
109
  return null;
105
110
  };
106
111
 
107
- clear = () => {
112
+ clear = (): void => {
108
113
  Object.keys(this.#store).length > 0 &&
109
114
  this.#dispatchUpdate(
110
115
  "remove",
@@ -112,11 +117,11 @@ export class Storage implements PeprStore {
112
117
  );
113
118
  };
114
119
 
115
- removeItem = (key: string) => {
120
+ removeItem = (key: string): void => {
116
121
  this.#dispatchUpdate("remove", [v2StoreKey(key)]);
117
122
  };
118
123
 
119
- setItem = (key: string, value: string) => {
124
+ setItem = (key: string, value: string): void => {
120
125
  this.#dispatchUpdate("add", [v2StoreKey(key)], value);
121
126
  };
122
127
 
@@ -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) => {
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,31 +161,33 @@ 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) => {
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
 
175
- subscribe = (subscriber: DataReceiver) => {
184
+ subscribe = (subscriber: DataReceiver): (() => void) => {
176
185
  const idx = this.#subscriberId++;
177
186
  this.#subscribers[idx] = subscriber;
178
187
  return () => this.unsubscribe(idx);
179
188
  };
180
189
 
181
- onReady = (callback: DataReceiver) => {
190
+ onReady = (callback: DataReceiver): void => {
182
191
  this.#readyHandlers.push(callback);
183
192
  };
184
193
 
@@ -186,18 +195,18 @@ export class Storage implements PeprStore {
186
195
  * Remove a subscriber from the list of subscribers.
187
196
  * @param idx - The index of the subscriber to remove.
188
197
  */
189
- unsubscribe = (idx: number) => {
198
+ unsubscribe = (idx: number): void => {
190
199
  delete this.#subscribers[idx];
191
200
  };
192
201
 
193
- #onReady = () => {
202
+ #onReady = (): void => {
194
203
  // Notify all ready handlers with a clone of the store
195
204
  for (const handler of this.#readyHandlers) {
196
205
  handler(clone(this.#store));
197
206
  }
198
207
 
199
208
  // Make this a noop so that it can't be called again
200
- this.#onReady = () => {};
209
+ this.#onReady = (): void => {};
201
210
  };
202
211
 
203
212
  /**
@@ -206,7 +215,7 @@ export class Storage implements PeprStore {
206
215
  * @param keys - The keys to update.
207
216
  * @param [value] - The new value.
208
217
  */
209
- #dispatchUpdate = (op: DataOp, keys: string[], value?: string) => {
218
+ #dispatchUpdate = (op: DataOp, keys: string[], value?: string): void => {
210
219
  this.#send(op, keys, value);
211
220
  };
212
221
  }
@@ -3,7 +3,7 @@
3
3
 
4
4
  import { Operation } from "fast-json-patch";
5
5
  import { pino, stdTimeFunctions } from "pino";
6
- import { Store } from "./k8s";
6
+ import { Store } from "../k8s";
7
7
 
8
8
  const isPrettyLog = process.env.PEPR_PRETTY_LOGS === "true";
9
9
  const redactedValue = "**redacted**";
@@ -9,6 +9,7 @@ import Log from "./logger";
9
9
 
10
10
  const loggingPrefix = "MetricsCollector";
11
11
 
12
+ type MetricsCollectorInstance = InstanceType<typeof MetricsCollector>;
12
13
  interface MetricNames {
13
14
  errors: string;
14
15
  alerts: string;
@@ -60,7 +61,7 @@ export class MetricsCollector {
60
61
  this.addGauge(this.#metricNames.resyncFailureCount, "Number of failures per resync operation", ["count"]);
61
62
  }
62
63
 
63
- #getMetricName = (name: string) => `${this.#prefix}_${name}`;
64
+ #getMetricName = (name: string): string => `${this.#prefix}_${name}`;
64
65
 
65
66
  #addMetric = <T extends Counter<string> | Gauge<string> | Summary<string>>(
66
67
  collection: Map<string, T>,
@@ -68,7 +69,7 @@ export class MetricsCollector {
68
69
  name: string,
69
70
  help: string,
70
71
  labelNames?: string[],
71
- ) => {
72
+ ): void => {
72
73
  if (collection.has(this.#getMetricName(name))) {
73
74
  Log.debug(`Metric for ${name} already exists`, loggingPrefix);
74
75
  return;
@@ -84,42 +85,42 @@ export class MetricsCollector {
84
85
  collection.set(this.#getMetricName(name), metric);
85
86
  };
86
87
 
87
- addCounter = (name: string, help: string) => {
88
+ addCounter = (name: string, help: string): void => {
88
89
  this.#addMetric(this.#counters, promClient.Counter, name, help, []);
89
90
  };
90
91
 
91
- addSummary = (name: string, help: string) => {
92
+ addSummary = (name: string, help: string): void => {
92
93
  this.#addMetric(this.#summaries, promClient.Summary, name, help, []);
93
94
  };
94
95
 
95
- addGauge = (name: string, help: string, labelNames?: string[]) => {
96
+ addGauge = (name: string, help: string, labelNames?: string[]): void => {
96
97
  this.#addMetric(this.#gauges, promClient.Gauge, name, help, labelNames);
97
98
  };
98
99
 
99
- incCounter = (name: string) => {
100
+ incCounter = (name: string): void => {
100
101
  this.#counters.get(this.#getMetricName(name))?.inc();
101
102
  };
102
103
 
103
- incGauge = (name: string, labels?: Record<string, string>, value: number = 1) => {
104
+ incGauge = (name: string, labels?: Record<string, string>, value: number = 1): void => {
104
105
  this.#gauges.get(this.#getMetricName(name))?.inc(labels || {}, value);
105
106
  };
106
107
 
107
108
  /**
108
109
  * Increments the error counter.
109
110
  */
110
- error = () => this.incCounter(this.#metricNames.errors);
111
+ error = (): void => this.incCounter(this.#metricNames.errors);
111
112
 
112
113
  /**
113
114
  * Increments the alerts counter.
114
115
  */
115
- alert = () => this.incCounter(this.#metricNames.alerts);
116
+ alert = (): void => this.incCounter(this.#metricNames.alerts);
116
117
 
117
118
  /**
118
119
  * Observes the duration since the provided start time and updates the summary.
119
120
  * @param startTime - The start time.
120
121
  * @param name - The metrics summary to increment.
121
122
  */
122
- observeEnd = (startTime: number, name: string = this.#metricNames.mutate) => {
123
+ observeEnd = (startTime: number, name: string = this.#metricNames.mutate): void => {
123
124
  this.#summaries.get(this.#getMetricName(name))?.observe(performance.now() - startTime);
124
125
  };
125
126
 
@@ -127,13 +128,13 @@ export class MetricsCollector {
127
128
  * Fetches the current metrics from the registry.
128
129
  * @returns The metrics.
129
130
  */
130
- getMetrics = () => this.#registry.metrics();
131
+ getMetrics = (): Promise<string> => this.#registry.metrics();
131
132
 
132
133
  /**
133
134
  * Returns the current timestamp from performance.now() method. Useful for start timing an operation.
134
135
  * @returns The timestamp.
135
136
  */
136
- static observeStart() {
137
+ static observeStart(): number {
137
138
  return performance.now();
138
139
  }
139
140
 
@@ -141,7 +142,7 @@ export class MetricsCollector {
141
142
  * Increments the cache miss gauge for a given label.
142
143
  * @param label - The label for the cache miss.
143
144
  */
144
- incCacheMiss = (window: string) => {
145
+ incCacheMiss = (window: string): void => {
145
146
  this.incGauge(this.#metricNames.cacheMiss, { window });
146
147
  };
147
148
 
@@ -149,7 +150,7 @@ export class MetricsCollector {
149
150
  * Increments the retry count gauge.
150
151
  * @param count - The count to increment by.
151
152
  */
152
- incRetryCount = (count: string) => {
153
+ incRetryCount = (count: string): void => {
153
154
  this.incGauge(this.#metricNames.resyncFailureCount, { count });
154
155
  };
155
156
 
@@ -157,7 +158,7 @@ export class MetricsCollector {
157
158
  * Initializes the cache miss gauge for a given label.
158
159
  * @param label - The label for the cache miss.
159
160
  */
160
- initCacheMissWindow = (window: string) => {
161
+ initCacheMissWindow = (window: string): void => {
161
162
  this.#rollCacheMissWindows();
162
163
  this.#gauges.get(this.#getMetricName(this.#metricNames.cacheMiss))?.set({ window }, 0);
163
164
  this.#cacheMissWindows.set(window, 0);
@@ -166,7 +167,7 @@ export class MetricsCollector {
166
167
  /**
167
168
  * Manages the size of the cache miss gauge map.
168
169
  */
169
- #rollCacheMissWindows = () => {
170
+ #rollCacheMissWindows = (): void => {
170
171
  const maxCacheMissWindows = process.env.PEPR_MAX_CACHE_MISS_WINDOWS
171
172
  ? parseInt(process.env.PEPR_MAX_CACHE_MISS_WINDOWS, 10)
172
173
  : undefined;
@@ -181,4 +182,4 @@ export class MetricsCollector {
181
182
  };
182
183
  }
183
184
 
184
- export const metricsCollector = new MetricsCollector("pepr");
185
+ export const metricsCollector: MetricsCollectorInstance = new MetricsCollector("pepr");
package/src/lib/types.ts CHANGED
@@ -70,6 +70,17 @@ export interface RegExpFilter {
70
70
  obj: RegExp;
71
71
  source: string;
72
72
  }
73
+
74
+ export type Filters = {
75
+ annotations: Record<string, string>;
76
+ deletionTimestamp: boolean;
77
+ labels: Record<string, string>;
78
+ name: string;
79
+ namespaces: string[];
80
+ regexName: string;
81
+ regexNamespaces: string[];
82
+ };
83
+
73
84
  export type Binding = {
74
85
  event: Event;
75
86
  isMutate?: boolean;
@@ -79,15 +90,7 @@ export type Binding = {
79
90
  isFinalize?: boolean;
80
91
  readonly model: GenericClass;
81
92
  readonly kind: GroupVersionKind;
82
- readonly filters: {
83
- name: string;
84
- regexName: string;
85
- namespaces: string[];
86
- regexNamespaces: string[];
87
- labels: Record<string, string>;
88
- annotations: Record<string, string>;
89
- deletionTimestamp: boolean;
90
- };
93
+ readonly filters: Filters;
91
94
  alias?: string;
92
95
  readonly mutateCallback?: MutateAction<GenericClass, InstanceType<GenericClass>>;
93
96
  readonly validateCallback?: ValidateAction<GenericClass, InstanceType<GenericClass>>;
package/src/lib/utils.ts CHANGED
@@ -1,17 +1,17 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
- import Log from "./logger";
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";
10
- import Log from "./logger";
8
+ import { AdmissionRequest, Binding } from "./types";
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
 
@@ -5,11 +5,11 @@ import { WatchPhase } from "kubernetes-fluent-client/dist/fluent/types";
5
5
  import { Capability } from "./capability";
6
6
  import { filterNoMatchReason } from "./helpers";
7
7
  import { removeFinalizer } from "./finalizer";
8
- import Log from "./logger";
8
+ import Log from "./telemetry/logger";
9
9
  import { Queue } from "./queue";
10
10
  import { Binding } from "./types";
11
11
  import { Event } from "./enums";
12
- import { metricsCollector } from "./metrics";
12
+ import { metricsCollector } from "./telemetry/metrics";
13
13
 
14
14
  // stores Queue instances
15
15
  const queues: Record<string, Queue<KubernetesObject>> = {};
@@ -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);
@@ -62,11 +62,11 @@ const watchCfg: WatchCfg = {
62
62
 
63
63
  // Map the event to the watch phase
64
64
  const eventToPhaseMap = {
65
- [Event.Create]: [WatchPhase.Added],
66
- [Event.Update]: [WatchPhase.Modified],
67
- [Event.CreateOrUpdate]: [WatchPhase.Added, WatchPhase.Modified],
68
- [Event.Delete]: [WatchPhase.Deleted],
69
- [Event.Any]: [WatchPhase.Added, WatchPhase.Modified, WatchPhase.Deleted],
65
+ [Event.CREATE]: [WatchPhase.Added],
66
+ [Event.UPDATE]: [WatchPhase.Modified],
67
+ [Event.CREATE_OR_UPDATE]: [WatchPhase.Added, WatchPhase.Modified],
68
+ [Event.DELETE]: [WatchPhase.Deleted],
69
+ [Event.ANY]: [WatchPhase.Added, WatchPhase.Modified, WatchPhase.Deleted],
70
70
  };
71
71
 
72
72
  /**
@@ -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
- const phaseMatch: WatchPhase[] = eventToPhaseMap[binding.event] || eventToPhaseMap[Event.Any];
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/lib.ts CHANGED
@@ -2,7 +2,7 @@ import { K8s, RegisterKind, kind as a, fetch, fetchStatus, kind } from "kubernet
2
2
  import * as R from "ramda";
3
3
 
4
4
  import { Capability } from "./lib/capability";
5
- import Log from "./lib/logger";
5
+ import Log from "./lib/telemetry/logger";
6
6
  import { PeprModule } from "./lib/module";
7
7
  import { PeprMutateRequest } from "./lib/mutate-request";
8
8
  import * as PeprUtils from "./lib/utils";
@@ -8,7 +8,7 @@ import crypto from "crypto";
8
8
  import fs from "fs";
9
9
  import { gunzipSync } from "zlib";
10
10
  import { K8s, kind } from "kubernetes-fluent-client";
11
- import Log from "../lib/logger";
11
+ import Log from "../lib/telemetry/logger";
12
12
  import { packageJSON } from "../templates/data.json";
13
13
  import { peprStoreCRD } from "../lib/assets/store";
14
14
  import { validateHash } from "../lib/helpers";