pepr 0.33.0 → 0.34.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 (140) hide show
  1. package/README.md +2 -1
  2. package/dist/cli/banner.d.ts +2 -0
  3. package/dist/cli/banner.d.ts.map +1 -0
  4. package/dist/cli/build.d.ts +19 -0
  5. package/dist/cli/build.d.ts.map +1 -0
  6. package/dist/cli/deploy.d.ts +3 -0
  7. package/dist/cli/deploy.d.ts.map +1 -0
  8. package/dist/cli/dev.d.ts +3 -0
  9. package/dist/cli/dev.d.ts.map +1 -0
  10. package/dist/cli/format.d.ts +9 -0
  11. package/dist/cli/format.d.ts.map +1 -0
  12. package/dist/cli/init/index.d.ts +3 -0
  13. package/dist/cli/init/index.d.ts.map +1 -0
  14. package/dist/cli/init/templates.d.ts +196 -0
  15. package/dist/cli/init/templates.d.ts.map +1 -0
  16. package/dist/cli/init/utils.d.ts +21 -0
  17. package/dist/cli/init/utils.d.ts.map +1 -0
  18. package/dist/cli/init/utils.test.d.ts +2 -0
  19. package/dist/cli/init/utils.test.d.ts.map +1 -0
  20. package/dist/cli/init/walkthrough.d.ts +8 -0
  21. package/dist/cli/init/walkthrough.d.ts.map +1 -0
  22. package/dist/cli/init/walkthrough.test.d.ts +2 -0
  23. package/dist/cli/init/walkthrough.test.d.ts.map +1 -0
  24. package/dist/cli/kfc.d.ts +3 -0
  25. package/dist/cli/kfc.d.ts.map +1 -0
  26. package/dist/cli/monitor.d.ts +3 -0
  27. package/dist/cli/monitor.d.ts.map +1 -0
  28. package/dist/cli/root.d.ts +5 -0
  29. package/dist/cli/root.d.ts.map +1 -0
  30. package/dist/cli/update.d.ts +3 -0
  31. package/dist/cli/update.d.ts.map +1 -0
  32. package/dist/cli/uuid.d.ts +3 -0
  33. package/dist/cli/uuid.d.ts.map +1 -0
  34. package/dist/cli.js +68 -38
  35. package/dist/controller.js +1 -2
  36. package/dist/fixtures/loader.d.ts +5 -0
  37. package/dist/fixtures/loader.d.ts.map +1 -0
  38. package/dist/lib/assets/helm.d.ts +1 -0
  39. package/dist/lib/assets/helm.d.ts.map +1 -1
  40. package/dist/lib/assets/helm.test.d.ts +2 -0
  41. package/dist/lib/assets/helm.test.d.ts.map +1 -0
  42. package/dist/lib/assets/index.d.ts.map +1 -1
  43. package/dist/lib/assets/pods.d.ts +3 -0
  44. package/dist/lib/assets/pods.d.ts.map +1 -1
  45. package/dist/lib/assets/pods.test.d.ts +2 -0
  46. package/dist/lib/assets/pods.test.d.ts.map +1 -0
  47. package/dist/lib/assets/yaml.d.ts.map +1 -1
  48. package/dist/lib/errors.test.d.ts +2 -0
  49. package/dist/lib/errors.test.d.ts.map +1 -0
  50. package/dist/lib/filter.test.d.ts +2 -0
  51. package/dist/lib/filter.test.d.ts.map +1 -0
  52. package/dist/lib/helpers.d.ts +0 -5
  53. package/dist/lib/helpers.d.ts.map +1 -1
  54. package/dist/lib/helpers.test.d.ts +2 -0
  55. package/dist/lib/helpers.test.d.ts.map +1 -0
  56. package/dist/lib/included-files.test.d.ts +2 -0
  57. package/dist/lib/included-files.test.d.ts.map +1 -0
  58. package/dist/lib/logger.test.d.ts +2 -0
  59. package/dist/lib/logger.test.d.ts.map +1 -0
  60. package/dist/lib/metrics.d.ts +18 -0
  61. package/dist/lib/metrics.d.ts.map +1 -1
  62. package/dist/lib/metrics.test.d.ts +2 -0
  63. package/dist/lib/metrics.test.d.ts.map +1 -0
  64. package/dist/lib/module.test.d.ts +2 -0
  65. package/dist/lib/module.test.d.ts.map +1 -0
  66. package/dist/lib/mutate-request.test.d.ts +2 -0
  67. package/dist/lib/mutate-request.test.d.ts.map +1 -0
  68. package/dist/lib/queue.test.d.ts +2 -0
  69. package/dist/lib/queue.test.d.ts.map +1 -0
  70. package/dist/lib/schedule.test.d.ts +15 -0
  71. package/dist/lib/schedule.test.d.ts.map +1 -0
  72. package/dist/lib/storage.test.d.ts +2 -0
  73. package/dist/lib/storage.test.d.ts.map +1 -0
  74. package/dist/lib/tls.test.d.ts +2 -0
  75. package/dist/lib/tls.test.d.ts.map +1 -0
  76. package/dist/lib/utils.test.d.ts +2 -0
  77. package/dist/lib/utils.test.d.ts.map +1 -0
  78. package/dist/lib/validate-request.test.d.ts +2 -0
  79. package/dist/lib/validate-request.test.d.ts.map +1 -0
  80. package/dist/lib/watch-processor.d.ts.map +1 -1
  81. package/dist/lib/watch-processor.test.d.ts +2 -0
  82. package/dist/lib/watch-processor.test.d.ts.map +1 -0
  83. package/dist/lib.js +76 -20
  84. package/dist/lib.js.map +3 -3
  85. package/dist/sdk/sdk.test.d.ts +2 -0
  86. package/dist/sdk/sdk.test.d.ts.map +1 -0
  87. package/package.json +21 -15
  88. package/src/cli/banner.ts +63 -0
  89. package/src/cli/build.ts +370 -0
  90. package/src/cli/deploy.ts +105 -0
  91. package/src/cli/dev.ts +118 -0
  92. package/src/cli/format.ts +83 -0
  93. package/src/cli/init/index.ts +99 -0
  94. package/src/cli/init/templates.ts +124 -0
  95. package/src/cli/init/utils.test.ts +28 -0
  96. package/src/cli/init/utils.ts +55 -0
  97. package/src/cli/init/walkthrough.test.ts +21 -0
  98. package/src/cli/init/walkthrough.ts +96 -0
  99. package/src/cli/kfc.ts +45 -0
  100. package/src/cli/monitor.ts +101 -0
  101. package/src/cli/root.ts +12 -0
  102. package/src/cli/update.ts +95 -0
  103. package/src/cli/uuid.ts +44 -0
  104. package/src/fixtures/data/create-pod.json +271 -0
  105. package/src/fixtures/data/delete-pod.json +271 -0
  106. package/src/fixtures/loader.ts +18 -0
  107. package/src/lib/.prettierrc +14 -0
  108. package/src/lib/assets/helm.test.ts +64 -0
  109. package/src/lib/assets/helm.ts +35 -0
  110. package/src/lib/assets/index.ts +5 -1
  111. package/src/lib/assets/pods.test.ts +553 -0
  112. package/src/lib/assets/pods.ts +14 -6
  113. package/src/lib/assets/yaml.ts +15 -15
  114. package/src/lib/controller/index.ts +2 -2
  115. package/src/lib/errors.test.ts +85 -0
  116. package/src/lib/filter.test.ts +384 -0
  117. package/src/lib/helpers.test.ts +1192 -0
  118. package/src/lib/helpers.ts +0 -17
  119. package/src/lib/included-files.test.ts +22 -0
  120. package/src/lib/logger.test.ts +18 -0
  121. package/src/lib/metrics.test.ts +132 -0
  122. package/src/lib/metrics.ts +68 -6
  123. package/src/lib/module.test.ts +126 -0
  124. package/src/lib/mutate-request.test.ts +188 -0
  125. package/src/lib/queue.test.ts +58 -0
  126. package/src/lib/schedule.test.ts +217 -0
  127. package/src/lib/storage.test.ts +203 -0
  128. package/src/lib/tls.test.ts +18 -0
  129. package/src/lib/utils.test.ts +69 -0
  130. package/src/lib/validate-request.test.ts +124 -0
  131. package/src/lib/watch-processor.test.ts +322 -0
  132. package/src/lib/watch-processor.ts +20 -4
  133. package/src/sdk/sdk.test.ts +243 -0
  134. package/src/templates/.eslintrc.json +6 -0
  135. package/.prettierignore +0 -1
  136. package/CODE_OF_CONDUCT.md +0 -133
  137. package/SECURITY.md +0 -18
  138. package/SUPPORT.md +0 -16
  139. package/codecov.yaml +0 -19
  140. package/commitlint.config.js +0 -1
