pepr 0.12.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/CODE_OF_CONDUCT.md +83 -0
  2. package/CONTRIBUTING.md +70 -0
  3. package/README.md +28 -30
  4. package/dist/cli.js +644 -679
  5. package/dist/controller.js +13 -81
  6. package/dist/lib/assets/deploy.d.ts +3 -0
  7. package/dist/lib/assets/deploy.d.ts.map +1 -0
  8. package/dist/lib/assets/index.d.ts +18 -0
  9. package/dist/lib/assets/index.d.ts.map +1 -0
  10. package/dist/lib/assets/loader.d.ts +14 -0
  11. package/dist/lib/assets/loader.d.ts.map +1 -0
  12. package/dist/lib/assets/networking.d.ts +6 -0
  13. package/dist/lib/assets/networking.d.ts.map +1 -0
  14. package/dist/lib/assets/pods.d.ts +8 -0
  15. package/dist/lib/assets/pods.d.ts.map +1 -0
  16. package/dist/lib/assets/rbac.d.ts +11 -0
  17. package/dist/lib/assets/rbac.d.ts.map +1 -0
  18. package/dist/lib/assets/webhooks.d.ts +6 -0
  19. package/dist/lib/assets/webhooks.d.ts.map +1 -0
  20. package/dist/lib/assets/yaml.d.ts +4 -0
  21. package/dist/lib/assets/yaml.d.ts.map +1 -0
  22. package/dist/lib/capability.d.ts +1 -3
  23. package/dist/lib/capability.d.ts.map +1 -1
  24. package/dist/lib/controller.d.ts +45 -10
  25. package/dist/lib/controller.d.ts.map +1 -1
  26. package/dist/lib/filter.d.ts +1 -1
  27. package/dist/lib/filter.d.ts.map +1 -1
  28. package/dist/lib/k8s/index.d.ts +2 -1
  29. package/dist/lib/k8s/index.d.ts.map +1 -1
  30. package/dist/lib/k8s/kinds.d.ts.map +1 -1
  31. package/dist/lib/k8s/types.d.ts +13 -13
  32. package/dist/lib/k8s/types.d.ts.map +1 -1
  33. package/dist/lib/k8s/upstream.d.ts +2 -2
  34. package/dist/lib/k8s/upstream.d.ts.map +1 -1
  35. package/dist/lib/logger.d.ts +8 -54
  36. package/dist/lib/logger.d.ts.map +1 -1
  37. package/dist/lib/metrics.d.ts +11 -4
  38. package/dist/lib/metrics.d.ts.map +1 -1
  39. package/dist/lib/module.d.ts +2 -2
  40. package/dist/lib/module.d.ts.map +1 -1
  41. package/dist/lib/mutate-processor.d.ts +5 -0
  42. package/dist/lib/mutate-processor.d.ts.map +1 -0
  43. package/dist/lib/{request.d.ts → mutate-request.d.ts} +5 -5
  44. package/dist/lib/mutate-request.d.ts.map +1 -0
  45. package/dist/lib/types.d.ts +45 -46
  46. package/dist/lib/types.d.ts.map +1 -1
  47. package/dist/lib/utils.d.ts.map +1 -1
  48. package/dist/lib/validate-processor.d.ts +4 -0
  49. package/dist/lib/validate-processor.d.ts.map +1 -0
  50. package/dist/lib/validate-request.d.ts +54 -0
  51. package/dist/lib/validate-request.d.ts.map +1 -0
  52. package/dist/lib.d.ts +3 -2
  53. package/dist/lib.d.ts.map +1 -1
  54. package/dist/lib.js +510 -306
  55. package/dist/lib.js.map +4 -4
  56. package/package.json +15 -12
  57. package/src/cli.ts +2 -11
  58. package/src/lib/assets/deploy.ts +179 -0
  59. package/src/lib/assets/index.ts +46 -0
  60. package/src/lib/assets/loader.ts +49 -0
  61. package/src/lib/assets/networking.ts +58 -0
  62. package/src/lib/assets/pods.ts +148 -0
  63. package/src/lib/assets/rbac.ts +57 -0
  64. package/src/lib/assets/webhooks.ts +139 -0
  65. package/src/lib/assets/yaml.ts +75 -0
  66. package/src/lib/capability.ts +54 -44
  67. package/src/lib/controller.ts +171 -89
  68. package/src/lib/fetch.ts +1 -1
  69. package/src/lib/filter.ts +1 -3
  70. package/src/lib/k8s/index.ts +4 -1
  71. package/src/lib/k8s/kinds.ts +40 -0
  72. package/src/lib/k8s/types.ts +16 -14
  73. package/src/lib/k8s/upstream.ts +5 -1
  74. package/src/lib/logger.ts +14 -125
  75. package/src/lib/metrics.ts +67 -23
  76. package/src/lib/module.ts +13 -11
  77. package/src/lib/{processor.ts → mutate-processor.ts} +37 -28
  78. package/src/lib/{request.ts → mutate-request.ts} +4 -4
  79. package/src/lib/types.ts +51 -51
  80. package/src/lib/utils.ts +9 -7
  81. package/src/lib/validate-processor.ts +68 -0
  82. package/src/lib/validate-request.ts +94 -0
  83. package/src/lib.ts +4 -2
  84. package/src/runtime/controller.ts +1 -1
  85. package/dist/lib/k8s/webhook.d.ts +0 -37
  86. package/dist/lib/k8s/webhook.d.ts.map +0 -1
  87. package/dist/lib/processor.d.ts +0 -5
  88. package/dist/lib/processor.d.ts.map +0 -1
  89. package/dist/lib/request.d.ts.map +0 -1
  90. package/src/lib/k8s/webhook.ts +0 -643
