pepr 0.40.1 → 0.42.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/README.md +11 -5
- package/dist/cli/build.d.ts +1 -0
- package/dist/cli/build.d.ts.map +1 -1
- package/dist/cli/build.helpers.d.ts +66 -0
- package/dist/cli/build.helpers.d.ts.map +1 -1
- package/dist/cli/deploy.d.ts.map +1 -1
- package/dist/cli/init/templates.d.ts +2 -2
- package/dist/cli/monitor.d.ts +23 -0
- package/dist/cli/monitor.d.ts.map +1 -1
- package/dist/cli.js +536 -429
- package/dist/controller.js +52 -27
- package/dist/lib/assets/destroy.d.ts.map +1 -1
- package/dist/lib/assets/helm.d.ts +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/pods.d.ts +5 -19
- package/dist/lib/assets/pods.d.ts.map +1 -1
- package/dist/lib/assets/webhooks.d.ts.map +1 -1
- package/dist/lib/assets/yaml.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 +0 -1
- package/dist/lib/controller/store.d.ts.map +1 -1
- package/dist/lib/controller/storeCache.d.ts +1 -0
- package/dist/lib/controller/storeCache.d.ts.map +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/mutate-request.d.ts +2 -2
- package/dist/lib/mutate-request.d.ts.map +1 -1
- package/dist/lib/queue.d.ts.map +1 -1
- package/dist/lib/schedule.d.ts.map +1 -1
- package/dist/lib/storage.d.ts +5 -5
- 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/utils.d.ts.map +1 -1
- package/dist/lib/validate-processor.d.ts +4 -1
- package/dist/lib/validate-processor.d.ts.map +1 -1
- package/dist/lib/watch-processor.d.ts.map +1 -1
- package/dist/lib.d.ts +1 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +283 -231
- 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.helpers.ts +180 -0
- package/src/cli/build.ts +85 -132
- package/src/cli/deploy.ts +2 -1
- package/src/cli/init/templates.ts +1 -1
- package/src/cli/monitor.ts +108 -65
- package/src/lib/assets/deploy.ts +7 -7
- package/src/lib/assets/destroy.ts +2 -2
- package/src/lib/assets/helm.ts +6 -6
- package/src/lib/assets/index.ts +110 -89
- package/src/lib/assets/pods.ts +10 -5
- package/src/lib/assets/webhooks.ts +3 -3
- package/src/lib/assets/yaml.ts +12 -9
- package/src/lib/capability.ts +29 -19
- package/src/lib/controller/index.ts +41 -69
- package/src/lib/controller/index.util.ts +47 -0
- package/src/lib/controller/store.ts +24 -11
- package/src/lib/controller/storeCache.ts +11 -2
- 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/mutate-request.ts +11 -11
- package/src/lib/queue.ts +13 -5
- package/src/lib/schedule.ts +8 -8
- package/src/lib/storage.ts +48 -39
- 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 +6 -6
- package/src/lib/validate-processor.ts +48 -40
- package/src/lib/watch-processor.ts +19 -15
- package/src/lib.ts +1 -1
- package/src/runtime/controller.ts +1 -1
- package/src/sdk/cosign.ts +4 -4
- 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/src/cli/monitor.ts
CHANGED
|
@@ -1,92 +1,51 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
3
|
|
|
4
|
-
import { Log as K8sLog, KubeConfig } from "@kubernetes/client-node";
|
|
4
|
+
import { Log as K8sLog, KubeConfig, KubernetesListObject } from "@kubernetes/client-node";
|
|
5
5
|
import { K8s, kind } from "kubernetes-fluent-client";
|
|
6
6
|
import stream from "stream";
|
|
7
7
|
import { ResponseItem } from "../lib/types";
|
|
8
8
|
import { RootCmd } from "./root";
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
interface LogPayload {
|
|
11
|
+
namespace: string;
|
|
12
|
+
name: string;
|
|
13
|
+
res: {
|
|
14
|
+
uid: string;
|
|
15
|
+
allowed?: boolean;
|
|
16
|
+
patch?: string;
|
|
17
|
+
patchType?: string;
|
|
18
|
+
warnings?: string;
|
|
19
|
+
status?: {
|
|
20
|
+
message: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default function (program: RootCmd): void {
|
|
11
26
|
program
|
|
12
27
|
.command("monitor [module-uuid]")
|
|
13
28
|
.description("Monitor a Pepr Module")
|
|
14
29
|
.action(async uuid => {
|
|
15
|
-
|
|
16
|
-
let errorMessage: string;
|
|
17
|
-
|
|
18
|
-
if (!uuid) {
|
|
19
|
-
labels = ["pepr.dev/controller", "admission"];
|
|
20
|
-
errorMessage = `No pods found with admission labels`;
|
|
21
|
-
} else {
|
|
22
|
-
labels = ["app", `pepr-${uuid}`];
|
|
23
|
-
errorMessage = `No pods found for module ${uuid}`;
|
|
24
|
-
}
|
|
30
|
+
const { labels, errorMessage } = getLabelsAndErrorMessage(uuid);
|
|
25
31
|
|
|
26
32
|
// Get the logs for the `app=pepr-${module}` or `pepr.dev/controller=admission` pod selector
|
|
27
|
-
const pods = await K8s(kind.Pod)
|
|
33
|
+
const pods: KubernetesListObject<kind.Pod> = await K8s(kind.Pod)
|
|
28
34
|
.InNamespace("pepr-system")
|
|
29
35
|
.WithLabel(labels[0], labels[1])
|
|
30
36
|
.Get();
|
|
31
37
|
|
|
32
|
-
|
|
38
|
+
// Pods will ways have a metadata and name fields
|
|
39
|
+
const podNames: string[] = pods.items.flatMap(pod => pod.metadata!.name || "");
|
|
33
40
|
|
|
34
41
|
if (podNames.length < 1) {
|
|
35
42
|
console.error(errorMessage);
|
|
36
43
|
process.exit(1);
|
|
37
44
|
}
|
|
38
45
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const log = new K8sLog(kc);
|
|
43
|
-
|
|
44
|
-
const logStream = new stream.PassThrough();
|
|
45
|
-
logStream.on("data", async chunk => {
|
|
46
|
-
const respMsg = `"msg":"Check response"`;
|
|
47
|
-
// Split the chunk into lines
|
|
48
|
-
const lines = await chunk.toString().split("\n");
|
|
49
|
-
|
|
50
|
-
for (const line of lines) {
|
|
51
|
-
// Check for `"msg":"Hello Pepr"`
|
|
52
|
-
if (!line.includes(respMsg)) continue;
|
|
53
|
-
try {
|
|
54
|
-
const payload = JSON.parse(line.trim());
|
|
55
|
-
const isMutate = payload.res.patchType || payload.res.warnings;
|
|
56
|
-
|
|
57
|
-
const name = `${payload.namespace}${payload.name}`;
|
|
58
|
-
const uid = payload.res.uid;
|
|
59
|
-
|
|
60
|
-
if (isMutate) {
|
|
61
|
-
const plainPatch =
|
|
62
|
-
payload.res?.patch !== undefined && payload.res?.patch !== null
|
|
63
|
-
? atob(payload.res.patch)
|
|
64
|
-
: "";
|
|
65
|
-
|
|
66
|
-
const patch = plainPatch !== "" && JSON.stringify(JSON.parse(plainPatch), null, 2);
|
|
67
|
-
const patchType = payload.res.patchType || payload.res.warnings || "";
|
|
68
|
-
const allowOrDeny = payload.res.allowed ? "🔀" : "🚫";
|
|
69
|
-
console.log(`\n${allowOrDeny} MUTATE ${name} (${uid})`);
|
|
70
|
-
patchType.length > 0 && console.log(`\n\u001b[1;34m${patch}\u001b[0m`);
|
|
71
|
-
} else {
|
|
72
|
-
const failures = Array.isArray(payload.res) ? payload.res : [payload.res];
|
|
73
|
-
|
|
74
|
-
const filteredFailures = failures
|
|
75
|
-
.filter((r: ResponseItem) => !r.allowed)
|
|
76
|
-
.map((r: ResponseItem) => r.status.message);
|
|
77
|
-
|
|
78
|
-
console.log(
|
|
79
|
-
`\n${filteredFailures.length > 0 ? "❌" : "✅"} VALIDATE ${name} (${uid})`,
|
|
80
|
-
);
|
|
81
|
-
console.log(
|
|
82
|
-
filteredFailures.length > 0 ? `\u001b[1;31m${filteredFailures}\u001b[0m` : "",
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
} catch {
|
|
86
|
-
// Do nothing
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
});
|
|
46
|
+
const log = getK8sLogFromKubeConfig();
|
|
47
|
+
|
|
48
|
+
const logStream = createLogStream();
|
|
90
49
|
|
|
91
50
|
for (const podName of podNames) {
|
|
92
51
|
await log.log("pepr-system", podName, "server", logStream, {
|
|
@@ -97,3 +56,87 @@ export default function (program: RootCmd) {
|
|
|
97
56
|
}
|
|
98
57
|
});
|
|
99
58
|
}
|
|
59
|
+
|
|
60
|
+
export function getLabelsAndErrorMessage(uuid?: string): {
|
|
61
|
+
labels: string[];
|
|
62
|
+
errorMessage: string;
|
|
63
|
+
} {
|
|
64
|
+
let labels: string[];
|
|
65
|
+
let errorMessage: string;
|
|
66
|
+
|
|
67
|
+
if (!uuid) {
|
|
68
|
+
labels = ["pepr.dev/controller", "admission"];
|
|
69
|
+
errorMessage = `No pods found with admission labels`;
|
|
70
|
+
} else {
|
|
71
|
+
labels = ["app", `pepr-${uuid}`];
|
|
72
|
+
errorMessage = `No pods found for module ${uuid}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { labels, errorMessage };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function getK8sLogFromKubeConfig(): K8sLog {
|
|
79
|
+
const kc = new KubeConfig();
|
|
80
|
+
kc.loadFromDefault();
|
|
81
|
+
return new K8sLog(kc);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function createLogStream(): stream.PassThrough {
|
|
85
|
+
const logStream = new stream.PassThrough();
|
|
86
|
+
|
|
87
|
+
logStream.on("data", async chunk => {
|
|
88
|
+
const lines = chunk.toString().split("\n");
|
|
89
|
+
const respMsg = `"msg":"Check response"`;
|
|
90
|
+
|
|
91
|
+
for (const line of lines) {
|
|
92
|
+
if (!line.includes(respMsg)) continue;
|
|
93
|
+
processLogLine(line);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return logStream;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function processLogLine(line: string): void {
|
|
101
|
+
try {
|
|
102
|
+
const payload: LogPayload = JSON.parse(line.trim());
|
|
103
|
+
const isMutate = payload.res.patchType || payload.res.warnings;
|
|
104
|
+
const name = `${payload.namespace}${payload.name}`;
|
|
105
|
+
const uid = payload.res.uid;
|
|
106
|
+
|
|
107
|
+
if (isMutate) {
|
|
108
|
+
processMutateLog(payload, name, uid);
|
|
109
|
+
} else {
|
|
110
|
+
processValidateLog(payload, name, uid);
|
|
111
|
+
}
|
|
112
|
+
} catch {
|
|
113
|
+
// Do nothing
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function processMutateLog(payload: LogPayload, name: string, uid: string): void {
|
|
118
|
+
const plainPatch =
|
|
119
|
+
payload.res.patch !== undefined && payload.res.patch !== null ? atob(payload.res.patch) : "";
|
|
120
|
+
|
|
121
|
+
const patch = plainPatch !== "" && JSON.stringify(JSON.parse(plainPatch), null, 2);
|
|
122
|
+
const patchType = payload.res.patchType || payload.res.warnings || "";
|
|
123
|
+
const allowOrDeny = payload.res.allowed ? "🔀" : "🚫";
|
|
124
|
+
|
|
125
|
+
console.log(`\n${allowOrDeny} MUTATE ${name} (${uid})`);
|
|
126
|
+
if (patchType.length > 0) {
|
|
127
|
+
console.log(`\n\u001b[1;34m${patch}\u001b[0m`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function processValidateLog(payload: LogPayload, name: string, uid: string): void {
|
|
132
|
+
const failures = Array.isArray(payload.res) ? payload.res : [payload.res];
|
|
133
|
+
|
|
134
|
+
const filteredFailures = failures
|
|
135
|
+
.filter((r: ResponseItem) => !r.allowed)
|
|
136
|
+
.map((r: ResponseItem) => r.status?.message || "");
|
|
137
|
+
|
|
138
|
+
console.log(`\n${filteredFailures.length > 0 ? "❌" : "✅"} VALIDATE ${name} (${uid})`);
|
|
139
|
+
if (filteredFailures.length > 0) {
|
|
140
|
+
console.log(`\u001b[1;31m${filteredFailures}\u001b[0m`);
|
|
141
|
+
}
|
|
142
|
+
}
|
package/src/lib/assets/deploy.ts
CHANGED
|
@@ -7,9 +7,9 @@ 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
|
-
import {
|
|
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
15
|
import { webhookConfig } from "./webhooks";
|
|
@@ -19,7 +19,7 @@ export async function deployImagePullSecret(imagePullSecret: ImagePullSecret, na
|
|
|
19
19
|
try {
|
|
20
20
|
await K8s(kind.Namespace).Get("pepr-system");
|
|
21
21
|
} catch {
|
|
22
|
-
await K8s(kind.Namespace).Apply(
|
|
22
|
+
await K8s(kind.Namespace).Apply(getNamespace());
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
try {
|
|
@@ -48,7 +48,7 @@ export async function deploy(assets: Assets, force: boolean, webhookTimeout?: nu
|
|
|
48
48
|
const { name, host, path } = assets;
|
|
49
49
|
|
|
50
50
|
Log.info("Applying pepr-system namespace");
|
|
51
|
-
await K8s(kind.Namespace).Apply(
|
|
51
|
+
await K8s(kind.Namespace).Apply(getNamespace(assets.config.customLabels?.namespace));
|
|
52
52
|
|
|
53
53
|
// Create the mutating webhook configuration if it is needed
|
|
54
54
|
const mutateWebhook = await webhookConfig(assets, "mutate", webhookTimeout);
|
|
@@ -123,7 +123,7 @@ async function setupController(assets: Assets, code: Buffer, hash: string, force
|
|
|
123
123
|
const { name } = assets;
|
|
124
124
|
|
|
125
125
|
Log.info("Applying module secret");
|
|
126
|
-
const mod =
|
|
126
|
+
const mod = getModuleSecret(name, code, hash);
|
|
127
127
|
await K8s(kind.Secret).Apply(mod, { force });
|
|
128
128
|
|
|
129
129
|
Log.info("Applying controller service");
|
|
@@ -139,14 +139,14 @@ async function setupController(assets: Assets, code: Buffer, hash: string, force
|
|
|
139
139
|
await K8s(kind.Secret).Apply(apiToken, { force });
|
|
140
140
|
|
|
141
141
|
Log.info("Applying deployment");
|
|
142
|
-
const dep =
|
|
142
|
+
const dep = getDeployment(assets, hash, assets.buildTimestamp);
|
|
143
143
|
await K8s(kind.Deployment).Apply(dep, { force });
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
// Setup the watcher deployment and service
|
|
147
147
|
async function setupWatcher(assets: Assets, hash: string, force: boolean) {
|
|
148
148
|
// If the module has a watcher, deploy it
|
|
149
|
-
const watchDeployment =
|
|
149
|
+
const watchDeployment = getWatcher(assets, hash, assets.buildTimestamp);
|
|
150
150
|
if (watchDeployment) {
|
|
151
151
|
Log.info("Applying watcher deployment");
|
|
152
152
|
await K8s(kind.Deployment).Apply(watchDeployment, { force });
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
|
|
4
4
|
import { K8s, kind } from "kubernetes-fluent-client";
|
|
5
5
|
|
|
6
|
-
import Log from "../logger";
|
|
6
|
+
import Log from "../telemetry/logger";
|
|
7
7
|
import { peprStoreCRD } from "./store";
|
|
8
8
|
|
|
9
|
-
export async function destroyModule(name: string) {
|
|
9
|
+
export async function destroyModule(name: string): Promise<void> {
|
|
10
10
|
const namespace = "pepr-system";
|
|
11
11
|
|
|
12
12
|
Log.info("Destroying Pepr module");
|
package/src/lib/assets/helm.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
3
|
|
|
4
|
-
export function clusterRoleTemplate() {
|
|
4
|
+
export function clusterRoleTemplate(): string {
|
|
5
5
|
return `
|
|
6
6
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
7
7
|
kind: ClusterRole
|
|
@@ -15,7 +15,7 @@ export function clusterRoleTemplate() {
|
|
|
15
15
|
`;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export function
|
|
18
|
+
export function namespaceTemplate(): string {
|
|
19
19
|
return `
|
|
20
20
|
apiVersion: v1
|
|
21
21
|
kind: Namespace
|
|
@@ -32,7 +32,7 @@ export function nsTemplate() {
|
|
|
32
32
|
`;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export function chartYaml(name: string, description?: string) {
|
|
35
|
+
export function chartYaml(name: string, description?: string): string {
|
|
36
36
|
return `
|
|
37
37
|
apiVersion: v2
|
|
38
38
|
name: ${name}
|
|
@@ -61,7 +61,7 @@ export function chartYaml(name: string, description?: string) {
|
|
|
61
61
|
`;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
export function watcherDeployTemplate(buildTimestamp: string) {
|
|
64
|
+
export function watcherDeployTemplate(buildTimestamp: string): string {
|
|
65
65
|
return `
|
|
66
66
|
apiVersion: apps/v1
|
|
67
67
|
kind: Deployment
|
|
@@ -142,7 +142,7 @@ export function watcherDeployTemplate(buildTimestamp: string) {
|
|
|
142
142
|
`;
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
export function admissionDeployTemplate(buildTimestamp: string) {
|
|
145
|
+
export function admissionDeployTemplate(buildTimestamp: string): string {
|
|
146
146
|
return `
|
|
147
147
|
apiVersion: apps/v1
|
|
148
148
|
kind: Deployment
|
|
@@ -228,7 +228,7 @@ export function admissionDeployTemplate(buildTimestamp: string) {
|
|
|
228
228
|
`;
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
-
export function serviceMonitorTemplate(name: string) {
|
|
231
|
+
export function serviceMonitorTemplate(name: string): string {
|
|
232
232
|
return `
|
|
233
233
|
{{- if .Values.${name}.serviceMonitor.enabled }}
|
|
234
234
|
apiVersion: monitoring.coreos.com/v1
|
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,11 +12,11 @@ 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,
|
|
18
|
-
|
|
19
|
+
namespaceTemplate,
|
|
19
20
|
admissionDeployTemplate,
|
|
20
21
|
watcherDeployTemplate,
|
|
21
22
|
clusterRoleTemplate,
|
|
@@ -24,9 +25,72 @@ import {
|
|
|
24
25
|
import { promises as fs } from "fs";
|
|
25
26
|
import { webhookConfig } from "./webhooks";
|
|
26
27
|
import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking";
|
|
27
|
-
import {
|
|
28
|
+
import { getWatcher, getModuleSecret } 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): Record<string, Record<string, 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;
|
|
@@ -55,20 +119,20 @@ export class Assets {
|
|
|
55
119
|
this.apiToken = crypto.randomBytes(32).toString("hex");
|
|
56
120
|
}
|
|
57
121
|
|
|
58
|
-
setHash = (hash: string) => {
|
|
122
|
+
setHash = (hash: string): void => {
|
|
59
123
|
this.hash = hash;
|
|
60
124
|
};
|
|
61
125
|
|
|
62
|
-
deploy = async (force: boolean, webhookTimeout?: number) => {
|
|
126
|
+
deploy = async (force: boolean, webhookTimeout?: number): Promise<void> => {
|
|
63
127
|
this.capabilities = await loadCapabilities(this.path);
|
|
64
128
|
await deploy(this, force, webhookTimeout);
|
|
65
129
|
};
|
|
66
130
|
|
|
67
|
-
zarfYaml = (path: string) => zarfYaml(this, path);
|
|
131
|
+
zarfYaml = (path: string): string => zarfYaml(this, path);
|
|
68
132
|
|
|
69
|
-
zarfYamlChart = (path: string) => zarfYamlChart(this, path);
|
|
133
|
+
zarfYamlChart = (path: string): string => zarfYamlChart(this, path);
|
|
70
134
|
|
|
71
|
-
allYaml = async (imagePullSecret?: string) => {
|
|
135
|
+
allYaml = async (imagePullSecret?: string): Promise<string> => {
|
|
72
136
|
this.capabilities = await loadCapabilities(this.path);
|
|
73
137
|
// give error if namespaces are not respected
|
|
74
138
|
for (const capability of this.capabilities) {
|
|
@@ -78,102 +142,59 @@ export class Assets {
|
|
|
78
142
|
return allYaml(this, imagePullSecret);
|
|
79
143
|
};
|
|
80
144
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
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`);
|
|
145
|
+
/* eslint max-statements: ["warn", 21] */
|
|
146
|
+
generateHelmChart = async (basePath: string): Promise<void> => {
|
|
147
|
+
const helm = helmLayout(basePath, this.config.uuid);
|
|
111
148
|
|
|
112
|
-
|
|
113
|
-
await
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
// create the chart.yaml
|
|
119
|
-
await fs.writeFile(chartPath, dedent(chartYaml(this.config.uuid, this.config.description || "")));
|
|
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, (): string => dedent(chartYaml(this.config.uuid, this.config.description || ""))],
|
|
160
|
+
[helm.files.namespaceYaml, (): string => dedent(namespaceTemplate())],
|
|
161
|
+
[helm.files.watcherServiceYaml, (): string => toYaml(watcherService(this.name))],
|
|
162
|
+
[helm.files.admissionServiceYaml, (): string => toYaml(service(this.name))],
|
|
163
|
+
[helm.files.tlsSecretYaml, (): string => toYaml(tlsSecret(this.name, this.tls))],
|
|
164
|
+
[helm.files.apiTokenSecretYaml, (): string => toYaml(apiTokenSecret(this.name, this.apiToken))],
|
|
165
|
+
[helm.files.storeRoleYaml, (): string => toYaml(storeRole(this.name))],
|
|
166
|
+
[helm.files.storeRoleBindingYaml, (): string => toYaml(storeRoleBinding(this.name))],
|
|
167
|
+
[helm.files.clusterRoleYaml, (): string => dedent(clusterRoleTemplate())],
|
|
168
|
+
[helm.files.clusterRoleBindingYaml, (): string => toYaml(clusterRoleBinding(this.name))],
|
|
169
|
+
[helm.files.serviceAccountYaml, (): string => toYaml(serviceAccount(this.name))],
|
|
170
|
+
[helm.files.moduleSecretYaml, (): string => toYaml(getModuleSecret(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 = getWatcher(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}`);
|
package/src/lib/assets/pods.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
3
|
|
|
4
|
-
import { V1EnvVar } from "@kubernetes/client-node";
|
|
4
|
+
import { KubernetesObject, V1EnvVar } from "@kubernetes/client-node";
|
|
5
5
|
import { kind } from "kubernetes-fluent-client";
|
|
6
6
|
import { gzipSync } from "zlib";
|
|
7
7
|
import { secretOverLimit } from "../helpers";
|
|
@@ -10,7 +10,7 @@ import { ModuleConfig } from "../module";
|
|
|
10
10
|
import { Binding } from "../types";
|
|
11
11
|
|
|
12
12
|
/** Generate the pepr-system namespace */
|
|
13
|
-
export function
|
|
13
|
+
export function getNamespace(namespaceLabels?: Record<string, string>): KubernetesObject {
|
|
14
14
|
if (namespaceLabels) {
|
|
15
15
|
return {
|
|
16
16
|
apiVersion: "v1",
|
|
@@ -31,7 +31,12 @@ export function namespace(namespaceLabels?: Record<string, string>) {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
export function
|
|
34
|
+
export function getWatcher(
|
|
35
|
+
assets: Assets,
|
|
36
|
+
hash: string,
|
|
37
|
+
buildTimestamp: string,
|
|
38
|
+
imagePullSecret?: string,
|
|
39
|
+
): kind.Deployment | null {
|
|
35
40
|
const { name, image, capabilities, config } = assets;
|
|
36
41
|
|
|
37
42
|
let hasSchedule = false;
|
|
@@ -186,7 +191,7 @@ export function watcher(assets: Assets, hash: string, buildTimestamp: string, im
|
|
|
186
191
|
return deploy;
|
|
187
192
|
}
|
|
188
193
|
|
|
189
|
-
export function
|
|
194
|
+
export function getDeployment(
|
|
190
195
|
assets: Assets,
|
|
191
196
|
hash: string,
|
|
192
197
|
buildTimestamp: string,
|
|
@@ -336,7 +341,7 @@ export function deployment(
|
|
|
336
341
|
return deploy;
|
|
337
342
|
}
|
|
338
343
|
|
|
339
|
-
export function
|
|
344
|
+
export function getModuleSecret(name: string, data: Buffer, hash: string): kind.Secret {
|
|
340
345
|
// Compress the data
|
|
341
346
|
const compressed = gzipSync(data);
|
|
342
347
|
const path = `module-${hash}.js.gz`;
|
|
@@ -20,7 +20,7 @@ const peprIgnoreLabel: V1LabelSelectorRequirement = {
|
|
|
20
20
|
|
|
21
21
|
const peprIgnoreNamespaces: string[] = ["kube-system", "pepr-system"];
|
|
22
22
|
|
|
23
|
-
export async function generateWebhookRules(assets: Assets, isMutateWebhook: boolean) {
|
|
23
|
+
export async function generateWebhookRules(assets: Assets, isMutateWebhook: boolean): Promise<V1RuleWithOperations[]> {
|
|
24
24
|
const { config, capabilities } = assets;
|
|
25
25
|
const rules: V1RuleWithOperations[] = [];
|
|
26
26
|
|
|
@@ -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
|
}
|