pepr 0.44.0 → 0.45.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/README.md +1 -1
- package/dist/cli/build.d.ts +2 -2
- package/dist/cli/build.d.ts.map +1 -1
- package/dist/cli/build.helpers.d.ts +13 -7
- package/dist/cli/build.helpers.d.ts.map +1 -1
- package/dist/cli/deploy.d.ts.map +1 -1
- package/dist/cli/init/templates.d.ts +7 -7
- package/dist/cli/init/templates.d.ts.map +1 -1
- package/dist/cli.js +119 -99
- package/dist/controller.js +1 -1
- package/dist/lib/assets/rbac.d.ts.map +1 -1
- package/dist/lib/assets/webhooks.d.ts +2 -0
- package/dist/lib/assets/webhooks.d.ts.map +1 -1
- package/dist/lib/assets/yaml/overridesFile.d.ts +1 -3
- package/dist/lib/assets/yaml/overridesFile.d.ts.map +1 -1
- package/dist/lib/controller/index.d.ts +6 -1
- package/dist/lib/controller/index.d.ts.map +1 -1
- package/dist/lib/core/module.d.ts +23 -20
- package/dist/lib/core/module.d.ts.map +1 -1
- package/dist/lib/core/storage.d.ts.map +1 -1
- package/dist/lib/processors/watch-processor.d.ts.map +1 -1
- package/dist/lib/telemetry/metrics.d.ts.map +1 -1
- package/dist/lib.js +42 -24
- package/dist/lib.js.map +3 -3
- package/package.json +14 -10
- package/src/cli/build.helpers.ts +28 -13
- package/src/cli/build.ts +64 -64
- package/src/cli/deploy.ts +32 -26
- package/src/cli/init/index.ts +2 -2
- package/src/cli/init/templates.ts +6 -5
- package/src/cli/init/walkthrough.ts +1 -1
- package/src/lib/assets/rbac.ts +1 -2
- package/src/lib/assets/webhooks.ts +1 -1
- package/src/lib/assets/yaml/overridesFile.ts +3 -5
- package/src/lib/controller/index.ts +17 -10
- package/src/lib/core/module.ts +41 -31
- package/src/lib/core/schedule.ts +2 -2
- package/src/lib/core/storage.ts +2 -1
- package/src/lib/processors/mutate-processor.ts +1 -1
- package/src/lib/processors/watch-processor.ts +5 -3
- package/src/lib/telemetry/metrics.ts +4 -6
- package/src/sdk/heredoc.ts +2 -2
package/package.json
CHANGED
|
@@ -15,16 +15,16 @@
|
|
|
15
15
|
"!src/**/*.test.ts",
|
|
16
16
|
"!dist/**/*.test.d.ts*"
|
|
17
17
|
],
|
|
18
|
-
"version": "0.
|
|
18
|
+
"version": "0.45.0",
|
|
19
19
|
"main": "dist/lib.js",
|
|
20
20
|
"types": "dist/lib.d.ts",
|
|
21
21
|
"scripts": {
|
|
22
22
|
"ci": "npm ci",
|
|
23
23
|
"gen-data-json": "node hack/build-template-data.js",
|
|
24
24
|
"prebuild": "rm -fr dist/* && npm run gen-data-json",
|
|
25
|
-
"version": "node scripts/set-version.js",
|
|
26
25
|
"build": "tsc && node build.mjs && npm pack",
|
|
27
26
|
"build:image": "npm run build && docker buildx build --output type=docker --tag pepr:dev .",
|
|
27
|
+
"set:version": "node scripts/set-version.js",
|
|
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
30
|
"test:integration": "npm run test:integration:prep && npm run test:integration:run",
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
"sigstore": "3.0.0"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@commitlint/cli": "19.
|
|
61
|
-
"@commitlint/config-conventional": "19.
|
|
60
|
+
"@commitlint/cli": "19.7.1",
|
|
61
|
+
"@commitlint/config-conventional": "19.7.1",
|
|
62
62
|
"@fast-check/jest": "^2.0.1",
|
|
63
63
|
"@jest/globals": "29.7.0",
|
|
64
64
|
"@types/eslint": "9.6.1",
|
|
@@ -72,20 +72,24 @@
|
|
|
72
72
|
"husky": "^9.1.6",
|
|
73
73
|
"jest": "29.7.0",
|
|
74
74
|
"js-yaml": "^4.1.0",
|
|
75
|
+
"shellcheck": "^3.0.0",
|
|
75
76
|
"ts-jest": "29.2.5",
|
|
76
77
|
"undici": "^7.0.1"
|
|
77
78
|
},
|
|
79
|
+
"overrides": {
|
|
80
|
+
"glob": "^9.0.0"
|
|
81
|
+
},
|
|
78
82
|
"peerDependencies": {
|
|
79
|
-
"@typescript-eslint/eslint-plugin": "7.18.0",
|
|
80
|
-
"@typescript-eslint/parser": "7.18.0",
|
|
81
83
|
"@types/prompts": "2.4.9",
|
|
84
|
+
"@typescript-eslint/eslint-plugin": "8.23.0",
|
|
85
|
+
"@typescript-eslint/parser": "8.23.0",
|
|
86
|
+
"commander": "13.1.0",
|
|
87
|
+
"esbuild": "0.24.2",
|
|
82
88
|
"eslint": "8.57.0",
|
|
83
|
-
"commander": "12.1.0",
|
|
84
|
-
"esbuild": "0.24.0",
|
|
85
89
|
"node-forge": "1.3.1",
|
|
86
90
|
"prettier": "3.4.2",
|
|
87
91
|
"prompts": "2.4.2",
|
|
88
|
-
"typescript": "
|
|
89
|
-
"uuid": "11.0.
|
|
92
|
+
"typescript": "5.7.3",
|
|
93
|
+
"uuid": "11.0.5"
|
|
90
94
|
}
|
|
91
95
|
}
|
package/src/cli/build.helpers.ts
CHANGED
|
@@ -12,6 +12,34 @@ import { generateAllYaml } from "../lib/assets/yaml/generateAllYaml";
|
|
|
12
12
|
import { webhookConfigGenerator } from "../lib/assets/webhooks";
|
|
13
13
|
import { generateZarfYamlGeneric } from "../lib/assets/yaml/generateZarfYaml";
|
|
14
14
|
|
|
15
|
+
interface ImageOptions {
|
|
16
|
+
customImage?: string;
|
|
17
|
+
registryInfo?: string;
|
|
18
|
+
peprVersion?: string;
|
|
19
|
+
registry?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Assign image string
|
|
23
|
+
* @param imageOptions CLI options for image
|
|
24
|
+
* @returns image string
|
|
25
|
+
*/
|
|
26
|
+
export function assignImage(imageOptions: ImageOptions): string {
|
|
27
|
+
const { customImage, registryInfo, peprVersion, registry } = imageOptions;
|
|
28
|
+
if (customImage) {
|
|
29
|
+
return customImage;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (registryInfo) {
|
|
33
|
+
return `${registryInfo}/custom-pepr-controller:${peprVersion}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (registry) {
|
|
37
|
+
return checkIronBankImage(registry, "", peprVersion!);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return "";
|
|
41
|
+
}
|
|
42
|
+
|
|
15
43
|
export type Reloader = (opts: BuildResult<BuildOptions>) => void | Promise<void>;
|
|
16
44
|
/**
|
|
17
45
|
* Determine the RBAC mode based on the CLI options and the module's config
|
|
@@ -114,19 +142,6 @@ export async function handleCustomImageBuild(
|
|
|
114
142
|
}
|
|
115
143
|
}
|
|
116
144
|
|
|
117
|
-
/**
|
|
118
|
-
* Disables embedding of deployment files into output module
|
|
119
|
-
* @param embed
|
|
120
|
-
* @param path
|
|
121
|
-
* @returns
|
|
122
|
-
*/
|
|
123
|
-
export function handleEmbedding(embed: boolean, path: string): void {
|
|
124
|
-
if (!embed) {
|
|
125
|
-
console.info(`✅ Module built successfully at ${path}`);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
145
|
/**
|
|
131
146
|
* Check if the capability names are valid
|
|
132
147
|
* @param capabilities The capabilities to check
|
package/src/cli/build.ts
CHANGED
|
@@ -11,23 +11,21 @@ import { RootCmd } from "./root";
|
|
|
11
11
|
import { Option } from "commander";
|
|
12
12
|
import { parseTimeout } from "../lib/helpers";
|
|
13
13
|
import { peprFormat } from "./format";
|
|
14
|
+
import { ModuleConfig } from "../lib/core/module";
|
|
14
15
|
import {
|
|
15
16
|
watchForChanges,
|
|
16
17
|
determineRbacMode,
|
|
17
|
-
|
|
18
|
+
assignImage,
|
|
18
19
|
handleCustomOutputDir,
|
|
19
20
|
handleValidCapabilityNames,
|
|
20
21
|
handleCustomImageBuild,
|
|
21
|
-
checkIronBankImage,
|
|
22
22
|
validImagePullSecret,
|
|
23
23
|
generateYamlAndWriteToDisk,
|
|
24
24
|
} from "./build.helpers";
|
|
25
|
-
import { ModuleConfig } from "../lib/core/module";
|
|
26
25
|
|
|
27
26
|
const peprTS = "pepr.ts";
|
|
28
27
|
let outputDir: string = "dist";
|
|
29
28
|
export type Reloader = (opts: BuildResult<BuildOptions>) => void | Promise<void>;
|
|
30
|
-
|
|
31
29
|
export type PeprNestedFields = Pick<
|
|
32
30
|
ModuleConfig,
|
|
33
31
|
| "uuid"
|
|
@@ -64,7 +62,7 @@ type BuildModuleReturn = {
|
|
|
64
62
|
path: string;
|
|
65
63
|
cfg: PeprConfig;
|
|
66
64
|
uuid: string;
|
|
67
|
-
}
|
|
65
|
+
};
|
|
68
66
|
|
|
69
67
|
export default function (program: RootCmd): void {
|
|
70
68
|
program
|
|
@@ -134,68 +132,70 @@ export default function (program: RootCmd): void {
|
|
|
134
132
|
|
|
135
133
|
// Build the module
|
|
136
134
|
const buildModuleResult = await buildModule(undefined, opts.entryPoint, opts.embed);
|
|
137
|
-
if (buildModuleResult?.cfg && buildModuleResult.path && buildModuleResult.uuid) {
|
|
138
|
-
const { cfg, path, uuid } = buildModuleResult;
|
|
139
|
-
// Files to include in controller image for WASM support
|
|
140
|
-
const { includedFiles } = cfg.pepr;
|
|
141
|
-
|
|
142
|
-
let image = opts.customImage || "";
|
|
143
|
-
|
|
144
|
-
// Check if there is a custom timeout defined
|
|
145
|
-
if (opts.timeout !== undefined) {
|
|
146
|
-
cfg.pepr.webhookTimeout = opts.timeout;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (opts.registryInfo !== undefined) {
|
|
150
|
-
console.info(`Including ${includedFiles.length} files in controller image.`);
|
|
151
|
-
|
|
152
|
-
// for journey test to make sure the image is built
|
|
153
|
-
image = `${opts.registryInfo}/custom-pepr-controller:${cfg.pepr.peprVersion}`;
|
|
154
|
-
|
|
155
|
-
// only actually build/push if there are files to include
|
|
156
|
-
await handleCustomImageBuild(includedFiles, cfg.pepr.peprVersion, cfg.description, image);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// If building without embedding, exit after building
|
|
160
|
-
handleEmbedding(opts.embed, path);
|
|
161
|
-
|
|
162
|
-
// set the image version if provided
|
|
163
|
-
opts.version ? (cfg.pepr.peprVersion = opts.version) : null;
|
|
164
|
-
|
|
165
|
-
// Generate a secret for the module
|
|
166
|
-
const assets = new Assets(
|
|
167
|
-
{
|
|
168
|
-
...cfg.pepr,
|
|
169
|
-
appVersion: cfg.version,
|
|
170
|
-
description: cfg.description,
|
|
171
|
-
alwaysIgnore: {
|
|
172
|
-
namespaces: cfg.pepr.alwaysIgnore?.namespaces,
|
|
173
|
-
},
|
|
174
|
-
// Can override the rbacMode with the CLI option
|
|
175
|
-
rbacMode: determineRbacMode(opts, cfg),
|
|
176
|
-
},
|
|
177
|
-
path,
|
|
178
|
-
opts.withPullSecret === "" ? [] : [opts.withPullSecret],
|
|
179
|
-
);
|
|
180
135
|
|
|
181
|
-
|
|
182
|
-
|
|
136
|
+
const { cfg, path, uuid } = buildModuleResult!;
|
|
137
|
+
const image = assignImage({
|
|
138
|
+
customImage: opts.customImage,
|
|
139
|
+
registryInfo: opts.registryInfo,
|
|
140
|
+
peprVersion: cfg.pepr.peprVersion,
|
|
141
|
+
registry: opts.registry,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Check if there is a custom timeout defined
|
|
145
|
+
if (opts.timeout !== undefined) {
|
|
146
|
+
cfg.pepr.webhookTimeout = opts.timeout;
|
|
147
|
+
}
|
|
183
148
|
|
|
184
|
-
|
|
185
|
-
|
|
149
|
+
if (opts.registryInfo !== undefined) {
|
|
150
|
+
console.info(`Including ${cfg.pepr.includedFiles.length} files in controller image.`);
|
|
151
|
+
// for journey test to make sure the image is built
|
|
186
152
|
|
|
187
|
-
//
|
|
188
|
-
|
|
153
|
+
// only actually build/push if there are files to include
|
|
154
|
+
await handleCustomImageBuild(
|
|
155
|
+
cfg.pepr.includedFiles,
|
|
156
|
+
cfg.pepr.peprVersion,
|
|
157
|
+
cfg.description,
|
|
158
|
+
image,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
189
161
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
imagePullSecret: opts.withPullSecret,
|
|
195
|
-
zarf: opts.zarf,
|
|
196
|
-
assets,
|
|
197
|
-
});
|
|
162
|
+
// If building without embedding, exit after building
|
|
163
|
+
if (!opts.embed) {
|
|
164
|
+
console.info(`✅ Module built successfully at ${path}`);
|
|
165
|
+
return;
|
|
198
166
|
}
|
|
167
|
+
// set the image version if provided
|
|
168
|
+
if (opts.version) cfg.pepr.peprVersion = opts.version;
|
|
169
|
+
|
|
170
|
+
// Generate a secret for the module
|
|
171
|
+
const assets = new Assets(
|
|
172
|
+
{
|
|
173
|
+
...cfg.pepr,
|
|
174
|
+
appVersion: cfg.version,
|
|
175
|
+
description: cfg.description,
|
|
176
|
+
alwaysIgnore: {
|
|
177
|
+
namespaces: cfg.pepr.alwaysIgnore?.namespaces,
|
|
178
|
+
},
|
|
179
|
+
// Can override the rbacMode with the CLI option
|
|
180
|
+
rbacMode: determineRbacMode(opts, cfg),
|
|
181
|
+
},
|
|
182
|
+
path,
|
|
183
|
+
opts.withPullSecret === "" ? [] : [opts.withPullSecret],
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
if (image !== "") assets.image = image;
|
|
187
|
+
|
|
188
|
+
// Ensure imagePullSecret is valid
|
|
189
|
+
validImagePullSecret(opts.withPullSecret);
|
|
190
|
+
|
|
191
|
+
handleValidCapabilityNames(assets.capabilities);
|
|
192
|
+
await generateYamlAndWriteToDisk({
|
|
193
|
+
uuid,
|
|
194
|
+
outputDir,
|
|
195
|
+
imagePullSecret: opts.withPullSecret,
|
|
196
|
+
zarf: opts.zarf,
|
|
197
|
+
assets,
|
|
198
|
+
});
|
|
199
199
|
});
|
|
200
200
|
}
|
|
201
201
|
|
|
@@ -218,7 +218,7 @@ export async function loadModule(entryPoint = peprTS): Promise<LoadModuleReturn>
|
|
|
218
218
|
try {
|
|
219
219
|
await fs.access(cfgPath);
|
|
220
220
|
await fs.access(entryPointPath);
|
|
221
|
-
} catch
|
|
221
|
+
} catch {
|
|
222
222
|
console.error(
|
|
223
223
|
`Could not find ${cfgPath} or ${entryPointPath} in the current directory. Please run this command from the root of your module's directory.`,
|
|
224
224
|
);
|
|
@@ -253,7 +253,7 @@ export async function buildModule(
|
|
|
253
253
|
reloader?: Reloader,
|
|
254
254
|
entryPoint = peprTS,
|
|
255
255
|
embed = true,
|
|
256
|
-
): Promise<BuildModuleReturn> {
|
|
256
|
+
): Promise<BuildModuleReturn | void> {
|
|
257
257
|
try {
|
|
258
258
|
const { cfg, modulePath, path, uuid } = await loadModule(entryPoint);
|
|
259
259
|
|
package/src/cli/deploy.ts
CHANGED
|
@@ -93,6 +93,35 @@ export async function getUserConfirmation(opts: { confirm: boolean }): Promise<b
|
|
|
93
93
|
return confirm.confirm ? true : false;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
async function buildAndDeployModule(image: string, force: boolean): Promise<void> {
|
|
97
|
+
const builtModule = await buildModule();
|
|
98
|
+
if (!builtModule) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Generate a secret for the module
|
|
103
|
+
const webhook = new Assets(
|
|
104
|
+
{ ...builtModule.cfg.pepr, description: builtModule.cfg.description },
|
|
105
|
+
builtModule.path,
|
|
106
|
+
[],
|
|
107
|
+
);
|
|
108
|
+
webhook.image = image ?? webhook.image;
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
await webhook.deploy(deployWebhook, force, builtModule.cfg.pepr.webhookTimeout ?? 10);
|
|
112
|
+
|
|
113
|
+
// wait for capabilities to be loaded and test names
|
|
114
|
+
validateCapabilityNames(webhook.capabilities);
|
|
115
|
+
|
|
116
|
+
// Wait for the pepr-system resources to be fully up
|
|
117
|
+
await namespaceDeploymentsReady();
|
|
118
|
+
console.info(`✅ Module deployed successfully`);
|
|
119
|
+
} catch (e) {
|
|
120
|
+
console.error(`Error deploying module:`, e);
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
96
125
|
export default function (program: RootCmd): void {
|
|
97
126
|
program
|
|
98
127
|
.command("deploy")
|
|
@@ -117,33 +146,10 @@ export default function (program: RootCmd): void {
|
|
|
117
146
|
return;
|
|
118
147
|
}
|
|
119
148
|
|
|
120
|
-
(await getUserConfirmation(opts))
|
|
121
|
-
|
|
122
|
-
const builtModule = await buildModule();
|
|
123
|
-
if (!builtModule) {
|
|
124
|
-
return;
|
|
149
|
+
if (!(await getUserConfirmation(opts))) {
|
|
150
|
+
process.exit(0);
|
|
125
151
|
}
|
|
126
152
|
|
|
127
|
-
|
|
128
|
-
const webhook = new Assets(
|
|
129
|
-
{ ...builtModule.cfg.pepr, description: builtModule.cfg.description },
|
|
130
|
-
builtModule.path,
|
|
131
|
-
[],
|
|
132
|
-
);
|
|
133
|
-
webhook.image = opts.image ?? webhook.image;
|
|
134
|
-
|
|
135
|
-
try {
|
|
136
|
-
await webhook.deploy(deployWebhook, opts.force, builtModule.cfg.pepr.webhookTimeout ?? 10);
|
|
137
|
-
|
|
138
|
-
// wait for capabilities to be loaded and test names
|
|
139
|
-
validateCapabilityNames(webhook.capabilities);
|
|
140
|
-
|
|
141
|
-
// Wait for the pepr-system resources to be fully up
|
|
142
|
-
await namespaceDeploymentsReady();
|
|
143
|
-
console.info(`✅ Module deployed successfully`);
|
|
144
|
-
} catch (e) {
|
|
145
|
-
console.error(`Error deploying module:`, e);
|
|
146
|
-
process.exit(1);
|
|
147
|
-
}
|
|
153
|
+
await buildAndDeployModule(opts.image, opts.force);
|
|
148
154
|
});
|
|
149
155
|
}
|
package/src/cli/init/index.ts
CHANGED
|
@@ -107,7 +107,7 @@ const doPostInitActions = (dirName: string): void => {
|
|
|
107
107
|
execSync("code .", {
|
|
108
108
|
stdio: "inherit",
|
|
109
109
|
});
|
|
110
|
-
} catch
|
|
111
|
-
|
|
110
|
+
} catch {
|
|
111
|
+
console.warn("VSCode was not found, IDE will not automatically open.");
|
|
112
112
|
}
|
|
113
113
|
};
|
|
@@ -6,16 +6,17 @@ import { inspect } from "util";
|
|
|
6
6
|
import { v4 as uuidv4, v5 as uuidv5 } from "uuid";
|
|
7
7
|
|
|
8
8
|
import eslintJSON from "../../templates/.eslintrc.template.json";
|
|
9
|
+
import peprSnippetsJSON from "../../templates/pepr.code-snippets.json";
|
|
9
10
|
import prettierJSON from "../../templates/.prettierrc.json";
|
|
10
11
|
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
12
|
import settingsJSON from "../../templates/settings.json";
|
|
14
13
|
import tsConfigJSON from "../../templates/tsconfig.module.json";
|
|
15
|
-
import {
|
|
14
|
+
import { CustomLabels } from "../../lib/core/module";
|
|
16
15
|
import { InitOptions } from "../types";
|
|
17
|
-
import { V1PolicyRule as PolicyRule } from "@kubernetes/client-node";
|
|
18
16
|
import { OnError, RbacMode } from "./enums";
|
|
17
|
+
import { V1PolicyRule as PolicyRule } from "@kubernetes/client-node";
|
|
18
|
+
import { gitIgnore, helloPeprTS, packageJSON, peprTS, readmeMd } from "../../templates/data.json";
|
|
19
|
+
import { sanitizeName } from "./utils";
|
|
19
20
|
|
|
20
21
|
export const { dependencies, devDependencies, peerDependencies, scripts, version } = packageJSON;
|
|
21
22
|
|
|
@@ -30,7 +31,7 @@ type peprPackageJSON = {
|
|
|
30
31
|
uuid: string;
|
|
31
32
|
onError: OnError;
|
|
32
33
|
webhookTimeout: number;
|
|
33
|
-
customLabels:
|
|
34
|
+
customLabels: CustomLabels;
|
|
34
35
|
alwaysIgnore: { namespaces: string[] };
|
|
35
36
|
includedFiles: string[];
|
|
36
37
|
env: object;
|
|
@@ -38,7 +38,7 @@ export async function setName(name?: string): Promise<Answers<string>> {
|
|
|
38
38
|
await fs.access(name, fs.constants.F_OK);
|
|
39
39
|
|
|
40
40
|
return "A directory with this name already exists";
|
|
41
|
-
} catch
|
|
41
|
+
} catch {
|
|
42
42
|
return val.length > 2 || "The name must be at least 3 characters long";
|
|
43
43
|
}
|
|
44
44
|
},
|
package/src/lib/assets/rbac.ts
CHANGED
|
@@ -24,8 +24,7 @@ export function clusterRole(
|
|
|
24
24
|
const rbacMap = createRBACMap(capabilities);
|
|
25
25
|
// Generate scoped rules from rbacMap
|
|
26
26
|
const scopedRules = Object.keys(rbacMap).map(key => {
|
|
27
|
-
|
|
28
|
-
key.split("/").length < 3 ? (group = "") : (group = key.split("/")[0]);
|
|
27
|
+
const group: string = key.split("/").length < 3 ? "" : key.split("/")[0];
|
|
29
28
|
|
|
30
29
|
return {
|
|
31
30
|
apiGroups: [group],
|
|
@@ -15,7 +15,7 @@ import { Binding } from "../types";
|
|
|
15
15
|
|
|
16
16
|
export const peprIgnoreNamespaces: string[] = ["kube-system", "pepr-system"];
|
|
17
17
|
|
|
18
|
-
const validateRule = (binding: Binding, isMutateWebhook: boolean): V1RuleWithOperations | undefined => {
|
|
18
|
+
export const validateRule = (binding: Binding, isMutateWebhook: boolean): V1RuleWithOperations | undefined => {
|
|
19
19
|
const { event, kind, isMutate, isValidate } = binding;
|
|
20
20
|
|
|
21
21
|
// Skip invalid bindings based on webhook type
|
|
@@ -5,17 +5,15 @@ import { dumpYaml } from "@kubernetes/client-node";
|
|
|
5
5
|
import { clusterRole } from "../rbac";
|
|
6
6
|
import { promises as fs } from "fs";
|
|
7
7
|
|
|
8
|
-
type
|
|
8
|
+
type ChartOverrides = {
|
|
9
9
|
apiToken: string;
|
|
10
10
|
capabilities: CapabilityExport[];
|
|
11
11
|
config: ModuleConfig;
|
|
12
12
|
hash: string;
|
|
13
13
|
name: string;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
type ChartOverrides = CommonOverrideValues & {
|
|
17
14
|
image: string;
|
|
18
15
|
};
|
|
16
|
+
|
|
19
17
|
// Helm Chart overrides file (values.yaml) generated from assets
|
|
20
18
|
export async function overridesFile(
|
|
21
19
|
{ hash, name, image, config, apiToken, capabilities }: ChartOverrides,
|
|
@@ -34,7 +32,7 @@ export async function overridesFile(
|
|
|
34
32
|
hash,
|
|
35
33
|
namespace: {
|
|
36
34
|
annotations: {},
|
|
37
|
-
labels: {
|
|
35
|
+
labels: config.customLabels?.namespace ?? {
|
|
38
36
|
"pepr.dev": "",
|
|
39
37
|
},
|
|
40
38
|
},
|
|
@@ -16,6 +16,12 @@ import { StoreController } from "./store";
|
|
|
16
16
|
import { AdmissionRequest } from "../types";
|
|
17
17
|
import { karForMutate, karForValidate, KubeAdmissionReview } from "./index.util";
|
|
18
18
|
|
|
19
|
+
export interface ControllerHooks {
|
|
20
|
+
beforeHook?: (req: AdmissionRequest) => void;
|
|
21
|
+
afterHook?: (res: MutateResponse | ValidateResponse) => void;
|
|
22
|
+
onReady?: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
if (!process.env.PEPR_NODE_WARNINGS) {
|
|
20
26
|
process.removeAllListeners("warning");
|
|
21
27
|
}
|
|
@@ -39,20 +45,17 @@ export class Controller {
|
|
|
39
45
|
readonly #beforeHook?: (req: AdmissionRequest) => void;
|
|
40
46
|
readonly #afterHook?: (res: MutateResponse | ValidateResponse) => void;
|
|
41
47
|
|
|
42
|
-
constructor(
|
|
43
|
-
|
|
44
|
-
capabilities: Capability[],
|
|
45
|
-
beforeHook?: (req: AdmissionRequest) => void,
|
|
46
|
-
afterHook?: (res: MutateResponse | ValidateResponse) => void,
|
|
47
|
-
onReady?: () => void,
|
|
48
|
-
) {
|
|
48
|
+
constructor(config: ModuleConfig, capabilities: Capability[], hooks: ControllerHooks = {}) {
|
|
49
|
+
const { beforeHook, afterHook, onReady } = hooks;
|
|
49
50
|
this.#config = config;
|
|
50
51
|
this.#capabilities = capabilities;
|
|
51
52
|
|
|
52
53
|
// Initialize the Pepr store for each capability
|
|
53
54
|
new StoreController(capabilities, `pepr-${config.uuid}-store`, () => {
|
|
54
55
|
this.#bindEndpoints();
|
|
55
|
-
onReady
|
|
56
|
+
if (typeof onReady === "function") {
|
|
57
|
+
onReady();
|
|
58
|
+
}
|
|
56
59
|
Log.info("✅ Controller startup complete");
|
|
57
60
|
// Initialize the schedule store for each capability
|
|
58
61
|
new StoreController(capabilities, `pepr-${config.uuid}-schedule`, () => {
|
|
@@ -223,7 +226,9 @@ export class Controller {
|
|
|
223
226
|
Log.debug({ ...reqMetadata, request }, "Incoming request body");
|
|
224
227
|
|
|
225
228
|
// Run the before hook if it exists
|
|
226
|
-
|
|
229
|
+
if (typeof this.#beforeHook === "function") {
|
|
230
|
+
this.#beforeHook(request || {});
|
|
231
|
+
}
|
|
227
232
|
|
|
228
233
|
// Process the request
|
|
229
234
|
const response: MutateResponse | ValidateResponse[] =
|
|
@@ -233,7 +238,9 @@ export class Controller {
|
|
|
233
238
|
|
|
234
239
|
// Run the after hook if it exists
|
|
235
240
|
[response].flat().map(res => {
|
|
236
|
-
this.#afterHook
|
|
241
|
+
if (typeof this.#afterHook === "function") {
|
|
242
|
+
this.#afterHook(res);
|
|
243
|
+
}
|
|
237
244
|
Log.info({ ...reqMetadata, res }, "Check response");
|
|
238
245
|
});
|
|
239
246
|
|
package/src/lib/core/module.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
3
|
import { clone } from "ramda";
|
|
4
4
|
import { Capability } from "./capability";
|
|
5
|
-
import { Controller } from "../controller";
|
|
5
|
+
import { Controller, ControllerHooks } from "../controller";
|
|
6
6
|
import { ValidateError } from "../errors";
|
|
7
7
|
import { MutateResponse, ValidateResponse, WebhookIgnore } from "../k8s";
|
|
8
8
|
import { CapabilityExport, AdmissionRequest } from "../types";
|
|
@@ -12,37 +12,41 @@ import { V1PolicyRule as PolicyRule } from "@kubernetes/client-node";
|
|
|
12
12
|
import { resolveIgnoreNamespaces } from "../assets/webhooks";
|
|
13
13
|
|
|
14
14
|
/** Custom Labels Type for package.json */
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
export type
|
|
15
|
+
|
|
16
|
+
export type CustomLabels = { namespace: Record<string, string> } | Record<string, never>;
|
|
17
|
+
|
|
18
|
+
/** Configuration that MAY be set a Pepr module's package.json. */
|
|
19
|
+
export type ModuleConfigOptions = {
|
|
20
20
|
/** The Pepr version this module uses */
|
|
21
|
-
peprVersion
|
|
21
|
+
peprVersion: string;
|
|
22
22
|
/** The user-defined version of the module */
|
|
23
|
-
appVersion
|
|
24
|
-
/** A unique identifier for this Pepr module. This is automatically generated by Pepr. */
|
|
25
|
-
uuid: string;
|
|
23
|
+
appVersion: string;
|
|
26
24
|
/** A description of the Pepr module and what it does. */
|
|
27
|
-
description
|
|
25
|
+
description: string;
|
|
28
26
|
/** The webhookTimeout */
|
|
29
|
-
webhookTimeout
|
|
27
|
+
webhookTimeout: number;
|
|
30
28
|
/** Reject K8s resource AdmissionRequests on error. */
|
|
31
|
-
onError
|
|
32
|
-
/** Configure global exclusions that will never be processed by Pepr. */
|
|
33
|
-
alwaysIgnore: WebhookIgnore;
|
|
29
|
+
onError: string;
|
|
34
30
|
/** Define the log level for the in-cluster controllers */
|
|
35
|
-
logLevel
|
|
31
|
+
logLevel: string;
|
|
36
32
|
/** Propagate env variables to in-cluster controllers */
|
|
37
|
-
env
|
|
38
|
-
/** Custom Labels for Kubernetes Objects */
|
|
39
|
-
customLabels?: CustomLabels;
|
|
33
|
+
env: Record<string, string>;
|
|
40
34
|
/** Custom RBAC rules */
|
|
41
|
-
rbac
|
|
35
|
+
rbac: PolicyRule[];
|
|
42
36
|
/** The RBAC mode; if "scoped", generates scoped rules, otherwise uses wildcard rules. */
|
|
43
|
-
rbacMode
|
|
37
|
+
rbacMode: string;
|
|
38
|
+
/** Custom Labels for Kubernetes Objects */
|
|
39
|
+
customLabels: CustomLabels;
|
|
44
40
|
};
|
|
45
41
|
|
|
42
|
+
/** Global configuration for the Pepr runtime. */
|
|
43
|
+
export type ModuleConfig = {
|
|
44
|
+
/** A unique identifier for this Pepr module. This is automatically generated by Pepr. */
|
|
45
|
+
uuid: string;
|
|
46
|
+
/** Configure global exclusions that will never be processed by Pepr. */
|
|
47
|
+
alwaysIgnore: WebhookIgnore;
|
|
48
|
+
} & Partial<ModuleConfigOptions>;
|
|
49
|
+
|
|
46
50
|
export type PackageJSON = {
|
|
47
51
|
description: string;
|
|
48
52
|
pepr: ModuleConfig;
|
|
@@ -110,17 +114,23 @@ export class PeprModule {
|
|
|
110
114
|
return;
|
|
111
115
|
}
|
|
112
116
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
const controllerHooks: ControllerHooks = {
|
|
118
|
+
beforeHook: opts.beforeHook,
|
|
119
|
+
afterHook: opts.afterHook,
|
|
120
|
+
onReady: (): void => {
|
|
121
|
+
// Wait for the controller to be ready before setting up watches
|
|
122
|
+
if (isWatchMode() || isDevMode()) {
|
|
123
|
+
try {
|
|
124
|
+
setupWatch(capabilities, resolveIgnoreNamespaces(pepr?.alwaysIgnore?.namespaces));
|
|
125
|
+
} catch (e) {
|
|
126
|
+
Log.error(e, "Error setting up watch");
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
121
129
|
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
this.#controller = new Controller(config, capabilities, controllerHooks);
|
|
124
134
|
|
|
125
135
|
// Stop processing if deferStart is set to true
|
|
126
136
|
if (opts.deferStart) {
|
package/src/lib/core/schedule.ts
CHANGED
|
@@ -91,7 +91,7 @@ export class OnSchedule implements Schedule {
|
|
|
91
91
|
lastTimestamp: new Date(),
|
|
92
92
|
name: this.name,
|
|
93
93
|
};
|
|
94
|
-
this.store
|
|
94
|
+
if (this.store) this.store.setItem(this.name, JSON.stringify(schedule));
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
/**
|
|
@@ -170,6 +170,6 @@ export class OnSchedule implements Schedule {
|
|
|
170
170
|
clearInterval(this.intervalId);
|
|
171
171
|
this.intervalId = null;
|
|
172
172
|
}
|
|
173
|
-
this.store
|
|
173
|
+
if (this.store) this.store.removeItem(this.name);
|
|
174
174
|
}
|
|
175
175
|
}
|
package/src/lib/core/storage.ts
CHANGED
|
@@ -110,11 +110,12 @@ export class Storage implements PeprStore {
|
|
|
110
110
|
};
|
|
111
111
|
|
|
112
112
|
clear = (): void => {
|
|
113
|
-
Object.keys(this.#store).length > 0
|
|
113
|
+
if (Object.keys(this.#store).length > 0) {
|
|
114
114
|
this.#dispatchUpdate(
|
|
115
115
|
"remove",
|
|
116
116
|
Object.keys(this.#store).map(key => pointer.escape(key)),
|
|
117
117
|
);
|
|
118
|
+
}
|
|
118
119
|
};
|
|
119
120
|
|
|
120
121
|
removeItem = (key: string): void => {
|