pepr 0.49.0 → 0.50.0-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.
- package/dist/cli/crd/create.d.ts.map +1 -1
- package/dist/cli/crd/generate.d.ts.map +1 -1
- package/dist/cli/crd/messages.d.ts +11 -0
- package/dist/cli/crd/messages.d.ts.map +1 -0
- package/dist/cli/deploy.d.ts.map +1 -1
- package/dist/cli/docs/cli.helper.d.ts +12 -0
- package/dist/cli/docs/cli.helper.d.ts.map +1 -0
- package/dist/cli/docs/markdown.helper.d.ts +8 -0
- package/dist/cli/docs/markdown.helper.d.ts.map +1 -0
- package/dist/cli/format/format.helpers.d.ts.map +1 -0
- package/dist/cli/{format.d.ts → format/index.d.ts} +2 -2
- package/dist/cli/format/index.d.ts.map +1 -0
- package/dist/cli/init/templates.d.ts +12 -18
- package/dist/cli/init/templates.d.ts.map +1 -1
- package/dist/cli/update/index.d.ts +3 -0
- package/dist/cli/update/index.d.ts.map +1 -0
- package/dist/cli.js +321 -231
- package/dist/controller.js +1 -1
- package/dist/lib/assets/assets.d.ts.map +1 -1
- package/dist/lib/assets/defaultTestObjects.d.ts.map +1 -1
- package/dist/lib/assets/helm.d.ts.map +1 -1
- package/dist/lib/assets/ignoredNamespaces.d.ts +2 -0
- package/dist/lib/assets/ignoredNamespaces.d.ts.map +1 -0
- package/dist/lib/assets/webhooks.d.ts +0 -1
- package/dist/lib/assets/webhooks.d.ts.map +1 -1
- package/dist/lib/assets/yaml/overridesFile.d.ts.map +1 -1
- package/dist/lib/controller/index.d.ts.map +1 -1
- package/dist/lib/controller/storeCache.d.ts.map +1 -1
- package/dist/lib/core/capability.d.ts.map +1 -1
- package/dist/lib/core/module.d.ts.map +1 -1
- package/dist/lib/core/storage.d.ts.map +1 -1
- package/dist/lib/filter/adjudicators/binding.d.ts.map +1 -1
- package/dist/lib/helpers.d.ts +1 -1
- package/dist/lib/helpers.d.ts.map +1 -1
- package/dist/lib/mutate-request.d.ts.map +1 -1
- 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/metrics.d.ts.map +1 -1
- package/dist/lib/types.d.ts +8 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/validate-request.d.ts.map +1 -1
- package/dist/lib.js +86 -83
- package/dist/lib.js.map +4 -4
- package/dist/sdk/sdk.d.ts +2 -0
- package/dist/sdk/sdk.d.ts.map +1 -1
- package/package.json +17 -15
- package/src/cli/build.ts +1 -1
- package/src/cli/crd/generate.ts +8 -7
- package/src/cli/crd/messages.ts +15 -0
- package/src/cli/deploy.ts +12 -2
- package/src/cli/{format.ts → format/index.ts} +2 -2
- package/src/cli/init/templates.ts +29 -3
- package/src/cli/{update.ts → update/index.ts} +30 -3
- package/src/lib/assets/assets.ts +7 -0
- package/src/lib/assets/helm.ts +28 -1
- package/src/lib/assets/ignoredNamespaces.ts +17 -0
- package/src/lib/assets/webhooks.ts +6 -17
- package/src/lib/assets/yaml/overridesFile.ts +7 -2
- package/src/lib/controller/createHooks.ts +1 -1
- package/src/lib/core/module.ts +3 -1
- package/src/lib/helpers.ts +16 -6
- package/src/lib/processors/mutate-processor.ts +6 -2
- package/src/lib/processors/validate-processor.ts +9 -3
- package/src/lib/types.ts +8 -0
- package/src/sdk/sdk.ts +10 -7
- package/src/templates/capabilities/hello-pepr.ts +1 -1
- package/src/templates/eslint.config.mjs +45 -0
- package/src/templates/package.json +10 -0
- package/dist/cli/format.d.ts.map +0 -1
- package/dist/cli/format.helpers.d.ts.map +0 -1
- package/dist/cli/update.d.ts +0 -3
- package/dist/cli/update.d.ts.map +0 -1
- package/src/templates/.eslintrc.json +0 -6
- package/src/templates/.eslintrc.template.json +0 -18
- /package/dist/cli/{format.helpers.d.ts → format/format.helpers.d.ts} +0 -0
- /package/src/cli/{format.helpers.ts → format/format.helpers.ts} +0 -0
package/dist/sdk/sdk.d.ts
CHANGED
|
@@ -38,6 +38,8 @@ export declare function getOwnerRefFrom(customResource: GenericKind, blockOwnerD
|
|
|
38
38
|
*
|
|
39
39
|
* @param name the name of the resource to sanitize
|
|
40
40
|
* @returns the sanitized resource name
|
|
41
|
+
*
|
|
42
|
+
* https://kubernetes.io/docs/concepts/overview/working-with-objects/names/
|
|
41
43
|
*/
|
|
42
44
|
export declare function sanitizeResourceName(name: string): string;
|
|
43
45
|
//# sourceMappingURL=sdk.d.ts.map
|
package/dist/sdk/sdk.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../src/sdk/sdk.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,EAAE,WAAW,EAAO,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAElE;;;;;GAKG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,EACpE,aAAa,CAAC,EAAE,YAAY,GAAG,gBAAgB,GAAG,qBAAqB,GACtE,WAAW,EAAE,CAef;AAED;;;;;;;;;GASG;AAEH,wBAAsB,UAAU,CAC9B,EAAE,EAAE,WAAW,EACf,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAC9B,OAAO,EAAE;IACP,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;CAC3B,GACA,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,cAAc,EAAE,WAAW,EAC3B,kBAAkB,CAAC,EAAE,OAAO,EAC5B,UAAU,CAAC,EAAE,OAAO,GACnB,gBAAgB,EAAE,CAcpB;AAED
|
|
1
|
+
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../src/sdk/sdk.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,EAAE,WAAW,EAAO,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAElE;;;;;GAKG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,EACpE,aAAa,CAAC,EAAE,YAAY,GAAG,gBAAgB,GAAG,qBAAqB,GACtE,WAAW,EAAE,CAef;AAED;;;;;;;;;GASG;AAEH,wBAAsB,UAAU,CAC9B,EAAE,EAAE,WAAW,EACf,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAC9B,OAAO,EAAE;IACP,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;CAC3B,GACA,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,cAAc,EAAE,WAAW,EAC3B,kBAAkB,CAAC,EAAE,OAAO,EAC5B,UAAU,CAAC,EAAE,OAAO,GACnB,gBAAgB,EAAE,CAcpB;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAazD"}
|
package/package.json
CHANGED
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
"/src",
|
|
15
15
|
"!src/**/*.test.ts",
|
|
16
16
|
"!src/fixtures/**",
|
|
17
|
-
"!dist/**/*.test.d.ts*"
|
|
17
|
+
"!dist/**/*.test.d.ts*",
|
|
18
|
+
"!src/cli/docs/**"
|
|
18
19
|
],
|
|
19
|
-
"version": "0.
|
|
20
|
+
"version": "0.50.0-nightly.1",
|
|
20
21
|
"main": "dist/lib.js",
|
|
21
22
|
"types": "dist/lib.d.ts",
|
|
22
23
|
"scripts": {
|
|
@@ -29,6 +30,7 @@
|
|
|
29
30
|
"set:version": "node scripts/set-version.js",
|
|
30
31
|
"test": "npm run test:unit && npm run test:journey && npm run test:journey-wasm",
|
|
31
32
|
"test:artifacts": "npm run build && jest src/build-artifact.test.ts",
|
|
33
|
+
"test:docs": "jest --verbose src/cli/docs/*.test.ts",
|
|
32
34
|
"test:integration": "npm run test:integration:prep && npm run test:integration:run",
|
|
33
35
|
"test:integration:prep": "./integration/prep.sh",
|
|
34
36
|
"test:integration:run": "jest --maxWorkers=4 integration",
|
|
@@ -42,9 +44,9 @@
|
|
|
42
44
|
"test:journey:run-wasm": "jest --detectOpenHandles journey/entrypoint-wasm.test.ts",
|
|
43
45
|
"test:journey:unicorn": "npm run test:journey:k3d && npm run test:journey:image:unicorn && npm run test:journey:run",
|
|
44
46
|
"test:journey:upgrade": "npm run test:journey:k3d && npm run test:journey:image && jest --detectOpenHandles journey/pepr-upgrade.test.ts",
|
|
45
|
-
"test:unit": "npm run gen-data-json && jest src --coverage --detectOpenHandles --coverageDirectory=./coverage --testPathIgnorePatterns
|
|
46
|
-
"format:check": "eslint src && prettier --config .prettierrc src --check",
|
|
47
|
-
"format:fix": "eslint
|
|
47
|
+
"test:unit": "npm run gen-data-json && jest src --coverage --detectOpenHandles --coverageDirectory=./coverage --testPathIgnorePatterns=\"build-artifact.test.ts|src/cli/docs/.*\\.test\\.ts\"",
|
|
48
|
+
"format:check": "eslint --ignore-pattern src/templates/eslint.config.mjs src && prettier --config .prettierrc src --check",
|
|
49
|
+
"format:fix": "eslint --fix --ignore-pattern src/templates/eslint.config.mjs src && prettier --config .prettierrc src --write",
|
|
48
50
|
"prepare": "if [ \"$NODE_ENV\" != 'production' ]; then husky; fi"
|
|
49
51
|
},
|
|
50
52
|
"dependencies": {
|
|
@@ -54,7 +56,7 @@
|
|
|
54
56
|
"heredoc": "^1.3.1",
|
|
55
57
|
"http-status-codes": "^2.3.0",
|
|
56
58
|
"json-pointer": "^0.6.2",
|
|
57
|
-
"kubernetes-fluent-client": "3.5.
|
|
59
|
+
"kubernetes-fluent-client": "3.5.3",
|
|
58
60
|
"pino": "9.6.0",
|
|
59
61
|
"pino-pretty": "13.0.0",
|
|
60
62
|
"prom-client": "15.1.3",
|
|
@@ -63,8 +65,8 @@
|
|
|
63
65
|
"ts-morph": "^25.0.1"
|
|
64
66
|
},
|
|
65
67
|
"devDependencies": {
|
|
66
|
-
"@commitlint/cli": "19.8.
|
|
67
|
-
"@commitlint/config-conventional": "19.8.
|
|
68
|
+
"@commitlint/cli": "19.8.1",
|
|
69
|
+
"@commitlint/config-conventional": "19.8.1",
|
|
68
70
|
"@fast-check/jest": "^2.0.1",
|
|
69
71
|
"@jest/globals": "29.7.0",
|
|
70
72
|
"@types/eslint": "9.6.1",
|
|
@@ -87,15 +89,15 @@
|
|
|
87
89
|
},
|
|
88
90
|
"peerDependencies": {
|
|
89
91
|
"@types/prompts": "2.4.9",
|
|
90
|
-
"@typescript-eslint/eslint-plugin": "8.
|
|
91
|
-
"@typescript-eslint/parser": "8.
|
|
92
|
+
"@typescript-eslint/eslint-plugin": "8.32.1",
|
|
93
|
+
"@typescript-eslint/parser": "8.32.1",
|
|
92
94
|
"commander": "13.1.0",
|
|
93
95
|
"esbuild": "0.25.0",
|
|
94
|
-
"eslint": "
|
|
96
|
+
"eslint": "^9.26.0",
|
|
95
97
|
"node-forge": "1.3.1",
|
|
96
|
-
"prettier": "3.
|
|
98
|
+
"prettier": "3.5.3",
|
|
97
99
|
"prompts": "2.4.2",
|
|
98
|
-
"typescript": "5.
|
|
99
|
-
"uuid": "11.0
|
|
100
|
+
"typescript": "5.8.3",
|
|
101
|
+
"uuid": "11.1.0"
|
|
100
102
|
}
|
|
101
|
-
}
|
|
103
|
+
}
|
package/src/cli/build.ts
CHANGED
|
@@ -71,7 +71,7 @@ export default function (program: RootCmd): void {
|
|
|
71
71
|
.option("-e, --entry-point [file]", "Specify the entry point file to build with.", peprTS)
|
|
72
72
|
.option(
|
|
73
73
|
"-n, --no-embed",
|
|
74
|
-
"Disables embedding of deployment files into output module.
|
|
74
|
+
"Disables embedding of deployment files into output module. Useful when creating library modules intended solely for reuse/distribution via NPM.",
|
|
75
75
|
)
|
|
76
76
|
.addOption(
|
|
77
77
|
new Option(
|
package/src/cli/crd/generate.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import fs from "fs";
|
|
6
6
|
import path from "path";
|
|
7
|
-
import { stringify
|
|
7
|
+
import { stringify } from "yaml";
|
|
8
8
|
import {
|
|
9
9
|
Project,
|
|
10
10
|
InterfaceDeclaration,
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
import { createDirectoryIfNotExists } from "../../lib/filesystemService";
|
|
18
18
|
import { kind as k } from "kubernetes-fluent-client";
|
|
19
19
|
import { V1JSONSchemaProps } from "@kubernetes/client-node";
|
|
20
|
+
import { WarningMessages, ErrorMessages } from "./messages";
|
|
20
21
|
|
|
21
22
|
export default new Command("generate")
|
|
22
23
|
.description("Generate CRD manifests from TypeScript definitions")
|
|
@@ -82,13 +83,13 @@ export function processSourceFile(
|
|
|
82
83
|
const { kind, fqdn, scope, plural, shortNames } = extractCRDDetails(content, sourceFile);
|
|
83
84
|
|
|
84
85
|
if (!kind) {
|
|
85
|
-
console.warn(
|
|
86
|
+
console.warn(WarningMessages.MISSING_KIND_COMMENT(sourceFile.getBaseName()));
|
|
86
87
|
return;
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
const spec = sourceFile.getInterface(`${kind}Spec`);
|
|
90
91
|
if (!spec) {
|
|
91
|
-
console.warn(
|
|
92
|
+
console.warn(WarningMessages.MISSING_INTERFACE(sourceFile.getBaseName(), kind));
|
|
92
93
|
return;
|
|
93
94
|
}
|
|
94
95
|
|
|
@@ -108,7 +109,7 @@ export function processSourceFile(
|
|
|
108
109
|
});
|
|
109
110
|
|
|
110
111
|
const outPath = path.join(outputDir, `${kind.toLowerCase()}.yaml`);
|
|
111
|
-
fs.writeFileSync(outPath,
|
|
112
|
+
fs.writeFileSync(outPath, stringify(crd), "utf8");
|
|
112
113
|
console.log(`✔ Created ${outPath}`);
|
|
113
114
|
}
|
|
114
115
|
|
|
@@ -126,7 +127,7 @@ export function extractDetails(sourceFile: SourceFile): {
|
|
|
126
127
|
} {
|
|
127
128
|
const decl = sourceFile.getVariableDeclaration("details");
|
|
128
129
|
if (!decl) {
|
|
129
|
-
throw new Error(
|
|
130
|
+
throw new Error(ErrorMessages.MISSING_DETAILS);
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
const init = decl.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression);
|
|
@@ -135,7 +136,7 @@ export function extractDetails(sourceFile: SourceFile): {
|
|
|
135
136
|
const prop = init.getProperty(key);
|
|
136
137
|
const value = prop?.getFirstChildByKind(SyntaxKind.StringLiteral)?.getLiteralText();
|
|
137
138
|
if (!value) {
|
|
138
|
-
throw new Error(
|
|
139
|
+
throw new Error(ErrorMessages.MISSING_OR_INVALID_KEY(key));
|
|
139
140
|
}
|
|
140
141
|
return value;
|
|
141
142
|
};
|
|
@@ -149,7 +150,7 @@ export function extractDetails(sourceFile: SourceFile): {
|
|
|
149
150
|
};
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
throw new Error(
|
|
153
|
+
throw new Error(ErrorMessages.INVALID_SCOPE(scope));
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
export function getJsDocDescription(node: Node): string {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const ErrorMessages = {
|
|
2
|
+
MISSING_DETAILS: "Missing 'details' variable declaration.",
|
|
3
|
+
INVALID_SCOPE: (scope: string): string =>
|
|
4
|
+
`'scope' must be either "Cluster" or "Namespaced", got "${scope}"`,
|
|
5
|
+
MISSING_OR_INVALID_KEY: (key: string): string =>
|
|
6
|
+
`Missing or invalid value for required key: '${key}'`,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const WarningMessages = {
|
|
10
|
+
MISSING_DETAILS: "Missing 'details' variable declaration.",
|
|
11
|
+
MISSING_KIND_COMMENT: (fileName: string): string =>
|
|
12
|
+
`Skipping ${fileName}: missing '// Kind: <KindName>' comment`,
|
|
13
|
+
MISSING_INTERFACE: (fileName: string, kind: string): string =>
|
|
14
|
+
`Skipping ${fileName}: missing interface ${kind}Spec`,
|
|
15
|
+
};
|
package/src/cli/deploy.ts
CHANGED
|
@@ -11,7 +11,8 @@ import { deployImagePullSecret, deployWebhook } from "../lib/assets/deploy";
|
|
|
11
11
|
import { namespaceDeploymentsReady } from "../lib/deploymentChecks";
|
|
12
12
|
import { sanitizeName } from "./init/utils";
|
|
13
13
|
import { validateCapabilityNames } from "../lib/helpers";
|
|
14
|
-
|
|
14
|
+
import { namespaceComplianceValidator } from "../lib/helpers";
|
|
15
|
+
import { loadCapabilities } from "../lib/assets/loader";
|
|
15
16
|
export interface ImagePullSecretDetails {
|
|
16
17
|
pullSecret?: string;
|
|
17
18
|
dockerServer?: string;
|
|
@@ -106,7 +107,16 @@ async function buildAndDeployModule(image: string, force: boolean): Promise<void
|
|
|
106
107
|
[],
|
|
107
108
|
);
|
|
108
109
|
webhook.image = image ?? webhook.image;
|
|
109
|
-
|
|
110
|
+
const capabilities = await loadCapabilities(webhook.path);
|
|
111
|
+
for (const capability of capabilities) {
|
|
112
|
+
namespaceComplianceValidator(capability, webhook.alwaysIgnore?.namespaces);
|
|
113
|
+
namespaceComplianceValidator(
|
|
114
|
+
capability,
|
|
115
|
+
webhook.config.admission?.alwaysIgnore?.namespaces,
|
|
116
|
+
false,
|
|
117
|
+
);
|
|
118
|
+
namespaceComplianceValidator(capability, webhook.config.watch?.alwaysIgnore?.namespaces, true);
|
|
119
|
+
}
|
|
110
120
|
try {
|
|
111
121
|
await webhook.deploy(deployWebhook, force, builtModule.cfg.pepr.webhookTimeout ?? 10);
|
|
112
122
|
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
import { ESLint } from "eslint";
|
|
5
5
|
import { formatWithPrettier } from "./format.helpers";
|
|
6
6
|
|
|
7
|
-
import { RootCmd } from "
|
|
7
|
+
import { RootCmd } from "../root";
|
|
8
8
|
|
|
9
9
|
export default function (program: RootCmd): void {
|
|
10
10
|
program
|
|
11
11
|
.command("format")
|
|
12
12
|
.description("Lint and format this Pepr module")
|
|
13
|
-
.option("-v, --validate-only", "Do not modify files, only validate formatting")
|
|
13
|
+
.option("-v, --validate-only", "Do not modify files, only validate formatting.")
|
|
14
14
|
.action(async opts => {
|
|
15
15
|
const success = await peprFormat(opts.validateOnly);
|
|
16
16
|
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
import { dumpYaml } from "@kubernetes/client-node";
|
|
5
5
|
import { inspect } from "util";
|
|
6
6
|
import { v4 as uuidv4 } from "uuid";
|
|
7
|
+
import { readFileSync } from "fs";
|
|
8
|
+
import path from "path";
|
|
7
9
|
|
|
8
|
-
import eslintJSON from "../../templates/.eslintrc.template.json";
|
|
9
10
|
import peprSnippetsJSON from "../../templates/pepr.code-snippets.json";
|
|
10
11
|
import prettierJSON from "../../templates/.prettierrc.json";
|
|
11
12
|
import samplesJSON from "../../templates/capabilities/hello-pepr.samples.json";
|
|
@@ -33,6 +34,8 @@ export type peprPackageJSON = {
|
|
|
33
34
|
webhookTimeout: number;
|
|
34
35
|
customLabels: CustomLabels;
|
|
35
36
|
alwaysIgnore: { namespaces: string[] };
|
|
37
|
+
admission: { alwaysIgnore: { namespaces: string[] } };
|
|
38
|
+
watch: { alwaysIgnore: { namespaces: string[] } };
|
|
36
39
|
includedFiles: string[];
|
|
37
40
|
env: object;
|
|
38
41
|
rbac?: PolicyRule[];
|
|
@@ -79,6 +82,16 @@ export function genPkgJSON(opts: InitOptions, pgkVerOverride?: string): peprPack
|
|
|
79
82
|
alwaysIgnore: {
|
|
80
83
|
namespaces: [],
|
|
81
84
|
},
|
|
85
|
+
admission: {
|
|
86
|
+
alwaysIgnore: {
|
|
87
|
+
namespaces: [],
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
watch: {
|
|
91
|
+
alwaysIgnore: {
|
|
92
|
+
namespaces: [],
|
|
93
|
+
},
|
|
94
|
+
},
|
|
82
95
|
includedFiles: [],
|
|
83
96
|
env: pgkVerOverride ? testEnv : {},
|
|
84
97
|
},
|
|
@@ -147,6 +160,19 @@ export const prettier = {
|
|
|
147
160
|
};
|
|
148
161
|
|
|
149
162
|
export const eslint = {
|
|
150
|
-
path: ".
|
|
151
|
-
data:
|
|
163
|
+
path: "eslint.config.mjs",
|
|
164
|
+
data: readFileSync(
|
|
165
|
+
path.resolve(
|
|
166
|
+
((): string => {
|
|
167
|
+
const fullPath = __dirname;
|
|
168
|
+
const lengthOfSuffix = "pepr/".length;
|
|
169
|
+
// Find the last occurrence of "pepr/"
|
|
170
|
+
const lastPeprIndex = fullPath.lastIndexOf("pepr/");
|
|
171
|
+
// Return the path up to and including the last "pepr/"
|
|
172
|
+
return fullPath.substring(0, lastPeprIndex + lengthOfSuffix);
|
|
173
|
+
})(),
|
|
174
|
+
"src/templates/eslint.config.mjs",
|
|
175
|
+
),
|
|
176
|
+
"utf-8",
|
|
177
|
+
),
|
|
152
178
|
};
|
|
@@ -13,9 +13,9 @@ import {
|
|
|
13
13
|
samplesYaml,
|
|
14
14
|
snippet,
|
|
15
15
|
tsConfig,
|
|
16
|
-
} from "
|
|
17
|
-
import { write } from "
|
|
18
|
-
import { RootCmd } from "
|
|
16
|
+
} from "../init/templates";
|
|
17
|
+
import { write } from "../init/utils";
|
|
18
|
+
import { RootCmd } from "../root";
|
|
19
19
|
|
|
20
20
|
export default function (program: RootCmd): void {
|
|
21
21
|
program
|
|
@@ -41,6 +41,33 @@ export default function (program: RootCmd): void {
|
|
|
41
41
|
console.log("Updating the Pepr module...");
|
|
42
42
|
|
|
43
43
|
try {
|
|
44
|
+
// Check if eslint v8 is a project dependency and warn about future upgrade
|
|
45
|
+
let packageLockContent = "";
|
|
46
|
+
let foundPackageLock = false;
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
// Try to find package-lock.json in the current directory
|
|
50
|
+
if (fs.existsSync("./package-lock.json")) {
|
|
51
|
+
packageLockContent = fs.readFileSync("./package-lock.json", "utf-8");
|
|
52
|
+
foundPackageLock = true;
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
// Ignore errors and continue with installation
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// If we found the package-lock.json and could read it, check for eslint v8
|
|
59
|
+
if (foundPackageLock && packageLockContent) {
|
|
60
|
+
// Look for eslint version 8.x.x pattern in the file content
|
|
61
|
+
if (
|
|
62
|
+
packageLockContent.indexOf('"eslint":') >= 0 &&
|
|
63
|
+
packageLockContent.match(/"eslint":\s*"[~^]?8\.[0-9]+\.[0-9]+"/)
|
|
64
|
+
) {
|
|
65
|
+
console.warn(
|
|
66
|
+
"\nWarning: This Pepr module uses ESLint v8. Pepr will be upgraded to use v9 in a future release.\nSee eslint@9.0.0 release notes for more details: https://eslint.org/blog/2024/04/eslint-v9.0.0-released/",
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
44
71
|
// Update Pepr for the module
|
|
45
72
|
execSync("npm install pepr@latest", {
|
|
46
73
|
stdio: "inherit",
|
package/src/lib/assets/assets.ts
CHANGED
|
@@ -101,7 +101,14 @@ export class Assets {
|
|
|
101
101
|
this.capabilities = await loadCapabilities(this.path);
|
|
102
102
|
// give error if namespaces are not respected
|
|
103
103
|
for (const capability of this.capabilities) {
|
|
104
|
+
// until deployment, Pepr does not distinguish between watch and admission
|
|
104
105
|
namespaceComplianceValidator(capability, this.alwaysIgnore?.namespaces);
|
|
106
|
+
namespaceComplianceValidator(
|
|
107
|
+
capability,
|
|
108
|
+
this.config.admission?.alwaysIgnore?.namespaces,
|
|
109
|
+
false,
|
|
110
|
+
);
|
|
111
|
+
namespaceComplianceValidator(capability, this.config.watch?.alwaysIgnore?.namespaces, true);
|
|
105
112
|
}
|
|
106
113
|
|
|
107
114
|
const code = await fs.readFile(this.path);
|
package/src/lib/assets/helm.ts
CHANGED
|
@@ -94,7 +94,13 @@ export function watcherDeployTemplate(buildTimestamp: string): string {
|
|
|
94
94
|
terminationGracePeriodSeconds: {{ .Values.watcher.terminationGracePeriodSeconds }}
|
|
95
95
|
serviceAccountName: {{ .Values.uuid }}
|
|
96
96
|
securityContext:
|
|
97
|
-
{{- toYaml .Values.
|
|
97
|
+
{{- toYaml .Values.watcher.securityContext | nindent 8 }}
|
|
98
|
+
nodeSelector:
|
|
99
|
+
{{- toYaml .Values.watcher.nodeSelector | nindent 8 }}
|
|
100
|
+
tolerations:
|
|
101
|
+
{{- toYaml .Values.watcher.tolerations | nindent 8 }}
|
|
102
|
+
affinity:
|
|
103
|
+
{{- toYaml .Values.watcher.affinity | nindent 8 }}
|
|
98
104
|
containers:
|
|
99
105
|
- name: watcher
|
|
100
106
|
image: {{ .Values.watcher.image }}
|
|
@@ -179,6 +185,27 @@ export function admissionDeployTemplate(buildTimestamp: string): string {
|
|
|
179
185
|
app: {{ .Values.uuid }}
|
|
180
186
|
pepr.dev/controller: admission
|
|
181
187
|
spec:
|
|
188
|
+
{{- if or .Values.admission.antiAffinity .Values.admission.affinity }}
|
|
189
|
+
affinity:
|
|
190
|
+
{{- if .Values.admission.antiAffinity }}
|
|
191
|
+
podAntiAffinity:
|
|
192
|
+
requiredDuringSchedulingIgnoredDuringExecution:
|
|
193
|
+
- labelSelector:
|
|
194
|
+
matchExpressions:
|
|
195
|
+
- key: pepr.dev/controller
|
|
196
|
+
operator: In
|
|
197
|
+
values:
|
|
198
|
+
- admission
|
|
199
|
+
topologyKey: "kubernetes.io/hostname"
|
|
200
|
+
{{- end }}
|
|
201
|
+
{{- if .Values.admission.affinity }}
|
|
202
|
+
{{- toYaml .Values.admission.affinity | nindent 8 }}
|
|
203
|
+
{{- end }}
|
|
204
|
+
{{- end }}
|
|
205
|
+
nodeSelector:
|
|
206
|
+
{{- toYaml .Values.admission.nodeSelector | nindent 8 }}
|
|
207
|
+
tolerations:
|
|
208
|
+
{{- toYaml .Values.admission.tolerations | nindent 8 }}
|
|
182
209
|
terminationGracePeriodSeconds: {{ .Values.admission.terminationGracePeriodSeconds }}
|
|
183
210
|
priorityClassName: system-node-critical
|
|
184
211
|
serviceAccountName: {{ .Values.uuid }}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
|
+
|
|
4
|
+
export function resolveIgnoreNamespaces(ignoredNSConfig: string[] = []): string[] {
|
|
5
|
+
const ignoredNSEnv = process.env.PEPR_ADDITIONAL_IGNORED_NAMESPACES;
|
|
6
|
+
if (!ignoredNSEnv) {
|
|
7
|
+
return ignoredNSConfig;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const namespaces = ignoredNSEnv.split(",").map(ns => ns.trim());
|
|
11
|
+
|
|
12
|
+
// add alwaysIgnore.namespaces to the list
|
|
13
|
+
if (ignoredNSConfig) {
|
|
14
|
+
namespaces.push(...ignoredNSConfig);
|
|
15
|
+
}
|
|
16
|
+
return namespaces.filter(ns => ns.length > 0);
|
|
17
|
+
}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "@kubernetes/client-node";
|
|
9
9
|
import { kind } from "kubernetes-fluent-client";
|
|
10
10
|
import { concat, equals, uniqWith } from "ramda";
|
|
11
|
-
|
|
11
|
+
import { resolveIgnoreNamespaces } from "./ignoredNamespaces";
|
|
12
12
|
import { Assets } from "./assets";
|
|
13
13
|
import { Event, WebhookType } from "../enums";
|
|
14
14
|
import { Binding } from "../types";
|
|
@@ -42,21 +42,6 @@ export const validateRule = (
|
|
|
42
42
|
return ruleObject;
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
export function resolveIgnoreNamespaces(ignoredNSConfig: string[] = []): string[] {
|
|
46
|
-
const ignoredNSEnv = process.env.PEPR_ADDITIONAL_IGNORED_NAMESPACES;
|
|
47
|
-
if (!ignoredNSEnv) {
|
|
48
|
-
return ignoredNSConfig;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const namespaces = ignoredNSEnv.split(",").map(ns => ns.trim());
|
|
52
|
-
|
|
53
|
-
// add alwaysIgnore.namespaces to the list
|
|
54
|
-
if (ignoredNSConfig) {
|
|
55
|
-
namespaces.push(...ignoredNSConfig);
|
|
56
|
-
}
|
|
57
|
-
return namespaces.filter(ns => ns.length > 0);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
45
|
export async function generateWebhookRules(
|
|
61
46
|
assets: Assets,
|
|
62
47
|
isMutateWebhook: boolean,
|
|
@@ -84,7 +69,11 @@ export async function webhookConfigGenerator(
|
|
|
84
69
|
const { name, tls, config, apiPath, host } = assets;
|
|
85
70
|
const ignoreNS = concat(
|
|
86
71
|
peprIgnoreNamespaces,
|
|
87
|
-
resolveIgnoreNamespaces(
|
|
72
|
+
resolveIgnoreNamespaces(
|
|
73
|
+
config?.alwaysIgnore?.namespaces?.length
|
|
74
|
+
? config?.alwaysIgnore?.namespaces
|
|
75
|
+
: config?.admission?.alwaysIgnore?.namespaces,
|
|
76
|
+
),
|
|
88
77
|
);
|
|
89
78
|
|
|
90
79
|
// Add any namespaces to ignore
|
|
@@ -3,7 +3,7 @@ import { CapabilityExport, ModuleConfig } from "../../types";
|
|
|
3
3
|
import { dumpYaml } from "@kubernetes/client-node";
|
|
4
4
|
import { clusterRole } from "../rbac";
|
|
5
5
|
import { promises as fs } from "fs";
|
|
6
|
-
|
|
6
|
+
import { resolveIgnoreNamespaces } from "../ignoredNamespaces";
|
|
7
7
|
export type ChartOverrides = {
|
|
8
8
|
apiPath: string;
|
|
9
9
|
capabilities: CapabilityExport[];
|
|
@@ -23,7 +23,11 @@ export async function overridesFile(
|
|
|
23
23
|
|
|
24
24
|
const overrides = {
|
|
25
25
|
imagePullSecrets,
|
|
26
|
-
additionalIgnoredNamespaces:
|
|
26
|
+
additionalIgnoredNamespaces: resolveIgnoreNamespaces(
|
|
27
|
+
config?.alwaysIgnore?.namespaces?.length
|
|
28
|
+
? config?.alwaysIgnore?.namespaces
|
|
29
|
+
: config?.admission?.alwaysIgnore?.namespaces,
|
|
30
|
+
),
|
|
27
31
|
rbac: rbacOverrides,
|
|
28
32
|
secrets: {
|
|
29
33
|
apiPath: Buffer.from(apiPath).toString("base64"),
|
|
@@ -37,6 +41,7 @@ export async function overridesFile(
|
|
|
37
41
|
},
|
|
38
42
|
uuid: name,
|
|
39
43
|
admission: {
|
|
44
|
+
antiAffinity: false,
|
|
40
45
|
terminationGracePeriodSeconds: 5,
|
|
41
46
|
failurePolicy: config.onError === "reject" ? "Fail" : "Ignore",
|
|
42
47
|
webhookTimeout: config.webhookTimeout,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ControllerHooks } from ".";
|
|
2
|
-
import { resolveIgnoreNamespaces } from "../assets/
|
|
2
|
+
import { resolveIgnoreNamespaces } from "../assets/ignoredNamespaces";
|
|
3
3
|
import { Capability } from "../core/capability";
|
|
4
4
|
import { isWatchMode, isDevMode } from "../core/envChecks";
|
|
5
5
|
import { setupWatch } from "../processors/watch-processor";
|
package/src/lib/core/module.ts
CHANGED
|
@@ -60,7 +60,9 @@ export class PeprModule {
|
|
|
60
60
|
const controllerHooks = createControllerHooks(
|
|
61
61
|
opts,
|
|
62
62
|
capabilities,
|
|
63
|
-
pepr?.alwaysIgnore?.namespaces
|
|
63
|
+
pepr?.alwaysIgnore?.namespaces?.length
|
|
64
|
+
? pepr.alwaysIgnore.namespaces
|
|
65
|
+
: config?.watch?.alwaysIgnore?.namespaces,
|
|
64
66
|
);
|
|
65
67
|
|
|
66
68
|
this.#controller = new Controller(config, capabilities, controllerHooks);
|
package/src/lib/helpers.ts
CHANGED
|
@@ -138,20 +138,30 @@ export function generateWatchNamespaceError(
|
|
|
138
138
|
export function namespaceComplianceValidator(
|
|
139
139
|
capability: CapabilityExport,
|
|
140
140
|
ignoredNamespaces?: string[],
|
|
141
|
+
watch?: boolean,
|
|
141
142
|
): void {
|
|
142
143
|
const { namespaces: capabilityNamespaces, bindings, name } = capability;
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
|
|
145
|
+
const shouldInclude = (binding: Binding): boolean => {
|
|
146
|
+
if (watch === true) return !!binding.isWatch;
|
|
147
|
+
if (watch === false) return !!binding.isMutate;
|
|
148
|
+
return true;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const bindingNamespaces: string[] = bindings.flatMap(binding =>
|
|
152
|
+
shouldInclude(binding) ? binding.filters.namespaces || [] : [],
|
|
145
153
|
);
|
|
146
|
-
|
|
147
|
-
|
|
154
|
+
|
|
155
|
+
const bindingRegexNamespaces: string[] = bindings.flatMap(binding =>
|
|
156
|
+
shouldInclude(binding) ? binding.filters.regexNamespaces || [] : [],
|
|
148
157
|
);
|
|
149
158
|
|
|
150
159
|
const namespaceError = generateWatchNamespaceError(
|
|
151
|
-
ignoredNamespaces
|
|
160
|
+
ignoredNamespaces ?? [],
|
|
152
161
|
bindingNamespaces,
|
|
153
|
-
capabilityNamespaces
|
|
162
|
+
capabilityNamespaces ?? [],
|
|
154
163
|
);
|
|
164
|
+
|
|
155
165
|
if (namespaceError !== "") {
|
|
156
166
|
throw new Error(
|
|
157
167
|
`Error in ${name} capability. A binding violates namespace rules. Please check ignoredNamespaces and capability namespaces: ${namespaceError}`,
|
|
@@ -13,7 +13,7 @@ import { ModuleConfig } from "../types";
|
|
|
13
13
|
import { PeprMutateRequest } from "../mutate-request";
|
|
14
14
|
import { base64Encode } from "../utils";
|
|
15
15
|
import { OnError } from "../../cli/init/enums";
|
|
16
|
-
import { resolveIgnoreNamespaces } from "../assets/
|
|
16
|
+
import { resolveIgnoreNamespaces } from "../assets/ignoredNamespaces";
|
|
17
17
|
import { Operation } from "fast-json-patch";
|
|
18
18
|
import { WebhookType } from "../enums";
|
|
19
19
|
|
|
@@ -149,7 +149,11 @@ export async function mutateProcessor(
|
|
|
149
149
|
bind.binding,
|
|
150
150
|
bind.req,
|
|
151
151
|
bind.namespaces,
|
|
152
|
-
resolveIgnoreNamespaces(
|
|
152
|
+
resolveIgnoreNamespaces(
|
|
153
|
+
bind?.config?.alwaysIgnore?.namespaces?.length
|
|
154
|
+
? bind.config?.alwaysIgnore?.namespaces
|
|
155
|
+
: bind.config?.admission?.alwaysIgnore?.namespaces,
|
|
156
|
+
),
|
|
153
157
|
);
|
|
154
158
|
if (shouldSkip !== "") {
|
|
155
159
|
Log.debug(shouldSkip);
|
|
@@ -10,7 +10,7 @@ import Log from "../telemetry/logger";
|
|
|
10
10
|
import { convertFromBase64Map } from "../utils";
|
|
11
11
|
import { PeprValidateRequest } from "../validate-request";
|
|
12
12
|
import { ModuleConfig } from "../types";
|
|
13
|
-
import { resolveIgnoreNamespaces } from "../assets/
|
|
13
|
+
import { resolveIgnoreNamespaces } from "../assets/ignoredNamespaces";
|
|
14
14
|
import { MeasureWebhookTimeout } from "../telemetry/webhookTimeouts";
|
|
15
15
|
import { WebhookType } from "../enums";
|
|
16
16
|
import { AdmissionRequest } from "../common-types";
|
|
@@ -37,7 +37,9 @@ export async function processRequest(
|
|
|
37
37
|
if (callbackResp.statusCode || callbackResp.statusMessage) {
|
|
38
38
|
valResp.status = {
|
|
39
39
|
code: callbackResp.statusCode || 400,
|
|
40
|
-
message:
|
|
40
|
+
message:
|
|
41
|
+
callbackResp.statusMessage ||
|
|
42
|
+
`Validation failed for ${peprValidateRequest.Request.kind.kind.toLowerCase()}/${peprValidateRequest.Request.name}${peprValidateRequest.Request.namespace ? ` in ${peprValidateRequest.Request.namespace} namespace.` : ""}`,
|
|
41
43
|
};
|
|
42
44
|
}
|
|
43
45
|
|
|
@@ -95,7 +97,11 @@ export async function validateProcessor(
|
|
|
95
97
|
binding,
|
|
96
98
|
req,
|
|
97
99
|
namespaces,
|
|
98
|
-
resolveIgnoreNamespaces(
|
|
100
|
+
resolveIgnoreNamespaces(
|
|
101
|
+
config?.alwaysIgnore?.namespaces?.length
|
|
102
|
+
? config?.alwaysIgnore?.namespaces
|
|
103
|
+
: config?.admission?.alwaysIgnore?.namespaces,
|
|
104
|
+
),
|
|
99
105
|
);
|
|
100
106
|
if (shouldSkip !== "") {
|
|
101
107
|
Log.debug(shouldSkip);
|
package/src/lib/types.ts
CHANGED
|
@@ -292,6 +292,14 @@ export type ModuleConfig = {
|
|
|
292
292
|
uuid: string;
|
|
293
293
|
/** Configure global exclusions that will never be processed by Pepr. */
|
|
294
294
|
alwaysIgnore: WebhookIgnore;
|
|
295
|
+
/** admission specific ignore */
|
|
296
|
+
admission?: {
|
|
297
|
+
alwaysIgnore: WebhookIgnore;
|
|
298
|
+
};
|
|
299
|
+
/** watch specific ignore */
|
|
300
|
+
watch?: {
|
|
301
|
+
alwaysIgnore: WebhookIgnore;
|
|
302
|
+
};
|
|
295
303
|
} & Partial<ModuleConfigOptions>;
|
|
296
304
|
|
|
297
305
|
export type PackageJSON = {
|
package/src/sdk/sdk.ts
CHANGED
|
@@ -109,17 +109,20 @@ export function getOwnerRefFrom(
|
|
|
109
109
|
*
|
|
110
110
|
* @param name the name of the resource to sanitize
|
|
111
111
|
* @returns the sanitized resource name
|
|
112
|
+
*
|
|
113
|
+
* https://kubernetes.io/docs/concepts/overview/working-with-objects/names/
|
|
112
114
|
*/
|
|
113
115
|
export function sanitizeResourceName(name: string): string {
|
|
114
116
|
return (
|
|
115
117
|
name
|
|
116
|
-
// The name must be lowercase
|
|
117
118
|
.toLowerCase()
|
|
118
|
-
// Replace
|
|
119
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
120
|
-
//
|
|
121
|
-
.slice(0,
|
|
122
|
-
// Remove leading
|
|
123
|
-
.replace(/^[^a-
|
|
119
|
+
// Replace invalid characters (anything not a-z, 0-9, or '-') with '-'
|
|
120
|
+
.replace(/[^a-z0-9-]+/g, "-")
|
|
121
|
+
// Trim to 63 characters (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#rfc-1035-label-names)
|
|
122
|
+
.slice(0, 63)
|
|
123
|
+
// Remove leading non-alphanumeric characters
|
|
124
|
+
.replace(/^[^a-z0-9]+/, "")
|
|
125
|
+
// Remove trailing non-alphanumeric characters
|
|
126
|
+
.replace(/[^a-z0-9]+$/, "")
|
|
124
127
|
);
|
|
125
128
|
}
|