@@ -1,7 +1,9 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
- import { V1ListMeta, V1ObjectMeta } from "@kubernetes/client-node";
4
+ import { KubernetesListObject, KubernetesObject, V1ObjectMeta } from "@kubernetes/client-node";
5
+
6
+ export { KubernetesListObject, KubernetesObject };
5
7
 
6
8
  export enum Operation {
7
9
  CREATE = "CREATE",
@@ -10,18 +12,6 @@ export enum Operation {
10
12
  CONNECT = "CONNECT",
11
13
  }
12
14
 
13
- export interface KubernetesObject {
14
- apiVersion?: string;
15
- kind?: string;
16
- metadata?: V1ObjectMeta;
17
- }
18
- export interface KubernetesListObject<T extends KubernetesObject> {
19
- apiVersion?: string;
20
- kind?: string;
21
- metadata?: V1ListMeta;
22
- items: T[];
23
- }
24
-
25
15
  /**
26
16
  * GenericKind is a generic Kubernetes object that can be used to represent any Kubernetes object
27
17
  * that is not explicitly supported by Pepr. This can be used on its own or as a base class for
@@ -138,7 +128,7 @@ export interface Request<T = KubernetesObject> {
138
128
  readonly options?: any;
139
129
  }
140
130
 
141
- export interface Response {
131
+ export interface MutateResponse {
142
132
  /** UID is an identifier for the individual request/response. This must be copied over from the corresponding AdmissionRequest. */
143
133
  uid: string;
144
134
 
@@ -163,6 +153,18 @@ export interface Response {
163
153
  warnings?: string[];
164
154
  }
165
155
 
156
+ export interface ValidateResponse extends MutateResponse {
157
+ /** Status contains extra details into why an admission request was denied. This field IS NOT consulted in any way if "Allowed" is "true". */
158
+ status?: {
159
+ /** A machine-readable description of why this operation is in the
160
+ "Failure" status. If this value is empty there is no information available. */
161
+ code: number;
162
+
163
+ /** A human-readable description of the status of this operation. */
164
+ message: string;
165
+ };
166
+ }
167
+
166
168
  export type WebhookIgnore = {
167
169
  /**
168
170
  * List of Kubernetes namespaces to always ignore.
@@ -1,10 +1,12 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
- /** a is a collection of K8s types to be used within a CapabilityAction: `When(a.Configmap)` */
4
+ /** a is a collection of K8s types to be used within an action: `When(a.Configmap)` */
5
5
  export {
6
6
  V1APIService as APIService,
7
7
  V1CertificateSigningRequest as CertificateSigningRequest,
8
+ V1ClusterRole as ClusterRole,
9
+ V1ClusterRoleBinding as ClusterRoleBinding,
8
10
  V1ConfigMap as ConfigMap,
9
11
  V1ControllerRevision as ControllerRevision,
10
12
  V1CronJob as CronJob,
@@ -32,6 +34,8 @@ export {
32
34
  V1ReplicaSet as ReplicaSet,
33
35
  V1ReplicationController as ReplicationController,
34
36
  V1ResourceQuota as ResourceQuota,
37
+ V1Role as Role,
38
+ V1RoleBinding as RoleBinding,
35
39
  V1RuntimeClass as RuntimeClass,
36
40
  V1Secret as Secret,
37
41
  V1SelfSubjectAccessReview as SelfSubjectAccessReview,
package/src/lib/logger.ts CHANGED
@@ -1,136 +1,25 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
- /**
5
- * Enumeration representing different logging levels.
6
- */
7
- export enum LogLevel {
8
- debug = 0,
9
- info = 1,
10
- warn = 2,
11
- error = 3,
12
- }
13
-
14
- enum ConsoleColors {
15
- Reset = "\x1b[0m",
16
- Bright = "\x1b[1m",
17
- Dim = "\x1b[2m",
18
- Underscore = "\x1b[4m",
19
- Blink = "\x1b[5m",
20
- Reverse = "\x1b[7m",
21
- Hidden = "\x1b[8m",
22
-
23
- FgBlack = "\x1b[30m",
24
- FgRed = "\x1b[31m",
25
- FgGreen = "\x1b[32m",
26
- FgYellow = "\x1b[33m",
27
- FgBlue = "\x1b[34m",
28
- FgMagenta = "\x1b[35m",
29
- FgCyan = "\x1b[36m",
30
- FgWhite = "\x1b[37m",
31
-
32
- BgBlack = "\x1b[40m",
33
- BgRed = "\x1b[41m",
34
- BgGreen = "\x1b[42m",
35
- BgYellow = "\x1b[43m",
36
- BgBlue = "\x1b[44m",
37
- BgMagenta = "\x1b[45m",
38
- BgCyan = "\x1b[46m",
39
- BgWhite = "\x1b[47m",
40
- }
41
-
42
- /**
43
- * Simple logger class that logs messages at different log levels.
44
- */
45
- export class Logger {
46
- private _logLevel: LogLevel;
47
-
48
- /**
49
- * Create a new logger instance.
50
- * @param logLevel - The minimum log level to log messages for.
51
- */
52
- constructor(logLevel: LogLevel) {
53
- this._logLevel = logLevel;
54
- }
55
-
56
- /**
57
- * Change the log level of the logger.
58
- * @param logLevel - The log level to log the message at.
59
- */
60
- public SetLogLevel(logLevel: string): void {
61
- this._logLevel = LogLevel[logLevel as keyof typeof LogLevel];
62
- this.debug(`Log level set to ${logLevel}`);
63
- }
4
+ import { pino } from "pino";
64
5
 
65
- /**
66
- * Log a debug message.
67
- * @param message - The message to log.
68
- */
69
- public debug<T>(message: T, prefix?: string): void {
70
- this.log(LogLevel.debug, message, prefix);
71
- }
6
+ const isPrettyLog = process.env.PEPR_PRETTY_LOGS === "true";
72
7
 
73
- /**
74
- * Log an info message.
75
- * @param message - The message to log.
76
- */
77
- public info<T>(message: T, prefix?: string): void {
78
- this.log(LogLevel.info, message, prefix);
79
- }
8
+ const pretty = {
9
+ target: "pino-pretty",
10
+ options: {
11
+ colorize: true,
12
+ },
13
+ };
80
14
 
81
- /**
82
- * Log a warning message.
83
- * @param message - The message to log.
84
- */
85
- public warn<T>(message: T, prefix?: string): void {
86
- this.log(LogLevel.warn, message, prefix);
87
- }
15
+ const transport = isPrettyLog ? pretty : undefined;
88
16
 
89
- /**
90
- * Log an error message.
91
- * @param message - The message to log.
92
- */
93
- public error<T>(message: T, prefix?: string): void {
94
- this.log(LogLevel.error, message, prefix);
95
- }
17
+ const Log = pino({
18
+ transport,
19
+ });
96
20
 
97
- /**
98
- * Log a message at the specified log level.
99
- * @param logLevel - The log level of the message.
100
- * @param message - The message to log.
101
- */
102
- private log<T>(logLevel: LogLevel, message: T, callerPrefix = ""): void {
103
- const color = {
104
- [LogLevel.debug]: ConsoleColors.FgBlack,
105
- [LogLevel.info]: ConsoleColors.FgCyan,
106
- [LogLevel.warn]: ConsoleColors.FgYellow,
107
- [LogLevel.error]: ConsoleColors.FgRed,
108
- };
109
-
110
- if (logLevel >= this._logLevel) {
111
- // Prefix the message with the colored log level.
112
- let prefix = "[" + LogLevel[logLevel] + "]\t" + callerPrefix;
113
-
114
- prefix = this.colorize(prefix, color[logLevel]);
115
-
116
- // If the message is not a string, use the debug method to log the object.
117
- if (typeof message !== "string") {
118
- console.log(prefix);
119
- console.debug("%o", message);
120
- } else {
121
- console.log(prefix + "\t" + message);
122
- }
123
- }
124
- }
125
-
126
- private colorize(text: string, color: ConsoleColors): string {
127
- return color + text + ConsoleColors.Reset;
128
- }
129
- }
130
-
131
- /** Log is an instance of Logger used to generate log entries. */
132
- const Log = new Logger(LogLevel.info);
133
21
  if (process.env.LOG_LEVEL) {
134
- Log.SetLogLevel(process.env.LOG_LEVEL);
22
+ Log.level = process.env.LOG_LEVEL;
135
23
  }
24
+
136
25
  export default Log;
@@ -1,56 +1,99 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
- import promClient from "prom-client";
4
+ import promClient, { Counter, Summary, Registry } from "prom-client";
5
5
  import { performance } from "perf_hooks";
6
+ import Log from "./logger";
7
+
8
+ const loggingPrefix = "MetricsCollector";
9
+
10
+ interface MetricNames {
11
+ errors: string;
12
+ alerts: string;
13
+ mutate: string;
14
+ validate: string;
15
+ }
16
+
17
+ interface MetricArgs {
18
+ name: string;
19
+ help: string;
20
+ registers: Registry[];
21
+ }
6
22
 
7
23
  /**
8
24
  * MetricsCollector class handles metrics collection using prom-client and performance hooks.
9
25
  */
10
26
  export class MetricsCollector {
11
- private _registry: promClient.Registry;
12
- private _errors: promClient.Counter<string>;
13
- private _alerts: promClient.Counter<string>;
14
- private _summary: promClient.Summary<string>;
27
+ private _registry: Registry;
28
+ private _counters: Map<string, Counter<string>> = new Map();
29
+ private _summaries: Map<string, Summary<string>> = new Map();
30
+ private _prefix: string;
31
+
32
+ private _metricNames: MetricNames = {
33
+ errors: "errors",
34
+ alerts: "alerts",
35
+ mutate: "Mutate",
36
+ validate: "Validate",
37
+ };
15
38
 
16
39
  /**
17
40
  * Creates a MetricsCollector instance with prefixed metrics.
18
41
  * @param {string} [prefix='pepr'] - The prefix for the metric names.
19
42
  */
20
43
  constructor(prefix = "pepr") {
21
- this._registry = new promClient.Registry();
44
+ this._registry = new Registry();
45
+ this._prefix = prefix;
46
+ this.addCounter(this._metricNames.errors, "Mutation/Validate errors encountered");
47
+ this.addCounter(this._metricNames.alerts, "Mutation/Validate bad api token received");
48
+ this.addSummary(this._metricNames.mutate, "Mutation operation summary");
49
+ this.addSummary(this._metricNames.validate, "Validation operation summary");
50
+ }
51
+ private getMetricName(name: string) {
52
+ return `${this._prefix}_${name}`;
53
+ }
22
54
 
23
- this._errors = new promClient.Counter({
24
- name: `${prefix}_errors`,
25
- help: "error counter",
55
+ private addMetric<T extends Counter<string> | Summary<string>>(
56
+ collection: Map<string, T>,
57
+ MetricType: new (args: MetricArgs) => T,
58
+ name: string,
59
+ help: string,
60
+ ) {
61
+ if (collection.has(this.getMetricName(name))) {
62
+ Log.debug(`Metric for ${name} already exists`, loggingPrefix);
63
+ return;
64
+ }
65
+ const metric = new MetricType({
66
+ name: this.getMetricName(name),
67
+ help,
26
68
  registers: [this._registry],
27
69
  });
70
+ collection.set(this.getMetricName(name), metric);
71
+ }
28
72
 
29
- this._alerts = new promClient.Counter({
30
- name: `${prefix}_alerts`,
31
- help: "alerts counter",
32
- registers: [this._registry],
33
- });
73
+ addCounter(name: string, help: string) {
74
+ this.addMetric(this._counters, promClient.Counter, name, help);
75
+ }
34
76
 
35
- this._summary = new promClient.Summary({
36
- name: `${prefix}_summary`,
37
- help: "summary",
38
- registers: [this._registry],
39
- });
77
+ addSummary(name: string, help: string) {
78
+ this.addMetric(this._summaries, promClient.Summary, name, help);
79
+ }
80
+
81
+ incCounter(name: string) {
82
+ this._counters.get(this.getMetricName(name))?.inc();
40
83
  }
41
84
 
42
85
  /**
43
86
  * Increments the error counter.
44
87
  */
45
88
  error() {
46
- this._errors.inc();
89
+ this.incCounter(this._metricNames.errors);
47
90
  }
48
91
 
49
92
  /**
50
93
  * Increments the alerts counter.
51
94
  */
52
95
  alert() {
53
- this._alerts.inc();
96
+ this.incCounter(this._metricNames.alerts);
54
97
  }
55
98
 
56
99
  /**
@@ -64,9 +107,10 @@ export class MetricsCollector {
64
107
  /**
65
108
  * Observes the duration since the provided start time and updates the summary.
66
109
  * @param {number} startTime - The start time.
110
+ * @param {string} name - The metrics summary to increment.
67
111
  */
68
- observeEnd(startTime: number) {
69
- this._summary.observe(performance.now() - startTime);
112
+ observeEnd(startTime: number, name: string = this._metricNames.mutate) {
113
+ this._summaries.get(this.getMetricName(name))?.observe(performance.now() - startTime);
70
114
  }
71
115
 
72
116
  /**
package/src/lib/module.ts CHANGED
@@ -1,18 +1,13 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
- import { concat, mergeDeepWith } from "ramda";
4
+ import { clone } from "ramda";
5
5
 
6
6
  import { Capability } from "./capability";
7
7
  import { Controller } from "./controller";
8
- import { Request, Response } from "./k8s/types";
8
+ import { MutateResponse, Request, ValidateResponse } from "./k8s/types";
9
9
  import { ModuleConfig } from "./types";
10
10
 
11
- const alwaysIgnore = {
12
- namespaces: ["kube-system", "pepr-system"],
13
- labels: [{ "pepr.dev": "ignore" }],
14
- };
15
-
16
11
  export type PackageJSON = {
17
12
  description: string;
18
13
  pepr: ModuleConfig;
@@ -25,7 +20,7 @@ export type PeprModuleOptions = {
25
20
  beforeHook?: (req: Request) => void;
26
21
 
27
22
  /** A user-defined callback to post-process or intercept a Pepr response just before it is returned to K8s */
28
- afterHook?: (res: Response) => void;
23
+ afterHook?: (res: MutateResponse | ValidateResponse) => void;
29
24
  };
30
25
 
31
26
  export class PeprModule {
@@ -39,12 +34,19 @@ export class PeprModule {
39
34
  * @param _deferStart (optional) If set to `true`, the Pepr runtime will not be started automatically. This can be used to start the Pepr runtime manually with `start()`.
40
35
  */
41
36
  constructor({ description, pepr }: PackageJSON, capabilities: Capability[] = [], opts: PeprModuleOptions = {}) {
42
- const config: ModuleConfig = mergeDeepWith(concat, pepr, alwaysIgnore);
37
+ const config: ModuleConfig = clone(pepr);
43
38
  config.description = description;
44
39
 
40
+ // Need to validate at runtime since TS gets sad about parsing the package.json
41
+ const validOnErrors = ["ignore", "warn", "fail"];
42
+ if (!validOnErrors.includes(config.onError || "")) {
43
+ throw new Error(`Invalid onErrors value: ${config.onError}`);
44
+ }
45
+
45
46
  // Handle build mode
46
- if (process.env.PEPR_MODE === "build") {
47
- process.send?.({ capabilities });
47
+ if (process.env.PEPR_MODE === "build" && process.send) {
48
+ // Send capability map to parent process
49
+ process.send(capabilities);
48
50
  return;
49
51
  }
50
52
 
@@ -5,28 +5,28 @@ import jsonPatch from "fast-json-patch";
5
5
 
6
6
  import { Capability } from "./capability";
7
7
  import { shouldSkipRequest } from "./filter";
8
- import { Request, Response } from "./k8s/types";
8
+ import { MutateResponse, Request } from "./k8s/types";
9
9
  import { Secret } from "./k8s/upstream";
10
10
  import Log from "./logger";
11
- import { PeprRequest } from "./request";
11
+ import { PeprMutateRequest } from "./mutate-request";
12
12
  import { ModuleConfig } from "./types";
13
- import { convertFromBase64Map, convertToBase64Map } from "./utils";
13
+ import { base64Encode, convertFromBase64Map, convertToBase64Map } from "./utils";
14
14
 
15
- export async function processor(
15
+ export async function mutateProcessor(
16
16
  config: ModuleConfig,
17
17
  capabilities: Capability[],
18
18
  req: Request,
19
- parentPrefix: string
20
- ): Promise<Response> {
21
- const wrapped = new PeprRequest(req);
22
- const response: Response = {
19
+ reqMetadata: Record<string, string>,
20
+ ): Promise<MutateResponse> {
21
+ const wrapped = new PeprMutateRequest(req);
22
+ const response: MutateResponse = {
23
23
  uid: req.uid,
24
24
  warnings: [],
25
25
  allowed: false,
26
26
  };
27
27
 
28
28
  // Track whether any capability matched the request
29
- let matchedCapabilityAction = false;
29
+ let matchedAction = false;
30
30
 
31
31
  // Track data fields that should be skipped during decoding
32
32
  let skipDecode: string[] = [];
@@ -37,21 +37,26 @@ export async function processor(
37
37
  skipDecode = convertFromBase64Map(wrapped.Raw as unknown as Secret);
38
38
  }
39
39
 
40
- Log.info(`Processing request`, parentPrefix);
40
+ Log.info(reqMetadata, `Processing request`);
41
41
 
42
42
  for (const { name, bindings } of capabilities) {
43
- const prefix = `${parentPrefix} ${name}:`;
43
+ const actionMetadata = { ...reqMetadata, name };
44
44
 
45
45
  for (const action of bindings) {
46
+ // Skip this action if it's not a mutate action
47
+ if (!action.mutateCallback) {
48
+ continue;
49
+ }
50
+
46
51
  // Continue to the next action without doing anything if this one should be skipped
47
52
  if (shouldSkipRequest(action, req)) {
48
53
  continue;
49
54
  }
50
55
 
51
- const label = action.callback.name;
52
- Log.info(`Processing matched action ${label}`, prefix);
56
+ const label = action.mutateCallback.name;
57
+ Log.info(actionMetadata, `Processing matched action ${label}`);
53
58
 
54
- matchedCapabilityAction = true;
59
+ matchedAction = true;
55
60
 
56
61
  // Add annotations to the request to indicate that the capability started processing
57
62
  // this will allow tracking of failed mutations that were permitted to continue
@@ -71,25 +76,29 @@ export async function processor(
71
76
 
72
77
  try {
73
78
  // Run the action
74
- await action.callback(wrapped);
79
+ await action.mutateCallback(wrapped);
75
80
 
76
- Log.info(`Action succeeded`, prefix);
81
+ Log.info(actionMetadata, `Action succeeded`);
77
82
 
78
83
  // Add annotations to the request to indicate that the capability succeeded
79
84
  updateStatus("succeeded");
80
85
  } catch (e) {
86
+ Log.warn(actionMetadata, `Action failed: ${e}`);
87
+ updateStatus("warning");
88
+
81
89
  // Annoying ts false positive
82
90
  response.warnings = response.warnings || [];
83
91
  response.warnings.push(`Action failed: ${e}`);
84
92
 
85
- // If errors are not allowed, note the failure in the Response
86
- if (config.onError) {
87
- Log.error(`Action failed: ${e}`, prefix);
88
- response.result = "Pepr module configured to reject on error";
89
- return response;
90
- } else {
91
- Log.warn(`Action failed: ${e}`, prefix);
92
- updateStatus("warning");
93
+ switch (config.onError) {
94
+ case "reject":
95
+ Log.error(actionMetadata, `Action failed: ${e}`);
96
+ response.result = "Pepr module configured to reject on error";
97
+ return response;
98
+
99
+ case "audit":
100
+ // @todo: implement audit logging
101
+ break;
93
102
  }
94
103
  }
95
104
  }
@@ -99,8 +108,8 @@ export async function processor(
99
108
  response.allowed = true;
100
109
 
101
110
  // If no capability matched the request, exit early
102
- if (!matchedCapabilityAction) {
103
- Log.info(`No matching capability action found`, parentPrefix);
111
+ if (!matchedAction) {
112
+ Log.info(reqMetadata, `No matching actions found`);
104
113
  return response;
105
114
  }
106
115
 
@@ -124,7 +133,7 @@ export async function processor(
124
133
  response.patchType = "JSONPatch";
125
134
  // Webhook must be base64-encoded
126
135
  // https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#response
127
- response.patch = Buffer.from(JSON.stringify(patches)).toString("base64");
136
+ response.patch = base64Encode(JSON.stringify(patches));
128
137
  }
129
138
 
130
139
  // Remove the warnings array if it's empty
@@ -132,7 +141,7 @@ export async function processor(
132
141
  delete response.warnings;
133
142
  }
134
143
 
135
- Log.debug(patches, parentPrefix);
144
+ Log.debug({ ...reqMetadata, patches }, `Patches generated`);
136
145
 
137
146
  return response;
138
147
  }
@@ -10,7 +10,7 @@ import { DeepPartial } from "./types";
10
10
  * The RequestWrapper class provides methods to modify Kubernetes objects in the context
11
11
  * of a mutating webhook request.
12
12
  */
13
- export class PeprRequest<T extends KubernetesObject> {
13
+ export class PeprMutateRequest<T extends KubernetesObject> {
14
14
  public Raw: T;
15
15
 
16
16
  get PermitSideEffects() {
@@ -42,7 +42,7 @@ export class PeprRequest<T extends KubernetesObject> {
42
42
  }
43
43
 
44
44
  /**
45
- * Creates a new instance of the Action class.
45
+ * Creates a new instance of the action class.
46
46
  * @param input - The request object containing the Kubernetes resource to modify.
47
47
  */
48
48
  constructor(private _input: Request<T>) {
@@ -72,7 +72,7 @@ export class PeprRequest<T extends KubernetesObject> {
72
72
  * Updates a label on the Kubernetes resource.
73
73
  * @param key - The key of the label to update.
74
74
  * @param value - The value of the label.
75
- * @returns The current Action instance for method chaining.
75
+ * @returns The current action instance for method chaining.
76
76
  */
77
77
  SetLabel(key: string, value: string) {
78
78
  const ref = this.Raw;
@@ -88,7 +88,7 @@ export class PeprRequest<T extends KubernetesObject> {
88
88
  * Updates an annotation on the Kubernetes resource.
89
89
  * @param key - The key of the annotation to update.
90
90
  * @param value - The value of the annotation.
91
- * @returns The current Action instance for method chaining.
91
+ * @returns The current action instance for method chaining.
92
92
  */
93
93
  SetAnnotation(key: string, value: string) {
94
94
  const ref = this.Raw;