pepr 0.52.2 → 0.52.3-nightly.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 (88) hide show
  1. package/dist/cli/banner.d.ts +1 -1
  2. package/dist/cli/banner.d.ts.map +1 -1
  3. package/dist/cli/{build.helpers.d.ts → build/build.helpers.d.ts} +5 -5
  4. package/dist/cli/build/build.helpers.d.ts.map +1 -0
  5. package/dist/cli/build/buildModule.d.ts +11 -0
  6. package/dist/cli/build/buildModule.d.ts.map +1 -0
  7. package/dist/cli/build/index.d.ts +3 -0
  8. package/dist/cli/build/index.d.ts.map +1 -0
  9. package/dist/cli/build/loadModule.d.ts +11 -0
  10. package/dist/cli/build/loadModule.d.ts.map +1 -0
  11. package/dist/cli/crd/generate/generators.d.ts +1 -28
  12. package/dist/cli/crd/generate/generators.d.ts.map +1 -1
  13. package/dist/cli/deploy/buildAndDeploy.d.ts +5 -0
  14. package/dist/cli/deploy/buildAndDeploy.d.ts.map +1 -0
  15. package/dist/cli/deploy/imagePullSecret.d.ts +16 -0
  16. package/dist/cli/deploy/imagePullSecret.d.ts.map +1 -0
  17. package/dist/cli/deploy/index.d.ts +3 -0
  18. package/dist/cli/deploy/index.d.ts.map +1 -0
  19. package/dist/cli/deploy/userConfirmation.d.ts +4 -0
  20. package/dist/cli/deploy/userConfirmation.d.ts.map +1 -0
  21. package/dist/cli/dev.d.ts.map +1 -1
  22. package/dist/cli/init/templates.d.ts +0 -1
  23. package/dist/cli/init/templates.d.ts.map +1 -1
  24. package/dist/cli/monitor.d.ts +3 -0
  25. package/dist/cli/monitor.d.ts.map +1 -1
  26. package/dist/cli/types.d.ts +14 -0
  27. package/dist/cli/types.d.ts.map +1 -1
  28. package/dist/cli/uuid.d.ts.map +1 -1
  29. package/dist/cli.js +1198 -1215
  30. package/dist/controller.js +24 -11
  31. package/dist/lib/assets/assets.d.ts.map +1 -1
  32. package/dist/lib/assets/environment.d.ts +1 -0
  33. package/dist/lib/assets/environment.d.ts.map +1 -1
  34. package/dist/lib/assets/loader.d.ts.map +1 -1
  35. package/dist/lib/assets/webhooks.d.ts.map +1 -1
  36. package/dist/lib/assets/yaml/generateAllYaml.d.ts +1 -2
  37. package/dist/lib/assets/yaml/generateAllYaml.d.ts.map +1 -1
  38. package/dist/lib/core/storage.d.ts +2 -1
  39. package/dist/lib/core/storage.d.ts.map +1 -1
  40. package/dist/lib/filter/adjudicators/binding.d.ts +2 -1
  41. package/dist/lib/filter/adjudicators/binding.d.ts.map +1 -1
  42. package/dist/lib/filter/adjudicators/kubernetesObject.d.ts +0 -1
  43. package/dist/lib/filter/adjudicators/kubernetesObject.d.ts.map +1 -1
  44. package/dist/lib/processors/mutate-processor.d.ts +2 -1
  45. package/dist/lib/processors/mutate-processor.d.ts.map +1 -1
  46. package/dist/lib/types.d.ts +0 -4
  47. package/dist/lib/types.d.ts.map +1 -1
  48. package/dist/lib.js +7 -7
  49. package/dist/lib.js.map +2 -2
  50. package/dist/runtime/controller.d.ts +1 -1
  51. package/dist/runtime/controller.d.ts.map +1 -1
  52. package/package.json +10 -11
  53. package/src/cli/banner.ts +25 -59
  54. package/src/cli/{build.helpers.ts → build/build.helpers.ts} +15 -21
  55. package/src/cli/build/buildModule.ts +160 -0
  56. package/src/cli/build/index.ts +155 -0
  57. package/src/cli/build/loadModule.ts +54 -0
  58. package/src/cli/crd/generate/generators.ts +6 -6
  59. package/src/cli/deploy/buildAndDeploy.ts +48 -0
  60. package/src/cli/deploy/imagePullSecret.ts +68 -0
  61. package/src/cli/deploy/index.ts +40 -0
  62. package/src/cli/deploy/userConfirmation.ts +16 -0
  63. package/src/cli/dev.ts +4 -3
  64. package/src/cli/monitor.ts +2 -2
  65. package/src/cli/types.ts +26 -0
  66. package/src/cli/uuid.ts +5 -6
  67. package/src/lib/assets/assets.ts +5 -1
  68. package/src/lib/assets/deploy.ts +1 -1
  69. package/src/lib/assets/environment.ts +8 -1
  70. package/src/lib/assets/loader.ts +0 -6
  71. package/src/lib/assets/webhooks.ts +5 -8
  72. package/src/lib/assets/yaml/generateAllYaml.ts +1 -1
  73. package/src/lib/controller/index.ts +3 -3
  74. package/src/lib/controller/store.ts +1 -1
  75. package/src/lib/core/capability.ts +3 -3
  76. package/src/lib/core/storage.ts +1 -1
  77. package/src/lib/filter/adjudicators/binding.ts +2 -1
  78. package/src/lib/filter/adjudicators/kubernetesObject.ts +1 -1
  79. package/src/lib/processors/mutate-processor.ts +1 -1
  80. package/src/lib/types.ts +0 -4
  81. package/src/runtime/controller.ts +8 -14
  82. package/dist/cli/build.d.ts +0 -34
  83. package/dist/cli/build.d.ts.map +0 -1
  84. package/dist/cli/build.helpers.d.ts.map +0 -1
  85. package/dist/cli/deploy.d.ts +0 -21
  86. package/dist/cli/deploy.d.ts.map +0 -1
  87. package/src/cli/build.ts +0 -375
  88. package/src/cli/deploy.ts +0 -169
