pepr 0.13.0 → 0.13.2

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 (51) hide show
  1. package/dist/cli.js +54 -28
  2. package/dist/controller.js +1 -1
  3. package/dist/lib/assets/index.d.ts +5 -6
  4. package/dist/lib/assets/index.d.ts.map +1 -1
  5. package/dist/lib/assets/loader.d.ts +2 -8
  6. package/dist/lib/assets/loader.d.ts.map +1 -1
  7. package/dist/lib/capability.d.ts +4 -7
  8. package/dist/lib/capability.d.ts.map +1 -1
  9. package/dist/lib/controller.d.ts +3 -49
  10. package/dist/lib/controller.d.ts.map +1 -1
  11. package/dist/lib/errors.d.ts +12 -0
  12. package/dist/lib/errors.d.ts.map +1 -0
  13. package/dist/lib/k8s/types.d.ts +5 -1
  14. package/dist/lib/k8s/types.d.ts.map +1 -1
  15. package/dist/lib/metrics.d.ts +6 -12
  16. package/dist/lib/metrics.d.ts.map +1 -1
  17. package/dist/lib/module.d.ts +2 -2
  18. package/dist/lib/module.d.ts.map +1 -1
  19. package/dist/lib/mutate-processor.d.ts.map +1 -1
  20. package/dist/lib/mutate-request.d.ts +2 -2
  21. package/dist/lib/mutate-request.d.ts.map +1 -1
  22. package/dist/lib/types.d.ts +3 -9
  23. package/dist/lib/types.d.ts.map +1 -1
  24. package/dist/lib/validate-processor.d.ts.map +1 -1
  25. package/dist/lib/validate-request.d.ts +2 -2
  26. package/dist/lib/validate-request.d.ts.map +1 -1
  27. package/dist/lib.js +247 -191
  28. package/dist/lib.js.map +3 -3
  29. package/jest.config.json +4 -0
  30. package/journey/before.ts +21 -0
  31. package/journey/k8s.ts +81 -0
  32. package/journey/pepr-build.ts +69 -0
  33. package/journey/pepr-deploy.ts +133 -0
  34. package/journey/pepr-dev.ts +155 -0
  35. package/journey/pepr-format.ts +13 -0
  36. package/journey/pepr-init.ts +12 -0
  37. package/package.json +20 -21
  38. package/src/lib/assets/index.ts +15 -8
  39. package/src/lib/assets/loader.ts +4 -12
  40. package/src/lib/assets/webhooks.ts +2 -2
  41. package/src/lib/capability.ts +34 -32
  42. package/src/lib/controller.ts +111 -93
  43. package/src/lib/errors.ts +20 -0
  44. package/src/lib/k8s/types.ts +5 -1
  45. package/src/lib/metrics.ts +45 -32
  46. package/src/lib/module.ts +24 -10
  47. package/src/lib/mutate-processor.ts +5 -3
  48. package/src/lib/mutate-request.ts +22 -9
  49. package/src/lib/types.ts +4 -10
  50. package/src/lib/validate-processor.ts +8 -0
  51. package/src/lib/validate-request.ts +19 -7
package/src/lib/module.ts CHANGED
@@ -5,8 +5,9 @@ import { clone } from "ramda";
5
5
 
6
6
  import { Capability } from "./capability";
7
7
  import { Controller } from "./controller";
8
+ import { ValidateError } from "./errors";
8
9
  import { MutateResponse, Request, ValidateResponse } from "./k8s/types";
9
- import { ModuleConfig } from "./types";
10
+ import { CapabilityExport, ModuleConfig } from "./types";
10
11
 
