pepr 0.13.4 → 0.14.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 (106) hide show
  1. package/README.md +25 -5
  2. package/commitlint.config.js +1 -0
  3. package/dist/cli.js +375 -204
  4. package/dist/controller.js +1 -1
  5. package/dist/lib/assets/deploy.d.ts.map +1 -1
  6. package/dist/lib/assets/destroy.d.ts +2 -0
  7. package/dist/lib/assets/destroy.d.ts.map +1 -0
  8. package/dist/lib/assets/index.d.ts +6 -5
  9. package/dist/lib/assets/index.d.ts.map +1 -1
  10. package/dist/lib/assets/networking.d.ts +6 -5
  11. package/dist/lib/assets/networking.d.ts.map +1 -1
  12. package/dist/lib/assets/pods.d.ts +84 -4
  13. package/dist/lib/assets/pods.d.ts.map +1 -1
  14. package/dist/lib/assets/rbac.d.ts +6 -4
  15. package/dist/lib/assets/rbac.d.ts.map +1 -1
  16. package/dist/lib/assets/store.d.ts +7 -0
  17. package/dist/lib/assets/store.d.ts.map +1 -0
  18. package/dist/lib/assets/webhooks.d.ts +2 -2
  19. package/dist/lib/assets/webhooks.d.ts.map +1 -1
  20. package/dist/lib/assets/yaml.d.ts.map +1 -1
  21. package/dist/lib/capability.d.ts +21 -4
  22. package/dist/lib/capability.d.ts.map +1 -1
  23. package/dist/lib/controller/index.d.ts +10 -0
  24. package/dist/lib/controller/index.d.ts.map +1 -0
  25. package/dist/lib/controller/store.d.ts +7 -0
  26. package/dist/lib/controller/store.d.ts.map +1 -0
  27. package/dist/lib/filter.d.ts +2 -2
  28. package/dist/lib/filter.d.ts.map +1 -1
  29. package/dist/lib/{k8s/types.d.ts → k8s.d.ts} +14 -25
  30. package/dist/lib/k8s.d.ts.map +1 -0
  31. package/dist/lib/metrics.d.ts +12 -12
  32. package/dist/lib/metrics.d.ts.map +1 -1
  33. package/dist/lib/module.d.ts +25 -4
  34. package/dist/lib/module.d.ts.map +1 -1
  35. package/dist/lib/mutate-processor.d.ts +3 -3
  36. package/dist/lib/mutate-processor.d.ts.map +1 -1
  37. package/dist/lib/mutate-request.d.ts +11 -10
  38. package/dist/lib/mutate-request.d.ts.map +1 -1
  39. package/dist/lib/storage.d.ts +56 -0
  40. package/dist/lib/storage.d.ts.map +1 -0
  41. package/dist/lib/tls.d.ts.map +1 -0
  42. package/dist/lib/types.d.ts +28 -48
  43. package/dist/lib/types.d.ts.map +1 -1
  44. package/dist/lib/validate-processor.d.ts +2 -2
  45. package/dist/lib/validate-processor.d.ts.map +1 -1
  46. package/dist/lib/validate-request.d.ts +9 -8
  47. package/dist/lib/validate-request.d.ts.map +1 -1
  48. package/dist/lib/watch-processor.d.ts +3 -0
  49. package/dist/lib/watch-processor.d.ts.map +1 -0
  50. package/dist/lib.d.ts +3 -7
  51. package/dist/lib.d.ts.map +1 -1
  52. package/dist/lib.js +484 -807
  53. package/dist/lib.js.map +4 -4
  54. package/package.json +20 -22
  55. package/src/lib/assets/deploy.ts +69 -127
  56. package/src/lib/assets/destroy.ts +33 -0
  57. package/src/lib/assets/index.ts +8 -14
  58. package/src/lib/assets/networking.ts +28 -5
  59. package/src/lib/assets/pods.ts +130 -11
  60. package/src/lib/assets/rbac.ts +42 -4
  61. package/src/lib/assets/store.ts +49 -0
  62. package/src/lib/assets/webhooks.ts +2 -2
  63. package/src/lib/assets/yaml.ts +13 -3
  64. package/src/lib/capability.ts +69 -14
  65. package/src/lib/{controller.ts → controller/index.ts} +25 -23
  66. package/src/lib/controller/store.ts +197 -0
  67. package/src/lib/filter.ts +2 -2
  68. package/src/lib/{k8s/types.ts → k8s.ts} +15 -26
  69. package/src/lib/metrics.ts +22 -38
  70. package/src/lib/module.ts +47 -10
  71. package/src/lib/mutate-processor.ts +6 -6
  72. package/src/lib/mutate-request.ts +18 -26
  73. package/src/lib/storage.ts +128 -0
  74. package/src/lib/types.ts +30 -53
  75. package/src/lib/validate-processor.ts +5 -4
  76. package/src/lib/validate-request.ts +15 -19
  77. package/src/lib/watch-processor.ts +55 -0
  78. package/src/lib.ts +4 -8
  79. package/src/templates/capabilities/hello-pepr.ts +54 -5
  80. package/src/templates/package.json +1 -0
  81. package/dist/lib/controller.d.ts +0 -10
  82. package/dist/lib/controller.d.ts.map +0 -1
  83. package/dist/lib/fetch.d.ts +0 -23
  84. package/dist/lib/fetch.d.ts.map +0 -1
  85. package/dist/lib/k8s/index.d.ts +0 -7
  86. package/dist/lib/k8s/index.d.ts.map +0 -1
  87. package/dist/lib/k8s/kinds.d.ts +0 -12
  88. package/dist/lib/k8s/kinds.d.ts.map +0 -1
  89. package/dist/lib/k8s/tls.d.ts.map +0 -1
  90. package/dist/lib/k8s/types.d.ts.map +0 -1
  91. package/dist/lib/k8s/upstream.d.ts +0 -4
  92. package/dist/lib/k8s/upstream.d.ts.map +0 -1
  93. package/jest.config.json +0 -4
  94. package/journey/before.ts +0 -21
  95. package/journey/k8s.ts +0 -100
  96. package/journey/pepr-build.ts +0 -69
  97. package/journey/pepr-deploy.ts +0 -174
  98. package/journey/pepr-dev.ts +0 -155
  99. package/journey/pepr-format.ts +0 -13
  100. package/journey/pepr-init.ts +0 -12
  101. package/src/lib/fetch.ts +0 -76
  102. package/src/lib/k8s/index.ts +0 -14
  103. package/src/lib/k8s/kinds.ts +0 -531
  104. package/src/lib/k8s/upstream.ts +0 -53
  105. /package/dist/lib/{k8s/tls.d.ts → tls.d.ts} +0 -0
  106. /package/src/lib/{k8s/tls.ts → tls.ts} +0 -0