@@ -1,23 +1,24 @@
1
- import { createDirectoryIfNotExists } from "../lib/filesystemService";
2
- import { sanitizeResourceName } from "../sdk/sdk";
3
- import { createDockerfile } from "../lib/included-files";
1
+ import { createDirectoryIfNotExists } from "../../lib/filesystemService";
2
+ import { sanitizeResourceName } from "../../sdk/sdk";
3
+ import { createDockerfile } from "../../lib/included-files";
4
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/assets";
5
+ import { CapabilityExport } from "../../lib/types";
6
+ import { validateCapabilityNames } from "../../lib/helpers";
7
+ import { BuildOptions, context, BuildContext } from "esbuild";
8
+ import { Assets } from "../../lib/assets/assets";
9
9
  import { resolve } from "path";
10
10
  import { promises as fs } from "fs";
11
- import { generateAllYaml } from "../lib/assets/yaml/generateAllYaml";
12
- import { webhookConfigGenerator } from "../lib/assets/webhooks";
13
- import { generateZarfYamlGeneric } from "../lib/assets/yaml/generateZarfYaml";
11
+ import { generateAllYaml } from "../../lib/assets/yaml/generateAllYaml";
12
+ import { webhookConfigGenerator } from "../../lib/assets/webhooks";
13
+ import { generateZarfYamlGeneric } from "../../lib/assets/yaml/generateZarfYaml";
14
14
  import {
15
15
  getDeployment,
16
16
  getModuleSecret,
17
17
  getWatcher,
18
18
  service,
19
19
  watcherService,
20
- } from "../lib/assets/k8sObjects";
20
+ } from "../../lib/assets/k8sObjects";
21
+ import { Reloader } from "../types";
21
22
 