11
12
  export type PackageJSON = {
12
13
  description: string;
@@ -24,33 +25,46 @@ export type PeprModuleOptions = {
24
25
  };
25
26
 
26
27
  export class PeprModule {
27
- private _controller!: Controller;
28
+ #controller!: Controller;
28
29
 
29
30
  /**
30
31
  * Create a new Pepr runtime
31
32
  *
32
33
  * @param config The configuration for the Pepr runtime
33
34
  * @param capabilities The capabilities to be loaded into the Pepr runtime
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()`.
35
+ * @param opts Options for the Pepr runtime
35
36
  */
36
37
  constructor({ description, pepr }: PackageJSON, capabilities: Capability[] = [], opts: PeprModuleOptions = {}) {
37
38
  const config: ModuleConfig = clone(pepr);
38
39
  config.description = description;
39
40
 
40
41
  // 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
- }
42
+ ValidateError(config.onError);
43
+
44
+ // Bind public methods
45
+ this.start = this.start.bind(this);
45
46
 
46
47
  // Handle build mode
47
48
  if (process.env.PEPR_MODE === "build" && process.send) {
49
+ const exportedCapabilities: CapabilityExport[] = [];
50
+
48
51
  // Send capability map to parent process
49
- process.send(capabilities);
52
+ for (const capability of capabilities) {
53
+ // Convert the capability to a capability config
54
+ exportedCapabilities.push({
55
+ name: capability.name,
56
+ description: capability.description,
57
+ namespaces: capability.namespaces,
58
+ bindings: capability.bindings,
59
+ });
60
+ }
61
+
62
+ process.send(exportedCapabilities);
63
+
50
64
  return;
51
65
  }
52
66
 
53
- this._controller = new Controller(config, capabilities, opts.beforeHook, opts.afterHook);
67
+ this.#controller = new Controller(config, capabilities, opts.beforeHook, opts.afterHook);
54
68
 
55
69
  // Stop processing if deferStart is set to true
56
70
  if (opts.deferStart) {
@@ -67,6 +81,6 @@ export class PeprModule {
67
81
  * @param port
68
82
  */
69
83
  start(port = 3000) {
70
- this._controller.startServer(port);
84
+ this.#controller.startServer(port);
71
85
  }
72
86
  }
@@ -4,6 +4,7 @@
4
4
  import jsonPatch from "fast-json-patch";
5
5
 
6
6
  import { Capability } from "./capability";
7
+ import { Errors } from "./errors";
7
8
  import { shouldSkipRequest } from "./filter";
8
9
  import { MutateResponse, Request } from "./k8s/types";
9
10
  import { Secret } from "./k8s/upstream";
@@ -91,13 +92,14 @@ export async function mutateProcessor(
91
92
  response.warnings.push(`Action failed: ${e}`);
92
93
 
93
94
  switch (config.onError) {
94
- case "reject":
95
+ case Errors.reject:
95
96
  Log.error(actionMetadata, `Action failed: ${e}`);
96
97
  response.result = "Pepr module configured to reject on error";
97
98
  return response;
98
99
 
99
- case "audit":
100
- // @todo: implement audit logging
100
+ case Errors.audit:
101
+ response.auditAnnotations = response.auditAnnotations || {};
102
+ response.auditAnnotations[Date.now()] = e;
101
103
  break;
102
104
  }
103
105
  }
@@ -11,10 +11,12 @@ import { DeepPartial } from "./types";
11
11
  * of a mutating webhook request.
12
12
  */
13
13
  export class PeprMutateRequest<T extends KubernetesObject> {
14
- public Raw: T;
14
+ Raw: T;
15
+
16
+ #input: Request<T>;
15
17
 
16
18
  get PermitSideEffects() {
17
- return !this._input.dryRun;
19
+ return !this.#input.dryRun;
18
20
  }
19
21
 
20
22
  /**
@@ -22,7 +24,7 @@ export class PeprMutateRequest<T extends KubernetesObject> {
22
24
  * @returns true if the request is a dry run, false otherwise.
23
25
  */
24
26
  get IsDryRun() {
25
- return this._input.dryRun;
27
+ return this.#input.dryRun;
26
28
  }
27
29
 
28
30
  /**
@@ -30,7 +32,7 @@ export class PeprMutateRequest<T extends KubernetesObject> {
30
32
  * @returns The old Kubernetes resource object or null if not available.
31
33
  */
32
34
  get OldResource() {
33
- return this._input.oldObject;
35
+ return this.#input.oldObject;
34
36
  }
35
37
 
36
38
  /**
@@ -38,20 +40,31 @@ export class PeprMutateRequest<T extends KubernetesObject> {
38
40
  * @returns The request object containing the Kubernetes resource.
39
41
  */
40
42
  get Request() {
41
- return this._input;
43
+ return this.#input;
42
44
  }
43
45
 
44
46
  /**
45
47
  * Creates a new instance of the action class.
46
48
  * @param input - The request object containing the Kubernetes resource to modify.
47
49
  */
48
- constructor(private _input: Request<T>) {
50
+ constructor(input: Request<T>) {
51
+ this.#input = input;
52
+
53
+ // Bind public methods
54
+ this.Merge = this.Merge.bind(this);
55
+ this.SetLabel = this.SetLabel.bind(this);
56
+ this.SetAnnotation = this.SetAnnotation.bind(this);
57
+ this.RemoveLabel = this.RemoveLabel.bind(this);
58
+ this.RemoveAnnotation = this.RemoveAnnotation.bind(this);
59
+ this.HasLabel = this.HasLabel.bind(this);
60
+ this.HasAnnotation = this.HasAnnotation.bind(this);
61
+
49
62
  // If this is a DELETE operation, use the oldObject instead
50
- if (_input.operation.toUpperCase() === Operation.DELETE) {
51
- this.Raw = clone(_input.oldObject as T);
63
+ if (input.operation.toUpperCase() === Operation.DELETE) {
64
+ this.Raw = clone(input.oldObject as T);
52
65
  } else {
53
66
  // Otherwise, use the incoming object
54
- this.Raw = clone(_input.object);
67
+ this.Raw = clone(input.object);
55
68
  }
56
69
 
57
70
  if (!this.Raw) {
package/src/lib/types.ts CHANGED
@@ -10,16 +10,6 @@ export type PackageJSON = {
10
10
  pepr: ModuleConfig;
11
11
  };
12
12
 
13
- /**
14
- * The phase of the Kubernetes admission webhook that the capability is registered for.
15
- *
16
- * Currently only `mutate` is supported.
17
- */
18
- export enum HookPhase {
19
- mutate = "mutate",
20
- validate = "validate",
21
- }
22
-
23
13
  /**
24
14
  * Recursively make all properties in T optional.
25
15
  */
@@ -54,6 +44,10 @@ export interface CapabilityCfg {
54
44
  namespaces?: string[];
55
45
  }
56
46
 
47
+ export interface CapabilityExport extends CapabilityCfg {
48
+ bindings: Binding[];
49
+ }
50
+
57
51
  export type ModuleSigning = {
58
52
  /**
59
53
  * Specifies the signing policy.
@@ -4,7 +4,9 @@
4
4
  import { Capability } from "./capability";
5
5
  import { shouldSkipRequest } from "./filter";
6
6
  import { Request, ValidateResponse } from "./k8s/types";
7
+ import { Secret } from "./k8s/upstream";
7
8
  import Log from "./logger";
9
+ import { convertFromBase64Map } from "./utils";
8
10
  import { PeprValidateRequest } from "./validate-request";
9
11
 
10
12
  export async function validateProcessor(
@@ -18,6 +20,12 @@ export async function validateProcessor(
18
20
  allowed: true, // Assume it's allowed until a validation check fails
19
21
  };
20
22
 
23
+ // If the resource is a secret, decode the data
24
+ const isSecret = req.kind.version == "v1" && req.kind.kind == "Secret";
25
+ if (isSecret) {
26
+ convertFromBase64Map(wrapped.Raw as unknown as Secret);
27
+ }
28
+
21
29
  Log.info(reqMetadata, `Processing validation request`);
22
30
 
23
31
  for (const { name, bindings } of capabilities) {
@@ -1,6 +1,8 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
+ /* eslint-disable class-methods-use-this */
5
+
4
6
  import { clone } from "ramda";
5
7
  import { KubernetesObject, Operation, Request } from "./k8s/types";
6
8
  import { ValidateResponse } from "./types";
@@ -10,14 +12,16 @@ import { ValidateResponse } from "./types";
10
12
  * of a mutating webhook request.
11
13
  */
12
14
  export class PeprValidateRequest<T extends KubernetesObject> {
13
- public Raw: T;
15
+ Raw: T;
16
+
17
+ #input: Request<T>;
14
18
 
15
19
  /**
16
20
  * Provides access to the old resource in the request if available.
17
21
  * @returns The old Kubernetes resource object or null if not available.
18
22
  */
19
23
  get OldResource() {
20
- return this._input.oldObject;
24
+ return this.#input.oldObject;
21
25
  }
22
26
 
23
27
  /**
@@ -25,20 +29,28 @@ export class PeprValidateRequest<T extends KubernetesObject> {
25
29
  * @returns The request object containing the Kubernetes resource.
26
30
  */
27
31
  get Request() {
28
- return this._input;
32
+ return this.#input;
29
33
  }
30
34
 
31
35
  /**
32
36
  * Creates a new instance of the Action class.
33
37
  * @param input - The request object containing the Kubernetes resource to modify.
34
38
  */
35
- constructor(protected _input: Request<T>) {
39
+ constructor(input: Request<T>) {
40
+ this.#input = input;
41
+
42
+ // Bind public methods to this instance
43
+ this.HasLabel = this.HasLabel.bind(this);
44
+ this.HasAnnotation = this.HasAnnotation.bind(this);
45
+ this.Approve = this.Approve.bind(this);
46
+ this.Deny = this.Deny.bind(this);
47
+
36
48
  // If this is a DELETE operation, use the oldObject instead
37
- if (_input.operation.toUpperCase() === Operation.DELETE) {
38
- this.Raw = clone(_input.oldObject as T);
49
+ if (input.operation.toUpperCase() === Operation.DELETE) {
50
+ this.Raw = clone(input.oldObject as T);
39
51
  } else {
40
52
  // Otherwise, use the incoming object
41
- this.Raw = clone(_input.object);
53
+ this.Raw = clone(input.object);
42
54
  }
43
55
 
44
56
  if (!this.Raw) {