pepr 0.40.0 → 0.42.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 +11 -5
- package/dist/cli/build.d.ts.map +1 -1
- package/dist/cli/deploy.d.ts.map +1 -1
- package/dist/cli/init/templates.d.ts +2 -2
- package/dist/cli.js +228 -179
- package/dist/controller.js +52 -27
- package/dist/lib/assets/index.d.ts.map +1 -1
- package/dist/lib/capability.d.ts.map +1 -1
- package/dist/lib/controller/index.d.ts.map +1 -1
- package/dist/lib/controller/index.util.d.ts +10 -0
- package/dist/lib/controller/index.util.d.ts.map +1 -0
- package/dist/lib/controller/store.d.ts +1 -1
- package/dist/lib/deploymentChecks.d.ts +3 -0
- package/dist/lib/deploymentChecks.d.ts.map +1 -0
- package/dist/lib/enums.d.ts +5 -5
- package/dist/lib/enums.d.ts.map +1 -1
- package/dist/lib/filesystemService.d.ts +2 -0
- package/dist/lib/filesystemService.d.ts.map +1 -0
- package/dist/lib/filter/adjudicators/adjudicators.d.ts +73 -0
- package/dist/lib/filter/adjudicators/adjudicators.d.ts.map +1 -0
- package/dist/lib/filter/adjudicators/defaultTestObjects.d.ts +7 -0
- package/dist/lib/filter/adjudicators/defaultTestObjects.d.ts.map +1 -0
- package/dist/lib/helpers.d.ts +1 -4
- package/dist/lib/helpers.d.ts.map +1 -1
- package/dist/lib/schedule.d.ts.map +1 -1
- package/dist/lib/storage.d.ts +1 -1
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/{logger.d.ts → telemetry/logger.d.ts} +1 -1
- package/dist/lib/telemetry/logger.d.ts.map +1 -0
- package/dist/lib/{metrics.d.ts → telemetry/metrics.d.ts} +3 -1
- package/dist/lib/telemetry/metrics.d.ts.map +1 -0
- package/dist/lib/types.d.ts +10 -9
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib.d.ts +1 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +151 -126
- package/dist/lib.js.map +4 -4
- package/dist/sdk/sdk.d.ts +3 -4
- package/dist/sdk/sdk.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/cli/build.ts +2 -1
- package/src/cli/deploy.ts +2 -1
- package/src/cli/init/templates.ts +1 -1
- package/src/lib/assets/deploy.ts +1 -1
- package/src/lib/assets/destroy.ts +1 -1
- package/src/lib/assets/index.ts +102 -81
- package/src/lib/assets/webhooks.ts +2 -2
- package/src/lib/capability.ts +8 -9
- package/src/lib/controller/index.ts +32 -62
- package/src/lib/controller/index.util.ts +47 -0
- package/src/lib/controller/store.ts +2 -2
- package/src/lib/controller/storeCache.ts +1 -1
- package/src/lib/deploymentChecks.ts +43 -0
- package/src/lib/enums.ts +5 -5
- package/src/lib/filesystemService.ts +16 -0
- package/src/lib/filter/{adjudicators.ts → adjudicators/adjudicators.ts} +67 -35
- package/src/lib/filter/adjudicators/defaultTestObjects.ts +46 -0
- package/src/lib/filter/filter.ts +1 -1
- package/src/lib/finalizer.ts +1 -1
- package/src/lib/helpers.ts +31 -88
- package/src/lib/mutate-processor.ts +1 -1
- package/src/lib/queue.ts +1 -1
- package/src/lib/schedule.ts +8 -8
- package/src/lib/storage.ts +17 -17
- package/src/lib/{logger.ts → telemetry/logger.ts} +1 -1
- package/src/lib/{metrics.ts → telemetry/metrics.ts} +18 -17
- package/src/lib/types.ts +12 -9
- package/src/lib/utils.ts +1 -1
- package/src/lib/validate-processor.ts +1 -1
- package/src/lib/watch-processor.ts +8 -8
- package/src/lib.ts +1 -1
- package/src/runtime/controller.ts +1 -1
- package/src/sdk/sdk.ts +6 -9
- package/src/templates/capabilities/hello-pepr.ts +19 -9
- package/dist/lib/filter/adjudicators.d.ts +0 -69
- package/dist/lib/filter/adjudicators.d.ts.map +0 -1
- package/dist/lib/logger.d.ts.map +0 -1
- package/dist/lib/metrics.d.ts.map +0 -1
package/dist/sdk/sdk.d.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { PeprValidateRequest } from "../lib/validate-request";
|
|
2
2
|
import { PeprMutateRequest } from "../lib/mutate-request";
|
|
3
|
-
import { V1OwnerReference } from "@kubernetes/client-node";
|
|
4
|
-
import { GenericKind } from "kubernetes-fluent-client";
|
|
5
|
-
import { kind } from "kubernetes-fluent-client";
|
|
3
|
+
import { V1OwnerReference, V1Container } from "@kubernetes/client-node";
|
|
4
|
+
import { GenericKind, kind } from "kubernetes-fluent-client";
|
|
6
5
|
/**
|
|
7
6
|
* Returns all containers in a pod
|
|
8
7
|
* @param request the request/pod to get the containers from
|
|
9
8
|
* @param containerType the type of container to get
|
|
10
9
|
* @returns the list of containers in the pod
|
|
11
10
|
*/
|
|
12
|
-
export declare function containers(request: PeprValidateRequest<kind.Pod> | PeprMutateRequest<kind.Pod>, containerType?: "containers" | "initContainers" | "ephemeralContainers"):
|
|
11
|
+
export declare function containers(request: PeprValidateRequest<kind.Pod> | PeprMutateRequest<kind.Pod>, containerType?: "containers" | "initContainers" | "ephemeralContainers"): V1Container[];
|
|
13
12
|
/**
|
|
14
13
|
* Write a K8s event for a CRD
|
|
15
14
|
*
|
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,MAAM,yBAAyB,CAAC;
|
|
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,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,MAAM,EAC1B,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,cAAc,EAAE,WAAW,EAC3B,kBAAkB,CAAC,EAAE,OAAO,EAC5B,UAAU,CAAC,EAAE,OAAO,GACnB,gBAAgB,EAAE,CAcpB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAYzD"}
|
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.42.0",
|
|
19
19
|
"main": "dist/lib.js",
|
|
20
20
|
"types": "dist/lib.d.ts",
|
|
21
21
|
"scripts": {
|
|
@@ -41,12 +41,12 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@types/ramda": "0.30.2",
|
|
44
|
-
"express": "4.21.
|
|
44
|
+
"express": "4.21.2",
|
|
45
45
|
"fast-json-patch": "3.1.1",
|
|
46
46
|
"follow-redirects": "1.15.9",
|
|
47
47
|
"http-status-codes": "^2.3.0",
|
|
48
48
|
"json-pointer": "^0.6.2",
|
|
49
|
-
"kubernetes-fluent-client": "3.3.
|
|
49
|
+
"kubernetes-fluent-client": "3.3.7",
|
|
50
50
|
"pino": "9.5.0",
|
|
51
51
|
"pino-pretty": "13.0.0",
|
|
52
52
|
"prom-client": "15.1.3",
|
|
@@ -69,8 +69,8 @@
|
|
|
69
69
|
"husky": "^9.1.6",
|
|
70
70
|
"jest": "29.7.0",
|
|
71
71
|
"js-yaml": "^4.1.0",
|
|
72
|
-
"
|
|
73
|
-
"
|
|
72
|
+
"ts-jest": "29.2.5",
|
|
73
|
+
"undici": "^7.0.1"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"@types/prompts": "2.4.9",
|
package/src/cli/build.ts
CHANGED
|
@@ -11,9 +11,10 @@ import { dependencies, version } from "./init/templates";
|
|
|
11
11
|
import { RootCmd } from "./root";
|
|
12
12
|
import { peprFormat } from "./format";
|
|
13
13
|
import { Option } from "commander";
|
|
14
|
-
import {
|
|
14
|
+
import { validateCapabilityNames, parseTimeout } from "../lib/helpers";
|
|
15
15
|
import { sanitizeResourceName } from "../sdk/sdk";
|
|
16
16
|
import { determineRbacMode } from "./build.helpers";
|
|
17
|
+
import { createDirectoryIfNotExists } from "../lib/filesystemService";
|
|
17
18
|
const peprTS = "pepr.ts";
|
|
18
19
|
let outputDir: string = "dist";
|
|
19
20
|
export type Reloader = (opts: BuildResult<BuildOptions>) => void | Promise<void>;
|
package/src/cli/deploy.ts
CHANGED
|
@@ -6,10 +6,11 @@ import prompt from "prompts";
|
|
|
6
6
|
import { Assets } from "../lib/assets";
|
|
7
7
|
import { buildModule } from "./build";
|
|
8
8
|
import { RootCmd } from "./root";
|
|
9
|
-
import { validateCapabilityNames
|
|
9
|
+
import { validateCapabilityNames } from "../lib/helpers";
|
|
10
10
|
import { ImagePullSecret } from "../lib/types";
|
|
11
11
|
import { sanitizeName } from "./init/utils";
|
|
12
12
|
import { deployImagePullSecret } from "../lib/assets/deploy";
|
|
13
|
+
import { namespaceDeploymentsReady } from "../lib/deploymentChecks";
|
|
13
14
|
|
|
14
15
|
export default function (program: RootCmd) {
|
|
15
16
|
program
|
package/src/lib/assets/deploy.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { K8s, kind } from "kubernetes-fluent-client";
|
|
|
7
7
|
import { V1PolicyRule as PolicyRule } from "@kubernetes/client-node";
|
|
8
8
|
|
|
9
9
|
import { Assets } from ".";
|
|
10
|
-
import Log from "../logger";
|
|
10
|
+
import Log from "../telemetry/logger";
|
|
11
11
|
import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking";
|
|
12
12
|
import { deployment, moduleSecret, namespace, watcher } from "./pods";
|
|
13
13
|
import { clusterRole, clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "./rbac";
|
package/src/lib/assets/index.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import crypto from "crypto";
|
|
5
5
|
import { dumpYaml } from "@kubernetes/client-node";
|
|
6
|
+
import { kind } from "kubernetes-fluent-client";
|
|
6
7
|
import { ModuleConfig } from "../module";
|
|
7
8
|
import { TLSOut, genTLS } from "../tls";
|
|
8
9
|
import { CapabilityExport } from "../types";
|
|
@@ -11,7 +12,7 @@ import { deploy } from "./deploy";
|
|
|
11
12
|
import { loadCapabilities } from "./loader";
|
|
12
13
|
import { allYaml, zarfYaml, overridesFile, zarfYamlChart } from "./yaml";
|
|
13
14
|
import { namespaceComplianceValidator, replaceString } from "../helpers";
|
|
14
|
-
import {
|
|
15
|
+
import { dedent } from "../helpers";
|
|
15
16
|
import { resolve } from "path";
|
|
16
17
|
import {
|
|
17
18
|
chartYaml,
|
|
@@ -27,6 +28,69 @@ import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking
|
|
|
27
28
|
import { watcher, moduleSecret } from "./pods";
|
|
28
29
|
|
|
29
30
|
import { clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "./rbac";
|
|
31
|
+
import { createDirectoryIfNotExists } from "../filesystemService";
|
|
32
|
+
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
+
function toYaml(obj: any): string {
|
|
35
|
+
return dumpYaml(obj, { noRefs: true });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function createWebhookYaml(
|
|
39
|
+
assets: Assets,
|
|
40
|
+
webhookConfiguration: kind.MutatingWebhookConfiguration | kind.ValidatingWebhookConfiguration,
|
|
41
|
+
): string {
|
|
42
|
+
const yaml = toYaml(webhookConfiguration);
|
|
43
|
+
return replaceString(
|
|
44
|
+
replaceString(
|
|
45
|
+
replaceString(yaml, assets.name, "{{ .Values.uuid }}"),
|
|
46
|
+
assets.config.onError === "reject" ? "Fail" : "Ignore",
|
|
47
|
+
"{{ .Values.admission.failurePolicy }}",
|
|
48
|
+
),
|
|
49
|
+
`${assets.config.webhookTimeout}` || "10",
|
|
50
|
+
"{{ .Values.admission.webhookTimeout }}",
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function helmLayout(basePath: string, unique: string) {
|
|
55
|
+
const helm: Record<string, Record<string, string>> = {
|
|
56
|
+
dirs: {
|
|
57
|
+
chart: resolve(`${basePath}/${unique}-chart`),
|
|
58
|
+
},
|
|
59
|
+
files: {},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
helm.dirs = {
|
|
63
|
+
...helm.dirs,
|
|
64
|
+
charts: `${helm.dirs.chart}/charts`,
|
|
65
|
+
tmpls: `${helm.dirs.chart}/templates`,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
helm.files = {
|
|
69
|
+
...helm.files,
|
|
70
|
+
valuesYaml: `${helm.dirs.chart}/values.yaml`,
|
|
71
|
+
chartYaml: `${helm.dirs.chart}/Chart.yaml`,
|
|
72
|
+
namespaceYaml: `${helm.dirs.tmpls}/namespace.yaml`,
|
|
73
|
+
watcherServiceYaml: `${helm.dirs.tmpls}/watcher-service.yaml`,
|
|
74
|
+
admissionServiceYaml: `${helm.dirs.tmpls}/admission-service.yaml`,
|
|
75
|
+
mutationWebhookYaml: `${helm.dirs.tmpls}/mutation-webhook.yaml`,
|
|
76
|
+
validationWebhookYaml: `${helm.dirs.tmpls}/validation-webhook.yaml`,
|
|
77
|
+
admissionDeploymentYaml: `${helm.dirs.tmpls}/admission-deployment.yaml`,
|
|
78
|
+
admissionServiceMonitorYaml: `${helm.dirs.tmpls}/admission-service-monitor.yaml`,
|
|
79
|
+
watcherDeploymentYaml: `${helm.dirs.tmpls}/watcher-deployment.yaml`,
|
|
80
|
+
watcherServiceMonitorYaml: `${helm.dirs.tmpls}/watcher-service-monitor.yaml`,
|
|
81
|
+
tlsSecretYaml: `${helm.dirs.tmpls}/tls-secret.yaml`,
|
|
82
|
+
apiTokenSecretYaml: `${helm.dirs.tmpls}/api-token-secret.yaml`,
|
|
83
|
+
moduleSecretYaml: `${helm.dirs.tmpls}/module-secret.yaml`,
|
|
84
|
+
storeRoleYaml: `${helm.dirs.tmpls}/store-role.yaml`,
|
|
85
|
+
storeRoleBindingYaml: `${helm.dirs.tmpls}/store-role-binding.yaml`,
|
|
86
|
+
clusterRoleYaml: `${helm.dirs.tmpls}/cluster-role.yaml`,
|
|
87
|
+
clusterRoleBindingYaml: `${helm.dirs.tmpls}/cluster-role-binding.yaml`,
|
|
88
|
+
serviceAccountYaml: `${helm.dirs.tmpls}/service-account.yaml`,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return helm;
|
|
92
|
+
}
|
|
93
|
+
|
|
30
94
|
export class Assets {
|
|
31
95
|
readonly name: string;
|
|
32
96
|
readonly tls: TLSOut;
|
|
@@ -78,102 +142,59 @@ export class Assets {
|
|
|
78
142
|
return allYaml(this, imagePullSecret);
|
|
79
143
|
};
|
|
80
144
|
|
|
145
|
+
/* eslint max-statements: ["warn", 21] */
|
|
81
146
|
generateHelmChart = async (basePath: string) => {
|
|
82
|
-
const
|
|
83
|
-
const CHAR_TEMPLATES_DIR = `${CHART_DIR}/templates`;
|
|
84
|
-
const valuesPath = resolve(CHART_DIR, `values.yaml`);
|
|
85
|
-
const chartPath = resolve(CHART_DIR, `Chart.yaml`);
|
|
86
|
-
const nsPath = resolve(CHAR_TEMPLATES_DIR, `namespace.yaml`);
|
|
87
|
-
const watcherSVCPath = resolve(CHAR_TEMPLATES_DIR, `watcher-service.yaml`);
|
|
88
|
-
const admissionSVCPath = resolve(CHAR_TEMPLATES_DIR, `admission-service.yaml`);
|
|
89
|
-
const mutationWebhookPath = resolve(CHAR_TEMPLATES_DIR, `mutation-webhook.yaml`);
|
|
90
|
-
const validationWebhookPath = resolve(CHAR_TEMPLATES_DIR, `validation-webhook.yaml`);
|
|
91
|
-
const admissionDeployPath = resolve(CHAR_TEMPLATES_DIR, `admission-deployment.yaml`);
|
|
92
|
-
const admissionServiceMonitorPath = resolve(CHAR_TEMPLATES_DIR, `admission-service-monitor.yaml`);
|
|
93
|
-
const watcherDeployPath = resolve(CHAR_TEMPLATES_DIR, `watcher-deployment.yaml`);
|
|
94
|
-
const watcherServiceMonitorPath = resolve(CHAR_TEMPLATES_DIR, `watcher-service-monitor.yaml`);
|
|
95
|
-
const tlsSecretPath = resolve(CHAR_TEMPLATES_DIR, `tls-secret.yaml`);
|
|
96
|
-
const apiTokenSecretPath = resolve(CHAR_TEMPLATES_DIR, `api-token-secret.yaml`);
|
|
97
|
-
const moduleSecretPath = resolve(CHAR_TEMPLATES_DIR, `module-secret.yaml`);
|
|
98
|
-
const storeRolePath = resolve(CHAR_TEMPLATES_DIR, `store-role.yaml`);
|
|
99
|
-
const storeRoleBindingPath = resolve(CHAR_TEMPLATES_DIR, `store-role-binding.yaml`);
|
|
100
|
-
const clusterRolePath = resolve(CHAR_TEMPLATES_DIR, `cluster-role.yaml`);
|
|
101
|
-
const clusterRoleBindingPath = resolve(CHAR_TEMPLATES_DIR, `cluster-role-binding.yaml`);
|
|
102
|
-
const serviceAccountPath = resolve(CHAR_TEMPLATES_DIR, `service-account.yaml`);
|
|
103
|
-
|
|
104
|
-
// create helm chart
|
|
105
|
-
try {
|
|
106
|
-
// create chart dir
|
|
107
|
-
await createDirectoryIfNotExists(CHART_DIR);
|
|
108
|
-
|
|
109
|
-
// create charts dir
|
|
110
|
-
await createDirectoryIfNotExists(`${CHART_DIR}/charts`);
|
|
111
|
-
|
|
112
|
-
// create templates dir
|
|
113
|
-
await createDirectoryIfNotExists(`${CHAR_TEMPLATES_DIR}`);
|
|
147
|
+
const helm = helmLayout(basePath, this.config.uuid);
|
|
114
148
|
|
|
115
|
-
|
|
116
|
-
await
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
// create the namespace.yaml in templates
|
|
122
|
-
await fs.writeFile(nsPath, dedent(nsTemplate()));
|
|
149
|
+
try {
|
|
150
|
+
await Promise.all(
|
|
151
|
+
Object.values(helm.dirs)
|
|
152
|
+
.sort((l, r) => l.split("/").length - r.split("/").length)
|
|
153
|
+
.map(async dir => await createDirectoryIfNotExists(dir)),
|
|
154
|
+
);
|
|
123
155
|
|
|
124
156
|
const code = await fs.readFile(this.path);
|
|
125
157
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
158
|
+
const pairs: [string, () => string][] = [
|
|
159
|
+
[helm.files.chartYaml, () => dedent(chartYaml(this.config.uuid, this.config.description || ""))],
|
|
160
|
+
[helm.files.namespaceYaml, () => dedent(nsTemplate())],
|
|
161
|
+
[helm.files.watcherServiceYaml, () => toYaml(watcherService(this.name))],
|
|
162
|
+
[helm.files.admissionServiceYaml, () => toYaml(service(this.name))],
|
|
163
|
+
[helm.files.tlsSecretYaml, () => toYaml(tlsSecret(this.name, this.tls))],
|
|
164
|
+
[helm.files.apiTokenSecretYaml, () => toYaml(apiTokenSecret(this.name, this.apiToken))],
|
|
165
|
+
[helm.files.storeRoleYaml, () => toYaml(storeRole(this.name))],
|
|
166
|
+
[helm.files.storeRoleBindingYaml, () => toYaml(storeRoleBinding(this.name))],
|
|
167
|
+
[helm.files.clusterRoleYaml, () => dedent(clusterRoleTemplate())],
|
|
168
|
+
[helm.files.clusterRoleBindingYaml, () => toYaml(clusterRoleBinding(this.name))],
|
|
169
|
+
[helm.files.serviceAccountYaml, () => toYaml(serviceAccount(this.name))],
|
|
170
|
+
[helm.files.moduleSecretYaml, () => toYaml(moduleSecret(this.name, code, this.hash))],
|
|
171
|
+
];
|
|
172
|
+
await Promise.all(pairs.map(async ([file, content]) => await fs.writeFile(file, content())));
|
|
173
|
+
|
|
174
|
+
await overridesFile(this, helm.files.valuesYaml);
|
|
175
|
+
|
|
176
|
+
const [mutateWebhook, validateWebhook] = await Promise.all([
|
|
177
|
+
webhookConfig(this, "mutate", this.config.webhookTimeout),
|
|
178
|
+
webhookConfig(this, "validate", this.config.webhookTimeout),
|
|
179
|
+
]);
|
|
140
180
|
|
|
141
181
|
if (validateWebhook || mutateWebhook) {
|
|
142
|
-
await fs.writeFile(
|
|
143
|
-
await fs.writeFile(
|
|
182
|
+
await fs.writeFile(helm.files.admissionDeploymentYaml, dedent(admissionDeployTemplate(this.buildTimestamp)));
|
|
183
|
+
await fs.writeFile(helm.files.admissionServiceMonitorYaml, dedent(serviceMonitorTemplate("admission")));
|
|
144
184
|
}
|
|
145
185
|
|
|
146
186
|
if (mutateWebhook) {
|
|
147
|
-
|
|
148
|
-
const mutateWebhookTemplate = replaceString(
|
|
149
|
-
replaceString(
|
|
150
|
-
replaceString(yamlMutateWebhook, this.name, "{{ .Values.uuid }}"),
|
|
151
|
-
this.config.onError === "reject" ? "Fail" : "Ignore",
|
|
152
|
-
"{{ .Values.admission.failurePolicy }}",
|
|
153
|
-
),
|
|
154
|
-
`${this.config.webhookTimeout}` || "10",
|
|
155
|
-
"{{ .Values.admission.webhookTimeout }}",
|
|
156
|
-
);
|
|
157
|
-
await fs.writeFile(mutationWebhookPath, mutateWebhookTemplate);
|
|
187
|
+
await fs.writeFile(helm.files.mutationWebhookYaml, createWebhookYaml(this, mutateWebhook));
|
|
158
188
|
}
|
|
159
189
|
|
|
160
190
|
if (validateWebhook) {
|
|
161
|
-
|
|
162
|
-
const validateWebhookTemplate = replaceString(
|
|
163
|
-
replaceString(
|
|
164
|
-
replaceString(yamlValidateWebhook, this.name, "{{ .Values.uuid }}"),
|
|
165
|
-
this.config.onError === "reject" ? "Fail" : "Ignore",
|
|
166
|
-
"{{ .Values.admission.failurePolicy }}",
|
|
167
|
-
),
|
|
168
|
-
`${this.config.webhookTimeout}` || "10",
|
|
169
|
-
"{{ .Values.admission.webhookTimeout }}",
|
|
170
|
-
);
|
|
171
|
-
await fs.writeFile(validationWebhookPath, validateWebhookTemplate);
|
|
191
|
+
await fs.writeFile(helm.files.validationWebhookYaml, createWebhookYaml(this, validateWebhook));
|
|
172
192
|
}
|
|
173
193
|
|
|
194
|
+
const watchDeployment = watcher(this, this.hash, this.buildTimestamp);
|
|
174
195
|
if (watchDeployment) {
|
|
175
|
-
await fs.writeFile(
|
|
176
|
-
await fs.writeFile(
|
|
196
|
+
await fs.writeFile(helm.files.watcherDeploymentYaml, dedent(watcherDeployTemplate(this.buildTimestamp)));
|
|
197
|
+
await fs.writeFile(helm.files.watcherServiceMonitorYaml, dedent(serviceMonitorTemplate("watcher")));
|
|
177
198
|
}
|
|
178
199
|
} catch (err) {
|
|
179
200
|
console.error(`Error generating helm chart: ${err.message}`);
|
|
@@ -44,8 +44,8 @@ export async function generateWebhookRules(assets: Assets, isMutateWebhook: bool
|
|
|
44
44
|
const operations: string[] = [];
|
|
45
45
|
|
|
46
46
|
// CreateOrUpdate is a Pepr-specific event that is translated to Create and Update
|
|
47
|
-
if (event === Event.
|
|
48
|
-
operations.push(Event.
|
|
47
|
+
if (event === Event.CREATE_OR_UPDATE) {
|
|
48
|
+
operations.push(Event.CREATE, Event.UPDATE);
|
|
49
49
|
} else {
|
|
50
50
|
operations.push(event);
|
|
51
51
|
}
|
package/src/lib/capability.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
/* eslint-disable max-statements */
|
|
2
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
2
|
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
4
3
|
|
|
5
4
|
import { GenericClass, GroupVersionKind, modelToGroupVersionKind } from "kubernetes-fluent-client";
|
|
6
5
|
import { pickBy } from "ramda";
|
|
7
|
-
import Log from "./logger";
|
|
6
|
+
import Log from "./telemetry/logger";
|
|
8
7
|
import { isBuildMode, isDevMode, isWatchMode } from "./module";
|
|
9
8
|
import { PeprStore, Storage } from "./storage";
|
|
10
9
|
import { OnSchedule, Schedule } from "./schedule";
|
|
@@ -193,7 +192,7 @@ export class Capability implements CapabilityExport {
|
|
|
193
192
|
model,
|
|
194
193
|
// If the kind is not specified, use the matched kind from the model
|
|
195
194
|
kind: kind || matchedKind,
|
|
196
|
-
event: Event.
|
|
195
|
+
event: Event.ANY,
|
|
197
196
|
filters: {
|
|
198
197
|
name: "",
|
|
199
198
|
namespaces: [],
|
|
@@ -317,7 +316,7 @@ export class Capability implements CapabilityExport {
|
|
|
317
316
|
...binding,
|
|
318
317
|
isMutate: true,
|
|
319
318
|
isFinalize: true,
|
|
320
|
-
event: Event.
|
|
319
|
+
event: Event.ANY,
|
|
321
320
|
mutateCallback: addFinalizer,
|
|
322
321
|
};
|
|
323
322
|
bindings.push(mutateBinding);
|
|
@@ -329,7 +328,7 @@ export class Capability implements CapabilityExport {
|
|
|
329
328
|
...binding,
|
|
330
329
|
isWatch: true,
|
|
331
330
|
isFinalize: true,
|
|
332
|
-
event: Event.
|
|
331
|
+
event: Event.UPDATE,
|
|
333
332
|
finalizeCallback: async (update: InstanceType<T>, logger = aliasLogger) => {
|
|
334
333
|
Log.info(`Executing finalize action with alias: ${binding.alias || "no alias provided"}`);
|
|
335
334
|
return await finalizeCallback(update, logger);
|
|
@@ -401,10 +400,10 @@ export class Capability implements CapabilityExport {
|
|
|
401
400
|
}
|
|
402
401
|
|
|
403
402
|
return {
|
|
404
|
-
IsCreatedOrUpdated: () => bindEvent(Event.
|
|
405
|
-
IsCreated: () => bindEvent(Event.
|
|
406
|
-
IsUpdated: () => bindEvent(Event.
|
|
407
|
-
IsDeleted: () => bindEvent(Event.
|
|
403
|
+
IsCreatedOrUpdated: () => bindEvent(Event.CREATE_OR_UPDATE),
|
|
404
|
+
IsCreated: () => bindEvent(Event.CREATE),
|
|
405
|
+
IsUpdated: () => bindEvent(Event.UPDATE),
|
|
406
|
+
IsDeleted: () => bindEvent(Event.DELETE),
|
|
408
407
|
};
|
|
409
408
|
};
|
|
410
409
|
}
|
|
@@ -7,17 +7,19 @@ import https from "https";
|
|
|
7
7
|
|
|
8
8
|
import { Capability } from "../capability";
|
|
9
9
|
import { MutateResponse, ValidateResponse } from "../k8s";
|
|
10
|
-
import Log from "../logger";
|
|
11
|
-
import { metricsCollector, MetricsCollector } from "../metrics";
|
|
10
|
+
import Log from "../telemetry/logger";
|
|
11
|
+
import { metricsCollector, MetricsCollector } from "../telemetry/metrics";
|
|
12
12
|
import { ModuleConfig, isWatchMode } from "../module";
|
|
13
13
|
import { mutateProcessor } from "../mutate-processor";
|
|
14
14
|
import { validateProcessor } from "../validate-processor";
|
|
15
15
|
import { StoreController } from "./store";
|
|
16
|
-
import {
|
|
16
|
+
import { AdmissionRequest } from "../types";
|
|
17
|
+
import { karForMutate, karForValidate, KubeAdmissionReview } from "./index.util";
|
|
17
18
|
|
|
18
19
|
if (!process.env.PEPR_NODE_WARNINGS) {
|
|
19
20
|
process.removeAllListeners("warning");
|
|
20
21
|
}
|
|
22
|
+
|
|
21
23
|
export class Controller {
|
|
22
24
|
// Track whether the server is running
|
|
23
25
|
#running = false;
|
|
@@ -183,6 +185,8 @@ export class Controller {
|
|
|
183
185
|
*/
|
|
184
186
|
#metrics = async (req: express.Request, res: express.Response) => {
|
|
185
187
|
try {
|
|
188
|
+
// https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md#basic-info
|
|
189
|
+
res.set("Content-Type", "text/plain; version=0.0.4");
|
|
186
190
|
res.send(await this.#metricsCollector.getMetrics());
|
|
187
191
|
} catch (err) {
|
|
188
192
|
Log.error(err, `Error getting metrics`);
|
|
@@ -206,77 +210,38 @@ export class Controller {
|
|
|
206
210
|
// Get the request from the body or create an empty request
|
|
207
211
|
const request: AdmissionRequest = req.body?.request || ({} as AdmissionRequest);
|
|
208
212
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const name = request?.name ? `/${request.name}` : "";
|
|
214
|
-
const namespace = request?.namespace || "";
|
|
215
|
-
const gvk = request?.kind || { group: "", version: "", kind: "" };
|
|
216
|
-
|
|
217
|
-
const reqMetadata = {
|
|
218
|
-
uid: request.uid,
|
|
219
|
-
namespace,
|
|
220
|
-
name,
|
|
213
|
+
const { name, namespace, gvk } = {
|
|
214
|
+
name: request?.name ? `/${request.name}` : "",
|
|
215
|
+
namespace: request?.namespace || "",
|
|
216
|
+
gvk: request?.kind || { group: "", version: "", kind: "" },
|
|
221
217
|
};
|
|
222
218
|
|
|
219
|
+
const reqMetadata = { uid: request.uid, namespace, name };
|
|
223
220
|
Log.info({ ...reqMetadata, gvk, operation: request.operation, admissionKind }, "Incoming request");
|
|
224
221
|
Log.debug({ ...reqMetadata, request }, "Incoming request body");
|
|
225
222
|
|
|
226
|
-
//
|
|
227
|
-
|
|
223
|
+
// Run the before hook if it exists
|
|
224
|
+
this.#beforeHook && this.#beforeHook(request || {});
|
|
228
225
|
|
|
229
|
-
//
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
226
|
+
// Process the request
|
|
227
|
+
const response: MutateResponse | ValidateResponse[] =
|
|
228
|
+
admissionKind === "Mutate"
|
|
229
|
+
? await mutateProcessor(this.#config, this.#capabilities, request, reqMetadata)
|
|
230
|
+
: await validateProcessor(this.#config, this.#capabilities, request, reqMetadata);
|
|
235
231
|
|
|
236
232
|
// Run the after hook if it exists
|
|
237
|
-
|
|
238
|
-
responseList.map(res => {
|
|
233
|
+
[response].flat().map(res => {
|
|
239
234
|
this.#afterHook && this.#afterHook(res);
|
|
240
|
-
// Log the response
|
|
241
235
|
Log.info({ ...reqMetadata, res }, "Check response");
|
|
242
236
|
});
|
|
243
237
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
kind: "AdmissionReview",
|
|
252
|
-
response: kubeAdmissionResponse,
|
|
253
|
-
});
|
|
254
|
-
} else {
|
|
255
|
-
kubeAdmissionResponse =
|
|
256
|
-
responseList.length === 0
|
|
257
|
-
? {
|
|
258
|
-
uid: request.uid,
|
|
259
|
-
allowed: true,
|
|
260
|
-
status: { message: "no in-scope validations -- allowed!" },
|
|
261
|
-
}
|
|
262
|
-
: {
|
|
263
|
-
uid: responseList[0].uid,
|
|
264
|
-
allowed: responseList.filter(r => !r.allowed).length === 0,
|
|
265
|
-
status: {
|
|
266
|
-
message: (responseList as ValidateResponse[])
|
|
267
|
-
.filter(rl => !rl.allowed)
|
|
268
|
-
.map(curr => curr.status?.message)
|
|
269
|
-
.join("; "),
|
|
270
|
-
},
|
|
271
|
-
};
|
|
272
|
-
res.send({
|
|
273
|
-
apiVersion: "admission.k8s.io/v1",
|
|
274
|
-
kind: "AdmissionReview",
|
|
275
|
-
response: kubeAdmissionResponse,
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
Log.debug({ ...reqMetadata, kubeAdmissionResponse }, "Outgoing response");
|
|
238
|
+
const kar: KubeAdmissionReview =
|
|
239
|
+
admissionKind === "Mutate"
|
|
240
|
+
? karForMutate(response as MutateResponse)
|
|
241
|
+
: karForValidate(request, response as ValidateResponse[]);
|
|
242
|
+
|
|
243
|
+
Log.debug({ ...reqMetadata, kubeAdmissionResponse: kar.response }, "Outgoing response");
|
|
244
|
+
res.send(kar);
|
|
280
245
|
|
|
281
246
|
this.#metricsCollector.observeEnd(startTime, admissionKind);
|
|
282
247
|
} catch (err) {
|
|
@@ -298,6 +263,11 @@ export class Controller {
|
|
|
298
263
|
const startTime = Date.now();
|
|
299
264
|
|
|
300
265
|
res.on("finish", () => {
|
|
266
|
+
const excludedRoutes = ["/healthz", "/metrics"];
|
|
267
|
+
if (excludedRoutes.includes(req.originalUrl)) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
301
271
|
const elapsedTime = Date.now() - startTime;
|
|
302
272
|
const message = {
|
|
303
273
|
uid: req.body?.request?.uid,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
|
+
|
|
4
|
+
import { MutateResponse, ValidateResponse } from "../k8s";
|
|
5
|
+
import { ResponseItem, AdmissionRequest } from "../types";
|
|
6
|
+
|
|
7
|
+
export interface KubeAdmissionReview {
|
|
8
|
+
apiVersion: string;
|
|
9
|
+
kind: string;
|
|
10
|
+
response: ValidateResponse[] | MutateResponse | ResponseItem;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function karForMutate(mr: MutateResponse): KubeAdmissionReview {
|
|
14
|
+
return {
|
|
15
|
+
apiVersion: "admission.k8s.io/v1",
|
|
16
|
+
kind: "AdmissionReview",
|
|
17
|
+
response: mr,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function karForValidate(ar: AdmissionRequest, vr: ValidateResponse[]): KubeAdmissionReview {
|
|
22
|
+
const isAllowed = vr.filter(r => !r.allowed).length === 0;
|
|
23
|
+
|
|
24
|
+
const resp: ValidateResponse =
|
|
25
|
+
vr.length === 0
|
|
26
|
+
? {
|
|
27
|
+
uid: ar.uid,
|
|
28
|
+
allowed: true,
|
|
29
|
+
status: { code: 200, message: "no in-scope validations -- allowed!" },
|
|
30
|
+
}
|
|
31
|
+
: {
|
|
32
|
+
uid: vr[0].uid,
|
|
33
|
+
allowed: isAllowed,
|
|
34
|
+
status: {
|
|
35
|
+
code: isAllowed ? 200 : 422,
|
|
36
|
+
message: vr
|
|
37
|
+
.filter(rl => !rl.allowed)
|
|
38
|
+
.map(curr => curr.status?.message)
|
|
39
|
+
.join("; "),
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
return {
|
|
43
|
+
apiVersion: "admission.k8s.io/v1",
|
|
44
|
+
kind: "AdmissionReview",
|
|
45
|
+
response: resp,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -7,12 +7,12 @@ import { startsWith } from "ramda";
|
|
|
7
7
|
|
|
8
8
|
import { Capability } from "../capability";
|
|
9
9
|
import { Store } from "../k8s";
|
|
10
|
-
import Log, { redactedPatch, redactedStore } from "../logger";
|
|
10
|
+
import Log, { redactedPatch, redactedStore } from "../telemetry/logger";
|
|
11
11
|
import { DataOp, DataSender, DataStore, Storage } from "../storage";
|
|
12
12
|
import { fillStoreCache, sendUpdatesAndFlushCache } from "./storeCache";
|
|
13
13
|
|
|
14
14
|
const namespace = "pepr-system";
|
|
15
|
-
export const debounceBackoff =
|
|
15
|
+
export const debounceBackoff = 1000;
|
|
16
16
|
|
|
17
17
|
export class StoreController {
|
|
18
18
|
#name: string;
|