pepr 0.52.3 → 0.53.0-nightly.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/banner.d.ts +1 -1
- package/dist/cli/banner.d.ts.map +1 -1
- package/dist/cli/build/build.helpers.d.ts +1 -1
- package/dist/cli/build/build.helpers.d.ts.map +1 -1
- package/dist/cli/build/buildModule.d.ts +1 -2
- package/dist/cli/build/buildModule.d.ts.map +1 -1
- package/dist/cli/build/index.d.ts.map +1 -1
- package/dist/cli/deploy/buildAndDeploy.d.ts +5 -0
- package/dist/cli/deploy/buildAndDeploy.d.ts.map +1 -0
- package/dist/cli/deploy/imagePullSecret.d.ts +16 -0
- package/dist/cli/deploy/imagePullSecret.d.ts.map +1 -0
- package/dist/cli/deploy/index.d.ts +3 -0
- package/dist/cli/deploy/index.d.ts.map +1 -0
- package/dist/cli/deploy/userConfirmation.d.ts +4 -0
- package/dist/cli/deploy/userConfirmation.d.ts.map +1 -0
- package/dist/cli/dev.d.ts.map +1 -1
- package/dist/cli/init/createProjectFiles.d.ts +3 -0
- package/dist/cli/init/createProjectFiles.d.ts.map +1 -0
- package/dist/cli/init/doPostInitActions.d.ts +2 -0
- package/dist/cli/init/doPostInitActions.d.ts.map +1 -0
- package/dist/cli/init/index.d.ts +1 -1
- package/dist/cli/init/index.d.ts.map +1 -1
- package/dist/cli/init/setupProjectStructure.d.ts +2 -0
- package/dist/cli/init/setupProjectStructure.d.ts.map +1 -0
- package/dist/cli/init/templates.d.ts +9 -9
- package/dist/cli/init/templates.d.ts.map +1 -1
- package/dist/cli/init/walkthrough.d.ts.map +1 -1
- package/dist/cli.js +260 -229
- package/dist/controller.js +1 -1
- package/dist/lib/assets/assets.d.ts.map +1 -1
- package/dist/lib/assets/loader.d.ts.map +1 -1
- package/dist/lib/assets/webhooks.d.ts +5 -1
- package/dist/lib/assets/webhooks.d.ts.map +1 -1
- package/dist/lib/assets/yaml/overridesFile.d.ts +1 -0
- package/dist/lib/assets/yaml/overridesFile.d.ts.map +1 -1
- package/dist/lib/controller/index.d.ts.map +1 -1
- package/dist/lib/types.d.ts +7 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib.js +1 -0
- package/dist/lib.js.map +2 -2
- package/package.json +16 -16
- package/src/cli/banner.ts +25 -59
- package/src/cli/build/build.helpers.ts +3 -9
- package/src/cli/build/buildModule.ts +1 -1
- package/src/cli/build/index.ts +9 -5
- package/src/cli/deploy/buildAndDeploy.ts +48 -0
- package/src/cli/deploy/imagePullSecret.ts +68 -0
- package/src/cli/deploy/index.ts +40 -0
- package/src/cli/deploy/userConfirmation.ts +16 -0
- package/src/cli/dev.ts +6 -5
- package/src/cli/init/createProjectFiles.ts +48 -0
- package/src/cli/init/doPostInitActions.ts +26 -0
- package/src/cli/init/index.ts +18 -84
- package/src/cli/init/setupProjectStructure.ts +8 -0
- package/src/cli/init/templates.ts +2 -4
- package/src/cli/init/walkthrough.ts +3 -0
- package/src/cli/kfc.ts +2 -2
- package/src/cli/monitor.ts +6 -6
- package/src/cli.ts +5 -5
- package/src/lib/assets/assets.ts +5 -1
- package/src/lib/assets/k8sObjects.ts +6 -6
- package/src/lib/assets/loader.ts +0 -6
- package/src/lib/assets/webhooks.ts +58 -9
- package/src/lib/assets/yaml/overridesFile.ts +29 -7
- package/src/lib/controller/index.ts +1 -0
- package/src/lib/types.ts +7 -0
- package/dist/cli/deploy.d.ts +0 -21
- package/dist/cli/deploy.d.ts.map +0 -1
- package/src/cli/deploy.ts +0 -170
package/src/lib/assets/assets.ts
CHANGED
|
@@ -27,7 +27,7 @@ import { storeRole, storeRoleBinding, clusterRoleBinding, serviceAccount } from
|
|
|
27
27
|
import { tlsSecret, apiPathSecret } from "./networking";
|
|
28
28
|
import { WebhookType } from "../enums";
|
|
29
29
|
import { kind } from "kubernetes-fluent-client";
|
|
30
|
-
|
|
30
|
+
import Log from "../telemetry/logger";
|
|
31
31
|
export function norWatchOrAdmission(capabilities: CapabilityExport[]): boolean {
|
|
32
32
|
return !isAdmission(capabilities) && !isWatcher(capabilities);
|
|
33
33
|
}
|
|
@@ -134,6 +134,10 @@ export class Assets {
|
|
|
134
134
|
imagePullSecret?: string,
|
|
135
135
|
): Promise<string> => {
|
|
136
136
|
this.capabilities = await loadCapabilities(this.path);
|
|
137
|
+
this.capabilities.flatMap(capability => {
|
|
138
|
+
Log.info(`Module ${this.config.uuid} has capability: ${capability.name}`);
|
|
139
|
+
Log.info(`Registered Pepr Capability "${capability.name}"`);
|
|
140
|
+
});
|
|
137
141
|
// give error if namespaces are not respected
|
|
138
142
|
for (const capability of this.capabilities) {
|
|
139
143
|
// until deployment, Pepr does not distinguish between watch and admission
|
|
@@ -85,9 +85,9 @@ export function getWatcher(
|
|
|
85
85
|
serviceAccountName: name,
|
|
86
86
|
securityContext: {
|
|
87
87
|
runAsUser: image.includes("private") ? 1000 : 65532,
|
|
88
|
-
runAsGroup: 65532,
|
|
88
|
+
runAsGroup: image.includes("private") ? 1000 : 65532,
|
|
89
89
|
runAsNonRoot: true,
|
|
90
|
-
fsGroup: 65532,
|
|
90
|
+
fsGroup: image.includes("private") ? 1000 : 65532,
|
|
91
91
|
},
|
|
92
92
|
containers: [
|
|
93
93
|
{
|
|
@@ -128,7 +128,7 @@ export function getWatcher(
|
|
|
128
128
|
},
|
|
129
129
|
securityContext: {
|
|
130
130
|
runAsUser: image.includes("private") ? 1000 : 65532,
|
|
131
|
-
runAsGroup: 65532,
|
|
131
|
+
runAsGroup: image.includes("private") ? 1000 : 65532,
|
|
132
132
|
runAsNonRoot: true,
|
|
133
133
|
allowPrivilegeEscalation: false,
|
|
134
134
|
capabilities: {
|
|
@@ -228,9 +228,9 @@ export function getDeployment(
|
|
|
228
228
|
serviceAccountName: name,
|
|
229
229
|
securityContext: {
|
|
230
230
|
runAsUser: image.includes("private") ? 1000 : 65532,
|
|
231
|
-
runAsGroup: 65532,
|
|
231
|
+
runAsGroup: image.includes("private") ? 1000 : 65532,
|
|
232
232
|
runAsNonRoot: true,
|
|
233
|
-
fsGroup: 65532,
|
|
233
|
+
fsGroup: image.includes("private") ? 1000 : 65532,
|
|
234
234
|
},
|
|
235
235
|
containers: [
|
|
236
236
|
{
|
|
@@ -272,7 +272,7 @@ export function getDeployment(
|
|
|
272
272
|
env: genEnv(config),
|
|
273
273
|
securityContext: {
|
|
274
274
|
runAsUser: image.includes("private") ? 1000 : 65532,
|
|
275
|
-
runAsGroup: 65532,
|
|
275
|
+
runAsGroup: image.includes("private") ? 1000 : 65532,
|
|
276
276
|
runAsNonRoot: true,
|
|
277
277
|
allowPrivilegeEscalation: false,
|
|
278
278
|
capabilities: {
|
package/src/lib/assets/loader.ts
CHANGED
|
@@ -26,12 +26,6 @@ export function loadCapabilities(path: string): Promise<CapabilityExport[]> {
|
|
|
26
26
|
program.on("message", message => {
|
|
27
27
|
// Cast the message to the ModuleCapabilities type
|
|
28
28
|
const capabilities = message.valueOf() as CapabilityExport[];
|
|
29
|
-
|
|
30
|
-
// Iterate through the capabilities and generate the rules
|
|
31
|
-
for (const capability of capabilities) {
|
|
32
|
-
console.debug(`Registered Pepr Capability "${capability.name}"`);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
29
|
resolve(capabilities);
|
|
36
30
|
});
|
|
37
31
|
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
AdmissionregistrationV1WebhookClientConfig as AdmissionRegnV1WebhookClientCfg,
|
|
6
6
|
V1LabelSelectorRequirement,
|
|
7
7
|
V1RuleWithOperations,
|
|
8
|
+
V1MutatingWebhookConfiguration,
|
|
9
|
+
V1ValidatingWebhookConfiguration,
|
|
8
10
|
} from "@kubernetes/client-node";
|
|
9
11
|
import { kind } from "kubernetes-fluent-client";
|
|
10
12
|
import { concat, equals, uniqWith } from "ramda";
|
|
@@ -12,7 +14,6 @@ import { resolveIgnoreNamespaces } from "./ignoredNamespaces";
|
|
|
12
14
|
import { Assets } from "./assets";
|
|
13
15
|
import { Event, WebhookType } from "../enums";
|
|
14
16
|
import { Binding } from "../types";
|
|
15
|
-
import Log from "../telemetry/logger";
|
|
16
17
|
|
|
17
18
|
export const peprIgnoreNamespaces: string[] = ["kube-system", "pepr-system"];
|
|
18
19
|
|
|
@@ -47,15 +48,13 @@ export async function generateWebhookRules(
|
|
|
47
48
|
assets: Assets,
|
|
48
49
|
isMutateWebhook: boolean,
|
|
49
50
|
): Promise<V1RuleWithOperations[]> {
|
|
50
|
-
const {
|
|
51
|
+
const { capabilities } = assets;
|
|
51
52
|
|
|
52
|
-
const rules = capabilities.flatMap(capability =>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return capability.bindings
|
|
53
|
+
const rules = capabilities.flatMap(capability =>
|
|
54
|
+
capability.bindings
|
|
56
55
|
.map(binding => validateRule(binding, isMutateWebhook))
|
|
57
|
-
.filter((rule): rule is V1RuleWithOperations => !!rule)
|
|
58
|
-
|
|
56
|
+
.filter((rule): rule is V1RuleWithOperations => !!rule),
|
|
57
|
+
);
|
|
59
58
|
|
|
60
59
|
return uniqWith(equals, rules);
|
|
61
60
|
}
|
|
@@ -113,7 +112,7 @@ export async function webhookConfigGenerator(
|
|
|
113
112
|
return null;
|
|
114
113
|
}
|
|
115
114
|
|
|
116
|
-
|
|
115
|
+
const webhookConfig = {
|
|
117
116
|
apiVersion: "admissionregistration.k8s.io/v1",
|
|
118
117
|
kind: isMutate ? "MutatingWebhookConfiguration" : "ValidatingWebhookConfiguration",
|
|
119
118
|
metadata: { name },
|
|
@@ -134,4 +133,54 @@ export async function webhookConfigGenerator(
|
|
|
134
133
|
},
|
|
135
134
|
],
|
|
136
135
|
};
|
|
136
|
+
|
|
137
|
+
// If additional webhooks are specified, add them to the config
|
|
138
|
+
if (config.additionalWebhooks) {
|
|
139
|
+
return configureAdditionalWebhooks(webhookConfig, config.additionalWebhooks);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return webhookConfig;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function configureAdditionalWebhooks(
|
|
146
|
+
webhookConfig: V1MutatingWebhookConfiguration | V1ValidatingWebhookConfiguration,
|
|
147
|
+
additionalWebhooks: {
|
|
148
|
+
failurePolicy: "Fail" | "Ignore";
|
|
149
|
+
namespace: string;
|
|
150
|
+
}[],
|
|
151
|
+
): V1MutatingWebhookConfiguration | V1ValidatingWebhookConfiguration {
|
|
152
|
+
if (!additionalWebhooks || additionalWebhooks.length === 0) {
|
|
153
|
+
return webhookConfig;
|
|
154
|
+
}
|
|
155
|
+
// set the ignored namespace to the additional webhook namespaces
|
|
156
|
+
const webhooks = webhookConfig.webhooks ?? [];
|
|
157
|
+
if (webhooks.length === 0) {
|
|
158
|
+
return webhookConfig;
|
|
159
|
+
}
|
|
160
|
+
const expr = webhooks[0]!.namespaceSelector!.matchExpressions![0]!;
|
|
161
|
+
expr.values!.push(...additionalWebhooks.map(w => w.namespace));
|
|
162
|
+
|
|
163
|
+
additionalWebhooks.forEach(additionalWebhook => {
|
|
164
|
+
webhooks.push({
|
|
165
|
+
name: `${webhookConfig.metadata!.name}-${additionalWebhook.namespace}.pepr.dev`,
|
|
166
|
+
admissionReviewVersions: ["v1", "v1beta1"],
|
|
167
|
+
clientConfig: webhooks[0]!.clientConfig,
|
|
168
|
+
failurePolicy: additionalWebhook.failurePolicy,
|
|
169
|
+
matchPolicy: "Equivalent",
|
|
170
|
+
timeoutSeconds: webhooks[0]!.timeoutSeconds,
|
|
171
|
+
namespaceSelector: {
|
|
172
|
+
matchExpressions: [
|
|
173
|
+
{
|
|
174
|
+
key: "kubernetes.io/metadata.name",
|
|
175
|
+
operator: "In",
|
|
176
|
+
values: [additionalWebhook.namespace],
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
},
|
|
180
|
+
rules: webhooks[0].rules,
|
|
181
|
+
sideEffects: "None",
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
return webhookConfig;
|
|
137
186
|
}
|
|
@@ -4,6 +4,8 @@ import { dumpYaml } from "@kubernetes/client-node";
|
|
|
4
4
|
import { clusterRole } from "../rbac";
|
|
5
5
|
import { promises as fs } from "fs";
|
|
6
6
|
import { resolveIgnoreNamespaces } from "../ignoredNamespaces";
|
|
7
|
+
import { quicktype, InputData, jsonInputForTargetLanguage } from "quicktype-core";
|
|
8
|
+
|
|
7
9
|
export type ChartOverrides = {
|
|
8
10
|
apiPath: string;
|
|
9
11
|
capabilities: CapabilityExport[];
|
|
@@ -60,9 +62,9 @@ export async function overridesFile(
|
|
|
60
62
|
},
|
|
61
63
|
securityContext: {
|
|
62
64
|
runAsUser: image.includes("private") ? 1000 : 65532,
|
|
63
|
-
runAsGroup: 65532,
|
|
65
|
+
runAsGroup: image.includes("private") ? 1000 : 65532,
|
|
64
66
|
runAsNonRoot: true,
|
|
65
|
-
fsGroup: 65532,
|
|
67
|
+
fsGroup: image.includes("private") ? 1000 : 65532,
|
|
66
68
|
},
|
|
67
69
|
readinessProbe: {
|
|
68
70
|
httpGet: {
|
|
@@ -92,7 +94,7 @@ export async function overridesFile(
|
|
|
92
94
|
},
|
|
93
95
|
containerSecurityContext: {
|
|
94
96
|
runAsUser: image.includes("private") ? 1000 : 65532,
|
|
95
|
-
runAsGroup: 65532,
|
|
97
|
+
runAsGroup: image.includes("private") ? 1000 : 65532,
|
|
96
98
|
runAsNonRoot: true,
|
|
97
99
|
allowPrivilegeEscalation: false,
|
|
98
100
|
capabilities: {
|
|
@@ -128,9 +130,9 @@ export async function overridesFile(
|
|
|
128
130
|
},
|
|
129
131
|
securityContext: {
|
|
130
132
|
runAsUser: image.includes("private") ? 1000 : 65532,
|
|
131
|
-
runAsGroup: 65532,
|
|
133
|
+
runAsGroup: image.includes("private") ? 1000 : 65532,
|
|
132
134
|
runAsNonRoot: true,
|
|
133
|
-
fsGroup: 65532,
|
|
135
|
+
fsGroup: image.includes("private") ? 1000 : 65532,
|
|
134
136
|
},
|
|
135
137
|
readinessProbe: {
|
|
136
138
|
httpGet: {
|
|
@@ -160,7 +162,7 @@ export async function overridesFile(
|
|
|
160
162
|
},
|
|
161
163
|
containerSecurityContext: {
|
|
162
164
|
runAsUser: image.includes("private") ? 1000 : 65532,
|
|
163
|
-
runAsGroup: 65532,
|
|
165
|
+
runAsGroup: image.includes("private") ? 1000 : 65532,
|
|
164
166
|
runAsNonRoot: true,
|
|
165
167
|
allowPrivilegeEscalation: false,
|
|
166
168
|
capabilities: {
|
|
@@ -181,6 +183,26 @@ export async function overridesFile(
|
|
|
181
183
|
},
|
|
182
184
|
},
|
|
183
185
|
};
|
|
184
|
-
|
|
186
|
+
/** write values.schema.yaml */
|
|
187
|
+
await writeSchemaYamlFromObject(JSON.stringify(overrides, null, 2), path);
|
|
185
188
|
await fs.writeFile(path, dumpYaml(overrides, { noRefs: true, forceQuotes: true }));
|
|
186
189
|
}
|
|
190
|
+
|
|
191
|
+
export async function writeSchemaYamlFromObject(
|
|
192
|
+
valuesString: string,
|
|
193
|
+
valuesFilePath: string,
|
|
194
|
+
): Promise<void> {
|
|
195
|
+
const schemaPath = valuesFilePath.replace(/\.yaml$/, ".schema.json");
|
|
196
|
+
const jsonInput = jsonInputForTargetLanguage("schema");
|
|
197
|
+
await jsonInput.addSource({ name: "Values", samples: [valuesString] });
|
|
198
|
+
|
|
199
|
+
const inputData = new InputData();
|
|
200
|
+
inputData.addInput(jsonInput);
|
|
201
|
+
|
|
202
|
+
const { lines } = await quicktype({ inputData, lang: "schema" });
|
|
203
|
+
|
|
204
|
+
const schemaJson = lines.join("\n");
|
|
205
|
+
const schemaObj = JSON.parse(schemaJson);
|
|
206
|
+
|
|
207
|
+
await fs.writeFile(schemaPath, JSON.stringify(schemaObj, null, 2), "utf8");
|
|
208
|
+
}
|
|
@@ -50,6 +50,7 @@ export class Controller {
|
|
|
50
50
|
const { beforeHook, afterHook, onReady } = hooks;
|
|
51
51
|
this.#config = config;
|
|
52
52
|
this.#capabilities = capabilities;
|
|
53
|
+
Log.info({ config }, "Controller configuration");
|
|
53
54
|
|
|
54
55
|
// Initialize the Pepr store for each capability
|
|
55
56
|
new StoreController(capabilities, `pepr-${config.uuid}-store`, () => {
|
package/src/lib/types.ts
CHANGED
|
@@ -296,6 +296,13 @@ export type ModuleConfig = {
|
|
|
296
296
|
watch?: {
|
|
297
297
|
alwaysIgnore: WebhookIgnore;
|
|
298
298
|
};
|
|
299
|
+
/** Additional webhooks config to be used by the module */
|
|
300
|
+
additionalWebhooks?: [
|
|
301
|
+
{
|
|
302
|
+
failurePolicy: "Fail" | "Ignore";
|
|
303
|
+
namespace: string;
|
|
304
|
+
},
|
|
305
|
+
];
|
|
299
306
|
} & Partial<ModuleConfigOptions>;
|
|
300
307
|
|
|
301
308
|
export type PackageJSON = {
|
package/dist/cli/deploy.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { CapabilityExport } from "../lib/types";
|
|
2
|
-
import { Assets } from "../lib/assets/assets";
|
|
3
|
-
import { Command } from "commander";
|
|
4
|
-
interface ImagePullSecretDetails {
|
|
5
|
-
pullSecret?: string;
|
|
6
|
-
dockerServer?: string;
|
|
7
|
-
dockerUsername?: string;
|
|
8
|
-
dockerEmail?: string;
|
|
9
|
-
dockerPassword?: string;
|
|
10
|
-
}
|
|
11
|
-
export declare function validateImagePullSecretDetails(details: ImagePullSecretDetails): {
|
|
12
|
-
valid: boolean;
|
|
13
|
-
error?: string;
|
|
14
|
-
};
|
|
15
|
-
export declare function getUserConfirmation(opts: {
|
|
16
|
-
yes: boolean;
|
|
17
|
-
}): Promise<boolean>;
|
|
18
|
-
export default function (program: Command): void;
|
|
19
|
-
export declare function validateNamespaces(capability: CapabilityExport, webhook: Assets): void;
|
|
20
|
-
export {};
|
|
21
|
-
//# sourceMappingURL=deploy.d.ts.map
|
package/dist/cli/deploy.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/cli/deploy.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,UAAU,sBAAsB;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,sBAAsB,GAAG;IAC/E,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAmCA;AAoBD,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAalF;AAkCD,MAAM,CAAC,OAAO,WAAW,OAAO,EAAE,OAAO,GAAG,IAAI,CA8B/C;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAQtF"}
|
package/src/cli/deploy.ts
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
|
-
|
|
4
|
-
import prompt from "prompts";
|
|
5
|
-
import { CapabilityExport } from "../lib/types";
|
|
6
|
-
import { Assets } from "../lib/assets/assets";
|
|
7
|
-
import { ImagePullSecret } from "../lib/types";
|
|
8
|
-
import { Command } from "commander";
|
|
9
|
-
import { buildModule } from "./build/buildModule";
|
|
10
|
-
import { deployImagePullSecret, deployWebhook } from "../lib/assets/deploy";
|
|
11
|
-
import { namespaceDeploymentsReady } from "../lib/deploymentChecks";
|
|
12
|
-
import { sanitizeName } from "./init/utils";
|
|
13
|
-
import { validateCapabilityNames } from "../lib/helpers";
|
|
14
|
-
import { namespaceComplianceValidator } from "../lib/helpers";
|
|
15
|
-
import { loadCapabilities } from "../lib/assets/loader";
|
|
16
|
-
|
|
17
|
-
interface ImagePullSecretDetails {
|
|
18
|
-
pullSecret?: string;
|
|
19
|
-
dockerServer?: string;
|
|
20
|
-
dockerUsername?: string;
|
|
21
|
-
dockerEmail?: string;
|
|
22
|
-
dockerPassword?: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function validateImagePullSecretDetails(details: ImagePullSecretDetails): {
|
|
26
|
-
valid: boolean;
|
|
27
|
-
error?: string;
|
|
28
|
-
} {
|
|
29
|
-
if (!details.pullSecret) {
|
|
30
|
-
return { valid: true };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names
|
|
34
|
-
if (details.pullSecret !== sanitizeName(details.pullSecret)) {
|
|
35
|
-
return {
|
|
36
|
-
valid: false,
|
|
37
|
-
error: `Invalid --pullSecret. Must be valid name as defined in RFC 1123.`,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const missing: string[] = [];
|
|
42
|
-
if (!details.dockerEmail) {
|
|
43
|
-
missing.push("--docker-email");
|
|
44
|
-
}
|
|
45
|
-
if (!details.dockerServer) {
|
|
46
|
-
missing.push("--docker-server");
|
|
47
|
-
}
|
|
48
|
-
if (!details.dockerUsername) {
|
|
49
|
-
missing.push("--docker-username");
|
|
50
|
-
}
|
|
51
|
-
if (!details.dockerPassword) {
|
|
52
|
-
missing.push("--docker-password");
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (missing.length > 0) {
|
|
56
|
-
return {
|
|
57
|
-
valid: false,
|
|
58
|
-
error: `Error: Must provide ${missing.join(", ")} when providing --pull-secret`,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return { valid: true };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
type ValidatedImagePullSecretDetails = Required<ImagePullSecretDetails>;
|
|
66
|
-
|
|
67
|
-
function generateImagePullSecret(details: ValidatedImagePullSecretDetails): ImagePullSecret {
|
|
68
|
-
const auth = Buffer.from(`${details.dockerUsername}:${details.dockerPassword}`).toString(
|
|
69
|
-
"base64",
|
|
70
|
-
);
|
|
71
|
-
return {
|
|
72
|
-
auths: {
|
|
73
|
-
[details.dockerServer]: {
|
|
74
|
-
username: details.dockerUsername,
|
|
75
|
-
password: details.dockerPassword,
|
|
76
|
-
email: details.dockerEmail,
|
|
77
|
-
auth,
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export async function getUserConfirmation(opts: { yes: boolean }): Promise<boolean> {
|
|
84
|
-
if (opts.yes) {
|
|
85
|
-
return true;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Prompt the user to confirm
|
|
89
|
-
const confirmation = await prompt({
|
|
90
|
-
type: "confirm",
|
|
91
|
-
name: "yes",
|
|
92
|
-
message: "This will remove and redeploy the module. Continue?",
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
return confirmation.yes ? true : false;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
async function buildAndDeployModule(image: string, force: boolean): Promise<void> {
|
|
99
|
-
const builtModule = await buildModule("dist");
|
|
100
|
-
if (!builtModule) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Generate a secret for the module
|
|
105
|
-
const webhook = new Assets(
|
|
106
|
-
{ ...builtModule.cfg.pepr, description: builtModule.cfg.description },
|
|
107
|
-
builtModule.path,
|
|
108
|
-
[],
|
|
109
|
-
);
|
|
110
|
-
webhook.image = image ?? webhook.image;
|
|
111
|
-
const capabilities = await loadCapabilities(webhook.path);
|
|
112
|
-
for (const capability of capabilities) {
|
|
113
|
-
validateNamespaces(capability, webhook);
|
|
114
|
-
}
|
|
115
|
-
try {
|
|
116
|
-
await webhook.deploy(deployWebhook, force, builtModule.cfg.pepr.webhookTimeout ?? 10);
|
|
117
|
-
|
|
118
|
-
// wait for capabilities to be loaded and test names
|
|
119
|
-
validateCapabilityNames(webhook.capabilities);
|
|
120
|
-
|
|
121
|
-
// Wait for the pepr-system resources to be fully up
|
|
122
|
-
await namespaceDeploymentsReady();
|
|
123
|
-
console.info(`✅ Module deployed successfully`);
|
|
124
|
-
} catch (e) {
|
|
125
|
-
console.error(`Error deploying module:`, e);
|
|
126
|
-
process.exit(1);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export default function (program: Command): void {
|
|
131
|
-
program
|
|
132
|
-
.command("deploy")
|
|
133
|
-
.description("Deploy a Pepr Module")
|
|
134
|
-
.option("-E, --docker-email <email>", "Email for Docker registry.")
|
|
135
|
-
.option("-P, --docker-password <password>", "Password for Docker registry.")
|
|
136
|
-
.option("-S, --docker-server <server>", "Docker server address.")
|
|
137
|
-
.option("-U, --docker-username <username>", "Docker registry username.")
|
|
138
|
-
.option("-f, --force", "Force deploy the module, override manager field.")
|
|
139
|
-
.option("-i, --image <image>", "Override the image tag.")
|
|
140
|
-
.option("-p, --pull-secret <name>", "Deploy imagePullSecret for Controller private registry.")
|
|
141
|
-
.option("-y, --yes", "Skip confirmation prompts.")
|
|
142
|
-
.action(async opts => {
|
|
143
|
-
const valResp = validateImagePullSecretDetails(opts);
|
|
144
|
-
if (!valResp.valid) {
|
|
145
|
-
console.error(valResp.error);
|
|
146
|
-
process.exit(1);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (opts.pullSecret) {
|
|
150
|
-
await deployImagePullSecret(generateImagePullSecret(opts), opts.pullSecret);
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (!(await getUserConfirmation(opts))) {
|
|
155
|
-
process.exit(0);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
await buildAndDeployModule(opts.image, opts.force);
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export function validateNamespaces(capability: CapabilityExport, webhook: Assets): void {
|
|
163
|
-
namespaceComplianceValidator(capability, webhook.alwaysIgnore?.namespaces);
|
|
164
|
-
namespaceComplianceValidator(
|
|
165
|
-
capability,
|
|
166
|
-
webhook.config.admission?.alwaysIgnore?.namespaces,
|
|
167
|
-
false,
|
|
168
|
-
);
|
|
169
|
-
namespaceComplianceValidator(capability, webhook.config.watch?.alwaysIgnore?.namespaces, true);
|
|
170
|
-
}
|