@@ -0,0 +1,99 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { execSync } from "child_process";
5
+ import { resolve } from "path";
6
+ import prompts from "prompts";
7
+
8
+ import { RootCmd } from "../root";
9
+ import {
10
+ codeSettings,
11
+ eslint,
12
+ genPeprTS,
13
+ genPkgJSON,
14
+ gitignore,
15
+ helloPepr,
16
+ prettier,
17
+ readme,
18
+ samplesYaml,
19
+ snippet,
20
+ tsConfig,
21
+ } from "./templates";
22
+ import { createDir, sanitizeName, write } from "./utils";
23
+ import { confirm, walkthrough } from "./walkthrough";
24
+
25
+ export default function (program: RootCmd) {
26
+ program
27
+ .command("init")
28
+ .description("Initialize a new Pepr Module")
29
+ // skip auto npm install and git init
30
+ .option("--skip-post-init", "Skip npm install, git init and VSCode launch")
31
+ .action(async opts => {
32
+ let pkgOverride = "";
33
+
34
+ // Overrides for testing. @todo: don't be so gross with Node CLI testing
35
+ if (process.env.TEST_MODE === "true") {
36
+ prompts.inject(["pepr-test-module", "A test module for Pepr", "ignore", "y"]);
37
+ pkgOverride = "file:../pepr-0.0.0-development.tgz";
38
+ }
39
+
40
+ const response = await walkthrough();
41
+ const dirName = sanitizeName(response.name);
42
+ const packageJSON = genPkgJSON(response, pkgOverride);
43
+ const peprTS = genPeprTS();
44
+
45
+ const confirmed = await confirm(dirName, packageJSON, peprTS.path);
46
+
47
+ if (confirmed) {
48
+ console.log("Creating new Pepr module...");
49
+
50
+ try {
51
+ await createDir(dirName);
52
+ await createDir(resolve(dirName, ".vscode"));
53
+ await createDir(resolve(dirName, "capabilities"));
54
+
55
+ await write(resolve(dirName, gitignore.path), gitignore.data);
56
+ await write(resolve(dirName, eslint.path), eslint.data);
57
+ await write(resolve(dirName, prettier.path), prettier.data);
58
+ await write(resolve(dirName, packageJSON.path), packageJSON.data);
59
+ await write(resolve(dirName, readme.path), readme.data);
60
+ await write(resolve(dirName, tsConfig.path), tsConfig.data);
61
+ await write(resolve(dirName, peprTS.path), peprTS.data);
62
+ await write(resolve(dirName, ".vscode", snippet.path), snippet.data);
63
+ await write(resolve(dirName, ".vscode", codeSettings.path), codeSettings.data);
64
+ await write(resolve(dirName, "capabilities", samplesYaml.path), samplesYaml.data);
65
+ await write(resolve(dirName, "capabilities", helloPepr.path), helloPepr.data);
66
+
67
+ if (!opts.skipPostInit) {
68
+ // run npm install from the new directory
69
+ process.chdir(dirName);
70
+ execSync("npm install", {
71
+ stdio: "inherit",
72
+ });
73
+
74
+ // setup git
75
+ execSync("git init", {
76
+ stdio: "inherit",
77
+ });
78
+
79
+ // try to open vscode
80
+ try {
81
+ execSync("code .", {
82
+ stdio: "inherit",
83
+ });
84
+ } catch (e) {
85
+ // vscode not found, do nothing
86
+ }
87
+ }
88
+
89
+ console.log(`New Pepr module created at ${dirName}`);
90
+ console.log(`Open VSCode or your editor of choice in ${dirName} to get started!`);
91
+ } catch (e) {
92
+ if (e instanceof Error) {
93
+ console.error(`Error creating Pepr module:`, e);
94
+ }
95
+ process.exit(1);
96
+ }
97
+ }
98
+ });
99
+ }
@@ -0,0 +1,124 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { dumpYaml } from "@kubernetes/client-node";
5
+ import { inspect } from "util";
6
+ import { v4 as uuidv4, v5 as uuidv5 } from "uuid";
7
+
8
+ import eslintJSON from "../../templates/.eslintrc.template.json";
9
+ import prettierJSON from "../../templates/.prettierrc.json";
10
+ import samplesJSON from "../../templates/capabilities/hello-pepr.samples.json";
11
+ import { gitIgnore, helloPeprTS, packageJSON, peprTS, readmeMd } from "../../templates/data.json";
12
+ import peprSnippetsJSON from "../../templates/pepr.code-snippets.json";
13
+ import settingsJSON from "../../templates/settings.json";
14
+ import tsConfigJSON from "../../templates/tsconfig.module.json";
15
+ import { sanitizeName } from "./utils";
16
+ import { InitOptions } from "./walkthrough";
17
+
18
+ export const { dependencies, devDependencies, peerDependencies, scripts, version } = packageJSON;
19
+
20
+ export function genPkgJSON(opts: InitOptions, pgkVerOverride?: string) {
21
+ // Generate a random UUID for the module based on the module name
22
+ const uuid = uuidv5(opts.name, uuidv4());
23
+ // Generate a name for the module based on the module name
24
+ const name = sanitizeName(opts.name);
25
+ // Make typescript a dev dependency
26
+ const { typescript } = peerDependencies;
27
+
28
+ const testEnv = {
29
+ MY_CUSTOM_VAR: "example-value",
30
+ ZARF_VAR: "###ZARF_VAR_THING###",
31
+ };
32
+
33
+ const data = {
34
+ name,
35
+ version: "0.0.1",
36
+ description: opts.description,
37
+ keywords: ["pepr", "k8s", "policy-engine", "pepr-module", "security"],
38
+ engines: {
39
+ node: ">=18.0.0",
40
+ },
41
+ pepr: {
42
+ uuid: pgkVerOverride ? "static-test" : uuid,
43
+ onError: opts.errorBehavior,
44
+ webhookTimeout: 10,
45
+ customLabels: {
46
+ namespace: {
47
+ "pepr.dev": "",
48
+ },
49
+ },
50
+ alwaysIgnore: {
51
+ namespaces: [],
52
+ },
53
+ includedFiles: [],
54
+ env: pgkVerOverride ? testEnv : {},
55
+ },
56
+ scripts: {
57
+ "k3d-setup": scripts["test:journey:k3d"],
58
+ },
59
+ dependencies: {
60
+ pepr: pgkVerOverride || version,
61
+ },
62
+ devDependencies: {
63
+ typescript,
64
+ },
65
+ };
66
+
67
+ return {
68
+ data,
69
+ path: "package.json",
70
+ print: inspect(data, false, 5, true),
71
+ };
72
+ }
73
+
74
+ export function genPeprTS() {
75
+ return {
76
+ path: "pepr.ts",
77
+ data: peprTS,
78
+ };
79
+ }
80
+
81
+ export const readme = {
82
+ path: "README.md",
83
+ data: readmeMd,
84
+ };
85
+
86
+ export const helloPepr = {
87
+ path: "hello-pepr.ts",
88
+ data: helloPeprTS,
89
+ };
90
+
91
+ export const gitignore = {
92
+ path: ".gitignore",
93
+ data: gitIgnore,
94
+ };
95
+
96
+ export const samplesYaml = {
97
+ path: "hello-pepr.samples.yaml",
98
+ data: samplesJSON.map(r => dumpYaml(r, { noRefs: true })).join("---\n"),
99
+ };
100
+
101
+ export const snippet = {
102
+ path: "pepr.code-snippets",
103
+ data: peprSnippetsJSON,
104
+ };
105
+
106
+ export const codeSettings = {
107
+ path: "settings.json",
108
+ data: settingsJSON,
109
+ };
110
+
111
+ export const tsConfig = {
112
+ path: "tsconfig.json",
113
+ data: tsConfigJSON,
114
+ };
115
+
116
+ export const prettier = {
117
+ path: ".prettierrc",
118
+ data: prettierJSON,
119
+ };
120
+
121
+ export const eslint = {
122
+ path: ".eslintrc.json",
123
+ data: eslintJSON,
124
+ };
@@ -0,0 +1,28 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { expect, test } from "@jest/globals";
5
+
6
+ import { sanitizeName } from "./utils";
7
+
8
+ test("sanitizeName() sanitizes names correctly", () => {
9
+ const cases = [
10
+ {
11
+ input: "My Test Module",
12
+ expected: "my-test-module",
13
+ },
14
+ {
15
+ input: "!! 123 @@ Module",
16
+ expected: "123-module",
17
+ },
18
+ {
19
+ input: "---Test-Module---",
20
+ expected: "test-module",
21
+ },
22
+ ];
23
+
24
+ for (const { input, expected } of cases) {
25
+ const result = sanitizeName(input);
26
+ expect(result).toBe(expected);
27
+ }
28
+ });
@@ -0,0 +1,55 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { promises as fs } from "fs";
5
+
6
+ /**
7
+ * Sanitize a user input name to be used as a pepr module directory name
8
+ *
9
+ * @param name the user input name
10
+ * @returns the sanitized name
11
+ */
12
+ export function sanitizeName(name: string) {
13
+ // Replace any characters outside of [^a-z0-9-] with "-"
14
+ let sanitized = name.toLowerCase().replace(/[^a-z0-9-]+/gi, "-");
15
+
16
+ // Remove any leading or trailing hyphens
17
+ sanitized = sanitized.replace(/^-+|-+$/g, "");
18
+
19
+ // Replace multiple hyphens with a single hyphen
20
+ sanitized = sanitized.replace(/--+/g, "-");
21
+
22
+ return sanitized;
23
+ }
24
+
25
+ /**
26
+ * Creates a directory and throws an error if it already exists
27
+ *
28
+ * @param dir - The directory to create
29
+ */
30
+ export async function createDir(dir: string) {
31
+ try {
32
+ await fs.mkdir(dir);
33
+ } catch (err) {
34
+ // The directory already exists
35
+ if (err && (err as NodeJS.ErrnoException).code === "EEXIST") {
36
+ throw new Error(`Directory ${dir} already exists`);
37
+ } else {
38
+ throw err;
39
+ }
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Write data to a file on disk
45
+ * @param path - The path to the file
46
+ * @param data - The data to write
47
+ * @returns A promise that resolves when the file has been written
48
+ */
49
+ export function write(path: string, data: unknown) {
50
+ // If the data is not a string, stringify it
51
+ if (typeof data !== "string") {
52
+ data = JSON.stringify(data, null, 2);
53
+ }
54
+ return fs.writeFile(path, data as string);
55
+ }
@@ -0,0 +1,21 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { expect, test } from "@jest/globals";
5
+ import prompts from "prompts";
6
+
7
+ import { walkthrough } from "./walkthrough";
8
+
9
+ test("walkthrough() returns expected results", async () => {
10
+ // Inject predefined answers for the prompts
11
+ prompts.inject(["My Test Module", "A test module for Pepr", 0]);
12
+
13
+ const result = await walkthrough();
14
+
15
+ // Check the returned object
16
+ expect(result).toEqual({
17
+ name: "My Test Module",
18
+ description: "A test module for Pepr",
19
+ errorBehavior: 0,
20
+ });
21
+ });
@@ -0,0 +1,96 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { promises as fs } from "fs";
5
+ import prompt, { Answers, PromptObject } from "prompts";
6
+
7
+ import { Errors } from "../../lib/errors";
8
+ import { eslint, gitignore, prettier, readme, tsConfig } from "./templates";
9
+ import { sanitizeName } from "./utils";
10
+
11
+ export type InitOptions = Answers<"name" | "description" | "errorBehavior">;
12
+
13
+ export function walkthrough(): Promise<InitOptions> {
14
+ const askName: PromptObject = {
15
+ type: "text",
16
+ name: "name",
17
+ message:
18
+ "Enter a name for the new Pepr module. This will create a new directory based on the name.\n",
19
+ validate: async val => {
20
+ try {
21
+ const name = sanitizeName(val);
22
+ await fs.access(name, fs.constants.F_OK);
23
+
24
+ return "A directory with this name already exists";
25
+ } catch (e) {
26
+ return val.length > 2 || "The name must be at least 3 characters long";
27
+ }
28
+ },
29
+ };
30
+
31
+ const askDescription: PromptObject = {
32
+ type: "text",
33
+ name: "description",
34
+ message: "(Recommended) Enter a description for the new Pepr module.\n",
35
+ };
36
+
37
+ const askErrorBehavior: PromptObject = {
38
+ type: "select",
39
+ name: "errorBehavior",
40
+ message: "How do you want Pepr to handle errors encountered during K8s operations?",
41
+ choices: [
42
+ {
43
+ title: "Reject the operation",
44
+ value: Errors.reject,
45
+ description:
46
+ "In the event that Pepr is down or other module errors occur, the operation will not be allowed to continue. (Recommended for production.)",
47
+ },
48
+ {
49
+ title: "Ignore",
50
+ value: Errors.ignore,
51
+ description:
52
+ "In the event that Pepr is down or other module errors occur, an entry will be generated in the Pepr Controller Log and the operation will be allowed to continue. (Recommended for development, not for production.)",
53
+ selected: true,
54
+ },
55
+ {
56
+ title: "Log an audit event",
57
+ value: Errors.audit,
58
+ description:
59
+ "Pepr will continue processing and generate an entry in the Pepr Controller log as well as an audit event in the cluster.",
60
+ },
61
+ ],
62
+ };
63
+
64
+ return prompt([askName, askDescription, askErrorBehavior]) as Promise<InitOptions>;
65
+ }
66
+
67
+ export async function confirm(
68
+ dirName: string,
69
+ packageJSON: { path: string; print: string },
70
+ peprTSPath: string,
71
+ ) {
72
+ console.log(`
73
+ To be generated:
74
+
75
+ \x1b[1m${dirName}\x1b[0m
76
+ ā”œā”€ā”€ \x1b[1m${eslint.path}\x1b[0m
77
+ ā”œā”€ā”€ \x1b[1m${gitignore.path}\x1b[0m
78
+ ā”œā”€ā”€ \x1b[1m${prettier.path}\x1b[0m
79
+ ā”œā”€ā”€ \x1b[1mcapabilties\x1b[0m
80
+ │ ā”œā”€ā”€ \x1b[1mhello-pepr.samples.yaml\x1b[0m
81
+ │ └── \x1b[1mhello-pepr.ts\x1b[0m
82
+ ā”œā”€ā”€ \x1b[1m${packageJSON.path}\x1b[0m
83
+ ${packageJSON.print.replace(/^/gm, " │ ")}
84
+ ā”œā”€ā”€ \x1b[1m${peprTSPath}\x1b[0m
85
+ ā”œā”€ā”€ \x1b[1m${readme.path}\x1b[0m
86
+ └── \x1b[1m${tsConfig.path}\x1b[0m
87
+ `);
88
+
89
+ const confirm = await prompt({
90
+ type: "confirm",
91
+ name: "confirm",
92
+ message: "Create the new Pepr module?",
93
+ });
94
+
95
+ return confirm.confirm;
96
+ }
package/src/cli/kfc.ts ADDED
@@ -0,0 +1,45 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { execSync } from "child_process";
5
+ import prompt from "prompts";
6
+
7
+ import { RootCmd } from "./root";
8
+
9
+ export default function (program: RootCmd) {
10
+ program
11
+ .command("kfc [args...]")
12
+ .description("Execute Kubernetes Fluent Client commands")
13
+ .action(async (args: string[]) => {
14
+ const { confirm } = await prompt({
15
+ type: "confirm",
16
+ name: "confirm",
17
+ message:
18
+ "For commands that generate files, this may overwrite any previously generated files.\n" +
19
+ "Are you sure you want to continue?",
20
+ });
21
+
22
+ // If the user doesn't confirm, exit
23
+ if (!confirm) {
24
+ return;
25
+ }
26
+
27
+ try {
28
+ // If the user doesn't provide any kfc arguments, show the kfc help
29
+ if (args.length === 0) {
30
+ args.push("--help");
31
+ }
32
+
33
+ // Join the args array into a space-separated string
34
+ const argsString = args.join(" ");
35
+
36
+ // Create the CRD generated class
37
+ execSync(`kubernetes-fluent-client ${argsString}`, {
38
+ stdio: "inherit",
39
+ });
40
+ } catch (e) {
41
+ console.error(`Error creating CRD generated class:`, e);
42
+ process.exit(1);
43
+ }
44
+ });
45
+ }
@@ -0,0 +1,101 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { Log as K8sLog, KubeConfig } from "@kubernetes/client-node";
5
+ import { K8s, kind } from "kubernetes-fluent-client";
6
+ import stream from "stream";
7
+ import { ResponseItem } from "../lib/types";
8
+ import { RootCmd } from "./root";
9
+
10
+ export default function (program: RootCmd) {
11
+ program
12
+ .command("monitor [module-uuid]")
13
+ .description("Monitor a Pepr Module")
14
+ .action(async uuid => {
15
+ let labels: string[];
16
+ let errorMessage: string;
17
+
18
+ if (!uuid) {
19
+ labels = ["pepr.dev/controller", "admission"];
20
+ errorMessage = `No pods found with admission labels`;
21
+ } else {
22
+ labels = ["app", `pepr-${uuid}`];
23
+ errorMessage = `No pods found for module ${uuid}`;
24
+ }
25
+
26
+ // Get the logs for the `app=pepr-${module}` or `pepr.dev/controller=admission` pod selector
27
+ const pods = await K8s(kind.Pod)
28
+ .InNamespace("pepr-system")
29
+ .WithLabel(labels[0], labels[1])
30
+ .Get();
31
+
32
+ const podNames = pods.items.flatMap(pod => pod.metadata!.name) as string[];
33
+
34
+ if (podNames.length < 1) {
35
+ console.error(errorMessage);
36
+ process.exit(1);
37
+ }
38
+
39
+ const kc = new KubeConfig();
40
+ kc.loadFromDefault();
41
+
42
+ const log = new K8sLog(kc);
43
+
44
+ const logStream = new stream.PassThrough();
45
+ logStream.on("data", async chunk => {
46
+ const respMsg = `"msg":"Check response"`;
47
+ // Split the chunk into lines
48
+ const lines = await chunk.toString().split("\n");
49
+
50
+ for (const line of lines) {
51
+ // Check for `"msg":"Hello Pepr"`
52
+ if (line.includes(respMsg)) {
53
+ try {
54
+ const payload = JSON.parse(line.trim());
55
+ const isMutate = payload.res.patchType || payload.res.warnings;
56
+
57
+ const name = `${payload.namespace}${payload.name}`;
58
+ const uid = payload.res.uid;
59
+
60
+ if (isMutate) {
61
+ const plainPatch =
62
+ payload.res?.patch !== undefined && payload.res?.patch !== null
63
+ ? atob(payload.res.patch)
64
+ : "";
65
+
66
+ const patch = plainPatch !== "" && JSON.stringify(JSON.parse(plainPatch), null, 2);
67
+ const patchType = payload.res.patchType || payload.res.warnings || "";
68
+ const allowOrDeny = payload.res.allowed ? "šŸ”€" : "🚫";
69
+ console.log(`\n${allowOrDeny} MUTATE ${name} (${uid})`);
70
+ if (patchType.length > 0) {
71
+ console.log(`\n\u001b[1;34m${patch}\u001b[0m`);
72
+ }
73
+ } else {
74
+ const failures = Array.isArray(payload.res) ? payload.res : [payload.res];
75
+
76
+ const filteredFailures = failures
77
+ .filter((r: ResponseItem) => !r.allowed)
78
+ .map((r: ResponseItem) => r.status.message);
79
+ if (filteredFailures.length > 0) {
80
+ console.log(`\nāŒ VALIDATE ${name} (${uid})`);
81
+ console.log(`\u001b[1;31m${filteredFailures}\u001b[0m`);
82
+ } else {
83
+ console.log(`\nāœ… VALIDATE ${name} (${uid})`);
84
+ }
85
+ }
86
+ } catch {
87
+ console.warn(`\nIGNORED - Unable to parse line: ${line}.`);
88
+ }
89
+ }
90
+ }
91
+ });
92
+
93
+ for (const podName of podNames) {
94
+ await log.log("pepr-system", podName, "server", logStream, {
95
+ follow: true,
96
+ pretty: false,
97
+ timestamps: false,
98
+ });
99
+ }
100
+ });
101
+ }
@@ -0,0 +1,12 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { Command } from "commander";
5
+
6
+ export class RootCmd extends Command {
7
+ // eslint-disable-next-line class-methods-use-this
8
+ createCommand(name: string) {
9
+ const cmd = new Command(name);
10
+ return cmd;
11
+ }
12
+ }
@@ -0,0 +1,95 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { execSync } from "child_process";
5
+ import fs from "fs";
6
+ import { resolve } from "path";
7
+ import prompt from "prompts";
8
+
9
+ import {
10
+ codeSettings,
11
+ helloPepr,
12
+ prettier,
13
+ samplesYaml,
14
+ snippet,
15
+ tsConfig,
16
+ } from "./init/templates";
17
+ import { write } from "./init/utils";
18
+ import { RootCmd } from "./root";
19
+
20
+ export default function (program: RootCmd) {
21
+ program
22
+ .command("update")
23
+ .description("Update this Pepr module. Not recommended for prod as it may change files.")
24
+ .option("--skip-template-update", "Skip updating the template files")
25
+ .action(async opts => {
26
+ if (!opts.skipTemplateUpdate) {
27
+ const { confirm } = await prompt({
28
+ type: "confirm",
29
+ name: "confirm",
30
+ message:
31
+ "This will overwrite previously auto-generated files including the capabilities/HelloPepr.ts file.\n" +
32
+ "Are you sure you want to continue?",
33
+ });
34
+
35
+ // If the user doesn't confirm, exit
36
+ if (!confirm) {
37
+ return;
38
+ }
39
+ }
40
+
41
+ console.log("Updating the Pepr module...");
42
+
43
+ try {
44
+ // Update Pepr for the module
45
+ execSync("npm install pepr@latest", {
46
+ stdio: "inherit",
47
+ });
48
+
49
+ // Don't update the template files if the user specified the --skip-template-update flag
50
+ if (!opts.skipTemplateUpdate) {
51
+ execSync("npx pepr update-templates", {
52
+ stdio: "inherit",
53
+ });
54
+ }
55
+
56
+ console.log(`āœ… Module updated successfully`);
57
+ } catch (e) {
58
+ console.error(`Error updating Pepr module:`, e);
59
+ process.exit(1);
60
+ }
61
+ });
62
+
63
+ program
64
+ .command("update-templates", { hidden: true })
65
+ .description("Perform template updates")
66
+ .action(async opts => {
67
+ console.log("Updating Pepr config and template tiles...");
68
+
69
+ try {
70
+ // Don't update the template files if the user specified the --skip-template-update flag
71
+ if (!opts.skipTemplateUpdate) {
72
+ await write(resolve(prettier.path), prettier.data);
73
+ await write(resolve(tsConfig.path), tsConfig.data);
74
+ await write(resolve(".vscode", snippet.path), snippet.data);
75
+ await write(resolve(".vscode", codeSettings.path), codeSettings.data);
76
+
77
+ // Update the samples.yaml file if it exists
78
+ const samplePath = resolve("capabilities", samplesYaml.path);
79
+ if (fs.existsSync(samplePath)) {
80
+ fs.unlinkSync(samplePath);
81
+ await write(samplePath, samplesYaml.data);
82
+ }
83
+
84
+ // Update the HelloPepr.ts file if it exists
85
+ const tsPath = resolve("capabilities", helloPepr.path);
86
+ if (fs.existsSync(tsPath)) {
87
+ await write(tsPath, helloPepr.data);
88
+ }
89
+ }
90
+ } catch (e) {
91
+ console.error(`Error updating template files:`, e);
92
+ process.exit(1);
93
+ }
94
+ });
95
+ }