pepr 0.42.3 → 0.44.0-nightly.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/build.d.ts +25 -11
- package/dist/cli/build.d.ts.map +1 -1
- package/dist/cli/build.helpers.d.ts +13 -14
- package/dist/cli/build.helpers.d.ts.map +1 -1
- package/dist/cli/deploy.d.ts.map +1 -1
- package/dist/cli/dev.d.ts.map +1 -1
- package/dist/cli/init/templates.d.ts +7 -5
- package/dist/cli/init/templates.d.ts.map +1 -1
- package/dist/cli.js +786 -739
- package/dist/controller.js +1 -1
- package/dist/fixtures/loader.d.ts.map +1 -1
- package/dist/lib/assets/assets.d.ts +15 -12
- package/dist/lib/assets/assets.d.ts.map +1 -1
- package/dist/lib/assets/deploy.d.ts +1 -1
- package/dist/lib/assets/deploy.d.ts.map +1 -1
- package/dist/lib/assets/helm.d.ts.map +1 -1
- package/dist/lib/assets/index.d.ts.map +1 -1
- package/dist/lib/assets/webhooks.d.ts +4 -1
- package/dist/lib/assets/webhooks.d.ts.map +1 -1
- package/dist/lib/assets/yaml/generateAllYaml.d.ts +9 -0
- package/dist/lib/assets/yaml/generateAllYaml.d.ts.map +1 -0
- package/dist/lib/assets/yaml/generateZarfYaml.d.ts +5 -0
- package/dist/lib/assets/yaml/generateZarfYaml.d.ts.map +1 -0
- package/dist/lib/assets/yaml/overridesFile.d.ts +15 -0
- package/dist/lib/assets/yaml/overridesFile.d.ts.map +1 -0
- package/dist/lib/core/module.d.ts +23 -20
- package/dist/lib/core/module.d.ts.map +1 -1
- package/dist/lib/enums.d.ts +4 -0
- package/dist/lib/enums.d.ts.map +1 -1
- package/dist/lib/processors/mutate-processor.d.ts +2 -0
- package/dist/lib/processors/mutate-processor.d.ts.map +1 -1
- package/dist/lib/processors/validate-processor.d.ts.map +1 -1
- package/dist/lib/telemetry/timeUtils.d.ts +2 -0
- package/dist/lib/telemetry/timeUtils.d.ts.map +1 -0
- package/dist/lib/telemetry/webhookTimeouts.d.ts +9 -0
- package/dist/lib/telemetry/webhookTimeouts.d.ts.map +1 -0
- package/dist/lib.js +79 -22
- package/dist/lib.js.map +4 -4
- package/package.json +7 -4
- package/src/cli/build.helpers.ts +35 -35
- package/src/cli/build.ts +126 -70
- package/src/cli/deploy.ts +7 -6
- package/src/cli/dev.ts +9 -6
- package/src/cli/init/templates.ts +6 -5
- package/src/fixtures/loader.ts +2 -2
- package/src/lib/assets/assets.ts +66 -53
- package/src/lib/assets/deploy.ts +32 -30
- package/src/lib/assets/helm.ts +22 -4
- package/src/lib/assets/index.ts +33 -9
- package/src/lib/assets/pods.ts +2 -2
- package/src/lib/assets/webhooks.ts +21 -6
- package/src/lib/assets/yaml/generateAllYaml.ts +50 -0
- package/src/lib/assets/yaml/generateZarfYaml.ts +38 -0
- package/src/lib/assets/{yaml.ts → yaml/overridesFile.ts} +8 -120
- package/src/lib/core/module.ts +26 -21
- package/src/lib/enums.ts +6 -0
- package/src/lib/processors/mutate-processor.ts +15 -7
- package/src/lib/processors/validate-processor.ts +13 -4
- package/src/lib/telemetry/timeUtils.ts +1 -0
- package/src/lib/telemetry/webhookTimeouts.ts +34 -0
- package/dist/lib/assets/yaml.d.ts +0 -32
- package/dist/lib/assets/yaml.d.ts.map +0 -1
package/src/lib/assets/assets.ts
CHANGED
|
@@ -11,92 +11,97 @@ import {
|
|
|
11
11
|
serviceMonitorTemplate,
|
|
12
12
|
watcherDeployTemplate,
|
|
13
13
|
} from "./helm";
|
|
14
|
+
import {
|
|
15
|
+
V1Deployment,
|
|
16
|
+
V1MutatingWebhookConfiguration,
|
|
17
|
+
V1ValidatingWebhookConfiguration,
|
|
18
|
+
} from "@kubernetes/client-node/dist/gen";
|
|
14
19
|
import { createDirectoryIfNotExists } from "../filesystemService";
|
|
15
|
-
import {
|
|
20
|
+
import { overridesFile } from "./yaml/overridesFile";
|
|
16
21
|
import { getDeployment, getModuleSecret, getWatcher } from "./pods";
|
|
17
22
|
import { helmLayout, createWebhookYaml, toYaml } from "./index";
|
|
18
23
|
import { loadCapabilities } from "./loader";
|
|
19
24
|
import { namespaceComplianceValidator, dedent } from "../helpers";
|
|
25
|
+
import { promises as fs } from "fs";
|
|
20
26
|
import { storeRole, storeRoleBinding, clusterRoleBinding, serviceAccount } from "./rbac";
|
|
21
27
|
import { watcherService, service, tlsSecret, apiTokenSecret } from "./networking";
|
|
22
|
-
import {
|
|
23
|
-
import { generateZarfYaml, generateZarfYamlChart, generateAllYaml, overridesFile } from "./yaml";
|
|
24
|
-
import { promises as fs } from "fs";
|
|
25
|
-
import { V1MutatingWebhookConfiguration, V1ValidatingWebhookConfiguration } from "@kubernetes/client-node/dist/gen";
|
|
28
|
+
import { WebhookType } from "../enums";
|
|
26
29
|
|
|
27
30
|
export class Assets {
|
|
28
31
|
readonly name: string;
|
|
29
32
|
readonly tls: TLSOut;
|
|
30
33
|
readonly apiToken: string;
|
|
34
|
+
readonly config: ModuleConfig;
|
|
35
|
+
readonly path: string;
|
|
31
36
|
readonly alwaysIgnore!: WebhookIgnore;
|
|
37
|
+
readonly imagePullSecrets: string[];
|
|
32
38
|
capabilities!: CapabilityExport[];
|
|
33
|
-
|
|
34
39
|
image: string;
|
|
35
40
|
buildTimestamp: string;
|
|
36
|
-
|
|
41
|
+
readonly host?: string;
|
|
37
42
|
|
|
38
|
-
constructor(
|
|
39
|
-
readonly config: ModuleConfig,
|
|
40
|
-
readonly path: string,
|
|
41
|
-
readonly host?: string,
|
|
42
|
-
) {
|
|
43
|
+
constructor(config: ModuleConfig, path: string, imagePullSecrets: string[], host?: string) {
|
|
43
44
|
this.name = `pepr-${config.uuid}`;
|
|
45
|
+
this.imagePullSecrets = imagePullSecrets;
|
|
44
46
|
this.buildTimestamp = `${Date.now()}`;
|
|
47
|
+
this.config = config;
|
|
48
|
+
this.path = path;
|
|
49
|
+
this.host = host;
|
|
45
50
|
this.alwaysIgnore = config.alwaysIgnore;
|
|
46
51
|
this.image = `ghcr.io/defenseunicorns/pepr/controller:v${config.peprVersion}`;
|
|
47
|
-
|
|
52
|
+
|
|
48
53
|
// Generate the ephemeral tls things
|
|
49
|
-
this.tls = genTLS(
|
|
54
|
+
this.tls = genTLS(host || `${this.name}.pepr-system.svc`);
|
|
50
55
|
|
|
51
56
|
// Generate the api token for the controller / webhook
|
|
52
57
|
this.apiToken = crypto.randomBytes(32).toString("hex");
|
|
53
58
|
}
|
|
54
59
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
async deploy(
|
|
61
|
+
deployFunction: (assets: Assets, force: boolean, webhookTimeout: number) => Promise<void>,
|
|
62
|
+
force: boolean,
|
|
63
|
+
webhookTimeout?: number,
|
|
64
|
+
): Promise<void> {
|
|
60
65
|
this.capabilities = await loadCapabilities(this.path);
|
|
61
|
-
await deploy(this, force, webhookTimeout);
|
|
62
|
-
};
|
|
63
66
|
|
|
64
|
-
|
|
67
|
+
const timeout = typeof webhookTimeout === "number" ? webhookTimeout : 10;
|
|
65
68
|
|
|
66
|
-
|
|
69
|
+
await deployFunction(this, force, timeout);
|
|
70
|
+
}
|
|
67
71
|
|
|
68
|
-
|
|
72
|
+
zarfYaml = (
|
|
73
|
+
zarfYamlGenerator: (assets: Assets, path: string, type: "manifests" | "charts") => string,
|
|
74
|
+
path: string,
|
|
75
|
+
): string => zarfYamlGenerator(this, path, "manifests");
|
|
76
|
+
|
|
77
|
+
zarfYamlChart = (
|
|
78
|
+
zarfYamlGenerator: (assets: Assets, path: string, type: "manifests" | "charts") => string,
|
|
79
|
+
path: string,
|
|
80
|
+
): string => zarfYamlGenerator(this, path, "charts");
|
|
81
|
+
|
|
82
|
+
allYaml = async (
|
|
83
|
+
yamlGenerationFunction: (
|
|
84
|
+
assyts: Assets,
|
|
85
|
+
deployments: { default: V1Deployment; watch: V1Deployment | null },
|
|
86
|
+
) => Promise<string>,
|
|
87
|
+
imagePullSecret?: string,
|
|
88
|
+
): Promise<string> => {
|
|
69
89
|
this.capabilities = await loadCapabilities(this.path);
|
|
70
90
|
// give error if namespaces are not respected
|
|
71
91
|
for (const capability of this.capabilities) {
|
|
72
92
|
namespaceComplianceValidator(capability, this.alwaysIgnore?.namespaces);
|
|
73
93
|
}
|
|
74
94
|
|
|
75
|
-
const webhooks = {
|
|
76
|
-
mutate: await webhookConfig(this, "mutate", this.config.webhookTimeout),
|
|
77
|
-
validate: await webhookConfig(this, "validate", this.config.webhookTimeout),
|
|
78
|
-
};
|
|
79
|
-
|
|
80
95
|
const code = await fs.readFile(this.path);
|
|
81
96
|
|
|
82
|
-
|
|
83
|
-
this.hash = crypto.createHash("sha256").update(code).digest("hex");
|
|
97
|
+
const moduleHash = crypto.createHash("sha256").update(code).digest("hex");
|
|
84
98
|
|
|
85
99
|
const deployments = {
|
|
86
|
-
default: getDeployment(this,
|
|
87
|
-
watch: getWatcher(this,
|
|
100
|
+
default: getDeployment(this, moduleHash, this.buildTimestamp, imagePullSecret),
|
|
101
|
+
watch: getWatcher(this, moduleHash, this.buildTimestamp, imagePullSecret),
|
|
88
102
|
};
|
|
89
103
|
|
|
90
|
-
|
|
91
|
-
apiToken: this.apiToken,
|
|
92
|
-
capabilities: this.capabilities,
|
|
93
|
-
config: this.config,
|
|
94
|
-
hash: this.hash,
|
|
95
|
-
name: this.name,
|
|
96
|
-
path: this.path,
|
|
97
|
-
tls: this.tls,
|
|
98
|
-
};
|
|
99
|
-
return generateAllYaml(webhooks, deployments, assetsInputs);
|
|
104
|
+
return yamlGenerationFunction(this, deployments);
|
|
100
105
|
};
|
|
101
106
|
|
|
102
107
|
writeWebhookFiles = async (
|
|
@@ -118,7 +123,14 @@ export class Assets {
|
|
|
118
123
|
}
|
|
119
124
|
};
|
|
120
125
|
|
|
121
|
-
generateHelmChart = async (
|
|
126
|
+
generateHelmChart = async (
|
|
127
|
+
webhookGeneratorFunction: (
|
|
128
|
+
assets: Assets,
|
|
129
|
+
mutateOrValidate: WebhookType,
|
|
130
|
+
timeoutSeconds: number | undefined,
|
|
131
|
+
) => Promise<V1MutatingWebhookConfiguration | V1ValidatingWebhookConfiguration | null>,
|
|
132
|
+
basePath: string,
|
|
133
|
+
): Promise<void> => {
|
|
122
134
|
const helm = helmLayout(basePath, this.config.uuid);
|
|
123
135
|
|
|
124
136
|
try {
|
|
@@ -129,6 +141,7 @@ export class Assets {
|
|
|
129
141
|
);
|
|
130
142
|
|
|
131
143
|
const code = await fs.readFile(this.path);
|
|
144
|
+
const moduleHash = crypto.createHash("sha256").update(code).digest("hex");
|
|
132
145
|
|
|
133
146
|
const pairs: [string, () => string][] = [
|
|
134
147
|
[helm.files.chartYaml, (): string => dedent(chartYaml(this.config.uuid, this.config.description || ""))],
|
|
@@ -142,28 +155,28 @@ export class Assets {
|
|
|
142
155
|
[helm.files.clusterRoleYaml, (): string => dedent(clusterRoleTemplate())],
|
|
143
156
|
[helm.files.clusterRoleBindingYaml, (): string => toYaml(clusterRoleBinding(this.name))],
|
|
144
157
|
[helm.files.serviceAccountYaml, (): string => toYaml(serviceAccount(this.name))],
|
|
145
|
-
[helm.files.moduleSecretYaml, (): string => toYaml(getModuleSecret(this.name, code,
|
|
158
|
+
[helm.files.moduleSecretYaml, (): string => toYaml(getModuleSecret(this.name, code, moduleHash))],
|
|
146
159
|
];
|
|
147
160
|
await Promise.all(pairs.map(async ([file, content]) => await fs.writeFile(file, content())));
|
|
148
161
|
|
|
149
162
|
const overrideData = {
|
|
150
|
-
hash:
|
|
163
|
+
hash: moduleHash,
|
|
151
164
|
name: this.name,
|
|
152
165
|
image: this.image,
|
|
153
166
|
config: this.config,
|
|
154
167
|
apiToken: this.apiToken,
|
|
155
168
|
capabilities: this.capabilities,
|
|
156
169
|
};
|
|
157
|
-
await overridesFile(overrideData, helm.files.valuesYaml);
|
|
170
|
+
await overridesFile(overrideData, helm.files.valuesYaml, this.imagePullSecrets);
|
|
158
171
|
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
172
|
+
const webhooks = {
|
|
173
|
+
mutate: await webhookGeneratorFunction(this, WebhookType.MUTATE, this.config.webhookTimeout),
|
|
174
|
+
validate: await webhookGeneratorFunction(this, WebhookType.VALIDATE, this.config.webhookTimeout),
|
|
175
|
+
};
|
|
163
176
|
|
|
164
|
-
await this.writeWebhookFiles(
|
|
177
|
+
await this.writeWebhookFiles(webhooks.validate, webhooks.mutate, helm);
|
|
165
178
|
|
|
166
|
-
const watchDeployment = getWatcher(this,
|
|
179
|
+
const watchDeployment = getWatcher(this, moduleHash, this.buildTimestamp);
|
|
167
180
|
if (watchDeployment) {
|
|
168
181
|
await fs.writeFile(helm.files.watcherDeploymentYaml, dedent(watcherDeployTemplate(this.buildTimestamp)));
|
|
169
182
|
await fs.writeFile(helm.files.watcherServiceMonitorYaml, dedent(serviceMonitorTemplate("watcher")));
|
package/src/lib/assets/deploy.ts
CHANGED
|
@@ -12,8 +12,9 @@ import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking
|
|
|
12
12
|
import { getDeployment, getModuleSecret, getNamespace, getWatcher } from "./pods";
|
|
13
13
|
import { clusterRole, clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "./rbac";
|
|
14
14
|
import { peprStoreCRD } from "./store";
|
|
15
|
-
import {
|
|
15
|
+
import { webhookConfigGenerator } from "./webhooks";
|
|
16
16
|
import { CapabilityExport, ImagePullSecret } from "../types";
|
|
17
|
+
import { WebhookType } from "../enums";
|
|
17
18
|
|
|
18
19
|
export async function deployImagePullSecret(imagePullSecret: ImagePullSecret, name: string): Promise<void> {
|
|
19
20
|
try {
|
|
@@ -42,50 +43,51 @@ export async function deployImagePullSecret(imagePullSecret: ImagePullSecret, na
|
|
|
42
43
|
Log.error(e);
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
|
-
export async function deploy(assets: Assets, force: boolean, webhookTimeout?: number): Promise<void> {
|
|
46
|
-
Log.info("Establishing connection to Kubernetes");
|
|
47
46
|
|
|
48
|
-
|
|
47
|
+
async function handleWebhookConfiguration(
|
|
48
|
+
assets: Assets,
|
|
49
|
+
type: WebhookType,
|
|
50
|
+
webhookTimeout: number,
|
|
51
|
+
force: boolean,
|
|
52
|
+
): Promise<void> {
|
|
53
|
+
const kindMap = {
|
|
54
|
+
mutate: kind.MutatingWebhookConfiguration,
|
|
55
|
+
validate: kind.ValidatingWebhookConfiguration,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const webhookConfig = await webhookConfigGenerator(assets, type, webhookTimeout);
|
|
59
|
+
|
|
60
|
+
if (webhookConfig) {
|
|
61
|
+
Log.info(`Applying ${type} webhook`);
|
|
62
|
+
await K8s(kindMap[type]).Apply(webhookConfig, { force });
|
|
63
|
+
} else {
|
|
64
|
+
Log.info(`${type.charAt(0).toUpperCase() + type.slice(1)} webhook not needed, removing if it exists`);
|
|
65
|
+
await K8s(kindMap[type]).Delete(assets.name);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function deployWebhook(assets: Assets, force: boolean, webhookTimeout: number): Promise<void> {
|
|
70
|
+
Log.info("Establishing connection to Kubernetes");
|
|
49
71
|
|
|
50
72
|
Log.info("Applying pepr-system namespace");
|
|
51
73
|
await K8s(kind.Namespace).Apply(getNamespace(assets.config.customLabels?.namespace));
|
|
52
74
|
|
|
53
75
|
// Create the mutating webhook configuration if it is needed
|
|
54
|
-
|
|
55
|
-
if (mutateWebhook) {
|
|
56
|
-
Log.info("Applying mutating webhook");
|
|
57
|
-
await K8s(kind.MutatingWebhookConfiguration).Apply(mutateWebhook, { force });
|
|
58
|
-
} else {
|
|
59
|
-
Log.info("Mutating webhook not needed, removing if it exists");
|
|
60
|
-
await K8s(kind.MutatingWebhookConfiguration).Delete(name);
|
|
61
|
-
}
|
|
76
|
+
await handleWebhookConfiguration(assets, WebhookType.MUTATE, webhookTimeout, force);
|
|
62
77
|
|
|
63
78
|
// Create the validating webhook configuration if it is needed
|
|
64
|
-
|
|
65
|
-
if (validateWebhook) {
|
|
66
|
-
Log.info("Applying validating webhook");
|
|
67
|
-
await K8s(kind.ValidatingWebhookConfiguration).Apply(validateWebhook, { force });
|
|
68
|
-
} else {
|
|
69
|
-
Log.info("Validating webhook not needed, removing if it exists");
|
|
70
|
-
await K8s(kind.ValidatingWebhookConfiguration).Delete(name);
|
|
71
|
-
}
|
|
79
|
+
await handleWebhookConfiguration(assets, WebhookType.VALIDATE, webhookTimeout, force);
|
|
72
80
|
|
|
73
81
|
Log.info("Applying the Pepr Store CRD if it doesn't exist");
|
|
74
82
|
await K8s(kind.CustomResourceDefinition).Apply(peprStoreCRD, { force });
|
|
75
83
|
|
|
76
|
-
|
|
77
|
-
if (host) {
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
84
|
+
if (assets.host) return; // Skip resource deployment if a host is already specified
|
|
80
85
|
|
|
81
|
-
const code = await fs.readFile(path);
|
|
86
|
+
const code = await fs.readFile(assets.path);
|
|
87
|
+
if (!code.length) throw new Error("No code provided");
|
|
82
88
|
const hash = crypto.createHash("sha256").update(code).digest("hex");
|
|
83
89
|
|
|
84
|
-
|
|
85
|
-
throw new Error("No code provided");
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
await setupRBAC(name, assets.capabilities, force, assets.config);
|
|
90
|
+
await setupRBAC(assets.name, assets.capabilities, force, assets.config);
|
|
89
91
|
await setupController(assets, code, hash, force);
|
|
90
92
|
await setupWatcher(assets, hash, force);
|
|
91
93
|
}
|
package/src/lib/assets/helm.ts
CHANGED
|
@@ -99,8 +99,13 @@ export function watcherDeployTemplate(buildTimestamp: string): string {
|
|
|
99
99
|
- name: watcher
|
|
100
100
|
image: {{ .Values.watcher.image }}
|
|
101
101
|
imagePullPolicy: IfNotPresent
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
{{- if gt (len .Values.imagePullSecrets) 0 }}
|
|
103
|
+
imagePullSecrets:
|
|
104
|
+
{{- range .Values.imagePullSecrets }}
|
|
105
|
+
- name: {{ . }}
|
|
106
|
+
{{- end }}
|
|
107
|
+
{{- end }}
|
|
108
|
+
args:
|
|
104
109
|
- /app/node_modules/pepr/dist/controller.js
|
|
105
110
|
- {{ .Values.hash }}
|
|
106
111
|
readinessProbe:
|
|
@@ -115,6 +120,10 @@ export function watcherDeployTemplate(buildTimestamp: string): string {
|
|
|
115
120
|
{{- toYaml .Values.watcher.env | nindent 12 }}
|
|
116
121
|
- name: PEPR_WATCH_MODE
|
|
117
122
|
value: "true"
|
|
123
|
+
{{- if .Values.additionalIgnoredNamespaces }}
|
|
124
|
+
- name: PEPR_ADDITIONAL_IGNORED_NAMESPACES
|
|
125
|
+
value: "{{ join ", " .Values.additionalIgnoredNamespaces }}"
|
|
126
|
+
{{- end }}
|
|
118
127
|
envFrom:
|
|
119
128
|
{{- toYaml .Values.watcher.envFrom | nindent 12 }}
|
|
120
129
|
securityContext:
|
|
@@ -179,8 +188,13 @@ export function admissionDeployTemplate(buildTimestamp: string): string {
|
|
|
179
188
|
- name: server
|
|
180
189
|
image: {{ .Values.admission.image }}
|
|
181
190
|
imagePullPolicy: IfNotPresent
|
|
182
|
-
|
|
183
|
-
|
|
191
|
+
{{- if gt (len .Values.imagePullSecrets) 0 }}
|
|
192
|
+
imagePullSecrets:
|
|
193
|
+
{{- range .Values.imagePullSecrets }}
|
|
194
|
+
- name: {{ . }}
|
|
195
|
+
{{- end }}
|
|
196
|
+
{{- end }}
|
|
197
|
+
args:
|
|
184
198
|
- /app/node_modules/pepr/dist/controller.js
|
|
185
199
|
- {{ .Values.hash }}
|
|
186
200
|
readinessProbe:
|
|
@@ -195,6 +209,10 @@ export function admissionDeployTemplate(buildTimestamp: string): string {
|
|
|
195
209
|
{{- toYaml .Values.admission.env | nindent 12 }}
|
|
196
210
|
- name: PEPR_WATCH_MODE
|
|
197
211
|
value: "false"
|
|
212
|
+
{{- if .Values.additionalIgnoredNamespaces }}
|
|
213
|
+
- name: PEPR_ADDITIONAL_IGNORED_NAMESPACES
|
|
214
|
+
value: "{{ join ", " .Values.additionalIgnoredNamespaces }}"
|
|
215
|
+
{{- end }}
|
|
198
216
|
envFrom:
|
|
199
217
|
{{- toYaml .Values.admission.envFrom | nindent 12 }}
|
|
200
218
|
securityContext:
|
package/src/lib/assets/index.ts
CHANGED
|
@@ -12,21 +12,45 @@ export function toYaml(obj: any): string {
|
|
|
12
12
|
return dumpYaml(obj, { noRefs: true });
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
// Unit Test Me!!
|
|
15
16
|
export function createWebhookYaml(
|
|
16
17
|
name: string,
|
|
17
18
|
config: ModuleConfig,
|
|
18
19
|
webhookConfiguration: kind.MutatingWebhookConfiguration | kind.ValidatingWebhookConfiguration,
|
|
19
20
|
): string {
|
|
20
21
|
const yaml = toYaml(webhookConfiguration);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
config.onError === "reject" ? "Fail" : "Ignore",
|
|
25
|
-
"{{ .Values.admission.failurePolicy }}",
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
const replacements = [
|
|
23
|
+
{ search: name, replace: "{{ .Values.uuid }}" },
|
|
24
|
+
{
|
|
25
|
+
search: config.onError === "reject" ? "Fail" : "Ignore",
|
|
26
|
+
replace: "{{ .Values.admission.failurePolicy }}",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
search: `${config.webhookTimeout}` || "10",
|
|
30
|
+
replace: "{{ .Values.admission.webhookTimeout }}",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
search: `
|
|
34
|
+
- key: kubernetes.io/metadata.name
|
|
35
|
+
operator: NotIn
|
|
36
|
+
values:
|
|
37
|
+
- kube-system
|
|
38
|
+
- pepr-system
|
|
39
|
+
`,
|
|
40
|
+
replace: `
|
|
41
|
+
- key: kubernetes.io/metadata.name
|
|
42
|
+
operator: NotIn
|
|
43
|
+
values:
|
|
44
|
+
- kube-system
|
|
45
|
+
- pepr-system
|
|
46
|
+
{{- range .Values.additionalIgnoredNamespaces }}
|
|
47
|
+
- {{ . }}
|
|
48
|
+
{{- end }}
|
|
49
|
+
`,
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
return replacements.reduce((updatedYaml, { search, replace }) => replaceString(updatedYaml, search, replace), yaml);
|
|
30
54
|
}
|
|
31
55
|
|
|
32
56
|
export function helmLayout(basePath: string, unique: string): Record<string, Record<string, string>> {
|
package/src/lib/assets/pods.ts
CHANGED
|
@@ -109,7 +109,7 @@ export function getWatcher(
|
|
|
109
109
|
name: "watcher",
|
|
110
110
|
image,
|
|
111
111
|
imagePullPolicy: "IfNotPresent",
|
|
112
|
-
|
|
112
|
+
args: ["/app/node_modules/pepr/dist/controller.js", hash],
|
|
113
113
|
readinessProbe: {
|
|
114
114
|
httpGet: {
|
|
115
115
|
path: "/healthz",
|
|
@@ -248,7 +248,7 @@ export function getDeployment(
|
|
|
248
248
|
name: "server",
|
|
249
249
|
image,
|
|
250
250
|
imagePullPolicy: "IfNotPresent",
|
|
251
|
-
|
|
251
|
+
args: ["/app/node_modules/pepr/dist/controller.js", hash],
|
|
252
252
|
readinessProbe: {
|
|
253
253
|
httpGet: {
|
|
254
254
|
path: "/healthz",
|
|
@@ -10,10 +10,10 @@ import { kind } from "kubernetes-fluent-client";
|
|
|
10
10
|
import { concat, equals, uniqWith } from "ramda";
|
|
11
11
|
|
|
12
12
|
import { Assets } from "./assets";
|
|
13
|
-
import { Event } from "../enums";
|
|
13
|
+
import { Event, WebhookType } from "../enums";
|
|
14
14
|
import { Binding } from "../types";
|
|
15
15
|
|
|
16
|
-
const peprIgnoreNamespaces: string[] = ["kube-system", "pepr-system"];
|
|
16
|
+
export const peprIgnoreNamespaces: string[] = ["kube-system", "pepr-system"];
|
|
17
17
|
|
|
18
18
|
const validateRule = (binding: Binding, isMutateWebhook: boolean): V1RuleWithOperations | undefined => {
|
|
19
19
|
const { event, kind, isMutate, isValidate } = binding;
|
|
@@ -39,6 +39,21 @@ const validateRule = (binding: Binding, isMutateWebhook: boolean): V1RuleWithOpe
|
|
|
39
39
|
return ruleObject;
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
+
export function resolveIgnoreNamespaces(ignoredNSConfig: string[] = []): string[] {
|
|
43
|
+
const ignoredNSEnv = process.env.PEPR_ADDITIONAL_IGNORED_NAMESPACES;
|
|
44
|
+
if (!ignoredNSEnv) {
|
|
45
|
+
return ignoredNSConfig;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const namespaces = ignoredNSEnv.split(",").map(ns => ns.trim());
|
|
49
|
+
|
|
50
|
+
// add alwaysIgnore.namespaces to the list
|
|
51
|
+
if (ignoredNSConfig) {
|
|
52
|
+
namespaces.push(...ignoredNSConfig);
|
|
53
|
+
}
|
|
54
|
+
return namespaces.filter(ns => ns.length > 0);
|
|
55
|
+
}
|
|
56
|
+
|
|
42
57
|
export async function generateWebhookRules(assets: Assets, isMutateWebhook: boolean): Promise<V1RuleWithOperations[]> {
|
|
43
58
|
const { config, capabilities } = assets;
|
|
44
59
|
|
|
@@ -53,15 +68,15 @@ export async function generateWebhookRules(assets: Assets, isMutateWebhook: bool
|
|
|
53
68
|
return uniqWith(equals, rules);
|
|
54
69
|
}
|
|
55
70
|
|
|
56
|
-
export async function
|
|
71
|
+
export async function webhookConfigGenerator(
|
|
57
72
|
assets: Assets,
|
|
58
|
-
mutateOrValidate:
|
|
73
|
+
mutateOrValidate: WebhookType,
|
|
59
74
|
timeoutSeconds = 10,
|
|
60
75
|
): Promise<kind.MutatingWebhookConfiguration | kind.ValidatingWebhookConfiguration | null> {
|
|
61
76
|
const ignore: V1LabelSelectorRequirement[] = [];
|
|
62
77
|
|
|
63
78
|
const { name, tls, config, apiToken, host } = assets;
|
|
64
|
-
const ignoreNS = concat(peprIgnoreNamespaces, config?.alwaysIgnore?.namespaces
|
|
79
|
+
const ignoreNS = concat(peprIgnoreNamespaces, resolveIgnoreNamespaces(config?.alwaysIgnore?.namespaces));
|
|
65
80
|
|
|
66
81
|
// Add any namespaces to ignore
|
|
67
82
|
if (ignoreNS) {
|
|
@@ -91,7 +106,7 @@ export async function webhookConfig(
|
|
|
91
106
|
};
|
|
92
107
|
}
|
|
93
108
|
|
|
94
|
-
const isMutate = mutateOrValidate ===
|
|
109
|
+
const isMutate = mutateOrValidate === WebhookType.MUTATE;
|
|
95
110
|
const rules = await generateWebhookRules(assets, isMutate);
|
|
96
111
|
|
|
97
112
|
// If there are no rules, return null
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
|
+
|
|
4
|
+
import crypto from "crypto";
|
|
5
|
+
import { Assets } from "../assets";
|
|
6
|
+
import { WebhookType } from "../../enums";
|
|
7
|
+
import { apiTokenSecret, service, tlsSecret, watcherService } from "../networking";
|
|
8
|
+
import { clusterRole, clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "../rbac";
|
|
9
|
+
import { dumpYaml, V1Deployment } from "@kubernetes/client-node";
|
|
10
|
+
import { getModuleSecret, getNamespace } from "../pods";
|
|
11
|
+
import { promises as fs } from "fs";
|
|
12
|
+
import { webhookConfigGenerator } from "../webhooks";
|
|
13
|
+
|
|
14
|
+
type deployments = { default: V1Deployment; watch: V1Deployment | null };
|
|
15
|
+
|
|
16
|
+
export async function generateAllYaml(assets: Assets, deployments: deployments): Promise<string> {
|
|
17
|
+
const { name, tls, apiToken, path, config } = assets;
|
|
18
|
+
const code = await fs.readFile(path);
|
|
19
|
+
const hash = crypto.createHash("sha256").update(code).digest("hex");
|
|
20
|
+
|
|
21
|
+
const resources = [
|
|
22
|
+
getNamespace(assets.config.customLabels?.namespace),
|
|
23
|
+
clusterRole(name, assets.capabilities, config.rbacMode, config.rbac),
|
|
24
|
+
clusterRoleBinding(name),
|
|
25
|
+
serviceAccount(name),
|
|
26
|
+
apiTokenSecret(name, apiToken),
|
|
27
|
+
tlsSecret(name, tls),
|
|
28
|
+
deployments.default,
|
|
29
|
+
service(name),
|
|
30
|
+
watcherService(name),
|
|
31
|
+
getModuleSecret(name, code, hash),
|
|
32
|
+
storeRole(name),
|
|
33
|
+
storeRoleBinding(name),
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const webhooks = {
|
|
37
|
+
mutate: await webhookConfigGenerator(assets, WebhookType.MUTATE, assets.config.webhookTimeout),
|
|
38
|
+
validate: await webhookConfigGenerator(assets, WebhookType.VALIDATE, assets.config.webhookTimeout),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Add webhooks and watch deployment if they exist
|
|
42
|
+
const additionalResources = [webhooks.mutate, webhooks.validate, deployments.watch].filter(
|
|
43
|
+
resource => resource !== null && resource !== undefined,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
resources.push(...additionalResources);
|
|
47
|
+
|
|
48
|
+
// Convert the resources to a single YAML string
|
|
49
|
+
return resources.map(resource => dumpYaml(resource, { noRefs: true })).join("---\n");
|
|
50
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { dumpYaml } from "@kubernetes/client-node";
|
|
2
|
+
import { Assets } from "../assets";
|
|
3
|
+
|
|
4
|
+
type ConfigType = "manifests" | "charts";
|
|
5
|
+
|
|
6
|
+
export function generateZarfYamlGeneric(assets: Assets, path: string, type: ConfigType): string {
|
|
7
|
+
const manifestSettings = {
|
|
8
|
+
name: "module",
|
|
9
|
+
namespace: "pepr-system",
|
|
10
|
+
files: [path],
|
|
11
|
+
};
|
|
12
|
+
const chartSettings = {
|
|
13
|
+
name: "module",
|
|
14
|
+
namespace: "pepr-system",
|
|
15
|
+
version: `${assets.config.appVersion || "0.0.1"}`,
|
|
16
|
+
localPath: path,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const component = {
|
|
20
|
+
name: "module",
|
|
21
|
+
required: true,
|
|
22
|
+
images: [assets.image],
|
|
23
|
+
[type]: [type === "manifests" ? manifestSettings : chartSettings],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const zarfCfg = {
|
|
27
|
+
kind: "ZarfPackageConfig",
|
|
28
|
+
metadata: {
|
|
29
|
+
name: assets.name,
|
|
30
|
+
description: `Pepr Module: ${assets.config.description}`,
|
|
31
|
+
url: "https://github.com/defenseunicorns/pepr",
|
|
32
|
+
version: `${assets.config.appVersion || "0.0.1"}`,
|
|
33
|
+
},
|
|
34
|
+
components: [component],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return dumpYaml(zarfCfg, { noRefs: true });
|
|
38
|
+
}
|