pepr 0.13.4 → 0.14.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.
Files changed (105) hide show
  1. package/README.md +23 -4
  2. package/dist/cli.js +375 -204
  3. package/dist/controller.js +1 -1
  4. package/dist/lib/assets/deploy.d.ts.map +1 -1
  5. package/dist/lib/assets/destroy.d.ts +2 -0
  6. package/dist/lib/assets/destroy.d.ts.map +1 -0
  7. package/dist/lib/assets/index.d.ts +6 -5
  8. package/dist/lib/assets/index.d.ts.map +1 -1
  9. package/dist/lib/assets/networking.d.ts +6 -5
  10. package/dist/lib/assets/networking.d.ts.map +1 -1
  11. package/dist/lib/assets/pods.d.ts +84 -4
  12. package/dist/lib/assets/pods.d.ts.map +1 -1
  13. package/dist/lib/assets/rbac.d.ts +6 -4
  14. package/dist/lib/assets/rbac.d.ts.map +1 -1
  15. package/dist/lib/assets/store.d.ts +7 -0
  16. package/dist/lib/assets/store.d.ts.map +1 -0
  17. package/dist/lib/assets/webhooks.d.ts +2 -2
  18. package/dist/lib/assets/webhooks.d.ts.map +1 -1
  19. package/dist/lib/assets/yaml.d.ts.map +1 -1
  20. package/dist/lib/capability.d.ts +21 -4
  21. package/dist/lib/capability.d.ts.map +1 -1
  22. package/dist/lib/controller/index.d.ts +10 -0
  23. package/dist/lib/controller/index.d.ts.map +1 -0
  24. package/dist/lib/controller/store.d.ts +7 -0
  25. package/dist/lib/controller/store.d.ts.map +1 -0
  26. package/dist/lib/filter.d.ts +2 -2
  27. package/dist/lib/filter.d.ts.map +1 -1
  28. package/dist/lib/{k8s/types.d.ts → k8s.d.ts} +14 -25
  29. package/dist/lib/k8s.d.ts.map +1 -0
  30. package/dist/lib/metrics.d.ts +12 -12
  31. package/dist/lib/metrics.d.ts.map +1 -1
  32. package/dist/lib/module.d.ts +25 -4
  33. package/dist/lib/module.d.ts.map +1 -1
  34. package/dist/lib/mutate-processor.d.ts +3 -3
  35. package/dist/lib/mutate-processor.d.ts.map +1 -1
  36. package/dist/lib/mutate-request.d.ts +11 -10
  37. package/dist/lib/mutate-request.d.ts.map +1 -1
  38. package/dist/lib/storage.d.ts +56 -0
  39. package/dist/lib/storage.d.ts.map +1 -0
  40. package/dist/lib/tls.d.ts.map +1 -0
  41. package/dist/lib/types.d.ts +28 -48
  42. package/dist/lib/types.d.ts.map +1 -1
  43. package/dist/lib/validate-processor.d.ts +2 -2
  44. package/dist/lib/validate-processor.d.ts.map +1 -1
  45. package/dist/lib/validate-request.d.ts +9 -8
  46. package/dist/lib/validate-request.d.ts.map +1 -1
  47. package/dist/lib/watch-processor.d.ts +3 -0
  48. package/dist/lib/watch-processor.d.ts.map +1 -0
  49. package/dist/lib.d.ts +3 -7
  50. package/dist/lib.d.ts.map +1 -1
  51. package/dist/lib.js +484 -807
  52. package/dist/lib.js.map +4 -4
  53. package/package.json +13 -17
  54. package/src/lib/assets/deploy.ts +69 -127
  55. package/src/lib/assets/destroy.ts +33 -0
  56. package/src/lib/assets/index.ts +8 -14
  57. package/src/lib/assets/networking.ts +28 -5
  58. package/src/lib/assets/pods.ts +130 -11
  59. package/src/lib/assets/rbac.ts +42 -4
  60. package/src/lib/assets/store.ts +49 -0
  61. package/src/lib/assets/webhooks.ts +2 -2
  62. package/src/lib/assets/yaml.ts +13 -3
  63. package/src/lib/capability.ts +69 -14
  64. package/src/lib/{controller.ts → controller/index.ts} +25 -23
  65. package/src/lib/controller/store.ts +197 -0
  66. package/src/lib/filter.ts +2 -2
  67. package/src/lib/{k8s/types.ts → k8s.ts} +15 -26
  68. package/src/lib/metrics.ts +22 -38
  69. package/src/lib/module.ts +47 -10
  70. package/src/lib/mutate-processor.ts +6 -6
  71. package/src/lib/mutate-request.ts +18 -26
  72. package/src/lib/storage.ts +128 -0
  73. package/src/lib/types.ts +30 -53
  74. package/src/lib/validate-processor.ts +5 -4
  75. package/src/lib/validate-request.ts +15 -19
  76. package/src/lib/watch-processor.ts +55 -0
  77. package/src/lib.ts +4 -8
  78. package/src/templates/capabilities/hello-pepr.ts +54 -5
  79. package/src/templates/package.json +1 -0
  80. package/dist/lib/controller.d.ts +0 -10
  81. package/dist/lib/controller.d.ts.map +0 -1
  82. package/dist/lib/fetch.d.ts +0 -23
  83. package/dist/lib/fetch.d.ts.map +0 -1
  84. package/dist/lib/k8s/index.d.ts +0 -7
  85. package/dist/lib/k8s/index.d.ts.map +0 -1
  86. package/dist/lib/k8s/kinds.d.ts +0 -12
  87. package/dist/lib/k8s/kinds.d.ts.map +0 -1
  88. package/dist/lib/k8s/tls.d.ts.map +0 -1
  89. package/dist/lib/k8s/types.d.ts.map +0 -1
  90. package/dist/lib/k8s/upstream.d.ts +0 -4
  91. package/dist/lib/k8s/upstream.d.ts.map +0 -1
  92. package/jest.config.json +0 -4
  93. package/journey/before.ts +0 -21
  94. package/journey/k8s.ts +0 -100
  95. package/journey/pepr-build.ts +0 -69
  96. package/journey/pepr-deploy.ts +0 -174
  97. package/journey/pepr-dev.ts +0 -155
  98. package/journey/pepr-format.ts +0 -13
  99. package/journey/pepr-init.ts +0 -12
  100. package/src/lib/fetch.ts +0 -76
  101. package/src/lib/k8s/index.ts +0 -14
  102. package/src/lib/k8s/kinds.ts +0 -531
  103. package/src/lib/k8s/upstream.ts +0 -53
  104. /package/dist/lib/{k8s/tls.d.ts → tls.d.ts} +0 -0
  105. /package/src/lib/{k8s/tls.ts → tls.ts} +0 -0