@@ -1,174 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
-
4
- import { describe, expect, it } from "@jest/globals";
5
- import { execSync, spawnSync } from "child_process";
6
- import { resolve } from "path";
7
-
8
- import { cwd } from "./entrypoint.test";
9
- import {
10
- createOrReplaceConfigMap,
11
- deleteConfigMap,
12
- waitForConfigMap,
13
- waitForDeploymentReady,
14
- waitForNamespace,
15
- waitForSecret,
16
- } from "./k8s";
17
-
18
- export function peprDeploy() {
19
- it("should deploy the Pepr controller into the test cluster", async () => {
20
- execSync("npx pepr deploy -i pepr:dev --confirm", { cwd, stdio: "inherit" });
21
-
22
- // Wait for the deployment to be ready
23
- await waitForDeploymentReady("pepr-system", "pepr-static-test");
24
- });
25
-
26
- cleanupSamples();
27
-
28
- describe("should ignore resources not defined in the capability namespace", testIgnore);
29
-
30
- it("should perform validation of resources applied to the test cluster", testValidate);
31
-
32
- describe("should perform mutation of resources applied to the test cluster", testMutate);
33
-
34
- cleanupSamples();
35
- }
36
-
37
- function cleanupSamples() {
38
- try {
39
- // Remove the sample yaml for the HelloPepr capability
40
- execSync("kubectl delete -f hello-pepr.samples.yaml --ignore-not-found", {
41
- cwd: resolve(cwd, "capabilities"),
42
- stdio: "inherit",
43
- });
44
-
45
- deleteConfigMap("default", "example-1");
46
- deleteConfigMap("default", "example-evil-cm");
47
- } catch (e) {
48
- // Ignore errors
49
- }
50
- }
51
-
52
- function testIgnore() {
53
- it("should ignore resources not in the capability namespaces during mutation", async () => {
54
- const cm = await createOrReplaceConfigMap({
55
- apiVersion: "v1",
56
- kind: "ConfigMap",
57
- metadata: { name: "example-1", namespace: "default" },
58
- });
59
- expect(cm.metadata?.annotations?.["static-test.pepr.dev/hello-pepr"]).toBeUndefined();
60
- expect(cm.metadata?.annotations?.["pepr.dev"]).toBeUndefined();
61
- expect(cm.metadata?.labels?.["pepr"]).toBeUndefined();
62
- });
63
-
64
- it("should ignore resources not in the capability namespaces during validation", async () => {
65
- const cm = await createOrReplaceConfigMap({
66
- apiVersion: "v1",
67
- kind: "ConfigMap",
68
- metadata: {
69
- name: "example-evil-cm",
70
- namespace: "default",
71
- annotations: {
72
- evil: "true",
73
- },
74
- },
75
- });
76
- expect(cm.metadata?.annotations?.["static-test.pepr.dev/hello-pepr"]).toBeUndefined();
77
- expect(cm.metadata?.annotations?.["pepr.dev"]).toBeUndefined();
78
- expect(cm.metadata?.labels?.["pepr"]).toBeUndefined();
79
- });
80
- }
81
- async function testValidate() {
82
- // Apply the sample yaml for the HelloPepr capability
83
- const applyOut = spawnSync("kubectl apply -f hello-pepr.samples.yaml", {
84
- shell: true, // Run command in a shell
85
- encoding: "utf-8", // Encode result as string
86
- cwd: resolve(cwd, "capabilities"),
87
- });
88
-
89
- const { stderr, status } = applyOut;
90
-
91
- // Validation should return an error
92
- expect(status).toBe(1);
93
-
94
- // Check if the expected lines are in the output
95
- const expected = [
96
- `Error from server: error when creating "hello-pepr.samples.yaml": `,
97
- `admission webhook "pepr-static-test.pepr.dev" denied the request: `,
98
- `No evil CM annotations allowed.\n`,
99
- ].join("");
100
- expect(stderr).toMatch(expected);
101
- }
102
-
103
- function testMutate() {
104
- it("should mutate the namespace", async () => {
105
- // Wait for the namespace to be created
106
- const ns = await waitForNamespace("pepr-demo");
107
-
108
- // Check if the namespace has the correct labels and annotations
109
- expect(ns.metadata?.labels).toEqual({
110
- "keep-me": "please",
111
- "kubernetes.io/metadata.name": "pepr-demo",
112
- });
113
- expect(ns.metadata?.annotations?.["static-test.pepr.dev/hello-pepr"]).toBe("succeeded");
114
- });
115
-
116
- it("should mutate example-1", async () => {
117
- const cm1 = await waitForConfigMap("pepr-demo", "example-1");
118
- expect(cm1.metadata?.annotations?.["static-test.pepr.dev/hello-pepr"]).toBe("succeeded");
119
- expect(cm1.metadata?.annotations?.["pepr.dev"]).toBe("annotations-work-too");
120
- expect(cm1.metadata?.labels?.["pepr"]).toBe("was-here");
121
- });
122
-
123
- it("should mutate example-2", async () => {
124
- const cm2 = await waitForConfigMap("pepr-demo", "example-2");
125
- expect(cm2.metadata?.annotations?.["static-test.pepr.dev/hello-pepr"]).toBe("succeeded");
126
- expect(cm2.metadata?.annotations?.["pepr.dev"]).toBe("annotations-work-too");
127
- expect(cm2.metadata?.labels?.["pepr"]).toBe("was-here");
128
- });
129
-
130
- it("should mutate example-3", async () => {
131
- const cm3 = await waitForConfigMap("pepr-demo", "example-3");
132
-
133
- expect(cm3.metadata?.annotations?.["static-test.pepr.dev/hello-pepr"]).toBe("succeeded");
134
- expect(cm3.metadata?.annotations?.["pepr.dev"]).toBe("making-waves");
135
- expect(cm3.data).toEqual({ key: "ex-3-val", username: "system:admin" });
136
- });
137
-
138
- it("should mutate example-4", async () => {
139
- const cm4 = await waitForConfigMap("pepr-demo", "example-4");
140
- expect(cm4.metadata?.annotations?.["static-test.pepr.dev/hello-pepr"]).toBe("succeeded");
141
- expect(cm4.metadata?.labels?.["pepr.dev/first"]).toBe("true");
142
- expect(cm4.metadata?.labels?.["pepr.dev/second"]).toBe("true");
143
- expect(cm4.metadata?.labels?.["pepr.dev/third"]).toBe("true");
144
- });
145
-
146
- it("should mutate example-4a", async () => {
147
- const cm4a = await waitForConfigMap("pepr-demo-2", "example-4a");
148
- expect(cm4a.metadata?.annotations?.["static-test.pepr.dev/hello-pepr"]).toBe("succeeded");
149
- expect(cm4a.metadata?.labels?.["pepr.dev/first"]).toBe("true");
150
- expect(cm4a.metadata?.labels?.["pepr.dev/second"]).toBe("true");
151
- expect(cm4a.metadata?.labels?.["pepr.dev/third"]).toBe("true");
152
- });
153
-
154
- it("should mutate example-5", async () => {
155
- const cm5 = await waitForConfigMap("pepr-demo", "example-5");
156
-
157
- expect(cm5.metadata?.annotations?.["static-test.pepr.dev/hello-pepr"]).toBe("succeeded");
158
- expect(cm5.data?.["chuck-says"]).toBeTruthy();
159
- });
160
-
161
- it("should mutate secret-1", async () => {
162
- const s1 = await waitForSecret("pepr-demo", "secret-1");
163
-
164
- expect(s1.metadata?.annotations?.["static-test.pepr.dev/hello-pepr"]).toBe("succeeded");
165
- expect(s1.data?.["example"]).toBe("dW5pY29ybiBtYWdpYyAtIG1vZGlmaWVkIGJ5IFBlcHI=");
166
- expect(s1.data?.["magic"]).toBe("Y2hhbmdlLXdpdGhvdXQtZW5jb2Rpbmc=");
167
- expect(s1.data?.["binary-data"]).toBe(
168
- "iCZQUg8xYucNUqD+8lyl2YcKjYYygvTtiDSEV9b9WKUkxSSLFJTgIWMJ9GcFFYs4T9JCdda51u74jfq8yHzRuEASl60EdTS/NfWgIIFTGqcNRfqMw+vgpyTMmCyJVaJEDFq6AA==",
169
- );
170
- expect(s1.data?.["ascii-with-white-space"]).toBe(
171
- "VGhpcyBpcyBzb21lIHJhbmRvbSB0ZXh0OgoKICAgIC0gd2l0aCBsaW5lIGJyZWFrcwogICAgLSBhbmQgdGFicw==",
172
- );
173
- });
174
- }
@@ -1,155 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
-
4
- import { afterAll, expect, it } from "@jest/globals";
5
- import { KubeConfig } from "@kubernetes/client-node";
6
- import { ChildProcessWithoutNullStreams, spawn } from "child_process";
7
- import { Agent } from "https";
8
- import { RequestInit } from "node-fetch";
9
-
10
- import { fetch } from "../src/lib/fetch";
11
- import { cwd } from "./entrypoint.test";
12
-
13
- const kc = new KubeConfig();
14
- kc.loadFromDefault();
15
-
16
- let expectedLines = [
17
- "Establishing connection to Kubernetes",
18
- "Capability hello-pepr registered",
19
- "Mutate Action configured for CREATE",
20
- "Validate Action configured for CREATE",
21
- "Server listening on port 3000",
22
- ];
23
-
24
- export function peprDev() {
25
- let cmd: ChildProcessWithoutNullStreams;
26
- let success = false;
27
-
28
- it("should start the Pepr dev server", () => {
29
- cmd = spawn("npx", ["pepr", "dev", "--confirm"], { cwd });
30
-
31
- // This command should not exit on its own
32
- cmd.on("close", code => {
33
- if (!success) {
34
- throw new Error(`Command exited with code ${code}`);
35
- }
36
- });
37
-
38
- // Log stderr
39
- cmd.stderr.on("data", data => {
40
- if (!success) {
41
- console.error(`stderr: ${data}`);
42
- }
43
- });
44
-
45
- // Reject on error
46
- cmd.on("error", error => {
47
- if (!success) {
48
- throw error;
49
- }
50
- });
51
- });
52
-
53
- it("should be properly configured by the test module", done => {
54
- cmd.stdout.on("data", (data: Buffer) => {
55
- if (success) {
56
- return;
57
- }
58
-
59
- // Convert buffer to string
60
- const strData = data.toString();
61
- console.log(strData);
62
-
63
- // Check if any expected lines are found
64
- expectedLines = expectedLines.filter(expectedLine => {
65
- // Check if the expected line is found in the output, ignoring whitespace
66
- return !strData.replace(/\s+/g, " ").includes(expectedLine);
67
- });
68
-
69
- // If all expected lines are found, resolve the promise
70
- if (expectedLines.length < 1) {
71
- // Abort all further processing
72
- success = true;
73
-
74
- // Finish the test
75
- done();
76
- }
77
- });
78
- });
79
-
80
- it("should protect the controller endpoint with an API token", async () => {
81
- await validateAPIKey();
82
- });
83
-
84
- it("should expose Prometheus metrics", async () => {
85
- const metrics = await validateMetrics();
86
- expect(metrics).toMatch("pepr_Validate");
87
- expect(metrics).toMatch("pepr_Mutate");
88
- expect(metrics).toMatch("pepr_errors");
89
- expect(metrics).toMatch("pepr_alerts");
90
- });
91
-
92
- afterAll(() => {
93
- // Close or destroy the streams
94
- if (cmd.stdin) {
95
- cmd.stdin.end();
96
- }
97
- if (cmd.stdout) {
98
- cmd.stdout.destroy();
99
- }
100
- if (cmd.stderr) {
101
- cmd.stderr.destroy();
102
- }
103
-
104
- // Remove the event listeners
105
- cmd.removeAllListeners("close");
106
- cmd.removeAllListeners("error");
107
-
108
- // Kill the process
109
- cmd.kill(9);
110
- });
111
- }
112
-
113
- async function validateAPIKey() {
114
- const base = "https://localhost:3000/mutate/";
115
-
116
- const fetchOpts: RequestInit = {
117
- agent: new Agent({
118
- // Avoid tls issues for self-signed certs
119
- rejectUnauthorized: false,
120
- }),
121
- method: "POST",
122
- };
123
-
124
- // Test api token validation
125
- const evilToken = await fetch(`${base}evil-token`, fetchOpts);
126
-
127
- // Test for empty api token
128
- const emptyToken = await fetch(base, fetchOpts);
129
-
130
- if (evilToken.status !== 401) {
131
- throw new Error("Expected evil token to return 401");
132
- }
133
-
134
- if (emptyToken.status !== 404) {
135
- throw new Error("Expected empty token to return 404");
136
- }
137
- }
138
-
139
- async function validateMetrics() {
140
- const metricsEndpoint = "https://localhost:3000/metrics";
141
-
142
- const fetchOpts: RequestInit = {
143
- agent: new Agent({
144
- // Avoid tls issues for self-signed certs
145
- rejectUnauthorized: false,
146
- }),
147
- };
148
- const metricsOk = await fetch<string>(metricsEndpoint, fetchOpts);
149
-
150
- if (metricsOk.status !== 200) {
151
- throw new Error("Expected metrics ok to return a 200");
152
- }
153
-
154
- return metricsOk.data;
155
- }
@@ -1,13 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
-
4
- import { it } from "@jest/globals";
5
- import { execSync } from "child_process";
6
-
7
- import { cwd } from "./entrypoint.test";
8
-
9
- export function peprFormat() {
10
- it("should format the Pepr project", () => {
11
- execSync("npx pepr format", { cwd, stdio: "inherit" });
12
- });
13
- }
@@ -1,12 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
-
4
- import { it } from "@jest/globals";
5
- import { execSync } from "child_process";
6
-
7
- export function peprInit() {
8
- it("should create a new Pepr project", () => {
9
- const peprAlias = "file:pepr-0.0.0-development.tgz";
10
- execSync(`TEST_MODE=true npx --yes ${peprAlias} init`, { stdio: "inherit" });
11
- });
12
- }
package/src/lib/fetch.ts DELETED
@@ -1,76 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
-
4
- import { StatusCodes } from "http-status-codes";
5
- import f, { FetchError, RequestInfo, RequestInit } from "node-fetch";
6
- import logger from "./logger";
7
-
8
- export const fetchRaw = f;
9
-
10
- export type FetchResponse<T> = {
11
- data: T;
12
- ok: boolean;
13
- status: number;
14
- statusText: string;
15
- };
16
-
17
- /**
18
- * Perform an async HTTP call and return the parsed JSON response, optionally
19
- * as a specific type.
20
- *
21
- * @example
22
- * ```ts
23
- * fetch<string[]>("https://example.com/api/foo");
24
- * ```
25
- *
26
- * @param url The URL or Request object to fetch
27
- * @param init Additional options for the request
28
- * @returns
29
- */
30
- export async function fetch<T>(url: URL | RequestInfo, init?: RequestInit): Promise<FetchResponse<T>> {
31
- let data = undefined as unknown as T;
32
- try {
33
- logger.debug(init, `Fetching ${url}`);
34
-
35
- const resp = await fetchRaw(url, init);
36
- const contentType = resp.headers.get("content-type") || "";
37
-
38
- if (resp.ok) {
39
- // Parse the response as JSON if the content type is JSON
40
- if (contentType.includes("application/json")) {
41
- data = await resp.json();
42
- } else {
43
- // Otherwise, return however the response was read
44
- data = (await resp.text()) as unknown as T;
45
- }
46
- }
47
-
48
- return {
49
- data,
50
- ok: resp.ok,
51
- status: resp.status,
52
- statusText: resp.statusText,
53
- };
54
- } catch (e) {
55
- if (e instanceof FetchError) {
56
- logger.debug(`Fetch failed: ${e instanceof Error ? e.message : e}`);
57
-
58
- // Parse the error code from the FetchError or default to 400 (Bad Request)
59
- const status = parseInt(e.code || "400");
60
-
61
- return {
62
- data,
63
- ok: false,
64
- status,
65
- statusText: e.message,
66
- };
67
- }
68
-
69
- return {
70
- data,
71
- ok: false,
72
- status: StatusCodes.BAD_REQUEST,
73
- statusText: "Unknown error",
74
- };
75
- }
76
- }
@@ -1,14 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
-
4
- // Export kinds as a single object
5
- import * as kind from "./upstream";
6
- /** a is a collection of K8s types to be used within a action: `When(a.Configmap)` */
7
- export { kind as a };
8
-
9
- export { modelToGroupVersionKind, gvkMap, RegisterKind } from "./kinds";
10
-
11
- // If the hostname is pepr-static-test-watcher-0, then we are running in watch mode
12
- export const isWatchMode = process.env.PEPR_WATCH_MODE === "true";
13
-
14
- export * from "./types";