pepr 0.42.0 → 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 (55) hide show
  1. package/dist/cli/build.d.ts +1 -0
  2. package/dist/cli/build.d.ts.map +1 -1
  3. package/dist/cli/build.helpers.d.ts +66 -0
  4. package/dist/cli/build.helpers.d.ts.map +1 -1
  5. package/dist/cli/monitor.d.ts +23 -0
  6. package/dist/cli/monitor.d.ts.map +1 -1
  7. package/dist/cli.js +341 -283
  8. package/dist/controller.js +1 -1
  9. package/dist/lib/assets/destroy.d.ts.map +1 -1
  10. package/dist/lib/assets/helm.d.ts +1 -1
  11. package/dist/lib/assets/helm.d.ts.map +1 -1
  12. package/dist/lib/assets/index.d.ts.map +1 -1
  13. package/dist/lib/assets/pods.d.ts +5 -19
  14. package/dist/lib/assets/pods.d.ts.map +1 -1
  15. package/dist/lib/assets/webhooks.d.ts.map +1 -1
  16. package/dist/lib/assets/yaml.d.ts.map +1 -1
  17. package/dist/lib/capability.d.ts.map +1 -1
  18. package/dist/lib/controller/index.d.ts.map +1 -1
  19. package/dist/lib/controller/store.d.ts +0 -1
  20. package/dist/lib/controller/store.d.ts.map +1 -1
  21. package/dist/lib/controller/storeCache.d.ts +1 -0
  22. package/dist/lib/controller/storeCache.d.ts.map +1 -1
  23. package/dist/lib/mutate-request.d.ts +2 -2
  24. package/dist/lib/mutate-request.d.ts.map +1 -1
  25. package/dist/lib/queue.d.ts.map +1 -1
  26. package/dist/lib/storage.d.ts +4 -4
  27. package/dist/lib/storage.d.ts.map +1 -1
  28. package/dist/lib/utils.d.ts.map +1 -1
  29. package/dist/lib/validate-processor.d.ts +4 -1
  30. package/dist/lib/validate-processor.d.ts.map +1 -1
  31. package/dist/lib/watch-processor.d.ts.map +1 -1
  32. package/dist/lib.js +136 -109
  33. package/dist/lib.js.map +3 -3
  34. package/package.json +1 -1
  35. package/src/cli/build.helpers.ts +180 -0
  36. package/src/cli/build.ts +85 -133
  37. package/src/cli/monitor.ts +108 -65
  38. package/src/lib/assets/deploy.ts +6 -6
  39. package/src/lib/assets/destroy.ts +1 -1
  40. package/src/lib/assets/helm.ts +6 -6
  41. package/src/lib/assets/index.ts +22 -22
  42. package/src/lib/assets/pods.ts +10 -5
  43. package/src/lib/assets/webhooks.ts +1 -1
  44. package/src/lib/assets/yaml.ts +12 -9
  45. package/src/lib/capability.ts +21 -10
  46. package/src/lib/controller/index.ts +9 -7
  47. package/src/lib/controller/store.ts +23 -10
  48. package/src/lib/controller/storeCache.ts +10 -1
  49. package/src/lib/mutate-request.ts +11 -11
  50. package/src/lib/queue.ts +12 -4
  51. package/src/lib/storage.ts +33 -24
  52. package/src/lib/utils.ts +5 -5
  53. package/src/lib/validate-processor.ts +47 -39
  54. package/src/lib/watch-processor.ts +11 -7
  55. package/src/sdk/cosign.ts +4 -4
package/package.json CHANGED
@@ -15,7 +15,7 @@
15
15
  "!src/**/*.test.ts",
16
16
  "!dist/**/*.test.d.ts*"
17
17
  ],
18
- "version": "0.42.0",
18
+ "version": "0.42.1",
19
19
  "main": "dist/lib.js",
20
20
  "types": "dist/lib.d.ts",