@@ -1,179 +1,121 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
- import {
5
- AdmissionregistrationV1Api as AdmissionRegV1API,
6
- AppsV1Api,
7
- CoreV1Api,
8
- HttpError,
9
- KubeConfig,
10
- RbacAuthorizationV1Api,
11
- } from "@kubernetes/client-node";
12
4
  import crypto from "crypto";
13
5
  import { promises as fs } from "fs";
6
+ import { K8s, kind } from "kubernetes-fluent-client";
14
7
 
15
8
  import { Assets } from ".";
16
9
  import Log from "../logger";
17
- import { apiTokenSecret, service, tlsSecret } from "./networking";
18
- import { deployment, moduleSecret, namespace } from "./pods";
19
- import { clusterRole, clusterRoleBinding, serviceAccount } from "./rbac";
10
+ import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking";
11
+ import { deployment, moduleSecret, namespace, watcher } from "./pods";
12
+ import { clusterRole, clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "./rbac";
13
+ import { peprStoreCRD } from "./store";
20
14
  import { webhookConfig } from "./webhooks";
21
15
 
22
16
  export async function deploy(assets: Assets, webhookTimeout?: number) {
23
17
  Log.info("Establishing connection to Kubernetes");
24
18
 
25
- const peprNS = "pepr-system";
26
19
  const { name, host, path } = assets;
27
20
 
28
- // Deploy the resources using the k8s API
29
- const kubeConfig = new KubeConfig();
30
- kubeConfig.loadFromDefault();
31
-
32
- const coreV1Api = kubeConfig.makeApiClient(CoreV1Api);
33
- const admissionApi = kubeConfig.makeApiClient(AdmissionRegV1API);
34
-
35
- try {
36
- Log.info("Checking for namespace");
37
- await coreV1Api.readNamespace(peprNS);
38
- } catch (e) {
39
- Log.debug(e instanceof HttpError ? e.body : e);
40
- Log.info("Creating namespace");
41
- await coreV1Api.createNamespace(namespace);
42
- }
21
+ Log.info("Applying pepr-system namespace");
22
+ await K8s(kind.Namespace).Apply(namespace);
43
23
 
44
24
  // Create the mutating webhook configuration if it is needed
45
25
  const mutateWebhook = await webhookConfig(assets, "mutate", webhookTimeout);
46
26
  if (mutateWebhook) {
47
- try {
48
- Log.info("Creating mutating webhook");
49
- await admissionApi.createMutatingWebhookConfiguration(mutateWebhook);
50
- } catch (e) {
51
- Log.debug(e instanceof HttpError ? e.body : e);
52
- Log.info("Removing and re-creating mutating webhook");
53
- await admissionApi.deleteMutatingWebhookConfiguration(mutateWebhook.metadata?.name ?? "");
54
- await admissionApi.createMutatingWebhookConfiguration(mutateWebhook);
55
- }
27
+ Log.info("Applying mutating webhook");
28
+ await K8s(kind.MutatingWebhookConfiguration).Apply(mutateWebhook);
29
+ } else {
30
+ Log.info("Mutating webhook not needed, removing if it exists");
31
+ await K8s(kind.MutatingWebhookConfiguration).Delete(name);
56
32
  }
57
33
 
58
34
  // Create the validating webhook configuration if it is needed
59
35
  const validateWebhook = await webhookConfig(assets, "validate", webhookTimeout);
60
36
  if (validateWebhook) {
61
- try {
62
- Log.info("Creating validating webhook");
63
- await admissionApi.createValidatingWebhookConfiguration(validateWebhook);
64
- } catch (e) {
65
- Log.debug(e instanceof HttpError ? e.body : e);
66
- Log.info("Removing and re-creating validating webhook");
67
- await admissionApi.deleteValidatingWebhookConfiguration(validateWebhook.metadata?.name ?? "");
68
- await admissionApi.createValidatingWebhookConfiguration(validateWebhook);
69
- }
37
+ Log.info("Applying validating webhook");
38
+ await K8s(kind.ValidatingWebhookConfiguration).Apply(validateWebhook);
39
+ } else {
40
+ Log.info("Validating webhook not needed, removing if it exists");
41
+ await K8s(kind.ValidatingWebhookConfiguration).Delete(name);
70
42
  }
71
43
 
44
+ Log.info("Applying the Pepr Store CRD if it doesn't exist");
45
+ await K8s(kind.CustomResourceDefinition).Apply(peprStoreCRD);
46
+
72
47
  // If a host is specified, we don't need to deploy the rest of the resources
73
48
  if (host) {
74
49
  return;
75
50
  }
76
51
 
77
- if (!path) {
78
- throw new Error("No code provided");
79
- }
80
-
81
52
  const code = await fs.readFile(path);
82
-
83
53
  const hash = crypto.createHash("sha256").update(code).digest("hex");
84
54
 
85
- const appsApi = kubeConfig.makeApiClient(AppsV1Api);
86
- const rbacApi = kubeConfig.makeApiClient(RbacAuthorizationV1Api);
55
+ if (code.length < 1) {
56
+ throw new Error("No code provided");
57
+ }
58
+
59
+ await setupRBAC(name);
60
+ await setupController(assets, code, hash);
61
+ await setupWatcher(assets, hash);
62
+ }
87
63
 
64
+ async function setupRBAC(name: string) {
65
+ Log.info("Applying cluster role binding");
88
66
  const crb = clusterRoleBinding(name);
89
- try {
90
- Log.info("Creating cluster role binding");
91
- await rbacApi.createClusterRoleBinding(crb);
92
- } catch (e) {
93
- Log.debug(e instanceof HttpError ? e.body : e);
94
- Log.info("Removing and re-creating cluster role binding");
95
- await rbacApi.deleteClusterRoleBinding(crb.metadata?.name ?? "");
96
- await rbacApi.createClusterRoleBinding(crb);
97
- }
67
+ await K8s(kind.ClusterRoleBinding).Apply(crb);
98
68
 
69
+ Log.info("Applying cluster role");
99
70
  const cr = clusterRole(name);
100
- try {
101
- Log.info("Creating cluster role");
102
- await rbacApi.createClusterRole(cr);
103
- } catch (e) {
104
- Log.debug(e instanceof HttpError ? e.body : e);
105
- Log.info("Removing and re-creating the cluster role");
106
- try {
107
- await rbacApi.deleteClusterRole(cr.metadata?.name ?? "");
108
- await rbacApi.createClusterRole(cr);
109
- } catch (e) {
110
- Log.debug(e instanceof HttpError ? e.body : e);
111
- }
112
- }
71
+ await K8s(kind.ClusterRole).Apply(cr);
113
72
 
73
+ Log.info("Applying service account");
114
74
  const sa = serviceAccount(name);
115
- try {
116
- Log.info("Creating service account");
117
- await coreV1Api.createNamespacedServiceAccount(peprNS, sa);
118
- } catch (e) {
119
- Log.debug(e instanceof HttpError ? e.body : e);
120
- Log.info("Removing and re-creating service account");
121
- await coreV1Api.deleteNamespacedServiceAccount(sa.metadata?.name ?? "", peprNS);
122
- await coreV1Api.createNamespacedServiceAccount(peprNS, sa);
123
- }
75
+ await K8s(kind.ServiceAccount).Apply(sa);
124
76
 
77
+ Log.info("Applying store role");
78
+ const role = storeRole(name);
79
+ await K8s(kind.Role).Apply(role);
80
+
81
+ Log.info("Applying store role binding");
82
+ const roleBinding = storeRoleBinding(name);
83
+ await K8s(kind.RoleBinding).Apply(roleBinding);
84
+ }
85
+
86
+ async function setupController(assets: Assets, code: Buffer, hash: string) {
87
+ const { name } = assets;
88
+
89
+ Log.info("Applying module secret");
125
90
  const mod = moduleSecret(name, code, hash);
126
- try {
127
- Log.info("Creating module secret");
128
- await coreV1Api.createNamespacedSecret(peprNS, mod);
129
- } catch (e) {
130
- Log.debug(e instanceof HttpError ? e.body : e);
131
- Log.info("Removing and re-creating module secret");
132
- await coreV1Api.deleteNamespacedSecret(mod.metadata?.name ?? "", peprNS);
133
- await coreV1Api.createNamespacedSecret(peprNS, mod);
134
- }
91
+ await K8s(kind.Secret).Apply(mod);
135
92
 
93
+ Log.info("Applying controller service");
136
94
  const svc = service(name);
137
- try {
138
- Log.info("Creating service");
139
- await coreV1Api.createNamespacedService(peprNS, svc);
140
- } catch (e) {
141
- Log.debug(e instanceof HttpError ? e.body : e);
142
- Log.info("Removing and re-creating service");
143
- await coreV1Api.deleteNamespacedService(svc.metadata?.name ?? "", peprNS);
144
- await coreV1Api.createNamespacedService(peprNS, svc);
145
- }
95
+ await K8s(kind.Service).Apply(svc);
146
96
 
97
+ Log.info("Applying TLS secret");
147
98
  const tls = tlsSecret(name, assets.tls);
148
- try {
149
- Log.info("Creating TLS secret");
150
- await coreV1Api.createNamespacedSecret(peprNS, tls);
151
- } catch (e) {
152
- Log.debug(e instanceof HttpError ? e.body : e);
153
- Log.info("Removing and re-creating TLS secret");
154
- await coreV1Api.deleteNamespacedSecret(tls.metadata?.name ?? "", peprNS);
155
- await coreV1Api.createNamespacedSecret(peprNS, tls);
156
- }
99
+ await K8s(kind.Secret).Apply(tls);
157
100
 
101
+ Log.info("Applying API token secret");
158
102
  const apiToken = apiTokenSecret(name, assets.apiToken);
159
- try {
160
- Log.info("Creating API token secret");
161
- await coreV1Api.createNamespacedSecret(peprNS, apiToken);
162
- } catch (e) {
163
- Log.debug(e instanceof HttpError ? e.body : e);
164
- Log.info("Removing and re-creating API token secret");
165
- await coreV1Api.deleteNamespacedSecret(apiToken.metadata?.name ?? "", peprNS);
166
- await coreV1Api.createNamespacedSecret(peprNS, apiToken);
167
- }
103
+ await K8s(kind.Secret).Apply(apiToken);
168
104
 
105
+ Log.info("Applying deployment");
169
106
  const dep = deployment(assets, hash);
170
- try {
171
- Log.info("Creating deployment");
172
- await appsApi.createNamespacedDeployment(peprNS, dep);
173
- } catch (e) {
174
- Log.debug(e instanceof HttpError ? e.body : e);
175
- Log.info("Removing and re-creating deployment");
176
- await appsApi.deleteNamespacedDeployment(dep.metadata?.name ?? "", peprNS);
177
- await appsApi.createNamespacedDeployment(peprNS, dep);
107
+ await K8s(kind.Deployment).Apply(dep);
108
+ }
109
+
110
+ async function setupWatcher(assets: Assets, hash: string) {
111
+ // If the module has a watcher, deploy it
112
+ const watchDeployment = watcher(assets, hash);
113
+ if (watchDeployment) {
114
+ Log.info("Applying watcher deployment");
115
+ await K8s(kind.Deployment).Apply(watchDeployment);
116
+
117
+ Log.info("Applying watcher service");
118
+ const watchSvc = watcherService(assets.name);
119
+ await K8s(kind.Service).Apply(watchSvc);
178
120
  }
179
121
  }
@@ -0,0 +1,33 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { K8s, kind } from "kubernetes-fluent-client";
5
+
6
+ import Log from "../logger";
7
+ import { peprStoreCRD } from "./store";
8
+
9
+ export async function destroyModule(name: string) {
10
+ const namespace = "pepr-system";
11
+
12
+ Log.info("Destroying Pepr module");
13
+
14
+ await Promise.all([
15
+ K8s(kind.MutatingWebhookConfiguration).Delete(name),
16
+ K8s(kind.ValidatingWebhookConfiguration).Delete(name),
17
+
18
+ K8s(kind.CustomResourceDefinition).Delete(peprStoreCRD),
19
+ K8s(kind.ClusterRoleBinding).Delete(name),
20
+ K8s(kind.ClusterRole).Delete(name),
21
+ K8s(kind.ServiceAccount, { namespace }).Delete(name),
22
+ K8s(kind.Role, { namespace }).Delete(name),
23
+ K8s(kind.RoleBinding, { namespace }).Delete(`${name}-store`),
24
+
25
+ K8s(kind.Secret, { namespace }).Delete(`${name}-module`),
26
+ K8s(kind.Service, { namespace }).Delete(name),
27
+ K8s(kind.Secret, { namespace }).Delete(`${name}-tls`),
28
+ K8s(kind.Secret, { namespace }).Delete(`${name}-api-token`),
29
+ K8s(kind.Deployment, { namespace }).Delete(name),
30
+ K8s(kind.Deployment, { namespace }).Delete(`${name}-watcher`),
31
+ K8s(kind.Service, { namespace }).Delete(`${name}-watcher`),
32
+ ]);
33
+ }
@@ -3,8 +3,9 @@
3
3
 
4
4
  import crypto from "crypto";
5
5
 
6
- import { TLSOut, genTLS } from "../k8s/tls";
7
- import { CapabilityExport, ModuleConfig } from "../types";
6
+ import { ModuleConfig } from "../module";
7
+ import { TLSOut, genTLS } from "../tls";
8
+ import { CapabilityExport } from "../types";
8
9
  import { deploy } from "./deploy";
9
10
  import { loadCapabilities } from "./loader";
10
11
  import { allYaml, zarfYaml } from "./yaml";
@@ -21,11 +22,6 @@ export class Assets {
21
22
  readonly path: string,
22
23
  readonly host?: string,
23
24
  ) {
24
- // Bind public methods
25
- this.deploy = this.deploy.bind(this);
26
- this.zarfYaml = this.zarfYaml.bind(this);
27
- this.allYaml = this.allYaml.bind(this);
28
-
29
25
  this.name = `pepr-${config.uuid}`;
30
26
 
31
27
  this.image = `ghcr.io/defenseunicorns/pepr/controller:v${config.peprVersion}`;
@@ -37,17 +33,15 @@ export class Assets {
37
33
  this.apiToken = crypto.randomBytes(32).toString("hex");
38
34
  }
39
35
 
40
- async deploy(webhookTimeout?: number) {
36
+ deploy = async (webhookTimeout?: number) => {
41
37
  this.capabilities = await loadCapabilities(this.path);
42
38
  await deploy(this, webhookTimeout);
43
- }
39
+ };
44
40
 
45
- zarfYaml(path: string) {
46
- return zarfYaml(this, path);
47
- }
41
+ zarfYaml = (path: string) => zarfYaml(this, path);
48
42
 
49
- async allYaml() {
43
+ allYaml = async () => {
50
44
  this.capabilities = await loadCapabilities(this.path);
51
45
  return allYaml(this);
52
- }
46
+ };
53
47
  }
@@ -1,10 +1,11 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
- import { TLSOut } from "../k8s/tls";
5
- import { Secret, Service } from "../k8s/upstream";
4
+ import { kind } from "kubernetes-fluent-client";
6
5
 
7
- export function apiTokenSecret(name: string, apiToken: string): Secret {
6
+ import { TLSOut } from "../tls";
7
+
8
+ export function apiTokenSecret(name: string, apiToken: string): kind.Secret {
8
9
  return {
9
10
  apiVersion: "v1",
10
11
  kind: "Secret",
@@ -19,7 +20,7 @@ export function apiTokenSecret(name: string, apiToken: string): Secret {
19
20
  };
20
21
  }
21
22
 
22
- export function tlsSecret(name: string, tls: TLSOut): Secret {
23
+ export function tlsSecret(name: string, tls: TLSOut): kind.Secret {
23
24
  return {
24
25
  apiVersion: "v1",
25
26
  kind: "Secret",
@@ -35,7 +36,7 @@ export function tlsSecret(name: string, tls: TLSOut): Secret {
35
36
  };
36
37
  }
37
38
 
38
- export function service(name: string): Service {
39
+ export function service(name: string): kind.Service {
39
40
  return {
40
41
  apiVersion: "v1",
41
42
  kind: "Service",
@@ -56,3 +57,25 @@ export function service(name: string): Service {
56
57
  },
57
58
  };
58
59
  }
60
+
61
+ export function watcherService(name: string): kind.Service {
62
+ return {
63
+ apiVersion: "v1",
64
+ kind: "Service",
65
+ metadata: {
66
+ name: `${name}-watcher`,
67
+ namespace: "pepr-system",
68
+ },
69
+ spec: {
70
+ selector: {
71
+ app: `${name}-watcher`,
72
+ },
73
+ ports: [
74
+ {
75
+ port: 443,
76
+ targetPort: 3000,
77
+ },
78
+ ],
79
+ },
80
+ };
81
+ }
@@ -1,20 +1,141 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
+ import { kind } from "kubernetes-fluent-client";
4
5
  import { gzipSync } from "zlib";
5
6
 
6
7
  import { Assets } from ".";
7
- import { Deployment, Namespace, Secret } from "../k8s/upstream";
8
+ import { Binding } from "../types";
8
9
 
9
10
  /** Generate the pepr-system namespace */
10
- export const namespace: Namespace = {
11
+ export const namespace: kind.Namespace = {
11
12
  apiVersion: "v1",
12
13
  kind: "Namespace",
13
14
  metadata: { name: "pepr-system" },
14
15
  };
15
16
 
16
- export function deployment(assets: Assets, hash: string): Deployment {
17
- const { name, image } = assets;
17
+ export function watcher(assets: Assets, hash: string) {
18
+ const { name, image, capabilities, config } = assets;
19
+
20
+ // Append the watcher suffix
21
+ const app = `${name}-watcher`;
22
+ const bindings: Binding[] = [];
23
+
24
+ // Loop through the capabilities and find any Watch Actions
25
+ for (const capability of capabilities) {
26
+ const watchers = capability.bindings.filter(binding => binding.isWatch);
27
+ bindings.push(...watchers);
28
+ }
29
+
30
+ // If there are no watchers, don't deploy the watcher
31
+ if (bindings.length < 1) {
32
+ return null;
33
+ }
34
+
35
+ return {
36
+ apiVersion: "apps/v1",
37
+ kind: "Deployment",
38
+ metadata: {
39
+ name: app,
40
+ namespace: "pepr-system",
41
+ labels: {
42
+ app,
43
+ },
44
+ },
45
+ spec: {
46
+ replicas: 1,
47
+ strategy: {
48
+ type: "Recreate",
49
+ },
50
+ selector: {
51
+ matchLabels: {
52
+ app,
53
+ },
54
+ },
55
+ template: {
56
+ metadata: {
57
+ labels: {
58
+ app,
59
+ },
60
+ },
61
+ spec: {
62
+ serviceAccountName: name,
63
+ containers: [
64
+ {
65
+ name: "watcher",
66
+ image,
67
+ imagePullPolicy: "IfNotPresent",
68
+ command: ["node", "/app/node_modules/pepr/dist/controller.js", hash],
69
+ readinessProbe: {
70
+ httpGet: {
71
+ path: "/healthz",
72
+ port: 3000,
73
+ scheme: "HTTPS",
74
+ },
75
+ },
76
+ livenessProbe: {
77
+ httpGet: {
78
+ path: "/healthz",
79
+ port: 3000,
80
+ scheme: "HTTPS",
81
+ },
82
+ },
83
+ ports: [
84
+ {
85
+ containerPort: 3000,
86
+ },
87
+ ],
88
+ resources: {
89
+ requests: {
90
+ memory: "64Mi",
91
+ cpu: "100m",
92
+ },
93
+ limits: {
94
+ memory: "256Mi",
95
+ cpu: "500m",
96
+ },
97
+ },
98
+ volumeMounts: [
99
+ {
100
+ name: "tls-certs",
101
+ mountPath: "/etc/certs",
102
+ readOnly: true,
103
+ },
104
+ {
105
+ name: "module",
106
+ mountPath: `/app/load`,
107
+ readOnly: true,
108
+ },
109
+ ],
110
+ env: [
111
+ { name: "PEPR_WATCH_MODE", value: "true" },
112
+ { name: "PEPR_PRETTY_LOG", value: "false" },
113
+ { name: "LOG_LEVEL", value: config.logLevel || "debug" },
114
+ ],
115
+ },
116
+ ],
117
+ volumes: [
118
+ {
119
+ name: "tls-certs",
120
+ secret: {
121
+ secretName: `${name}-tls`,
122
+ },
123
+ },
124
+ {
125
+ name: "module",
126
+ secret: {
127
+ secretName: `${name}-module`,
128
+ },
129
+ },
130
+ ],
131
+ },
132
+ },
133
+ },
134
+ };
135
+ }
136
+
137
+ export function deployment(assets: Assets, hash: string): kind.Deployment {
138
+ const { name, image, config } = assets;
18
139
  const app = name;
19
140
 
20
141
  return {
@@ -68,12 +189,6 @@ export function deployment(assets: Assets, hash: string): Deployment {
68
189
  containerPort: 3000,
69
190
  },
70
191
  ],
71
- env: [
72
- {
73
- name: "PEPR_PRETTY_LOG",
74
- value: "false",
75
- },
76
- ],
77
192
  resources: {
78
193
  requests: {
79
194
  memory: "64Mi",
@@ -84,6 +199,10 @@ export function deployment(assets: Assets, hash: string): Deployment {
84
199
  cpu: "500m",
85
200
  },
86
201
  },
202
+ env: [
203
+ { name: "PEPR_PRETTY_LOG", value: "false" },
204
+ { name: "LOG_LEVEL", value: config.logLevel || "debug" },
205
+ ],
87
206
  volumeMounts: [
88
207
  {
89
208
  name: "tls-certs",
@@ -129,7 +248,7 @@ export function deployment(assets: Assets, hash: string): Deployment {
129
248
  };
130
249
  }
131
250
 
132
- export function moduleSecret(name: string, data: Buffer, hash: string): Secret {
251
+ export function moduleSecret(name: string, data: Buffer, hash: string): kind.Secret {
133
252
  // Compress the data
134
253
  const compressed = gzipSync(data);
135
254
  const path = `module-${hash}.js.gz`;
@@ -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 { ClusterRole, ClusterRoleBinding, ServiceAccount } from "../k8s/upstream";
4
+ import { kind } from "kubernetes-fluent-client";
5
5
 
6
6
  /**
7
7
  * Grants the controller access to cluster resources beyond the mutating webhook.
@@ -9,7 +9,7 @@ import { ClusterRole, ClusterRoleBinding, ServiceAccount } from "../k8s/upstream
9
9
  * @todo: should dynamically generate this based on resources used by the module. will also need to explore how this should work for multiple modules.
10
10
  * @returns
11
11
  */
12
- export function clusterRole(name: string): ClusterRole {
12
+ export function clusterRole(name: string): kind.ClusterRole {
13
13
  return {
14
14
  apiVersion: "rbac.authorization.k8s.io/v1",
15
15
  kind: "ClusterRole",
@@ -25,7 +25,7 @@ export function clusterRole(name: string): ClusterRole {
25
25
  };
26
26
  }
27
27
 
28
- export function clusterRoleBinding(name: string): ClusterRoleBinding {
28
+ export function clusterRoleBinding(name: string): kind.ClusterRoleBinding {
29
29
  return {
30
30
  apiVersion: "rbac.authorization.k8s.io/v1",
31
31
  kind: "ClusterRoleBinding",
@@ -45,7 +45,7 @@ export function clusterRoleBinding(name: string): ClusterRoleBinding {
45
45
  };
46
46
  }
47
47
 
48
- export function serviceAccount(name: string): ServiceAccount {
48
+ export function serviceAccount(name: string): kind.ServiceAccount {
49
49
  return {
50
50
  apiVersion: "v1",
51
51
  kind: "ServiceAccount",
@@ -55,3 +55,41 @@ export function serviceAccount(name: string): ServiceAccount {
55
55
  },
56
56
  };
57
57
  }
58
+
59
+ export function storeRole(name: string): kind.Role {
60
+ name = `${name}-store`;
61
+ return {
62
+ apiVersion: "rbac.authorization.k8s.io/v1",
63
+ kind: "Role",
64
+ metadata: { name, namespace: "pepr-system" },
65
+ rules: [
66
+ {
67
+ apiGroups: ["pepr.dev/*"],
68
+ resources: ["peprstores"],
69
+ resourceNames: [""],
70
+ verbs: ["create", "get", "patch", "watch"],
71
+ },
72
+ ],
73
+ };
74
+ }
75
+
76
+ export function storeRoleBinding(name: string): kind.RoleBinding {
77
+ name = `${name}-store`;
78
+ return {
79
+ apiVersion: "rbac.authorization.k8s.io/v1",
80
+ kind: "RoleBinding",
81
+ metadata: { name, namespace: "pepr-system" },
82
+ roleRef: {
83
+ apiGroup: "rbac.authorization.k8s.io",
84
+ kind: "Role",
85
+ name,
86
+ },
87
+ subjects: [
88
+ {
89
+ kind: "ServiceAccount",
90
+ name,
91
+ namespace: "pepr-system",
92
+ },
93
+ ],
94
+ };
95
+ }
@@ -0,0 +1,49 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { kind as k } from "kubernetes-fluent-client";
5
+
6
+ import { peprStoreGVK } from "../k8s";
7
+
8
+ export const { group, version, kind } = peprStoreGVK;
9
+ export const singular = kind.toLocaleLowerCase();
10
+ export const plural = `${singular}s`;
11
+ export const name = `${plural}.${group}`;
12
+
13
+ export const peprStoreCRD: k.CustomResourceDefinition = {
14
+ apiVersion: "apiextensions.k8s.io/v1",
15
+ kind: "CustomResourceDefinition",
16
+ metadata: {
17
+ name,
18
+ },
19
+ spec: {
20
+ group,
21
+ versions: [
22
+ {
23
+ // typescript doesn't know this is really already set, which is kind of annoying
24
+ name: version || "v1",
25
+ served: true,
26
+ storage: true,
27
+ schema: {
28
+ openAPIV3Schema: {
29
+ type: "object",
30
+ properties: {
31
+ data: {
32
+ type: "object",
33
+ additionalProperties: {
34
+ type: "string",
35
+ },
36
+ },
37
+ },
38
+ },
39
+ },
40
+ },
41
+ ],
42
+ scope: "Namespaced",
43
+ names: {
44
+ plural,
45
+ singular,
46
+ kind,
47
+ },
48
+ },
49
+ };