22
23
  interface ImageOptions {
23
24
  customImage?: string;
@@ -47,7 +48,6 @@ export function assignImage(imageOptions: ImageOptions): string {
47
48
  return "";
48
49
  }
49
50
 
50
- export type Reloader = (opts: BuildResult<BuildOptions>) => void | Promise<void>;
51
51
  /**
52
52
  * Determine the RBAC mode based on the CLI options and the module's config
53
53
  * @param opts CLI options
@@ -113,15 +113,9 @@ export function checkIronBankImage(registry: string, image: string, peprVersion:
113
113
  * @param imagePullSecret
114
114
  * @returns boolean
115
115
  */
116
- export function validImagePullSecret(imagePullSecretName: string): void {
117
- if (imagePullSecretName) {
118
- const error = "Invalid imagePullSecret. Please provide a valid name as defined in RFC 1123.";
119
- if (sanitizeResourceName(imagePullSecretName) !== imagePullSecretName) {
120
- // https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names
121
- console.error(error);
122
- process.exit(1);
123
- }
124
- }
116
+ export function validImagePullSecret(imagePullSecretName: string): boolean {
117
+ // https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names
118
+ return sanitizeResourceName(imagePullSecretName) === imagePullSecretName;
125
119
  }
126
120
 
127
121
  /**
@@ -0,0 +1,160 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { execFileSync } from "child_process";
5
+ import { BuildOptions, analyzeMetafile } from "esbuild";
6
+ import { basename, extname, resolve } from "path";
7
+ import { dependencies } from "../init/templates";
8
+ import { peprFormat } from "../format";
9
+ import { watchForChanges } from "./build.helpers";
10
+ import { PeprConfig, Reloader } from "../types";
11
+ import { BuildContext } from "esbuild";
12
+ import { loadModule } from "./loadModule";
13
+
14
+ export type BuildModuleReturn = {
15
+ ctx: BuildContext<BuildOptions>;
16
+ path: string;
17
+ cfg: PeprConfig;
18
+ uuid: string;
19
+ };
20
+ // Create a list of external libraries to exclude from the bundle, these are already stored in the container
21
+ const externalLibs = Object.keys(dependencies);
22
+
23
+ // Add the pepr library to the list of external libraries
24
+ externalLibs.push("pepr");
25
+
26
+ // Add the kubernetes client to the list of external libraries as it is pulled in by kubernetes-fluent-client
27
+ externalLibs.push("@kubernetes/client-node");
28
+
29
+ export async function buildModule(
30
+ outputDir: string,
31
+ reloader?: Reloader,
32
+ entryPoint = "pepr.ts",
33
+ embed = true,
34
+ ): Promise<BuildModuleReturn | void> {
35
+ try {
36
+ const { cfg, modulePath, path, uuid } = await loadModule(outputDir, entryPoint);
37
+
38
+ await checkFormat();
39
+ // Resolve node_modules folder (in support of npm workspaces!)
40
+ const npmRoot = execFileSync("npm", ["root"]).toString().trim();
41
+
42
+ // Run `tsc` to validate the module's types & output sourcemaps
43
+ const args = ["--project", `${modulePath}/tsconfig.json`, "--outdir", outputDir];
44
+ execFileSync(`${npmRoot}/.bin/tsc`, args);
45
+
46
+ // Common build options for all builds
47
+ const ctxCfg: BuildOptions = {
48
+ bundle: true,
49
+ entryPoints: [entryPoint],
50
+ external: externalLibs,
51
+ format: "cjs",
52
+ keepNames: true,
53
+ legalComments: "external",
54
+ metafile: true,
55
+ minify: true,
56
+ outfile: path,
57
+ plugins: [
58
+ {
59
+ name: "reload-server",
60
+ setup(build): void | Promise<void> {
61
+ build.onEnd(async r => {
62
+ // Print the build size analysis
63
+ if (r?.metafile) {
64
+ console.info(await analyzeMetafile(r.metafile));
65
+ }
66
+
67
+ // If we're in dev mode, call the reloader function
68
+ if (reloader) {
69
+ await reloader(r);
70
+ }
71
+ });
72
+ },
73
+ },
74
+ ],
75
+ platform: "node",
76
+ sourcemap: true,
77
+ treeShaking: true,
78
+ };
79
+
80
+ if (reloader) {
81
+ // Only minify the code if we're not in dev mode
82
+ ctxCfg.minify = false;
83
+ }
84
+
85
+ // If not embedding (i.e. making a library module to be distro'd via NPM)
86
+ if (!embed) {
87
+ // Don't minify
88
+ ctxCfg.minify = false;
89
+
90
+ // Preserve the original file name
91
+ ctxCfg.outfile = resolve(outputDir, basename(entryPoint, extname(entryPoint))) + ".js";
92
+
93
+ // Don't bundle
94
+ ctxCfg.packages = "external";
95
+
96
+ // Don't tree shake
97
+ ctxCfg.treeShaking = false;
98
+ }
99
+
100
+ const ctx = await watchForChanges(ctxCfg, reloader);
101
+
102
+ return { ctx, path, cfg, uuid };
103
+ } catch (e) {
104
+ handleModuleBuildError(e);
105
+ }
106
+ }
107
+
108
+ interface BuildModuleResult {
109
+ stdout?: Buffer;
110
+ stderr: Buffer;
111
+ }
112
+
113
+ function handleModuleBuildError(e: BuildModuleResult): void {
114
+ console.error(`Error building module:`, e);
115
+
116
+ if (!e.stdout) process.exit(1); // Exit with a non-zero exit code on any other error
117
+
118
+ const out = e.stdout.toString() as string;
119
+ const err = e.stderr.toString();
120
+
121
+ console.info(out);
122
+ console.error(err);
123
+
124
+ // Check for version conflicts
125
+ if (out.includes("Types have separate declarations of a private property '_name'.")) {
126
+ // Try to find the conflicting package
127
+ const pgkErrMatch = /error TS2322: .*? 'import\("\/.*?\/node_modules\/(.*?)\/node_modules/g;
128
+ out.matchAll(pgkErrMatch);
129
+
130
+ // Look for package conflict errors
131
+ const conflicts = [...out.matchAll(pgkErrMatch)];
132
+
133
+ // If the regex didn't match, leave a generic error
134
+ if (conflicts.length < 1) {
135
+ console.info(
136
+ `\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.`,
137
+ "Version Conflict",
138
+ );
139
+ }
140
+
141
+ // Otherwise, loop through each conflicting package and print an error
142
+ conflicts.forEach(match => {
143
+ console.info(
144
+ `\n\tPackage '${match[1]}' seems to be incompatible with your current version of Pepr.\n\tTry updating to the latest version.`,
145
+ "Version Conflict",
146
+ );
147
+ });
148
+ }
149
+ }
150
+
151
+ async function checkFormat(): Promise<void> {
152
+ const validFormat = await peprFormat(true);
153
+
154
+ if (!validFormat) {
155
+ console.info(
156
+ "\x1b[33m%s\x1b[0m",
157
+ "Formatting errors were found. The build will continue, but you may want to run `npx pepr format` to address any issues.",
158
+ );
159
+ }
160
+ }
@@ -0,0 +1,155 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { Assets } from "../../lib/assets/assets";
5
+ import { Command } from "commander";
6
+ import { Option } from "commander";
7
+ import { parseTimeout } from "../../lib/helpers";
8
+ import {
9
+ determineRbacMode,
10
+ assignImage,
11
+ createOutputDirectory,
12
+ handleValidCapabilityNames,
13
+ handleCustomImageBuild,
14
+ validImagePullSecret,
15
+ generateYamlAndWriteToDisk,
16
+ } from "./build.helpers";
17
+ import { buildModule } from "./buildModule";
18
+ import Log from "../../lib/telemetry/logger";
19
+
20
+ export default function (program: Command): void {
21
+ program
22
+ .command("build")
23
+ .description("Build a Pepr Module for deployment")
24
+ .addOption(
25
+ new Option("-M, --rbac-mode <mode>", "Override module config and set RBAC mode.").choices([
26
+ "admin",
27
+ "scoped",
28
+ ]),
29
+ )
30
+ .addOption(
31
+ new Option(
32
+ "-I, --registry-info <registry/username>",
33
+ "Provide the image registry and username for building and pushing a custom WASM container. Requires authentication. Conflicts with --custom-image and --registry. Builds and pushes `'<registry/username>/custom-pepr-controller:<current-version>'`.",
34
+ ).conflicts(["customImage", "registry"]),
35
+ )
36
+ .option("-P, --with-pull-secret <name>", "Use image pull secret for controller Deployment.", "")
37
+ .addOption(
38
+ new Option(
39
+ "-c, --custom-name <name>",
40
+ "Set name for zarf component and service monitors in helm charts.",
41
+ ),
42
+ )
43
+ .option("-e, --entry-point <file>", "Specify the entry point file to build with.", "pepr.ts")
44
+ .addOption(
45
+ new Option(
46
+ "-i, --custom-image <image>",
47
+ "Specify a custom image with version for deployments. Conflicts with --registry-info and --registry. Example: 'docker.io/username/custom-pepr-controller:v1.0.0'",
48
+ ).conflicts(["registryInfo", "registry"]),
49
+ )
50
+ .option(
51
+ "-n, --no-embed",
52
+ "Disable embedding of deployment files into output module. Useful when creating library modules intended solely for reuse/distribution via NPM.",
53
+ )
54
+ .option("-o, --output <directory>", "Set output directory.", "dist")
55
+ .addOption(
56
+ new Option(
57
+ "-r, --registry <registry>",
58
+ "Container registry: Choose container registry for deployment manifests. Conflicts with --custom-image and --registry-info.",
59
+ )
60
+ .conflicts(["customImage", "registryInfo"])
61
+ .choices(["GitHub", "Iron Bank"]),
62
+ )
63
+ .option(
64
+ "-t, --timeout <seconds>",
65
+ "How long the API server should wait for a webhook to respond before treating the call as a failure.",
66
+ parseTimeout,
67
+ )
68
+ .addOption(
69
+ new Option("-z, --zarf <manifest|chart>", "Set Zarf package type")
70
+ .choices(["manifest", "chart"])
71
+ .default("manifest"),
72
+ )
73
+ .action(async opts => {
74
+ const outputDir = await createOutputDirectory(opts.output);
75
+
76
+ // Build the module
77
+ const buildModuleResult = await buildModule(
78
+ outputDir,
79
+ undefined,
80
+ opts.entryPoint,
81
+ opts.embed,
82
+ );
83
+
84
+ const { cfg, path } = buildModuleResult!;
85
+ // override the name if provided
86
+ if (opts.customName) {
87
+ process.env.PEPR_CUSTOM_BUILD_NAME = opts.customName;
88
+ }
89
+
90
+ const image = assignImage({
91
+ customImage: opts.customImage,
92
+ registryInfo: opts.registryInfo,
93
+ peprVersion: cfg.pepr.peprVersion,
94
+ registry: opts.registry,
95
+ });
96
+
97
+ // Check if there is a custom timeout defined
98
+ if (opts.timeout !== undefined) {
99
+ cfg.pepr.webhookTimeout = opts.timeout;
100
+ }
101
+
102
+ if (opts.registryInfo !== undefined) {
103
+ Log.info(`Including ${cfg.pepr.includedFiles.length} files in controller image.`);
104
+ // for journey test to make sure the image is built
105
+
106
+ // only actually build/push if there are files to include
107
+ await handleCustomImageBuild(
108
+ cfg.pepr.includedFiles,
109
+ cfg.pepr.peprVersion,
110
+ cfg.description,
111
+ image,
112
+ );
113
+ }
114
+
115
+ // If building without embedding, exit after building
116
+ if (!opts.embed) {
117
+ Log.info(`Module built successfully at ${path}`);
118
+ return;
119
+ }
120
+
121
+ // Generate a secret for the module
122
+ const assets = new Assets(
123
+ {
124
+ ...cfg.pepr,
125
+ appVersion: cfg.version,
126
+ description: cfg.description,
127
+ alwaysIgnore: {
128
+ namespaces: cfg.pepr.alwaysIgnore?.namespaces,
129
+ },
130
+ // Can override the rbacMode with the CLI option
131
+ rbacMode: determineRbacMode(opts, cfg),
132
+ },
133
+ path,
134
+ opts.withPullSecret === "" ? [] : [opts.withPullSecret],
135
+ );
136
+
137
+ if (image !== "") assets.image = image;
138
+
139
+ // Ensure imagePullSecret is valid
140
+ if (!validImagePullSecret(opts.withPullSecret)) {
141
+ throw new Error(
142
+ "Invalid imagePullSecret. Please provide a valid name as defined in RFC 1123.",
143
+ );
144
+ }
145
+
146
+ handleValidCapabilityNames(assets.capabilities);
147
+ await generateYamlAndWriteToDisk({
148
+ uuid: cfg.pepr.uuid,
149
+ outputDir,
150
+ imagePullSecret: opts.withPullSecret,
151
+ zarf: opts.zarf,
152
+ assets,
153
+ });
154
+ });
155
+ }
@@ -0,0 +1,54 @@
1
+ import fs from "fs/promises";
2
+ import { resolve, dirname } from "path/posix";
3
+ import { version } from "../init/templates";
4
+ import { PeprConfig } from "../types";
5
+
6
+ export type LoadModuleReturn = {
7
+ cfg: PeprConfig;
8
+ entryPointPath: string;
9
+ modulePath: string;
10
+ name: string;
11
+ path: string;
12
+ uuid: string;
13
+ };
14
+
15
+ export async function loadModule(outputDir: string, entryPoint: string): Promise<LoadModuleReturn> {
16
+ // Resolve path to the module / files
17
+ const entryPointPath = resolve(".", entryPoint);
18
+ const modulePath = dirname(entryPointPath);
19
+ const cfgPath = resolve(modulePath, "package.json");
20
+
21
+ // Ensure the module's package.json and entrypoint files exist
22
+ try {
23
+ await fs.access(cfgPath);
24
+ await fs.access(entryPointPath);
25
+ } catch {
26
+ console.error(
27
+ `Could not find ${cfgPath} or ${entryPointPath} in the current directory. Please run this command from the root of your module's directory.`,
28
+ );
29
+ process.exit(1);
30
+ }
31
+
32
+ // Read the module's UUID from the package.json file
33
+ const moduleText = await fs.readFile(cfgPath, { encoding: "utf-8" });
34
+ const cfg = JSON.parse(moduleText);
35
+ const { uuid } = cfg.pepr;
36
+ const name = `pepr-${uuid}.js`;
37
+
38
+ // Set the Pepr version from the current running version
39
+ cfg.pepr.peprVersion = version;
40
+
41
+ // Exit if the module's UUID could not be found
42
+ if (!uuid) {
43
+ throw new Error("Could not load the uuid in package.json");
44
+ }
45
+
46
+ return {
47
+ cfg,
48
+ entryPointPath,
49
+ modulePath,
50
+ name,
51
+ path: resolve(outputDir, name),
52
+ uuid,
53
+ };
54
+ }
@@ -16,7 +16,7 @@ import { kind as k } from "kubernetes-fluent-client";
16
16
  import { V1JSONSchemaProps } from "@kubernetes/client-node";
17
17
  import { WarningMessages, ErrorMessages } from "./messages";
18
18
 
19
- export function extractCRDDetails(
19
+ function extractCRDDetails(
20
20
  content: string,
21
21
  sourceFile: SourceFile,
22
22
  ): {
@@ -145,7 +145,7 @@ export function extractDetails(sourceFile: SourceFile): {
145
145
  throw new Error(ErrorMessages.INVALID_SCOPE(scope));
146
146
  }
147
147
 
148
- export function getJsDocDescription(node: Node): string {
148
+ function getJsDocDescription(node: Node): string {
149
149
  if (!Node.isPropertySignature(node) && !Node.isPropertyDeclaration(node)) return "";
150
150
  return node
151
151
  .getJsDocs()
@@ -155,7 +155,7 @@ export function getJsDocDescription(node: Node): string {
155
155
  .trim();
156
156
  }
157
157
 
158
- export function getSchemaFromType(decl: InterfaceDeclaration | TypeAliasDeclaration): {
158
+ function getSchemaFromType(decl: InterfaceDeclaration | TypeAliasDeclaration): {
159
159
  properties: Record<string, V1JSONSchemaProps>;
160
160
  required: string[];
161
161
  } {
@@ -183,7 +183,7 @@ export function getSchemaFromType(decl: InterfaceDeclaration | TypeAliasDeclarat
183
183
  return { properties, required };
184
184
  }
185
185
 
186
- export function mapTypeToSchema(type: Type): V1JSONSchemaProps {
186
+ function mapTypeToSchema(type: Type): V1JSONSchemaProps {
187
187
  if (type.getText() === "Date") return { type: "string", format: "date-time" };
188
188
  if (type.isString()) return { type: "string" };
189
189
  if (type.isNumber()) return { type: "number" };
@@ -199,7 +199,7 @@ export function mapTypeToSchema(type: Type): V1JSONSchemaProps {
199
199
  return { type: "string" };
200
200
  }
201
201
 
202
- export function buildObjectSchema(type: Type): V1JSONSchemaProps {
202
+ function buildObjectSchema(type: Type): V1JSONSchemaProps {
203
203
  const props: Record<string, V1JSONSchemaProps> = {};
204
204
  const required: string[] = [];
205
205
 
@@ -249,7 +249,7 @@ interface CRDConfig {
249
249
  conditionSchema: ReturnType<typeof getSchemaFromType>;
250
250
  }
251
251
 
252
- export function buildCRD(config: CRDConfig): k.CustomResourceDefinition {
252
+ function buildCRD(config: CRDConfig): k.CustomResourceDefinition {
253
253
  return {
254
254
  apiVersion: "apiextensions.k8s.io/v1",
255
255
  kind: "CustomResourceDefinition",
@@ -0,0 +1,48 @@
1
+ import { Assets } from "../../lib/assets/assets";
2
+ import { deployWebhook } from "../../lib/assets/deploy";
3
+ import { loadCapabilities } from "../../lib/assets/loader";
4
+ import { namespaceDeploymentsReady } from "../../lib/deploymentChecks";
5
+ import { namespaceComplianceValidator, validateCapabilityNames } from "../../lib/helpers";
6
+ import { CapabilityExport } from "../../lib/types";
7
+ import { buildModule } from "../build/buildModule";
8
+
9
+ export async function buildAndDeployModule(image: string, force: boolean): Promise<void> {
10
+ const builtModule = await buildModule("dist");
11
+ if (!builtModule) {
12
+ return;
13
+ }
14
+
15
+ // Generate a secret for the module
16
+ const webhook = new Assets(
17
+ { ...builtModule.cfg.pepr, description: builtModule.cfg.description },
18
+ builtModule.path,
19
+ [],
20
+ );
21
+ webhook.image = image ?? webhook.image;
22
+ const capabilities = await loadCapabilities(webhook.path);
23
+ for (const capability of capabilities) {
24
+ validateNamespaces(capability, webhook);
25
+ }
26
+ try {
27
+ await webhook.deploy(deployWebhook, force, builtModule.cfg.pepr.webhookTimeout ?? 10);
28
+
29
+ // wait for capabilities to be loaded and test names
30
+ validateCapabilityNames(webhook.capabilities);
31
+
32
+ // Wait for the pepr-system resources to be fully up
33
+ await namespaceDeploymentsReady();
34
+ console.info(`Module deployed successfully`);
35
+ } catch (e) {
36
+ console.error(`Error deploying module:`, e);
37
+ process.exit(1);
38
+ }
39
+ }
40
+ export function validateNamespaces(capability: CapabilityExport, webhook: Assets): void {
41
+ namespaceComplianceValidator(capability, webhook.alwaysIgnore?.namespaces);
42
+ namespaceComplianceValidator(
43
+ capability,
44
+ webhook.config.admission?.alwaysIgnore?.namespaces,
45
+ false,
46
+ );
47
+ namespaceComplianceValidator(capability, webhook.config.watch?.alwaysIgnore?.namespaces, true);
48
+ }
@@ -0,0 +1,68 @@
1
+ import { ImagePullSecret } from "../../lib/types";
2
+ import { sanitizeName } from "../init/utils";
3
+
4
+ export interface ImagePullSecretDetails {
5
+ pullSecret?: string;
6
+ dockerServer?: string;
7
+ dockerUsername?: string;
8
+ dockerEmail?: string;
9
+ dockerPassword?: string;
10
+ }
11
+
12
+ export function validateImagePullSecretDetails(details: ImagePullSecretDetails): {
13
+ valid: boolean;
14
+ error?: string;
15
+ } {
16
+ if (!details.pullSecret) {
17
+ return { valid: true };
18
+ }
19
+
20
+ // https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names
21
+ if (details.pullSecret !== sanitizeName(details.pullSecret)) {
22
+ return {
23
+ valid: false,
24
+ error: `Invalid --pullSecret. Must be valid name as defined in RFC 1123.`,
25
+ };
26
+ }
27
+
28
+ const missing: string[] = [];
29
+ if (!details.dockerEmail) {
30
+ missing.push("--docker-email");
31
+ }
32
+ if (!details.dockerServer) {
33
+ missing.push("--docker-server");
34
+ }
35
+ if (!details.dockerUsername) {
36
+ missing.push("--docker-username");
37
+ }
38
+ if (!details.dockerPassword) {
39
+ missing.push("--docker-password");
40
+ }
41
+
42
+ if (missing.length > 0) {
43
+ return {
44
+ valid: false,
45
+ error: `Error: Must provide ${missing.join(", ")} when providing --pull-secret`,
46
+ };
47
+ }
48
+
49
+ return { valid: true };
50
+ }
51
+
52
+ type ValidatedImagePullSecretDetails = Required<ImagePullSecretDetails>;
53
+
54
+ export function generateImagePullSecret(details: ValidatedImagePullSecretDetails): ImagePullSecret {
55
+ const auth = Buffer.from(`${details.dockerUsername}:${details.dockerPassword}`).toString(
56
+ "base64",
57
+ );
58
+ return {
59
+ auths: {
60
+ [details.dockerServer]: {
61
+ username: details.dockerUsername,
62
+ password: details.dockerPassword,
63
+ email: details.dockerEmail,
64
+ auth,
65
+ },
66
+ },
67
+ };
68
+ }
@@ -0,0 +1,40 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { Command } from "commander";
5
+ import { deployImagePullSecret } from "../../lib/assets/deploy";
6
+ import { validateImagePullSecretDetails, generateImagePullSecret } from "./imagePullSecret";
7
+ import { getUserConfirmation } from "./userConfirmation";
8
+ import { buildAndDeployModule } from "./buildAndDeploy";
9
+
10
+ export default function (program: Command): void {
11
+ program
12
+ .command("deploy")
13
+ .description("Deploy a Pepr Module")
14
+ .option("-E, --docker-email <email>", "Email for Docker registry.")
15
+ .option("-P, --docker-password <password>", "Password for Docker registry.")
16
+ .option("-S, --docker-server <server>", "Docker server address.")
17
+ .option("-U, --docker-username <username>", "Docker registry username.")
18
+ .option("-f, --force", "Force deploy the module, override manager field.")
19
+ .option("-i, --image <image>", "Override the image tag.")
20
+ .option("-p, --pull-secret <name>", "Deploy imagePullSecret for Controller private registry.")
21
+ .option("-y, --yes", "Skip confirmation prompts.")
22
+ .action(async opts => {
23
+ const valResp = validateImagePullSecretDetails(opts);
24
+ if (!valResp.valid) {
25
+ console.error(valResp.error);
26
+ process.exit(1);
27
+ }
28
+
29
+ if (opts.pullSecret) {
30
+ await deployImagePullSecret(generateImagePullSecret(opts), opts.pullSecret);
31
+ return;
32
+ }
33
+
34
+ if (!(await getUserConfirmation(opts))) {
35
+ process.exit(0);
36
+ }
37
+
38
+ await buildAndDeployModule(opts.image, opts.force);
39
+ });
40
+ }
@@ -0,0 +1,16 @@
1
+ import prompt from "prompts";
2
+
3
+ export async function getUserConfirmation(opts: { yes: boolean }): Promise<boolean> {
4
+ if (opts.yes) {
5
+ return true;
6
+ }
7
+
8
+ // Prompt the user to confirm
9
+ const confirmation = await prompt({
10
+ type: "confirm",
11
+ name: "yes",
12
+ message: "This will remove and redeploy the module. Continue?",
13
+ });
14
+
15
+ return confirmation.yes ? true : false;
16
+ }
package/src/cli/dev.ts CHANGED
@@ -7,7 +7,8 @@ import { ChildProcess, fork } from "child_process";
7
7
  import { K8s, kind } from "kubernetes-fluent-client";
8
8
  import { Command } from "commander";
9
9
  import { Store } from "../lib/k8s";
10
- import { buildModule, loadModule } from "./build";
10
+ import { buildModule } from "./build/buildModule";
11
+ import { loadModule } from "./build/loadModule";
11
12
  import { deployWebhook } from "../lib/assets/deploy";
12
13
  import { promises as fs } from "fs";
13
14
  import { validateCapabilityNames } from "../lib/helpers";
@@ -35,7 +36,7 @@ export default function (program: Command): void {
35
36
  }
36
37
 
37
38
  // Build the module
38
- const { cfg, path } = await loadModule();
39
+ const { cfg, path } = await loadModule("dist", "pepr.ts");
39
40
 
40
41
  // Generate a secret for the module
41
42
  const webhook = new Assets(
@@ -103,7 +104,7 @@ export default function (program: Command): void {
103
104
  });
104
105
  };
105
106
 
106
- await buildModule(async r => {
107
+ await buildModule("dist", async r => {
107
108
  if (r.errors.length > 0) {
108
109
  console.error(`Error compiling module: ${r.errors}`);
109
110
  return;