21
21
  "scripts": {
@@ -1,3 +1,15 @@
1
+ import { createDirectoryIfNotExists } from "../lib/filesystemService";
2
+ import { sanitizeResourceName } from "../sdk/sdk";
3
+ import { createDockerfile } from "../lib/included-files";
4
+ import { execSync } from "child_process";
5
+ import { CapabilityExport } from "../lib/types";
6
+ import { validateCapabilityNames } from "../lib/helpers";
7
+ import { BuildOptions, BuildResult, context, BuildContext } from "esbuild";
8
+ import { Assets } from "../lib/assets";
9
+ import { resolve } from "path";
10
+ import { promises as fs } from "fs";
11
+
12
+ export type Reloader = (opts: BuildResult<BuildOptions>) => void | Promise<void>;
1
13
  /**
2
14
  * Determine the RBAC mode based on the CLI options and the module's config
3
15
  * @param opts CLI options
@@ -26,3 +38,171 @@ export function determineRbacMode(
26
38
  // if nothing is defined return admin, else return scoped
27
39
  return cfg.pepr.rbacMode || "admin";
28
40
  }
41
+
42
+ /**
43
+ * Handle the custom output directory
44
+ * @param outputDir the desired output directory
45
+ * @returns The desired output directory or the default one
46
+ */
47
+
48
+ export async function handleCustomOutputDir(outputDir: string): Promise<string> {
49
+ const defaultOutputDir = "dist";
50
+ if (outputDir) {
51
+ try {
52
+ await createDirectoryIfNotExists(outputDir);
53
+ return outputDir;
54
+ } catch (error) {
55
+ console.error(`Error creating output directory: ${error.message}`);
56
+ process.exit(1);
57
+ }
58
+ }
59
+ return defaultOutputDir;
60
+ }
61
+
62
+ /**
63
+ * Check if the image is from Iron Bank and return the correct image
64
+ * @param registry The registry of the image
65
+ * @param image The image to check
66
+ * @param peprVersion The version of the PEPR controller
67
+ * @returns The image string
68
+ * @example
69
+ */
70
+ export function checkIronBankImage(registry: string, image: string, peprVersion: string): string {
71
+ return registry === "Iron Bank"
72
+ ? `registry1.dso.mil/ironbank/opensource/defenseunicorns/pepr/controller:v${peprVersion}`
73
+ : image;
74
+ }
75
+
76
+ /**
77
+ * Check if the image pull secret is a valid Kubernetes name
78
+ * @param imagePullSecret
79
+ * @returns boolean
80
+ */
81
+ export function validImagePullSecret(imagePullSecretName: string): void {
82
+ if (imagePullSecretName) {
83
+ const error = "Invalid imagePullSecret. Please provide a valid name as defined in RFC 1123.";
84
+ if (sanitizeResourceName(imagePullSecretName) !== imagePullSecretName) {
85
+ // https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names
86
+ console.error(error);
87
+ process.exit(1);
88
+ }
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Constraint to majke sure customImage and registry are not both used
94
+ * @param customImage
95
+ * @param registry
96
+ * @returns
97
+ */
98
+ export function handleCustomImage(customImage: string, registry: string): string {
99
+ let defaultImage = "";
100
+ if (customImage) {
101
+ if (registry) {
102
+ console.error(`Custom Image and registry cannot be used together.`);
103
+ process.exit(1);
104
+ }
105
+ defaultImage = customImage;
106
+ }
107
+ return defaultImage;
108
+ }
109
+
110
+ /**
111
+ * Creates and pushes a custom image for WASM or any other included files
112
+ * @param includedFiles
113
+ * @param peprVersion
114
+ * @param description
115
+ * @param image
116
+ */
117
+ export async function handleCustomImageBuild(
118
+ includedFiles: string[],
119
+ peprVersion: string,
120
+ description: string,
121
+ image: string,
122
+ ): Promise<void> {
123
+ if (includedFiles.length > 0) {
124
+ await createDockerfile(peprVersion, description, includedFiles);
125
+ execSync(`docker build --tag ${image} -f Dockerfile.controller .`, {
126
+ stdio: "inherit",
127
+ });
128
+ execSync(`docker push ${image}`, { stdio: "inherit" });
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Disables embedding of deployment files into output module
134
+ * @param embed
135
+ * @param path
136
+ * @returns
137
+ */
138
+ export function handleEmbedding(embed: boolean, path: string): void {
139
+ if (!embed) {
140
+ console.info(`✅ Module built successfully at ${path}`);
141
+ return;
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Check if the capability names are valid
147
+ * @param capabilities The capabilities to check
148
+ */
149
+ export function handleValidCapabilityNames(capabilities: CapabilityExport[]): void {
150
+ try {
151
+ // wait for capabilities to be loaded and test names
152
+ validateCapabilityNames(capabilities);
153
+ } catch (e) {
154
+ console.error(`Error loading capability:`, e);
155
+ process.exit(1);
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Watch for changes in the module
161
+ * @param ctxCfg The build options
162
+ * @param reloader The reloader function
163
+ * @returns The build context
164
+ */
165
+ export async function watchForChanges(
166
+ ctxCfg: BuildOptions,
167
+ reloader: Reloader | undefined,
168
+ ): Promise<BuildContext<BuildOptions>> {
169
+ const ctx = await context(ctxCfg);
170
+
171
+ // If the reloader function is defined, watch the module for changes
172
+ if (reloader) {
173
+ await ctx.watch();
174
+ } else {
175
+ // Otherwise, just build the module once
176
+ await ctx.rebuild();
177
+ await ctx.dispose();
178
+ }
179
+
180
+ return ctx;
181
+ }
182
+
183
+ export async function generateYamlAndWriteToDisk(obj: {
184
+ uuid: string;
185
+ imagePullSecret: string;
186
+ outputDir: string;
187
+ assets: Assets;
188
+ zarf: string;
189
+ }): Promise<void> {
190
+ const { uuid, imagePullSecret, outputDir, assets, zarf } = obj;
191
+ const yamlFile = `pepr-module-${uuid}.yaml`;
192
+ const chartPath = `${uuid}-chart`;
193
+ const yamlPath = resolve(outputDir, yamlFile);
194
+ const yaml = await assets.allYaml(imagePullSecret);
195
+ const zarfPath = resolve(outputDir, "zarf.yaml");
196
+
197
+ let localZarf = "";
198
+ if (zarf === "chart") {
199
+ localZarf = assets.zarfYamlChart(chartPath);
200
+ } else {
201
+ localZarf = assets.zarfYaml(yamlFile);
202
+ }
203
+ await fs.writeFile(yamlPath, yaml);
204
+ await fs.writeFile(zarfPath, localZarf);
205
+
206
+ await assets.generateHelmChart(outputDir);
207
+ console.info(`✅ K8s resource for the module saved to ${yamlPath}`);
208
+ }
package/src/cli/build.ts CHANGED
@@ -1,25 +1,34 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
- import { execSync, execFileSync } from "child_process";
5
- import { BuildOptions, BuildResult, analyzeMetafile, context } from "esbuild";
4
+ import { execFileSync } from "child_process";
5
+ import { BuildOptions, BuildResult, analyzeMetafile } from "esbuild";
6
6
  import { promises as fs } from "fs";
7
7
  import { basename, dirname, extname, resolve } from "path";
8
- import { createDockerfile } from "../lib/included-files";
9
8
  import { Assets } from "../lib/assets";
10
9
  import { dependencies, version } from "./init/templates";
11
10
  import { RootCmd } from "./root";
12
- import { peprFormat } from "./format";
13
11
  import { Option } from "commander";
14
- import { validateCapabilityNames, parseTimeout } from "../lib/helpers";
15
- import { sanitizeResourceName } from "../sdk/sdk";
16
- import { determineRbacMode } from "./build.helpers";
17
- import { createDirectoryIfNotExists } from "../lib/filesystemService";
12
+ import { parseTimeout } from "../lib/helpers";
13
+ import { peprFormat } from "./format";
14
+ import {
15
+ watchForChanges,
16
+ determineRbacMode,
17
+ handleEmbedding,
18
+ handleCustomOutputDir,
19
+ handleValidCapabilityNames,
20
+ handleCustomImage,
21
+ handleCustomImageBuild,
22
+ checkIronBankImage,
23
+ validImagePullSecret,
24
+ generateYamlAndWriteToDisk,
25
+ } from "./build.helpers";
26
+
18
27
  const peprTS = "pepr.ts";
19
28
  let outputDir: string = "dist";
20
29
  export type Reloader = (opts: BuildResult<BuildOptions>) => void | Promise<void>;
21
30
 
22
- export default function (program: RootCmd) {
31
+ export default function (program: RootCmd): void {
23
32
  program
24
33
  .command("build")
25
34
  .description("Build a Pepr Module for deployment")
@@ -73,13 +82,7 @@ export default function (program: RootCmd) {
73
82
  )
74
83
  .action(async opts => {
75
84
  // assign custom output directory if provided
76
- if (opts.outputDir) {
77
- outputDir = opts.outputDir;
78
- createDirectoryIfNotExists(outputDir).catch(error => {
79
- console.error(`Error creating output directory: ${error.message}`);
80
- process.exit(1);
81
- });
82
- }
85
+ outputDir = await handleCustomOutputDir(opts.outputDir);
83
86
 
84
87
  // Build the module
85
88
  const buildModuleResult = await buildModule(undefined, opts.entryPoint, opts.embed);
@@ -88,16 +91,7 @@ export default function (program: RootCmd) {
88
91
  // Files to include in controller image for WASM support
89
92
  const { includedFiles } = cfg.pepr;
90
93
 
91
- let image: string = "";
92
-
93
- // Build Kubernetes manifests with custom image
94
- if (opts.customImage) {
95
- if (opts.registry) {
96
- console.error(`Custom Image and registry cannot be used together.`);
97
- process.exit(1);
98
- }
99
- image = opts.customImage;
100
- }
94
+ let image = handleCustomImage(opts.customImage, opts.registry);
101
95
 
102
96
  // Check if there is a custom timeout defined
103
97
  if (opts.timeout !== undefined) {
@@ -111,25 +105,14 @@ export default function (program: RootCmd) {
111
105
  image = `${opts.registryInfo}/custom-pepr-controller:${cfg.pepr.peprVersion}`;
112
106
 
113
107
  // only actually build/push if there are files to include
114
- if (includedFiles.length > 0) {
115
- await createDockerfile(cfg.pepr.peprVersion, cfg.description, includedFiles);
116
- execSync(`docker build --tag ${image} -f Dockerfile.controller .`, {
117
- stdio: "inherit",
118
- });
119
- execSync(`docker push ${image}`, { stdio: "inherit" });
120
- }
108
+ await handleCustomImageBuild(includedFiles, cfg.pepr.peprVersion, cfg.description, image);
121
109
  }
122
110
 
123
111
  // If building without embedding, exit after building
124
- if (!opts.embed) {
125
- console.info(`✅ Module built successfully at ${path}`);
126
- return;
127
- }
112
+ handleEmbedding(opts.embed, path);
128
113
 
129
114
  // set the image version if provided
130
- if (opts.version) {
131
- cfg.pepr.peprVersion = opts.version;
132
- }
115
+ opts.version ? (cfg.pepr.peprVersion = opts.version) : null;
133
116
 
134
117
  // Generate a secret for the module
135
118
  const assets = new Assets(
@@ -144,56 +127,22 @@ export default function (program: RootCmd) {
144
127
  );
145
128
 
146
129
  // If registry is set to Iron Bank, use Iron Bank image
147
- if (opts?.registry === "Iron Bank") {
148
- console.info(
149
- `\n\tThis command assumes the latest release. Pepr's Iron Bank image release cycle is dictated by renovate and is typically released a few days after the GitHub release.\n\tAs an alternative you may consider custom --custom-image to target a specific image and version.`,
150
- );
151
- image = `registry1.dso.mil/ironbank/opensource/defenseunicorns/pepr/controller:v${cfg.pepr.peprVersion}`;
152
- }
130
+ image = checkIronBankImage(opts.registry, image, cfg.pepr.peprVersion);
153
131
 
154
132
  // if image is a custom image, use that instead of the default
155
- if (image !== "") {
156
- assets.image = image;
157
- }
133
+ image !== "" ? (assets.image = image) : null;
158
134
 
159
135
  // Ensure imagePullSecret is valid
160
- if (opts.withPullSecret) {
161
- if (sanitizeResourceName(opts.withPullSecret) !== opts.withPullSecret) {
162
- // https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names
163
- console.error(
164
- "Invalid imagePullSecret. Please provide a valid name as defined in RFC 1123.",
165
- );
166
- process.exit(1);
167
- }
168
- }
169
-
170
- const yamlFile = `pepr-module-${uuid}.yaml`;
171
- const chartPath = `${uuid}-chart`;
172
- const yamlPath = resolve(outputDir, yamlFile);
173
- const yaml = await assets.allYaml(opts.withPullSecret);
174
-
175
- try {
176
- // wait for capabilities to be loaded and test names
177
- validateCapabilityNames(assets.capabilities);
178
- } catch (e) {
179
- console.error(`Error loading capability:`, e);
180
- process.exit(1);
181
- }
182
-
183
- const zarfPath = resolve(outputDir, "zarf.yaml");
184
-
185
- let zarf = "";
186
- if (opts.zarf === "chart") {
187
- zarf = assets.zarfYamlChart(chartPath);
188
- } else {
189
- zarf = assets.zarfYaml(yamlFile);
190
- }
191
- await fs.writeFile(yamlPath, yaml);
192
- await fs.writeFile(zarfPath, zarf);
193
-
194
- await assets.generateHelmChart(outputDir);
195
-
196
- console.info(`✅ K8s resource for the module saved to ${yamlPath}`);
136
+ validImagePullSecret(opts.withPullSecret);
137
+
138
+ handleValidCapabilityNames(assets.capabilities);
139
+ await generateYamlAndWriteToDisk({
140
+ uuid,
141
+ outputDir,
142
+ imagePullSecret: opts.withPullSecret,
143
+ zarf: opts.zarf,
144
+ assets,
145
+ });
197
146
  }
198
147
  });
199
148
  }
@@ -252,15 +201,7 @@ export async function buildModule(reloader?: Reloader, entryPoint = peprTS, embe
252
201
  try {
253
202
  const { cfg, modulePath, path, uuid } = await loadModule(entryPoint);
254
203
 
255
- const validFormat = await peprFormat(true);
256
-
257
- if (!validFormat) {
258
- console.log(
259
- "\x1b[33m%s\x1b[0m",
260
- "Formatting errors were found. The build will continue, but you may want to run `npx pepr format` to address any issues.",
261
- );
262
- }
263
-
204
+ await checkFormat();
264
205
  // Resolve node_modules folder (in support of npm workspaces!)
265
206
  const npmRoot = execFileSync("npm", ["root"]).toString().trim();
266
207
 
@@ -282,7 +223,7 @@ export async function buildModule(reloader?: Reloader, entryPoint = peprTS, embe
282
223
  plugins: [
283
224
  {
284
225
  name: "reload-server",
285
- setup(build) {
226
+ setup(build): void | Promise<void> {
286
227
  build.onEnd(async r => {
287
228
  // Print the build size analysis
288
229
  if (r?.metafile) {
@@ -322,53 +263,64 @@ export async function buildModule(reloader?: Reloader, entryPoint = peprTS, embe
322
263
  ctxCfg.treeShaking = false;
323
264
  }
324
265
 
325
- const ctx = await context(ctxCfg);
326
-
327
- // If the reloader function is defined, watch the module for changes
328
- if (reloader) {
329
- await ctx.watch();
330
- } else {
331
- // Otherwise, just build the module once
332
- await ctx.rebuild();
333
- await ctx.dispose();
334
- }
266
+ const ctx = await watchForChanges(ctxCfg, reloader);
335
267
 
336
268
  return { ctx, path, cfg, uuid };
337
269
  } catch (e) {
338
- console.error(`Error building module:`, e);
270
+ handleModuleBuildError(e);
271
+ }
272
+ }
339
273
 
340
- if (!e.stdout) process.exit(1); // Exit with a non-zero exit code on any other error
274
+ interface BuildModuleResult {
275
+ stdout?: Buffer;
276
+ stderr: Buffer;
277
+ }
341
278
 
342
- const out = e.stdout.toString() as string;
343
- const err = e.stderr.toString();
279
+ function handleModuleBuildError(e: BuildModuleResult): void {
280
+ console.error(`Error building module:`, e);
344
281
 
345
- console.log(out);
346
- console.error(err);
282
+ if (!e.stdout) process.exit(1); // Exit with a non-zero exit code on any other error
347
283
 
348
- // Check for version conflicts
349
- if (out.includes("Types have separate declarations of a private property '_name'.")) {
350
- // Try to find the conflicting package
351
- const pgkErrMatch = /error TS2322: .*? 'import\("\/.*?\/node_modules\/(.*?)\/node_modules/g;
352
- out.matchAll(pgkErrMatch);
284
+ const out = e.stdout.toString() as string;
285
+ const err = e.stderr.toString();
353
286
 
354
- // Look for package conflict errors
355
- const conflicts = [...out.matchAll(pgkErrMatch)];
287
+ console.log(out);
288
+ console.error(err);
356
289
 
357
- // If the regex didn't match, leave a generic error
358
- if (conflicts.length < 1) {
359
- console.info(
360
- `\n\tOne or more imported Pepr Capabilities seem to be using an incompatible version of Pepr.\n\tTry updating your Pepr Capabilities to their latest versions.`,
361
- "Version Conflict",
362
- );
363
- }
290
+ // Check for version conflicts
291
+ if (out.includes("Types have separate declarations of a private property '_name'.")) {
292
+ // Try to find the conflicting package
293
+ const pgkErrMatch = /error TS2322: .*? 'import\("\/.*?\/node_modules\/(.*?)\/node_modules/g;
294
+ out.matchAll(pgkErrMatch);
364
295
 
365
- // Otherwise, loop through each conflicting package and print an error
366
- conflicts.forEach(match => {
367
- console.info(
368
- `\n\tPackage '${match[1]}' seems to be incompatible with your current version of Pepr.\n\tTry updating to the latest version.`,
369
- "Version Conflict",
370
- );
371
- });
296
+ // Look for package conflict errors
297
+ const conflicts = [...out.matchAll(pgkErrMatch)];
298
+
299
+ // If the regex didn't match, leave a generic error
300
+ if (conflicts.length < 1) {
301
+ console.info(
302
+ `\n\tOne or more imported Pepr Capabilities seem to be using an incompatible version of Pepr.\n\tTry updating your Pepr Capabilities to their latest versions.`,
303
+ "Version Conflict",
304
+ );
372
305
  }
306
+
307
+ // Otherwise, loop through each conflicting package and print an error
308
+ conflicts.forEach(match => {
309
+ console.info(
310
+ `\n\tPackage '${match[1]}' seems to be incompatible with your current version of Pepr.\n\tTry updating to the latest version.`,
311
+ "Version Conflict",
312
+ );
313
+ });
314
+ }
315
+ }
316
+
317
+ export async function checkFormat() {
318
+ const validFormat = await peprFormat(true);
319
+
320
+ if (!validFormat) {
321
+ console.log(
322
+ "\x1b[33m%s\x1b[0m",
323
+ "Formatting errors were found. The build will continue, but you may want to run `npx pepr format` to address any issues.",
324
+ );
373
325
  }
374
326
  }