pepr 0.42.3 → 0.44.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.
- package/dist/cli/build.d.ts +25 -11
- package/dist/cli/build.d.ts.map +1 -1
- package/dist/cli/build.helpers.d.ts +0 -7
- package/dist/cli/build.helpers.d.ts.map +1 -1
- package/dist/cli/deploy.d.ts.map +1 -1
- package/dist/cli/dev.d.ts.map +1 -1
- package/dist/cli/init/templates.d.ts +3 -0
- package/dist/cli/init/templates.d.ts.map +1 -1
- package/dist/cli.js +742 -710
- package/dist/controller.js +1 -1
- package/dist/fixtures/loader.d.ts.map +1 -1
- package/dist/lib/assets/assets.d.ts +15 -12
- package/dist/lib/assets/assets.d.ts.map +1 -1
- package/dist/lib/assets/deploy.d.ts +1 -1
- package/dist/lib/assets/deploy.d.ts.map +1 -1
- package/dist/lib/assets/helm.d.ts.map +1 -1
- package/dist/lib/assets/index.d.ts.map +1 -1
- package/dist/lib/assets/webhooks.d.ts +4 -1
- package/dist/lib/assets/webhooks.d.ts.map +1 -1
- package/dist/lib/assets/yaml/generateAllYaml.d.ts +9 -0
- package/dist/lib/assets/yaml/generateAllYaml.d.ts.map +1 -0
- package/dist/lib/assets/yaml/generateZarfYaml.d.ts +5 -0
- package/dist/lib/assets/yaml/generateZarfYaml.d.ts.map +1 -0
- package/dist/lib/assets/yaml/overridesFile.d.ts +15 -0
- package/dist/lib/assets/yaml/overridesFile.d.ts.map +1 -0
- package/dist/lib/core/module.d.ts.map +1 -1
- package/dist/lib/enums.d.ts +4 -0
- package/dist/lib/enums.d.ts.map +1 -1
- package/dist/lib/processors/mutate-processor.d.ts +2 -0
- package/dist/lib/processors/mutate-processor.d.ts.map +1 -1
- package/dist/lib/processors/validate-processor.d.ts.map +1 -1
- package/dist/lib/telemetry/timeUtils.d.ts +2 -0
- package/dist/lib/telemetry/timeUtils.d.ts.map +1 -0
- package/dist/lib/telemetry/webhookTimeouts.d.ts +9 -0
- package/dist/lib/telemetry/webhookTimeouts.d.ts.map +1 -0
- package/dist/lib.js +79 -22
- package/dist/lib.js.map +4 -4
- package/package.json +5 -2
- package/src/cli/build.helpers.ts +7 -22
- package/src/cli/build.ts +72 -16
- package/src/cli/deploy.ts +7 -6
- package/src/cli/dev.ts +9 -6
- package/src/fixtures/loader.ts +2 -2
- package/src/lib/assets/assets.ts +66 -53
- package/src/lib/assets/deploy.ts +32 -30
- package/src/lib/assets/helm.ts +22 -4
- package/src/lib/assets/index.ts +33 -9
- package/src/lib/assets/pods.ts +2 -2
- package/src/lib/assets/webhooks.ts +21 -6
- package/src/lib/assets/yaml/generateAllYaml.ts +50 -0
- package/src/lib/assets/yaml/generateZarfYaml.ts +38 -0
- package/src/lib/assets/{yaml.ts → yaml/overridesFile.ts} +8 -120
- package/src/lib/core/module.ts +2 -1
- package/src/lib/enums.ts +6 -0
- package/src/lib/processors/mutate-processor.ts +15 -7
- package/src/lib/processors/validate-processor.ts +13 -4
- package/src/lib/telemetry/timeUtils.ts +1 -0
- package/src/lib/telemetry/webhookTimeouts.ts +34 -0
- package/dist/lib/assets/yaml.d.ts +0 -32
- package/dist/lib/assets/yaml.d.ts.map +0 -1
package/package.json
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"!src/**/*.test.ts",
|
|
16
16
|
"!dist/**/*.test.d.ts*"
|
|
17
17
|
],
|
|
18
|
-
"version": "0.
|
|
18
|
+
"version": "0.44.0",
|
|
19
19
|
"main": "dist/lib.js",
|
|
20
20
|
"types": "dist/lib.d.ts",
|
|
21
21
|
"scripts": {
|
|
@@ -27,6 +27,9 @@
|
|
|
27
27
|
"build:image": "npm run build && docker buildx build --output type=docker --tag pepr:dev .",
|
|
28
28
|
"test": "npm run test:unit && npm run test:journey",
|
|
29
29
|
"test:unit": "npm run gen-data-json && jest src --coverage --detectOpenHandles --coverageDirectory=./coverage --testPathIgnorePatterns='cosign.e2e.test.ts'",
|
|
30
|
+
"test:integration": "npm run test:integration:prep && npm run test:integration:run",
|
|
31
|
+
"test:integration:prep": "./integration/prep.sh",
|
|
32
|
+
"test:integration:run": "jest --maxWorkers=4 integration",
|
|
30
33
|
"test:journey": "npm run test:journey:k3d && npm run build && npm run test:journey:image && npm run test:journey:run",
|
|
31
34
|
"test:journey:prep": "if [ ! -d ./pepr-upgrade-test ]; then git clone https://github.com/defenseunicorns/pepr-upgrade-test.git ; fi",
|
|
32
35
|
"test:journey-wasm": "npm run test:journey:k3d && npm run build && npm run test:journey:image && npm run test:journey:run-wasm",
|
|
@@ -46,7 +49,7 @@
|
|
|
46
49
|
"follow-redirects": "1.15.9",
|
|
47
50
|
"http-status-codes": "^2.3.0",
|
|
48
51
|
"json-pointer": "^0.6.2",
|
|
49
|
-
"kubernetes-fluent-client": "3.3.
|
|
52
|
+
"kubernetes-fluent-client": "3.3.8",
|
|
50
53
|
"pino": "9.6.0",
|
|
51
54
|
"pino-pretty": "13.0.0",
|
|
52
55
|
"prom-client": "15.1.3",
|
package/src/cli/build.helpers.ts
CHANGED
|
@@ -8,6 +8,9 @@ import { BuildOptions, BuildResult, context, BuildContext } from "esbuild";
|
|
|
8
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
14
|
|
|
12
15
|
export type Reloader = (opts: BuildResult<BuildOptions>) => void | Promise<void>;
|
|
13
16
|
/**
|
|
@@ -89,24 +92,6 @@ export function validImagePullSecret(imagePullSecretName: string): void {
|
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
94
|
|
|
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
95
|
/**
|
|
111
96
|
* Creates and pushes a custom image for WASM or any other included files
|
|
112
97
|
* @param includedFiles
|
|
@@ -191,18 +176,18 @@ export async function generateYamlAndWriteToDisk(obj: {
|
|
|
191
176
|
const yamlFile = `pepr-module-${uuid}.yaml`;
|
|
192
177
|
const chartPath = `${uuid}-chart`;
|
|
193
178
|
const yamlPath = resolve(outputDir, yamlFile);
|
|
194
|
-
const yaml = await assets.allYaml(imagePullSecret);
|
|
179
|
+
const yaml = await assets.allYaml(generateAllYaml, imagePullSecret);
|
|
195
180
|
const zarfPath = resolve(outputDir, "zarf.yaml");
|
|
196
181
|
|
|
197
182
|
let localZarf = "";
|
|
198
183
|
if (zarf === "chart") {
|
|
199
|
-
localZarf = assets.zarfYamlChart(chartPath);
|
|
184
|
+
localZarf = assets.zarfYamlChart(generateZarfYamlGeneric, chartPath);
|
|
200
185
|
} else {
|
|
201
|
-
localZarf = assets.zarfYaml(yamlFile);
|
|
186
|
+
localZarf = assets.zarfYaml(generateZarfYamlGeneric, yamlFile);
|
|
202
187
|
}
|
|
203
188
|
await fs.writeFile(yamlPath, yaml);
|
|
204
189
|
await fs.writeFile(zarfPath, localZarf);
|
|
205
190
|
|
|
206
|
-
await assets.generateHelmChart(outputDir);
|
|
191
|
+
await assets.generateHelmChart(webhookConfigGenerator, outputDir);
|
|
207
192
|
console.info(`✅ K8s resource for the module saved to ${yamlPath}`);
|
|
208
193
|
}
|
package/src/cli/build.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
3
|
|
|
4
4
|
import { execFileSync } from "child_process";
|
|
5
|
-
import { BuildOptions, BuildResult, analyzeMetafile } from "esbuild";
|
|
5
|
+
import { BuildContext, BuildOptions, BuildResult, analyzeMetafile } from "esbuild";
|
|
6
6
|
import { promises as fs } from "fs";
|
|
7
7
|
import { basename, dirname, extname, resolve } from "path";
|
|
8
8
|
import { Assets } from "../lib/assets/assets";
|
|
@@ -17,17 +17,55 @@ import {
|
|
|
17
17
|
handleEmbedding,
|
|
18
18
|
handleCustomOutputDir,
|
|
19
19
|
handleValidCapabilityNames,
|
|
20
|
-
handleCustomImage,
|
|
21
20
|
handleCustomImageBuild,
|
|
22
21
|
checkIronBankImage,
|
|
23
22
|
validImagePullSecret,
|
|
24
23
|
generateYamlAndWriteToDisk,
|
|
25
24
|
} from "./build.helpers";
|
|
25
|
+
import { ModuleConfig } from "../lib/core/module";
|
|
26
26
|
|
|
27
27
|
const peprTS = "pepr.ts";
|
|
28
28
|
let outputDir: string = "dist";
|
|
29
29
|
export type Reloader = (opts: BuildResult<BuildOptions>) => void | Promise<void>;
|
|
30
30
|
|
|
31
|
+
export type PeprNestedFields = Pick<
|
|
32
|
+
ModuleConfig,
|
|
33
|
+
| "uuid"
|
|
34
|
+
| "onError"
|
|
35
|
+
| "webhookTimeout"
|
|
36
|
+
| "customLabels"
|
|
37
|
+
| "alwaysIgnore"
|
|
38
|
+
| "env"
|
|
39
|
+
| "rbac"
|
|
40
|
+
| "rbacMode"
|
|
41
|
+
> & {
|
|
42
|
+
peprVersion: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type PeprConfig = Omit<ModuleConfig, keyof PeprNestedFields> & {
|
|
46
|
+
pepr: PeprNestedFields & {
|
|
47
|
+
includedFiles: string[];
|
|
48
|
+
};
|
|
49
|
+
description: string;
|
|
50
|
+
version: string;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
type LoadModuleReturn = {
|
|
54
|
+
cfg: PeprConfig;
|
|
55
|
+
entryPointPath: string;
|
|
56
|
+
modulePath: string;
|
|
57
|
+
name: string;
|
|
58
|
+
path: string;
|
|
59
|
+
uuid: string;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
type BuildModuleReturn = {
|
|
63
|
+
ctx: BuildContext<BuildOptions>;
|
|
64
|
+
path: string;
|
|
65
|
+
cfg: PeprConfig;
|
|
66
|
+
uuid: string;
|
|
67
|
+
} | void;
|
|
68
|
+
|
|
31
69
|
export default function (program: RootCmd): void {
|
|
32
70
|
program
|
|
33
71
|
.command("build")
|
|
@@ -37,34 +75,44 @@ export default function (program: RootCmd): void {
|
|
|
37
75
|
"-n, --no-embed",
|
|
38
76
|
"Disables embedding of deployment files into output module. Useful when creating library modules intended solely for reuse/distribution via NPM.",
|
|
39
77
|
)
|
|
40
|
-
.
|
|
41
|
-
|
|
42
|
-
|
|
78
|
+
.addOption(
|
|
79
|
+
new Option(
|
|
80
|
+
"-i, --custom-image <custom-image>",
|
|
81
|
+
"Specify a custom image (including version) for Admission and Watch Deployments. Example: 'docker.io/username/custom-pepr-controller:v1.0.0'",
|
|
82
|
+
).conflicts(["version", "registryInfo", "registry"]),
|
|
43
83
|
)
|
|
44
|
-
.
|
|
45
|
-
|
|
46
|
-
|
|
84
|
+
.addOption(
|
|
85
|
+
new Option(
|
|
86
|
+
"-r, --registry-info [<registry>/<username>]",
|
|
87
|
+
"Provide the image registry and username for building and pushing a custom WASM container. Requires authentication. Builds and pushes 'registry/username/custom-pepr-controller:<current-version>'.",
|
|
88
|
+
).conflicts(["customImage", "version", "registry"]),
|
|
47
89
|
)
|
|
90
|
+
|
|
48
91
|
.option("-o, --output-dir <output directory>", "Define where to place build output")
|
|
49
92
|
.option(
|
|
50
93
|
"--timeout <timeout>",
|
|
51
94
|
"How long the API server should wait for a webhook to respond before treating the call as a failure",
|
|
52
95
|
parseTimeout,
|
|
53
96
|
)
|
|
54
|
-
.
|
|
55
|
-
|
|
56
|
-
|
|
97
|
+
.addOption(
|
|
98
|
+
new Option(
|
|
99
|
+
"-v, --version <version>",
|
|
100
|
+
"The version of the Pepr image to use in the deployment manifests. Example: '0.27.3'.",
|
|
101
|
+
).conflicts(["customImage", "registryInfo"]),
|
|
57
102
|
)
|
|
58
103
|
.option(
|
|
59
104
|
"--withPullSecret <imagePullSecret>",
|
|
60
105
|
"Image Pull Secret: Use image pull secret for controller Deployment.",
|
|
106
|
+
"",
|
|
61
107
|
)
|
|
62
108
|
|
|
63
109
|
.addOption(
|
|
64
110
|
new Option(
|
|
65
111
|
"--registry <GitHub|Iron Bank>",
|
|
66
112
|
"Container registry: Choose container registry for deployment manifests. Can't be used with --custom-image.",
|
|
67
|
-
)
|
|
113
|
+
)
|
|
114
|
+
.conflicts(["customImage", "registryInfo"])
|
|
115
|
+
.choices(["GitHub", "Iron Bank"]),
|
|
68
116
|
)
|
|
69
117
|
|
|
70
118
|
.addOption(
|
|
@@ -91,7 +139,7 @@ export default function (program: RootCmd): void {
|
|
|
91
139
|
// Files to include in controller image for WASM support
|
|
92
140
|
const { includedFiles } = cfg.pepr;
|
|
93
141
|
|
|
94
|
-
let image =
|
|
142
|
+
let image = opts.customImage || "";
|
|
95
143
|
|
|
96
144
|
// Check if there is a custom timeout defined
|
|
97
145
|
if (opts.timeout !== undefined) {
|
|
@@ -120,10 +168,14 @@ export default function (program: RootCmd): void {
|
|
|
120
168
|
...cfg.pepr,
|
|
121
169
|
appVersion: cfg.version,
|
|
122
170
|
description: cfg.description,
|
|
171
|
+
alwaysIgnore: {
|
|
172
|
+
namespaces: cfg.pepr.alwaysIgnore?.namespaces,
|
|
173
|
+
},
|
|
123
174
|
// Can override the rbacMode with the CLI option
|
|
124
175
|
rbacMode: determineRbacMode(opts, cfg),
|
|
125
176
|
},
|
|
126
177
|
path,
|
|
178
|
+
opts.withPullSecret === "" ? [] : [opts.withPullSecret],
|
|
127
179
|
);
|
|
128
180
|
|
|
129
181
|
// If registry is set to Iron Bank, use Iron Bank image
|
|
@@ -156,7 +208,7 @@ externalLibs.push("pepr");
|
|
|
156
208
|
// Add the kubernetes client to the list of external libraries as it is pulled in by kubernetes-fluent-client
|
|
157
209
|
externalLibs.push("@kubernetes/client-node");
|
|
158
210
|
|
|
159
|
-
export async function loadModule(entryPoint = peprTS) {
|
|
211
|
+
export async function loadModule(entryPoint = peprTS): Promise<LoadModuleReturn> {
|
|
160
212
|
// Resolve path to the module / files
|
|
161
213
|
const entryPointPath = resolve(".", entryPoint);
|
|
162
214
|
const modulePath = dirname(entryPointPath);
|
|
@@ -197,7 +249,11 @@ export async function loadModule(entryPoint = peprTS) {
|
|
|
197
249
|
};
|
|
198
250
|
}
|
|
199
251
|
|
|
200
|
-
export async function buildModule(
|
|
252
|
+
export async function buildModule(
|
|
253
|
+
reloader?: Reloader,
|
|
254
|
+
entryPoint = peprTS,
|
|
255
|
+
embed = true,
|
|
256
|
+
): Promise<BuildModuleReturn> {
|
|
201
257
|
try {
|
|
202
258
|
const { cfg, modulePath, path, uuid } = await loadModule(entryPoint);
|
|
203
259
|
|
|
@@ -314,7 +370,7 @@ function handleModuleBuildError(e: BuildModuleResult): void {
|
|
|
314
370
|
}
|
|
315
371
|
}
|
|
316
372
|
|
|
317
|
-
export async function checkFormat() {
|
|
373
|
+
export async function checkFormat(): Promise<void> {
|
|
318
374
|
const validFormat = await peprFormat(true);
|
|
319
375
|
|
|
320
376
|
if (!validFormat) {
|
package/src/cli/deploy.ts
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
import prompt from "prompts";
|
|
5
5
|
|
|
6
6
|
import { Assets } from "../lib/assets/assets";
|
|
7
|
-
import { buildModule } from "./build";
|
|
8
|
-
import { RootCmd } from "./root";
|
|
9
|
-
import { validateCapabilityNames } from "../lib/helpers";
|
|
10
7
|
import { ImagePullSecret } from "../lib/types";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
8
|
+
import { RootCmd } from "./root";
|
|
9
|
+
import { buildModule } from "./build";
|
|
10
|
+
import { deployImagePullSecret, deployWebhook } from "../lib/assets/deploy";
|
|
13
11
|
import { namespaceDeploymentsReady } from "../lib/deploymentChecks";
|
|
12
|
+
import { sanitizeName } from "./init/utils";
|
|
13
|
+
import { validateCapabilityNames } from "../lib/helpers";
|
|
14
14
|
|
|
15
15
|
export interface ImagePullSecretDetails {
|
|
16
16
|
pullSecret?: string;
|
|
@@ -128,11 +128,12 @@ export default function (program: RootCmd): void {
|
|
|
128
128
|
const webhook = new Assets(
|
|
129
129
|
{ ...builtModule.cfg.pepr, description: builtModule.cfg.description },
|
|
130
130
|
builtModule.path,
|
|
131
|
+
[],
|
|
131
132
|
);
|
|
132
133
|
webhook.image = opts.image ?? webhook.image;
|
|
133
134
|
|
|
134
135
|
try {
|
|
135
|
-
await webhook.deploy(opts.force, builtModule.cfg.pepr.webhookTimeout ?? 10);
|
|
136
|
+
await webhook.deploy(deployWebhook, opts.force, builtModule.cfg.pepr.webhookTimeout ?? 10);
|
|
136
137
|
|
|
137
138
|
// wait for capabilities to be loaded and test names
|
|
138
139
|
validateCapabilityNames(webhook.capabilities);
|
package/src/cli/dev.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
3
|
|
|
4
|
-
import { ChildProcess, fork } from "child_process";
|
|
5
|
-
import { promises as fs } from "fs";
|
|
6
4
|
import prompt from "prompts";
|
|
7
|
-
import { validateCapabilityNames } from "../lib/helpers";
|
|
8
5
|
import { Assets } from "../lib/assets/assets";
|
|
9
|
-
import {
|
|
10
|
-
import { RootCmd } from "./root";
|
|
6
|
+
import { ChildProcess, fork } from "child_process";
|
|
11
7
|
import { K8s, kind } from "kubernetes-fluent-client";
|
|
8
|
+
import { RootCmd } from "./root";
|
|
12
9
|
import { Store } from "../lib/k8s";
|
|
10
|
+
import { buildModule, loadModule } from "./build";
|
|
11
|
+
import { deployWebhook } from "../lib/assets/deploy";
|
|
12
|
+
import { promises as fs } from "fs";
|
|
13
|
+
import { validateCapabilityNames } from "../lib/helpers";
|
|
14
|
+
|
|
13
15
|
export default function (program: RootCmd): void {
|
|
14
16
|
program
|
|
15
17
|
.command("dev")
|
|
@@ -41,6 +43,7 @@ export default function (program: RootCmd): void {
|
|
|
41
43
|
description: cfg.description,
|
|
42
44
|
},
|
|
43
45
|
path,
|
|
46
|
+
[],
|
|
44
47
|
opts.host,
|
|
45
48
|
);
|
|
46
49
|
|
|
@@ -59,7 +62,7 @@ export default function (program: RootCmd): void {
|
|
|
59
62
|
console.info(`Running module ${path}`);
|
|
60
63
|
|
|
61
64
|
// Deploy the webhook with a 30 second timeout for debugging, don't force
|
|
62
|
-
await webhook.deploy(false, 30);
|
|
65
|
+
await webhook.deploy(deployWebhook, false, 30);
|
|
63
66
|
|
|
64
67
|
try {
|
|
65
68
|
// wait for capabilities to be loaded and test names
|
package/src/fixtures/loader.ts
CHANGED
|
@@ -6,11 +6,11 @@ import admissionRequestDeletePod from "./data/admission-delete-pod.json";
|
|
|
6
6
|
import admissionRequestCreateClusterRole from "./data/admission-create-clusterrole.json";
|
|
7
7
|
import admissionRequestCreateDeployment from "./data/admission-create-deployment.json";
|
|
8
8
|
|
|
9
|
-
export function AdmissionRequestCreateDeployment() {
|
|
9
|
+
export function AdmissionRequestCreateDeployment(): AdmissionRequest<kind.Deployment> {
|
|
10
10
|
return cloneObject<kind.Deployment>(admissionRequestCreateDeployment);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function AdmissionRequestCreatePod() {
|
|
13
|
+
export function AdmissionRequestCreatePod(): AdmissionRequest<kind.Pod> {
|
|
14
14
|
return cloneObject<kind.Pod>(admissionRequestCreatePod);
|
|
15
15
|
}
|
|
16
16
|
|
package/src/lib/assets/assets.ts
CHANGED
|
@@ -11,92 +11,97 @@ import {
|
|
|
11
11
|
serviceMonitorTemplate,
|
|
12
12
|
watcherDeployTemplate,
|
|
13
13
|
} from "./helm";
|
|
14
|
+
import {
|
|
15
|
+
V1Deployment,
|
|
16
|
+
V1MutatingWebhookConfiguration,
|
|
17
|
+
V1ValidatingWebhookConfiguration,
|
|
18
|
+
} from "@kubernetes/client-node/dist/gen";
|
|
14
19
|
import { createDirectoryIfNotExists } from "../filesystemService";
|
|
15
|
-
import {
|
|
20
|
+
import { overridesFile } from "./yaml/overridesFile";
|
|
16
21
|
import { getDeployment, getModuleSecret, getWatcher } from "./pods";
|
|
17
22
|
import { helmLayout, createWebhookYaml, toYaml } from "./index";
|
|
18
23
|
import { loadCapabilities } from "./loader";
|
|
19
24
|
import { namespaceComplianceValidator, dedent } from "../helpers";
|
|
25
|
+
import { promises as fs } from "fs";
|
|
20
26
|
import { storeRole, storeRoleBinding, clusterRoleBinding, serviceAccount } from "./rbac";
|
|
21
27
|
import { watcherService, service, tlsSecret, apiTokenSecret } from "./networking";
|
|
22
|
-
import {
|
|
23
|
-
import { generateZarfYaml, generateZarfYamlChart, generateAllYaml, overridesFile } from "./yaml";
|
|
24
|
-
import { promises as fs } from "fs";
|
|
25
|
-
import { V1MutatingWebhookConfiguration, V1ValidatingWebhookConfiguration } from "@kubernetes/client-node/dist/gen";
|
|
28
|
+
import { WebhookType } from "../enums";
|
|
26
29
|
|
|
27
30
|
export class Assets {
|
|
28
31
|
readonly name: string;
|
|
29
32
|
readonly tls: TLSOut;
|
|
30
33
|
readonly apiToken: string;
|
|
34
|
+
readonly config: ModuleConfig;
|
|
35
|
+
readonly path: string;
|
|
31
36
|
readonly alwaysIgnore!: WebhookIgnore;
|
|
37
|
+
readonly imagePullSecrets: string[];
|
|
32
38
|
capabilities!: CapabilityExport[];
|
|
33
|
-
|
|
34
39
|
image: string;
|
|
35
40
|
buildTimestamp: string;
|
|
36
|
-
|
|
41
|
+
readonly host?: string;
|
|
37
42
|
|
|
38
|
-
constructor(
|
|
39
|
-
readonly config: ModuleConfig,
|
|
40
|
-
readonly path: string,
|
|
41
|
-
readonly host?: string,
|
|
42
|
-
) {
|
|
43
|
+
constructor(config: ModuleConfig, path: string, imagePullSecrets: string[], host?: string) {
|
|
43
44
|
this.name = `pepr-${config.uuid}`;
|
|
45
|
+
this.imagePullSecrets = imagePullSecrets;
|
|
44
46
|
this.buildTimestamp = `${Date.now()}`;
|
|
47
|
+
this.config = config;
|
|
48
|
+
this.path = path;
|
|
49
|
+
this.host = host;
|
|
45
50
|
this.alwaysIgnore = config.alwaysIgnore;
|
|
46
51
|
this.image = `ghcr.io/defenseunicorns/pepr/controller:v${config.peprVersion}`;
|
|
47
|
-
|
|
52
|
+
|
|
48
53
|
// Generate the ephemeral tls things
|
|
49
|
-
this.tls = genTLS(
|
|
54
|
+
this.tls = genTLS(host || `${this.name}.pepr-system.svc`);
|
|
50
55
|
|
|
51
56
|
// Generate the api token for the controller / webhook
|
|
52
57
|
this.apiToken = crypto.randomBytes(32).toString("hex");
|
|
53
58
|
}
|
|
54
59
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
async deploy(
|
|
61
|
+
deployFunction: (assets: Assets, force: boolean, webhookTimeout: number) => Promise<void>,
|
|
62
|
+
force: boolean,
|
|
63
|
+
webhookTimeout?: number,
|
|
64
|
+
): Promise<void> {
|
|
60
65
|
this.capabilities = await loadCapabilities(this.path);
|
|
61
|
-
await deploy(this, force, webhookTimeout);
|
|
62
|
-
};
|
|
63
66
|
|
|
64
|
-
|
|
67
|
+
const timeout = typeof webhookTimeout === "number" ? webhookTimeout : 10;
|
|
65
68
|
|
|
66
|
-
|
|
69
|
+
await deployFunction(this, force, timeout);
|
|
70
|
+
}
|
|
67
71
|
|
|
68
|
-
|
|
72
|
+
zarfYaml = (
|
|
73
|
+
zarfYamlGenerator: (assets: Assets, path: string, type: "manifests" | "charts") => string,
|
|
74
|
+
path: string,
|
|
75
|
+
): string => zarfYamlGenerator(this, path, "manifests");
|
|
76
|
+
|
|
77
|
+
zarfYamlChart = (
|
|
78
|
+
zarfYamlGenerator: (assets: Assets, path: string, type: "manifests" | "charts") => string,
|
|
79
|
+
path: string,
|
|
80
|
+
): string => zarfYamlGenerator(this, path, "charts");
|
|
81
|
+
|
|
82
|
+
allYaml = async (
|
|
83
|
+
yamlGenerationFunction: (
|
|
84
|
+
assyts: Assets,
|
|
85
|
+
deployments: { default: V1Deployment; watch: V1Deployment | null },
|
|
86
|
+
) => Promise<string>,
|
|
87
|
+
imagePullSecret?: string,
|
|
88
|
+
): Promise<string> => {
|
|
69
89
|
this.capabilities = await loadCapabilities(this.path);
|
|
70
90
|
// give error if namespaces are not respected
|
|
71
91
|
for (const capability of this.capabilities) {
|
|
72
92
|
namespaceComplianceValidator(capability, this.alwaysIgnore?.namespaces);
|
|
73
93
|
}
|
|
74
94
|
|
|
75
|
-
const webhooks = {
|
|
76
|
-
mutate: await webhookConfig(this, "mutate", this.config.webhookTimeout),
|
|
77
|
-
validate: await webhookConfig(this, "validate", this.config.webhookTimeout),
|
|
78
|
-
};
|
|
79
|
-
|
|
80
95
|
const code = await fs.readFile(this.path);
|
|
81
96
|
|
|
82
|
-
|
|
83
|
-
this.hash = crypto.createHash("sha256").update(code).digest("hex");
|
|
97
|
+
const moduleHash = crypto.createHash("sha256").update(code).digest("hex");
|
|
84
98
|
|
|
85
99
|
const deployments = {
|
|
86
|
-
default: getDeployment(this,
|
|
87
|
-
watch: getWatcher(this,
|
|
100
|
+
default: getDeployment(this, moduleHash, this.buildTimestamp, imagePullSecret),
|
|
101
|
+
watch: getWatcher(this, moduleHash, this.buildTimestamp, imagePullSecret),
|
|
88
102
|
};
|
|
89
103
|
|
|
90
|
-
|
|
91
|
-
apiToken: this.apiToken,
|
|
92
|
-
capabilities: this.capabilities,
|
|
93
|
-
config: this.config,
|
|
94
|
-
hash: this.hash,
|
|
95
|
-
name: this.name,
|
|
96
|
-
path: this.path,
|
|
97
|
-
tls: this.tls,
|
|
98
|
-
};
|
|
99
|
-
return generateAllYaml(webhooks, deployments, assetsInputs);
|
|
104
|
+
return yamlGenerationFunction(this, deployments);
|
|
100
105
|
};
|
|
101
106
|
|
|
102
107
|
writeWebhookFiles = async (
|
|
@@ -118,7 +123,14 @@ export class Assets {
|
|
|
118
123
|
}
|
|
119
124
|
};
|
|
120
125
|
|
|
121
|
-
generateHelmChart = async (
|
|
126
|
+
generateHelmChart = async (
|
|
127
|
+
webhookGeneratorFunction: (
|
|
128
|
+
assets: Assets,
|
|
129
|
+
mutateOrValidate: WebhookType,
|
|
130
|
+
timeoutSeconds: number | undefined,
|
|
131
|
+
) => Promise<V1MutatingWebhookConfiguration | V1ValidatingWebhookConfiguration | null>,
|
|
132
|
+
basePath: string,
|
|
133
|
+
): Promise<void> => {
|
|
122
134
|
const helm = helmLayout(basePath, this.config.uuid);
|
|
123
135
|
|
|
124
136
|
try {
|
|
@@ -129,6 +141,7 @@ export class Assets {
|
|
|
129
141
|
);
|
|
130
142
|
|
|
131
143
|
const code = await fs.readFile(this.path);
|
|
144
|
+
const moduleHash = crypto.createHash("sha256").update(code).digest("hex");
|
|
132
145
|
|
|
133
146
|
const pairs: [string, () => string][] = [
|
|
134
147
|
[helm.files.chartYaml, (): string => dedent(chartYaml(this.config.uuid, this.config.description || ""))],
|
|
@@ -142,28 +155,28 @@ export class Assets {
|
|
|
142
155
|
[helm.files.clusterRoleYaml, (): string => dedent(clusterRoleTemplate())],
|
|
143
156
|
[helm.files.clusterRoleBindingYaml, (): string => toYaml(clusterRoleBinding(this.name))],
|
|
144
157
|
[helm.files.serviceAccountYaml, (): string => toYaml(serviceAccount(this.name))],
|
|
145
|
-
[helm.files.moduleSecretYaml, (): string => toYaml(getModuleSecret(this.name, code,
|
|
158
|
+
[helm.files.moduleSecretYaml, (): string => toYaml(getModuleSecret(this.name, code, moduleHash))],
|
|
146
159
|
];
|
|
147
160
|
await Promise.all(pairs.map(async ([file, content]) => await fs.writeFile(file, content())));
|
|
148
161
|
|
|
149
162
|
const overrideData = {
|
|
150
|
-
hash:
|
|
163
|
+
hash: moduleHash,
|
|
151
164
|
name: this.name,
|
|
152
165
|
image: this.image,
|
|
153
166
|
config: this.config,
|
|
154
167
|
apiToken: this.apiToken,
|
|
155
168
|
capabilities: this.capabilities,
|
|
156
169
|
};
|
|
157
|
-
await overridesFile(overrideData, helm.files.valuesYaml);
|
|
170
|
+
await overridesFile(overrideData, helm.files.valuesYaml, this.imagePullSecrets);
|
|
158
171
|
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
172
|
+
const webhooks = {
|
|
173
|
+
mutate: await webhookGeneratorFunction(this, WebhookType.MUTATE, this.config.webhookTimeout),
|
|
174
|
+
validate: await webhookGeneratorFunction(this, WebhookType.VALIDATE, this.config.webhookTimeout),
|
|
175
|
+
};
|
|
163
176
|
|
|
164
|
-
await this.writeWebhookFiles(
|
|
177
|
+
await this.writeWebhookFiles(webhooks.validate, webhooks.mutate, helm);
|
|
165
178
|
|
|
166
|
-
const watchDeployment = getWatcher(this,
|
|
179
|
+
const watchDeployment = getWatcher(this, moduleHash, this.buildTimestamp);
|
|
167
180
|
if (watchDeployment) {
|
|
168
181
|
await fs.writeFile(helm.files.watcherDeploymentYaml, dedent(watcherDeployTemplate(this.buildTimestamp)));
|
|
169
182
|
await fs.writeFile(helm.files.watcherServiceMonitorYaml, dedent(serviceMonitorTemplate("watcher")));
|
package/src/lib/assets/deploy.ts
CHANGED
|
@@ -12,8 +12,9 @@ import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking
|
|
|
12
12
|
import { getDeployment, getModuleSecret, getNamespace, getWatcher } from "./pods";
|
|
13
13
|
import { clusterRole, clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "./rbac";
|
|
14
14
|
import { peprStoreCRD } from "./store";
|
|
15
|
-
import {
|
|
15
|
+
import { webhookConfigGenerator } from "./webhooks";
|
|
16
16
|
import { CapabilityExport, ImagePullSecret } from "../types";
|
|
17
|
+
import { WebhookType } from "../enums";
|
|
17
18
|
|
|
18
19
|
export async function deployImagePullSecret(imagePullSecret: ImagePullSecret, name: string): Promise<void> {
|
|
19
20
|
try {
|
|
@@ -42,50 +43,51 @@ export async function deployImagePullSecret(imagePullSecret: ImagePullSecret, na
|
|
|
42
43
|
Log.error(e);
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
|
-
export async function deploy(assets: Assets, force: boolean, webhookTimeout?: number): Promise<void> {
|
|
46
|
-
Log.info("Establishing connection to Kubernetes");
|
|
47
46
|
|
|
48
|
-
|
|
47
|
+
async function handleWebhookConfiguration(
|
|
48
|
+
assets: Assets,
|
|
49
|
+
type: WebhookType,
|
|
50
|
+
webhookTimeout: number,
|
|
51
|
+
force: boolean,
|
|
52
|
+
): Promise<void> {
|
|
53
|
+
const kindMap = {
|
|
54
|
+
mutate: kind.MutatingWebhookConfiguration,
|
|
55
|
+
validate: kind.ValidatingWebhookConfiguration,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const webhookConfig = await webhookConfigGenerator(assets, type, webhookTimeout);
|
|
59
|
+
|
|
60
|
+
if (webhookConfig) {
|
|
61
|
+
Log.info(`Applying ${type} webhook`);
|
|
62
|
+
await K8s(kindMap[type]).Apply(webhookConfig, { force });
|
|
63
|
+
} else {
|
|
64
|
+
Log.info(`${type.charAt(0).toUpperCase() + type.slice(1)} webhook not needed, removing if it exists`);
|
|
65
|
+
await K8s(kindMap[type]).Delete(assets.name);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function deployWebhook(assets: Assets, force: boolean, webhookTimeout: number): Promise<void> {
|
|
70
|
+
Log.info("Establishing connection to Kubernetes");
|
|
49
71
|
|
|
50
72
|
Log.info("Applying pepr-system namespace");
|
|
51
73
|
await K8s(kind.Namespace).Apply(getNamespace(assets.config.customLabels?.namespace));
|
|
52
74
|
|
|
53
75
|
// Create the mutating webhook configuration if it is needed
|
|
54
|
-
|
|
55
|
-
if (mutateWebhook) {
|
|
56
|
-
Log.info("Applying mutating webhook");
|
|
57
|
-
await K8s(kind.MutatingWebhookConfiguration).Apply(mutateWebhook, { force });
|
|
58
|
-
} else {
|
|
59
|
-
Log.info("Mutating webhook not needed, removing if it exists");
|
|
60
|
-
await K8s(kind.MutatingWebhookConfiguration).Delete(name);
|
|
61
|
-
}
|
|
76
|
+
await handleWebhookConfiguration(assets, WebhookType.MUTATE, webhookTimeout, force);
|
|
62
77
|
|
|
63
78
|
// Create the validating webhook configuration if it is needed
|
|
64
|
-
|
|
65
|
-
if (validateWebhook) {
|
|
66
|
-
Log.info("Applying validating webhook");
|
|
67
|
-
await K8s(kind.ValidatingWebhookConfiguration).Apply(validateWebhook, { force });
|
|
68
|
-
} else {
|
|
69
|
-
Log.info("Validating webhook not needed, removing if it exists");
|
|
70
|
-
await K8s(kind.ValidatingWebhookConfiguration).Delete(name);
|
|
71
|
-
}
|
|
79
|
+
await handleWebhookConfiguration(assets, WebhookType.VALIDATE, webhookTimeout, force);
|
|
72
80
|
|
|
73
81
|
Log.info("Applying the Pepr Store CRD if it doesn't exist");
|
|
74
82
|
await K8s(kind.CustomResourceDefinition).Apply(peprStoreCRD, { force });
|
|
75
83
|
|
|
76
|
-
|
|
77
|
-
if (host) {
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
84
|
+
if (assets.host) return; // Skip resource deployment if a host is already specified
|
|
80
85
|
|
|
81
|
-
const code = await fs.readFile(path);
|
|
86
|
+
const code = await fs.readFile(assets.path);
|
|
87
|
+
if (!code.length) throw new Error("No code provided");
|
|
82
88
|
const hash = crypto.createHash("sha256").update(code).digest("hex");
|
|
83
89
|
|
|
84
|
-
|
|
85
|
-
throw new Error("No code provided");
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
await setupRBAC(name, assets.capabilities, force, assets.config);
|
|
90
|
+
await setupRBAC(assets.name, assets.capabilities, force, assets.config);
|
|
89
91
|
await setupController(assets, code, hash, force);
|
|
90
92
|
await setupWatcher(assets, hash, force);
|
|
91
93
|
}
|