pepr 0.13.3 → 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.
- package/README.md +23 -4
- package/dist/cli.js +455 -266
- package/dist/controller.js +1 -1
- package/dist/lib/assets/deploy.d.ts.map +1 -1
- package/dist/lib/assets/destroy.d.ts +2 -0
- package/dist/lib/assets/destroy.d.ts.map +1 -0
- package/dist/lib/assets/index.d.ts +6 -5
- package/dist/lib/assets/index.d.ts.map +1 -1
- package/dist/lib/assets/networking.d.ts +6 -5
- package/dist/lib/assets/networking.d.ts.map +1 -1
- package/dist/lib/assets/pods.d.ts +84 -4
- package/dist/lib/assets/pods.d.ts.map +1 -1
- package/dist/lib/assets/rbac.d.ts +6 -4
- package/dist/lib/assets/rbac.d.ts.map +1 -1
- package/dist/lib/assets/store.d.ts +7 -0
- package/dist/lib/assets/store.d.ts.map +1 -0
- package/dist/lib/assets/webhooks.d.ts +2 -2
- 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 +21 -4
- package/dist/lib/capability.d.ts.map +1 -1
- package/dist/lib/controller/index.d.ts +10 -0
- package/dist/lib/controller/index.d.ts.map +1 -0
- package/dist/lib/controller/store.d.ts +7 -0
- package/dist/lib/controller/store.d.ts.map +1 -0
- package/dist/lib/filter.d.ts +2 -2
- package/dist/lib/filter.d.ts.map +1 -1
- package/dist/lib/{k8s/types.d.ts → k8s.d.ts} +14 -25
- package/dist/lib/k8s.d.ts.map +1 -0
- package/dist/lib/metrics.d.ts +12 -12
- package/dist/lib/metrics.d.ts.map +1 -1
- package/dist/lib/module.d.ts +25 -4
- package/dist/lib/module.d.ts.map +1 -1
- package/dist/lib/mutate-processor.d.ts +3 -3
- package/dist/lib/mutate-processor.d.ts.map +1 -1
- package/dist/lib/mutate-request.d.ts +11 -10
- package/dist/lib/mutate-request.d.ts.map +1 -1
- package/dist/lib/storage.d.ts +56 -0
- package/dist/lib/storage.d.ts.map +1 -0
- package/dist/lib/tls.d.ts.map +1 -0
- package/dist/lib/types.d.ts +28 -48
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/validate-processor.d.ts +2 -2
- package/dist/lib/validate-processor.d.ts.map +1 -1
- package/dist/lib/validate-request.d.ts +9 -8
- package/dist/lib/validate-request.d.ts.map +1 -1
- package/dist/lib/watch-processor.d.ts +3 -0
- package/dist/lib/watch-processor.d.ts.map +1 -0
- package/dist/lib.d.ts +3 -7
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +484 -807
- package/dist/lib.js.map +4 -4
- package/package.json +16 -20
- package/src/lib/assets/deploy.ts +69 -127
- package/src/lib/assets/destroy.ts +33 -0
- package/src/lib/assets/index.ts +8 -14
- package/src/lib/assets/networking.ts +28 -5
- package/src/lib/assets/pods.ts +130 -11
- package/src/lib/assets/rbac.ts +42 -4
- package/src/lib/assets/store.ts +49 -0
- package/src/lib/assets/webhooks.ts +2 -2
- package/src/lib/assets/yaml.ts +13 -3
- package/src/lib/capability.ts +69 -14
- package/src/lib/{controller.ts → controller/index.ts} +25 -23
- package/src/lib/controller/store.ts +197 -0
- package/src/lib/filter.ts +2 -2
- package/src/lib/{k8s/types.ts → k8s.ts} +15 -26
- package/src/lib/metrics.ts +22 -38
- package/src/lib/module.ts +47 -10
- package/src/lib/mutate-processor.ts +6 -6
- package/src/lib/mutate-request.ts +18 -26
- package/src/lib/storage.ts +128 -0
- package/src/lib/types.ts +30 -53
- package/src/lib/validate-processor.ts +5 -4
- package/src/lib/validate-request.ts +15 -19
- package/src/lib/watch-processor.ts +55 -0
- package/src/lib.ts +4 -8
- package/src/templates/.eslintrc.template.json +18 -0
- package/src/templates/capabilities/hello-pepr.ts +54 -5
- package/src/templates/package.json +1 -0
- package/dist/lib/controller.d.ts +0 -10
- package/dist/lib/controller.d.ts.map +0 -1
- package/dist/lib/fetch.d.ts +0 -23
- package/dist/lib/fetch.d.ts.map +0 -1
- package/dist/lib/k8s/index.d.ts +0 -7
- package/dist/lib/k8s/index.d.ts.map +0 -1
- package/dist/lib/k8s/kinds.d.ts +0 -12
- package/dist/lib/k8s/kinds.d.ts.map +0 -1
- package/dist/lib/k8s/tls.d.ts.map +0 -1
- package/dist/lib/k8s/types.d.ts.map +0 -1
- package/dist/lib/k8s/upstream.d.ts +0 -4
- package/dist/lib/k8s/upstream.d.ts.map +0 -1
- package/jest.config.json +0 -4
- package/journey/before.ts +0 -21
- package/journey/k8s.ts +0 -100
- package/journey/pepr-build.ts +0 -69
- package/journey/pepr-deploy.ts +0 -174
- package/journey/pepr-dev.ts +0 -155
- package/journey/pepr-format.ts +0 -13
- package/journey/pepr-init.ts +0 -12
- package/src/lib/fetch.ts +0 -76
- package/src/lib/k8s/index.ts +0 -14
- package/src/lib/k8s/kinds.ts +0 -531
- package/src/lib/k8s/upstream.ts +0 -53
- /package/dist/lib/{k8s/tls.d.ts → tls.d.ts} +0 -0
- /package/src/lib/{k8s/tls.ts → tls.ts} +0 -0
package/dist/lib.js
CHANGED
|
@@ -31,565 +31,26 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var lib_exports = {};
|
|
32
32
|
__export(lib_exports, {
|
|
33
33
|
Capability: () => Capability,
|
|
34
|
+
K8s: () => import_kubernetes_fluent_client5.K8s,
|
|
34
35
|
Log: () => logger_default,
|
|
35
36
|
PeprModule: () => PeprModule,
|
|
36
37
|
PeprMutateRequest: () => PeprMutateRequest,
|
|
37
38
|
PeprUtils: () => utils_exports,
|
|
38
39
|
PeprValidateRequest: () => PeprValidateRequest,
|
|
39
40
|
R: () => R,
|
|
40
|
-
RegisterKind: () => RegisterKind,
|
|
41
|
-
a: () =>
|
|
42
|
-
fetch: () => fetch,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
k8s: () => k8s
|
|
41
|
+
RegisterKind: () => import_kubernetes_fluent_client5.RegisterKind,
|
|
42
|
+
a: () => import_kubernetes_fluent_client5.kind,
|
|
43
|
+
fetch: () => import_kubernetes_fluent_client5.fetch,
|
|
44
|
+
fetchStatus: () => import_kubernetes_fluent_client5.fetchStatus,
|
|
45
|
+
kind: () => import_kubernetes_fluent_client5.kind
|
|
46
46
|
});
|
|
47
47
|
module.exports = __toCommonJS(lib_exports);
|
|
48
|
-
var
|
|
49
|
-
var import_http_status_codes2 = require("http-status-codes");
|
|
48
|
+
var import_kubernetes_fluent_client5 = require("kubernetes-fluent-client");
|
|
50
49
|
var R = __toESM(require("ramda"));
|
|
51
50
|
|
|
52
51
|
// src/lib/capability.ts
|
|
53
|
-
var
|
|
54
|
-
|
|
55
|
-
// src/lib/k8s/upstream.ts
|
|
56
|
-
var upstream_exports = {};
|
|
57
|
-
__export(upstream_exports, {
|
|
58
|
-
APIService: () => import_client_node2.V1APIService,
|
|
59
|
-
CSIDriver: () => import_client_node2.V1CSIDriver,
|
|
60
|
-
CSIStorageCapacity: () => import_client_node2.V1CSIStorageCapacity,
|
|
61
|
-
CertificateSigningRequest: () => import_client_node2.V1CertificateSigningRequest,
|
|
62
|
-
ClusterRole: () => import_client_node2.V1ClusterRole,
|
|
63
|
-
ClusterRoleBinding: () => import_client_node2.V1ClusterRoleBinding,
|
|
64
|
-
ConfigMap: () => import_client_node2.V1ConfigMap,
|
|
65
|
-
ControllerRevision: () => import_client_node2.V1ControllerRevision,
|
|
66
|
-
CronJob: () => import_client_node2.V1CronJob,
|
|
67
|
-
CustomResourceDefinition: () => import_client_node2.V1CustomResourceDefinition,
|
|
68
|
-
DaemonSet: () => import_client_node2.V1DaemonSet,
|
|
69
|
-
Deployment: () => import_client_node2.V1Deployment,
|
|
70
|
-
EndpointSlice: () => import_client_node2.V1EndpointSlice,
|
|
71
|
-
GenericKind: () => GenericKind,
|
|
72
|
-
HorizontalPodAutoscaler: () => import_client_node2.V1HorizontalPodAutoscaler,
|
|
73
|
-
Ingress: () => import_client_node2.V1Ingress,
|
|
74
|
-
IngressClass: () => import_client_node2.V1IngressClass,
|
|
75
|
-
Job: () => import_client_node2.V1Job,
|
|
76
|
-
LimitRange: () => import_client_node2.V1LimitRange,
|
|
77
|
-
LocalSubjectAccessReview: () => import_client_node2.V1LocalSubjectAccessReview,
|
|
78
|
-
MutatingWebhookConfiguration: () => import_client_node2.V1MutatingWebhookConfiguration,
|
|
79
|
-
Namespace: () => import_client_node2.V1Namespace,
|
|
80
|
-
NetworkPolicy: () => import_client_node2.V1NetworkPolicy,
|
|
81
|
-
Node: () => import_client_node2.V1Node,
|
|
82
|
-
PersistentVolume: () => import_client_node2.V1PersistentVolume,
|
|
83
|
-
PersistentVolumeClaim: () => import_client_node2.V1PersistentVolumeClaim,
|
|
84
|
-
Pod: () => import_client_node2.V1Pod,
|
|
85
|
-
PodDisruptionBudget: () => import_client_node2.V1PodDisruptionBudget,
|
|
86
|
-
PodTemplate: () => import_client_node2.V1PodTemplate,
|
|
87
|
-
ReplicaSet: () => import_client_node2.V1ReplicaSet,
|
|
88
|
-
ReplicationController: () => import_client_node2.V1ReplicationController,
|
|
89
|
-
ResourceQuota: () => import_client_node2.V1ResourceQuota,
|
|
90
|
-
Role: () => import_client_node2.V1Role,
|
|
91
|
-
RoleBinding: () => import_client_node2.V1RoleBinding,
|
|
92
|
-
RuntimeClass: () => import_client_node2.V1RuntimeClass,
|
|
93
|
-
Secret: () => import_client_node2.V1Secret,
|
|
94
|
-
SelfSubjectAccessReview: () => import_client_node2.V1SelfSubjectAccessReview,
|
|
95
|
-
SelfSubjectRulesReview: () => import_client_node2.V1SelfSubjectRulesReview,
|
|
96
|
-
Service: () => import_client_node2.V1Service,
|
|
97
|
-
ServiceAccount: () => import_client_node2.V1ServiceAccount,
|
|
98
|
-
StatefulSet: () => import_client_node2.V1StatefulSet,
|
|
99
|
-
StorageClass: () => import_client_node2.V1StorageClass,
|
|
100
|
-
SubjectAccessReview: () => import_client_node2.V1SubjectAccessReview,
|
|
101
|
-
TokenReview: () => import_client_node2.V1TokenReview,
|
|
102
|
-
ValidatingWebhookConfiguration: () => import_client_node2.V1ValidatingWebhookConfiguration,
|
|
103
|
-
VolumeAttachment: () => import_client_node2.V1VolumeAttachment
|
|
104
|
-
});
|
|
105
|
-
var import_client_node2 = require("@kubernetes/client-node");
|
|
106
|
-
|
|
107
|
-
// src/lib/k8s/types.ts
|
|
108
|
-
var import_client_node = require("@kubernetes/client-node");
|
|
109
|
-
var GenericKind = class {
|
|
110
|
-
apiVersion;
|
|
111
|
-
kind;
|
|
112
|
-
metadata;
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
// src/lib/k8s/kinds.ts
|
|
116
|
-
var gvkMap = {
|
|
117
|
-
/**
|
|
118
|
-
* Represents a K8s ClusterRole resource.
|
|
119
|
-
* ClusterRole is a set of permissions that can be bound to a user or group in a cluster-wide scope.
|
|
120
|
-
* @see {@link https://kubernetes.io/docs/reference/access-authn-authz/rbac/#role-and-clusterrole}
|
|
121
|
-
*/
|
|
122
|
-
V1ClusterRole: {
|
|
123
|
-
kind: "ClusterRole",
|
|
124
|
-
version: "v1",
|
|
125
|
-
group: "rbac.authorization.k8s.io"
|
|
126
|
-
},
|
|
127
|
-
/**
|
|
128
|
-
* Represents a K8s ClusterRoleBinding resource.
|
|
129
|
-
* ClusterRoleBinding binds a ClusterRole to a user or group in a cluster-wide scope.
|
|
130
|
-
* @see {@link https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-and-clusterrolebinding}
|
|
131
|
-
*/
|
|
132
|
-
V1ClusterRoleBinding: {
|
|
133
|
-
kind: "ClusterRoleBinding",
|
|
134
|
-
version: "v1",
|
|
135
|
-
group: "rbac.authorization.k8s.io"
|
|
136
|
-
},
|
|
137
|
-
/**
|
|
138
|
-
* Represents a K8s Role resource.
|
|
139
|
-
* Role is a set of permissions that can be bound to a user or group in a namespace scope.
|
|
140
|
-
* @see {@link https://kubernetes.io/docs/reference/access-authn-authz/rbac/#role-and-clusterrole}
|
|
141
|
-
*/
|
|
142
|
-
V1Role: {
|
|
143
|
-
kind: "Role",
|
|
144
|
-
version: "v1",
|
|
145
|
-
group: "rbac.authorization.k8s.io"
|
|
146
|
-
},
|
|
147
|
-
/**
|
|
148
|
-
* Represents a K8s RoleBinding resource.
|
|
149
|
-
* RoleBinding binds a Role to a user or group in a namespace scope.
|
|
150
|
-
* @see {@link https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-and-clusterrolebinding}
|
|
151
|
-
*/
|
|
152
|
-
V1RoleBinding: {
|
|
153
|
-
kind: "RoleBinding",
|
|
154
|
-
version: "v1",
|
|
155
|
-
group: "rbac.authorization.k8s.io"
|
|
156
|
-
},
|
|
157
|
-
/**
|
|
158
|
-
* Represents a K8s ConfigMap resource.
|
|
159
|
-
* ConfigMap holds configuration data for pods to consume.
|
|
160
|
-
* @see {@link https://kubernetes.io/docs/concepts/configuration/configmap/}
|
|
161
|
-
*/
|
|
162
|
-
V1ConfigMap: {
|
|
163
|
-
kind: "ConfigMap",
|
|
164
|
-
version: "v1",
|
|
165
|
-
group: ""
|
|
166
|
-
},
|
|
167
|
-
/**
|
|
168
|
-
* Represents a K8s Endpoints resource.
|
|
169
|
-
* Endpoints expose a service's IP addresses and ports to other resources.
|
|
170
|
-
* @see {@link https://kubernetes.io/docs/concepts/services-networking/service/#endpoints}
|
|
171
|
-
*/
|
|
172
|
-
V1Endpoint: {
|
|
173
|
-
kind: "Endpoints",
|
|
174
|
-
version: "v1",
|
|
175
|
-
group: "",
|
|
176
|
-
plural: "endpoints"
|
|
177
|
-
},
|
|
178
|
-
/**
|
|
179
|
-
* Represents a K8s LimitRange resource.
|
|
180
|
-
* LimitRange enforces constraints on the resource consumption of objects in a namespace.
|
|
181
|
-
* @see {@link https://kubernetes.io/docs/concepts/policy/limit-range/}
|
|
182
|
-
*/
|
|
183
|
-
V1LimitRange: {
|
|
184
|
-
kind: "LimitRange",
|
|
185
|
-
version: "v1",
|
|
186
|
-
group: ""
|
|
187
|
-
},
|
|
188
|
-
/**
|
|
189
|
-
* Represents a K8s Namespace resource.
|
|
190
|
-
* Namespace is a way to divide cluster resources between multiple users.
|
|
191
|
-
* @see {@link https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/}
|
|
192
|
-
*/
|
|
193
|
-
V1Namespace: {
|
|
194
|
-
kind: "Namespace",
|
|
195
|
-
version: "v1",
|
|
196
|
-
group: ""
|
|
197
|
-
},
|
|
198
|
-
/**
|
|
199
|
-
* Represents a K8s Node resource.
|
|
200
|
-
* Node is a worker machine in Kubernetes.
|
|
201
|
-
* @see {@link https://kubernetes.io/docs/concepts/architecture/nodes/}
|
|
202
|
-
*/
|
|
203
|
-
V1Node: {
|
|
204
|
-
kind: "Node",
|
|
205
|
-
version: "v1",
|
|
206
|
-
group: ""
|
|
207
|
-
},
|
|
208
|
-
/**
|
|
209
|
-
* Represents a K8s PersistentVolumeClaim resource.
|
|
210
|
-
* PersistentVolumeClaim is a user's request for and claim to a persistent volume.
|
|
211
|
-
* @see {@link https://kubernetes.io/docs/concepts/storage/persistent-volumes/#persistentvolumeclaims}
|
|
212
|
-
*/
|
|
213
|
-
V1PersistentVolumeClaim: {
|
|
214
|
-
kind: "PersistentVolumeClaim",
|
|
215
|
-
version: "v1",
|
|
216
|
-
group: ""
|
|
217
|
-
},
|
|
218
|
-
/**
|
|
219
|
-
* Represents a K8s PersistentVolume resource.
|
|
220
|
-
* PersistentVolume is a piece of storage in the cluster that has been provisioned by an administrator.
|
|
221
|
-
* @see {@link https://kubernetes.io/docs/concepts/storage/persistent-volumes/}
|
|
222
|
-
*/
|
|
223
|
-
V1PersistentVolume: {
|
|
224
|
-
kind: "PersistentVolume",
|
|
225
|
-
version: "v1",
|
|
226
|
-
group: ""
|
|
227
|
-
},
|
|
228
|
-
/**
|
|
229
|
-
* Represents a K8s Pod resource.
|
|
230
|
-
* Pod is the smallest and simplest unit in the Kubernetes object model.
|
|
231
|
-
* @see {@link https://kubernetes.io/docs/concepts/workloads/pods/}
|
|
232
|
-
*/
|
|
233
|
-
V1Pod: {
|
|
234
|
-
kind: "Pod",
|
|
235
|
-
version: "v1",
|
|
236
|
-
group: ""
|
|
237
|
-
},
|
|
238
|
-
/**
|
|
239
|
-
* Represents a K8s PodTemplate resource.
|
|
240
|
-
* PodTemplate is an object that describes the pod that will be created from a higher level abstraction.
|
|
241
|
-
* @see {@link https://kubernetes.io/docs/concepts/workloads/controllers/#pod-template}
|
|
242
|
-
*/
|
|
243
|
-
V1PodTemplate: {
|
|
244
|
-
kind: "PodTemplate",
|
|
245
|
-
version: "v1",
|
|
246
|
-
group: ""
|
|
247
|
-
},
|
|
248
|
-
/**
|
|
249
|
-
* Represents a K8s ReplicationController resource.
|
|
250
|
-
* ReplicationController ensures that a specified number of pod replicas are running at any given time.
|
|
251
|
-
* @see {@link https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/}
|
|
252
|
-
*/
|
|
253
|
-
V1ReplicationController: {
|
|
254
|
-
kind: "ReplicationController",
|
|
255
|
-
version: "v1",
|
|
256
|
-
group: ""
|
|
257
|
-
},
|
|
258
|
-
/**
|
|
259
|
-
* Represents a K8s ResourceQuota resource.
|
|
260
|
-
* ResourceQuota provides constraints that limit resource consumption per namespace.
|
|
261
|
-
* @see {@link https://kubernetes.io/docs/concepts/policy/resource-quotas/}
|
|
262
|
-
*/
|
|
263
|
-
V1ResourceQuota: {
|
|
264
|
-
kind: "ResourceQuota",
|
|
265
|
-
version: "v1",
|
|
266
|
-
group: ""
|
|
267
|
-
},
|
|
268
|
-
/**
|
|
269
|
-
* Represents a K8s Secret resource.
|
|
270
|
-
* Secret holds secret data of a certain type.
|
|
271
|
-
* @see {@link https://kubernetes.io/docs/concepts/configuration/secret/}
|
|
272
|
-
*/
|
|
273
|
-
V1Secret: {
|
|
274
|
-
kind: "Secret",
|
|
275
|
-
version: "v1",
|
|
276
|
-
group: ""
|
|
277
|
-
},
|
|
278
|
-
/**
|
|
279
|
-
* Represents a K8s ServiceAccount resource.
|
|
280
|
-
* ServiceAccount is an identity that processes in a pod can use to access the Kubernetes API.
|
|
281
|
-
* @see {@link https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/}
|
|
282
|
-
*/
|
|
283
|
-
V1ServiceAccount: {
|
|
284
|
-
kind: "ServiceAccount",
|
|
285
|
-
version: "v1",
|
|
286
|
-
group: ""
|
|
287
|
-
},
|
|
288
|
-
/**
|
|
289
|
-
* Represents a K8s Service resource.
|
|
290
|
-
* Service is an abstraction which defines a logical set of Pods and a policy by which to access them.
|
|
291
|
-
* @see {@link https://kubernetes.io/docs/concepts/services-networking/service/}
|
|
292
|
-
*/
|
|
293
|
-
V1Service: {
|
|
294
|
-
kind: "Service",
|
|
295
|
-
version: "v1",
|
|
296
|
-
group: ""
|
|
297
|
-
},
|
|
298
|
-
/**
|
|
299
|
-
* Represents a K8s MutatingWebhookConfiguration resource.
|
|
300
|
-
* MutatingWebhookConfiguration configures a mutating admission webhook.
|
|
301
|
-
* @see {@link https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#configure-admission-webhooks-on-the-fly}
|
|
302
|
-
*/
|
|
303
|
-
V1MutatingWebhookConfiguration: {
|
|
304
|
-
kind: "MutatingWebhookConfiguration",
|
|
305
|
-
version: "v1",
|
|
306
|
-
group: "admissionregistration.k8s.io"
|
|
307
|
-
},
|
|
308
|
-
/**
|
|
309
|
-
* Represents a K8s ValidatingWebhookConfiguration resource.
|
|
310
|
-
* ValidatingWebhookConfiguration configures a validating admission webhook.
|
|
311
|
-
* @see {@link https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#configure-admission-webhooks-on-the-fly}
|
|
312
|
-
*/
|
|
313
|
-
V1ValidatingWebhookConfiguration: {
|
|
314
|
-
kind: "ValidatingWebhookConfiguration",
|
|
315
|
-
version: "v1",
|
|
316
|
-
group: "admissionregistration.k8s.io"
|
|
317
|
-
},
|
|
318
|
-
/**
|
|
319
|
-
* Represents a K8s CustomResourceDefinition resource.
|
|
320
|
-
* CustomResourceDefinition is a custom resource in a Kubernetes cluster.
|
|
321
|
-
* @see {@link https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/}
|
|
322
|
-
*/
|
|
323
|
-
V1CustomResourceDefinition: {
|
|
324
|
-
kind: "CustomResourceDefinition",
|
|
325
|
-
version: "v1",
|
|
326
|
-
group: "apiextensions.k8s.io"
|
|
327
|
-
},
|
|
328
|
-
/**
|
|
329
|
-
* Represents a K8s APIService resource.
|
|
330
|
-
* APIService represents a server for a particular API version and group.
|
|
331
|
-
* @see {@link https://kubernetes.io/docs/tasks/access-kubernetes-api/setup-extension-api-server/}
|
|
332
|
-
*/
|
|
333
|
-
V1APIService: {
|
|
334
|
-
kind: "APIService",
|
|
335
|
-
version: "v1",
|
|
336
|
-
group: "apiregistration.k8s.io"
|
|
337
|
-
},
|
|
338
|
-
/**
|
|
339
|
-
* Represents a K8s ControllerRevision resource.
|
|
340
|
-
* ControllerRevision is used to manage the history of a StatefulSet or DaemonSet.
|
|
341
|
-
* @see {@link https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#revision-history}
|
|
342
|
-
*/
|
|
343
|
-
V1ControllerRevision: {
|
|
344
|
-
kind: "ControllerRevision",
|
|
345
|
-
version: "v1",
|
|
346
|
-
group: "apps"
|
|
347
|
-
},
|
|
348
|
-
/**
|
|
349
|
-
* Represents a K8s DaemonSet resource.
|
|
350
|
-
* DaemonSet ensures that all (or some) nodes run a copy of a Pod.
|
|
351
|
-
* @see {@link https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/}
|
|
352
|
-
*/
|
|
353
|
-
V1DaemonSet: {
|
|
354
|
-
kind: "DaemonSet",
|
|
355
|
-
version: "v1",
|
|
356
|
-
group: "apps"
|
|
357
|
-
},
|
|
358
|
-
/**
|
|
359
|
-
* Represents a K8s Deployment resource.
|
|
360
|
-
* Deployment provides declarative updates for Pods and ReplicaSets.
|
|
361
|
-
* @see {@link https://kubernetes.io/docs/concepts/workloads/controllers/deployment/}
|
|
362
|
-
*/
|
|
363
|
-
V1Deployment: {
|
|
364
|
-
kind: "Deployment",
|
|
365
|
-
version: "v1",
|
|
366
|
-
group: "apps"
|
|
367
|
-
},
|
|
368
|
-
/**
|
|
369
|
-
* Represents a K8s ReplicaSet resource.
|
|
370
|
-
* ReplicaSet ensures that a specified number of pod replicas are running at any given time.
|
|
371
|
-
* @see {@link https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/}
|
|
372
|
-
*/
|
|
373
|
-
V1ReplicaSet: {
|
|
374
|
-
kind: "ReplicaSet",
|
|
375
|
-
version: "v1",
|
|
376
|
-
group: "apps"
|
|
377
|
-
},
|
|
378
|
-
/**
|
|
379
|
-
* Represents a K8s StatefulSet resource.
|
|
380
|
-
* StatefulSet is used to manage stateful applications.
|
|
381
|
-
* @see {@link https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/}
|
|
382
|
-
*/
|
|
383
|
-
V1StatefulSet: {
|
|
384
|
-
kind: "StatefulSet",
|
|
385
|
-
version: "v1",
|
|
386
|
-
group: "apps"
|
|
387
|
-
},
|
|
388
|
-
/**
|
|
389
|
-
* Represents a K8s TokenReview resource.
|
|
390
|
-
* TokenReview attempts to authenticate a token to a known user.
|
|
391
|
-
* @see {@link https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#tokenreview-v1-authentication-k8s-io}
|
|
392
|
-
*/
|
|
393
|
-
V1TokenReview: {
|
|
394
|
-
kind: "TokenReview",
|
|
395
|
-
version: "v1",
|
|
396
|
-
group: "authentication.k8s.io"
|
|
397
|
-
},
|
|
398
|
-
/**
|
|
399
|
-
* Represents a K8s LocalSubjectAccessReview resource.
|
|
400
|
-
* LocalSubjectAccessReview checks whether a specific user can perform a specific action in a specific namespace.
|
|
401
|
-
* @see {@link https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#localsubjectaccessreview-v1-authorization-k8s-io}
|
|
402
|
-
*/
|
|
403
|
-
V1LocalSubjectAccessReview: {
|
|
404
|
-
kind: "LocalSubjectAccessReview",
|
|
405
|
-
version: "v1",
|
|
406
|
-
group: "authorization.k8s.io"
|
|
407
|
-
},
|
|
408
|
-
/**
|
|
409
|
-
* Represents a K8s SelfSubjectAccessReview resource.
|
|
410
|
-
* SelfSubjectAccessReview checks whether the current user can perform a specific action.
|
|
411
|
-
* @see {@link https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#selfsubjectaccessreview-v1-authorization-k8s-io}
|
|
412
|
-
*/
|
|
413
|
-
V1SelfSubjectAccessReview: {
|
|
414
|
-
kind: "SelfSubjectAccessReview",
|
|
415
|
-
version: "v1",
|
|
416
|
-
group: "authorization.k8s.io"
|
|
417
|
-
},
|
|
418
|
-
/**
|
|
419
|
-
* Represents a K8s SelfSubjectRulesReview resource.
|
|
420
|
-
* SelfSubjectRulesReview lists the permissions a specific user has within a namespace.
|
|
421
|
-
* @see {@link https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#selfsubjectrulesreview-v1-authorization-k8s-io}
|
|
422
|
-
*/
|
|
423
|
-
V1SelfSubjectRulesReview: {
|
|
424
|
-
kind: "SelfSubjectRulesReview",
|
|
425
|
-
version: "v1",
|
|
426
|
-
group: "authorization.k8s.io"
|
|
427
|
-
},
|
|
428
|
-
/**
|
|
429
|
-
* Represents a K8s SubjectAccessReview resource.
|
|
430
|
-
* SubjectAccessReview checks whether a specific user can perform a specific action.
|
|
431
|
-
* @see {@link https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#subjectaccessreview-v1-authorization-k8s-io}
|
|
432
|
-
*/
|
|
433
|
-
V1SubjectAccessReview: {
|
|
434
|
-
kind: "SubjectAccessReview",
|
|
435
|
-
version: "v1",
|
|
436
|
-
group: "authorization.k8s.io"
|
|
437
|
-
},
|
|
438
|
-
/**
|
|
439
|
-
* Represents a K8s HorizontalPodAutoscaler resource.
|
|
440
|
-
* HorizontalPodAutoscaler automatically scales the number of Pods in a replication controller, deployment, or replica set.
|
|
441
|
-
* @see {@link https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/}
|
|
442
|
-
*/
|
|
443
|
-
V1HorizontalPodAutoscaler: {
|
|
444
|
-
kind: "HorizontalPodAutoscaler",
|
|
445
|
-
version: "v2",
|
|
446
|
-
group: "autoscaling"
|
|
447
|
-
},
|
|
448
|
-
/**
|
|
449
|
-
* Represents a K8s CronJob resource.
|
|
450
|
-
* CronJob manages time-based jobs, specifically those that run periodically and complete after a successful execution.
|
|
451
|
-
* @see {@link https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/}
|
|
452
|
-
*/
|
|
453
|
-
V1CronJob: {
|
|
454
|
-
kind: "CronJob",
|
|
455
|
-
version: "v1",
|
|
456
|
-
group: "batch"
|
|
457
|
-
},
|
|
458
|
-
/**
|
|
459
|
-
* Represents a K8s Job resource.
|
|
460
|
-
* Job represents the configuration of a single job.
|
|
461
|
-
* @see {@link https://kubernetes.io/docs/concepts/workloads/controllers/job/}
|
|
462
|
-
*/
|
|
463
|
-
V1Job: {
|
|
464
|
-
kind: "Job",
|
|
465
|
-
version: "v1",
|
|
466
|
-
group: "batch"
|
|
467
|
-
},
|
|
468
|
-
/**
|
|
469
|
-
* Represents a K8s CertificateSigningRequest resource.
|
|
470
|
-
* CertificateSigningRequest represents a certificate signing request.
|
|
471
|
-
* @see {@link https://kubernetes.io/docs/reference/access-authn-authz/certificate-signing-requests/}
|
|
472
|
-
*/
|
|
473
|
-
V1CertificateSigningRequest: {
|
|
474
|
-
kind: "CertificateSigningRequest",
|
|
475
|
-
version: "v1",
|
|
476
|
-
group: "certificates.k8s.io"
|
|
477
|
-
},
|
|
478
|
-
/**
|
|
479
|
-
* Represents a K8s EndpointSlice resource.
|
|
480
|
-
* EndpointSlice represents a scalable set of network endpoints for a Kubernetes Service.
|
|
481
|
-
* @see {@link https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/}
|
|
482
|
-
*/
|
|
483
|
-
V1EndpointSlice: {
|
|
484
|
-
kind: "EndpointSlice",
|
|
485
|
-
version: "v1",
|
|
486
|
-
group: "discovery.k8s.io"
|
|
487
|
-
},
|
|
488
|
-
/**
|
|
489
|
-
* Represents a K8s IngressClass resource.
|
|
490
|
-
* IngressClass represents the class of the Ingress, referenced by the Ingress spec.
|
|
491
|
-
* @see {@link https://kubernetes.io/docs/concepts/services-networking/ingress/}
|
|
492
|
-
*/
|
|
493
|
-
V1IngressClass: {
|
|
494
|
-
kind: "IngressClass",
|
|
495
|
-
version: "v1",
|
|
496
|
-
group: "networking.k8s.io"
|
|
497
|
-
},
|
|
498
|
-
/**
|
|
499
|
-
* Represents a K8s Ingress resource.
|
|
500
|
-
* Ingress exposes HTTP and HTTPS routes from outside the cluster to services within the cluster.
|
|
501
|
-
* @see {@link https://kubernetes.io/docs/concepts/services-networking/ingress/}
|
|
502
|
-
*/
|
|
503
|
-
V1Ingress: {
|
|
504
|
-
kind: "Ingress",
|
|
505
|
-
version: "v1",
|
|
506
|
-
group: "networking.k8s.io",
|
|
507
|
-
plural: "ingresses"
|
|
508
|
-
},
|
|
509
|
-
/**
|
|
510
|
-
* Represents a K8s NetworkPolicy resource.
|
|
511
|
-
* NetworkPolicy defines a set of rules for how pods communicate with each other.
|
|
512
|
-
* @see {@link https://kubernetes.io/docs/concepts/services-networking/network-policies/}
|
|
513
|
-
*/
|
|
514
|
-
V1NetworkPolicy: {
|
|
515
|
-
kind: "NetworkPolicy",
|
|
516
|
-
version: "v1",
|
|
517
|
-
group: "networking.k8s.io"
|
|
518
|
-
},
|
|
519
|
-
/**
|
|
520
|
-
* Represents a K8s RuntimeClass resource.
|
|
521
|
-
* RuntimeClass is a cluster-scoped resource that surfaces container runtime properties to the control plane.
|
|
522
|
-
* @see {@link https://kubernetes.io/docs/concepts/containers/runtime-class/}
|
|
523
|
-
*/
|
|
524
|
-
V1RuntimeClass: {
|
|
525
|
-
kind: "RuntimeClass",
|
|
526
|
-
version: "v1",
|
|
527
|
-
group: "node.k8s.io"
|
|
528
|
-
},
|
|
529
|
-
/**
|
|
530
|
-
* Represents a K8s PodDisruptionBudget resource.
|
|
531
|
-
* PodDisruptionBudget is an API object that limits the number of pods of a replicated application that are down simultaneously.
|
|
532
|
-
* @see {@link https://kubernetes.io/docs/concepts/workloads/pods/disruptions/}
|
|
533
|
-
*/
|
|
534
|
-
V1PodDisruptionBudget: {
|
|
535
|
-
kind: "PodDisruptionBudget",
|
|
536
|
-
version: "v1",
|
|
537
|
-
group: "policy"
|
|
538
|
-
},
|
|
539
|
-
/**
|
|
540
|
-
* Represents a K8s VolumeAttachment resource.
|
|
541
|
-
* VolumeAttachment captures the intent to attach or detach the specified volume to/from the specified node.
|
|
542
|
-
* @see {@link https://kubernetes.io/docs/concepts/storage/storage-classes/}
|
|
543
|
-
*/
|
|
544
|
-
V1VolumeAttachment: {
|
|
545
|
-
kind: "VolumeAttachment",
|
|
546
|
-
version: "v1",
|
|
547
|
-
group: "storage.k8s.io"
|
|
548
|
-
},
|
|
549
|
-
/**
|
|
550
|
-
* Represents a K8s CSIDriver resource.
|
|
551
|
-
* CSIDriver captures information about a Container Storage Interface (CSI) volume driver.
|
|
552
|
-
* @see {@link https://kubernetes.io/docs/concepts/storage/volumes/}
|
|
553
|
-
*/
|
|
554
|
-
V1CSIDriver: {
|
|
555
|
-
kind: "CSIDriver",
|
|
556
|
-
version: "v1",
|
|
557
|
-
group: "storage.k8s.io"
|
|
558
|
-
},
|
|
559
|
-
/**
|
|
560
|
-
* Represents a K8s CSIStorageCapacity resource.
|
|
561
|
-
* CSIStorageCapacity stores the reported storage capacity of a CSI node or storage class.
|
|
562
|
-
* @see {@link https://kubernetes.io/docs/concepts/storage/csi/}
|
|
563
|
-
*/
|
|
564
|
-
V1CSIStorageCapacity: {
|
|
565
|
-
kind: "CSIStorageCapacity",
|
|
566
|
-
version: "v1",
|
|
567
|
-
group: "storage.k8s.io"
|
|
568
|
-
},
|
|
569
|
-
/**
|
|
570
|
-
* Represents a K8s StorageClass resource.
|
|
571
|
-
* StorageClass is a cluster-scoped resource that provides a way for administrators to describe the classes of storage they offer.
|
|
572
|
-
* @see {@link https://kubernetes.io/docs/concepts/storage/storage-classes/}
|
|
573
|
-
*/
|
|
574
|
-
V1StorageClass: {
|
|
575
|
-
kind: "StorageClass",
|
|
576
|
-
version: "v1",
|
|
577
|
-
group: "storage.k8s.io"
|
|
578
|
-
}
|
|
579
|
-
};
|
|
580
|
-
function modelToGroupVersionKind(key) {
|
|
581
|
-
return gvkMap[key];
|
|
582
|
-
}
|
|
583
|
-
var RegisterKind = (model, groupVersionKind) => {
|
|
584
|
-
const name = model.name;
|
|
585
|
-
if (gvkMap[name]) {
|
|
586
|
-
throw new Error(`GVK ${name} already registered`);
|
|
587
|
-
}
|
|
588
|
-
gvkMap[name] = groupVersionKind;
|
|
589
|
-
};
|
|
590
|
-
|
|
591
|
-
// src/lib/k8s/index.ts
|
|
592
|
-
var isWatchMode = process.env.PEPR_WATCH_MODE === "true";
|
|
52
|
+
var import_kubernetes_fluent_client4 = require("kubernetes-fluent-client");
|
|
53
|
+
var import_ramda6 = require("ramda");
|
|
593
54
|
|
|
594
55
|
// src/lib/logger.ts
|
|
595
56
|
var import_pino = require("pino");
|
|
@@ -609,172 +70,10 @@ if (process.env.LOG_LEVEL) {
|
|
|
609
70
|
}
|
|
610
71
|
var logger_default = Log;
|
|
611
72
|
|
|
612
|
-
// src/lib/capability.ts
|
|
613
|
-
var Capability = class {
|
|
614
|
-
#name;
|
|
615
|
-
#description;
|
|
616
|
-
#namespaces;
|
|
617
|
-
#bindings = [];
|
|
618
|
-
get bindings() {
|
|
619
|
-
return this.#bindings;
|
|
620
|
-
}
|
|
621
|
-
get name() {
|
|
622
|
-
return this.#name;
|
|
623
|
-
}
|
|
624
|
-
get description() {
|
|
625
|
-
return this.#description;
|
|
626
|
-
}
|
|
627
|
-
get namespaces() {
|
|
628
|
-
return this.#namespaces || [];
|
|
629
|
-
}
|
|
630
|
-
constructor(cfg) {
|
|
631
|
-
this.#name = cfg.name;
|
|
632
|
-
this.#description = cfg.description;
|
|
633
|
-
this.#namespaces = cfg.namespaces;
|
|
634
|
-
this.When = this.When.bind(this);
|
|
635
|
-
logger_default.info(`Capability ${this.#name} registered`);
|
|
636
|
-
logger_default.debug(cfg);
|
|
637
|
-
}
|
|
638
|
-
/**
|
|
639
|
-
* The When method is used to register a capability action to be executed when a Kubernetes resource is
|
|
640
|
-
* processed by Pepr. The action will be executed if the resource matches the specified kind and any
|
|
641
|
-
* filters that are applied.
|
|
642
|
-
*
|
|
643
|
-
* @param model the KubernetesObject model to match
|
|
644
|
-
* @param kind if using a custom KubernetesObject not available in `a.*`, specify the GroupVersionKind
|
|
645
|
-
* @returns
|
|
646
|
-
*/
|
|
647
|
-
When(model, kind) {
|
|
648
|
-
const matchedKind = modelToGroupVersionKind(model.name);
|
|
649
|
-
if (!matchedKind && !kind) {
|
|
650
|
-
throw new Error(`Kind not specified for ${model.name}`);
|
|
651
|
-
}
|
|
652
|
-
const binding = {
|
|
653
|
-
// If the kind is not specified, use the matched kind from the model
|
|
654
|
-
kind: kind || matchedKind,
|
|
655
|
-
event: "*" /* Any */,
|
|
656
|
-
filters: {
|
|
657
|
-
name: "",
|
|
658
|
-
namespaces: [],
|
|
659
|
-
labels: {},
|
|
660
|
-
annotations: {}
|
|
661
|
-
}
|
|
662
|
-
};
|
|
663
|
-
const bindings = this.#bindings;
|
|
664
|
-
const prefix = `${this.#name}: ${model.name}`;
|
|
665
|
-
const commonChain = { WithLabel, WithAnnotation, Mutate, Validate };
|
|
666
|
-
const isNotEmpty = (value) => Object.keys(value).length > 0;
|
|
667
|
-
const log = (message, cbString) => {
|
|
668
|
-
const filteredObj = (0, import_ramda.pickBy)(isNotEmpty, binding.filters);
|
|
669
|
-
logger_default.info(`${message} configured for ${binding.event}`, prefix);
|
|
670
|
-
logger_default.info(filteredObj, prefix);
|
|
671
|
-
logger_default.debug(cbString, prefix);
|
|
672
|
-
};
|
|
673
|
-
function Validate(validateCallback) {
|
|
674
|
-
if (!isWatchMode) {
|
|
675
|
-
log("Validate Action", validateCallback.toString());
|
|
676
|
-
bindings.push({
|
|
677
|
-
...binding,
|
|
678
|
-
isValidate: true,
|
|
679
|
-
validateCallback
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
function Mutate(mutateCallback) {
|
|
684
|
-
if (!isWatchMode) {
|
|
685
|
-
log("Mutate Action", mutateCallback.toString());
|
|
686
|
-
bindings.push({
|
|
687
|
-
...binding,
|
|
688
|
-
isMutate: true,
|
|
689
|
-
mutateCallback
|
|
690
|
-
});
|
|
691
|
-
}
|
|
692
|
-
return { Validate };
|
|
693
|
-
}
|
|
694
|
-
function InNamespace(...namespaces) {
|
|
695
|
-
logger_default.debug(`Add namespaces filter ${namespaces}`, prefix);
|
|
696
|
-
binding.filters.namespaces.push(...namespaces);
|
|
697
|
-
return { ...commonChain, WithName };
|
|
698
|
-
}
|
|
699
|
-
function WithName(name) {
|
|
700
|
-
logger_default.debug(`Add name filter ${name}`, prefix);
|
|
701
|
-
binding.filters.name = name;
|
|
702
|
-
return commonChain;
|
|
703
|
-
}
|
|
704
|
-
function WithLabel(key, value = "") {
|
|
705
|
-
logger_default.debug(`Add label filter ${key}=${value}`, prefix);
|
|
706
|
-
binding.filters.labels[key] = value;
|
|
707
|
-
return commonChain;
|
|
708
|
-
}
|
|
709
|
-
function WithAnnotation(key, value = "") {
|
|
710
|
-
logger_default.debug(`Add annotation filter ${key}=${value}`, prefix);
|
|
711
|
-
binding.filters.annotations[key] = value;
|
|
712
|
-
return commonChain;
|
|
713
|
-
}
|
|
714
|
-
function bindEvent(event) {
|
|
715
|
-
binding.event = event;
|
|
716
|
-
return {
|
|
717
|
-
...commonChain,
|
|
718
|
-
InNamespace,
|
|
719
|
-
WithName
|
|
720
|
-
};
|
|
721
|
-
}
|
|
722
|
-
return {
|
|
723
|
-
IsCreatedOrUpdated: () => bindEvent("CREATEORUPDATE" /* CreateOrUpdate */),
|
|
724
|
-
IsCreated: () => bindEvent("CREATE" /* Create */),
|
|
725
|
-
IsUpdated: () => bindEvent("UPDATE" /* Update */),
|
|
726
|
-
IsDeleted: () => bindEvent("DELETE" /* Delete */)
|
|
727
|
-
};
|
|
728
|
-
}
|
|
729
|
-
};
|
|
730
|
-
|
|
731
|
-
// src/lib/fetch.ts
|
|
732
|
-
var import_http_status_codes = require("http-status-codes");
|
|
733
|
-
var import_node_fetch = __toESM(require("node-fetch"));
|
|
734
|
-
var fetchRaw = import_node_fetch.default;
|
|
735
|
-
async function fetch(url, init) {
|
|
736
|
-
let data = void 0;
|
|
737
|
-
try {
|
|
738
|
-
logger_default.debug(init, `Fetching ${url}`);
|
|
739
|
-
const resp = await fetchRaw(url, init);
|
|
740
|
-
const contentType = resp.headers.get("content-type") || "";
|
|
741
|
-
if (resp.ok) {
|
|
742
|
-
if (contentType.includes("application/json")) {
|
|
743
|
-
data = await resp.json();
|
|
744
|
-
} else {
|
|
745
|
-
data = await resp.text();
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
return {
|
|
749
|
-
data,
|
|
750
|
-
ok: resp.ok,
|
|
751
|
-
status: resp.status,
|
|
752
|
-
statusText: resp.statusText
|
|
753
|
-
};
|
|
754
|
-
} catch (e) {
|
|
755
|
-
if (e instanceof import_node_fetch.FetchError) {
|
|
756
|
-
logger_default.debug(`Fetch failed: ${e instanceof Error ? e.message : e}`);
|
|
757
|
-
const status = parseInt(e.code || "400");
|
|
758
|
-
return {
|
|
759
|
-
data,
|
|
760
|
-
ok: false,
|
|
761
|
-
status,
|
|
762
|
-
statusText: e.message
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
return {
|
|
766
|
-
data,
|
|
767
|
-
ok: false,
|
|
768
|
-
status: import_http_status_codes.StatusCodes.BAD_REQUEST,
|
|
769
|
-
statusText: "Unknown error"
|
|
770
|
-
};
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
|
|
774
73
|
// src/lib/module.ts
|
|
775
74
|
var import_ramda4 = require("ramda");
|
|
776
75
|
|
|
777
|
-
// src/lib/controller.ts
|
|
76
|
+
// src/lib/controller/index.ts
|
|
778
77
|
var import_express = __toESM(require("express"));
|
|
779
78
|
var import_fs = __toESM(require("fs"));
|
|
780
79
|
var import_https = __toESM(require("https"));
|
|
@@ -806,69 +105,55 @@ var MetricsCollector = class {
|
|
|
806
105
|
this.addSummary(this.#metricNames.mutate, "Mutation operation summary");
|
|
807
106
|
this.addSummary(this.#metricNames.validate, "Validation operation summary");
|
|
808
107
|
}
|
|
809
|
-
#getMetricName(name) {
|
|
810
|
-
|
|
811
|
-
}
|
|
812
|
-
#addMetric(collection, MetricType, name, help) {
|
|
108
|
+
#getMetricName = (name) => `${this.#prefix}_${name}`;
|
|
109
|
+
#addMetric = (collection, MetricType, name, help) => {
|
|
813
110
|
if (collection.has(this.#getMetricName(name))) {
|
|
814
111
|
logger_default.debug(`Metric for ${name} already exists`, loggingPrefix);
|
|
815
112
|
return;
|
|
816
113
|
}
|
|
817
|
-
this.incCounter = this.incCounter.bind(this);
|
|
818
|
-
this.error = this.error.bind(this);
|
|
819
|
-
this.alert = this.alert.bind(this);
|
|
820
|
-
this.observeStart = this.observeStart.bind(this);
|
|
821
|
-
this.observeEnd = this.observeEnd.bind(this);
|
|
822
|
-
this.getMetrics = this.getMetrics.bind(this);
|
|
823
114
|
const metric = new MetricType({
|
|
824
115
|
name: this.#getMetricName(name),
|
|
825
116
|
help,
|
|
826
117
|
registers: [this.#registry]
|
|
827
118
|
});
|
|
828
119
|
collection.set(this.#getMetricName(name), metric);
|
|
829
|
-
}
|
|
830
|
-
addCounter(name, help) {
|
|
120
|
+
};
|
|
121
|
+
addCounter = (name, help) => {
|
|
831
122
|
this.#addMetric(this.#counters, import_prom_client.default.Counter, name, help);
|
|
832
|
-
}
|
|
833
|
-
addSummary(name, help) {
|
|
123
|
+
};
|
|
124
|
+
addSummary = (name, help) => {
|
|
834
125
|
this.#addMetric(this.#summaries, import_prom_client.default.Summary, name, help);
|
|
835
|
-
}
|
|
836
|
-
incCounter(name) {
|
|
126
|
+
};
|
|
127
|
+
incCounter = (name) => {
|
|
837
128
|
this.#counters.get(this.#getMetricName(name))?.inc();
|
|
838
|
-
}
|
|
129
|
+
};
|
|
839
130
|
/**
|
|
840
131
|
* Increments the error counter.
|
|
841
132
|
*/
|
|
842
|
-
error()
|
|
843
|
-
this.incCounter(this.#metricNames.errors);
|
|
844
|
-
}
|
|
133
|
+
error = () => this.incCounter(this.#metricNames.errors);
|
|
845
134
|
/**
|
|
846
135
|
* Increments the alerts counter.
|
|
847
136
|
*/
|
|
848
|
-
alert()
|
|
849
|
-
this.incCounter(this.#metricNames.alerts);
|
|
850
|
-
}
|
|
851
|
-
/**
|
|
852
|
-
* Returns the current timestamp from performance.now() method. Useful for start timing an operation.
|
|
853
|
-
* @returns The timestamp.
|
|
854
|
-
*/
|
|
855
|
-
observeStart() {
|
|
856
|
-
return import_perf_hooks.performance.now();
|
|
857
|
-
}
|
|
137
|
+
alert = () => this.incCounter(this.#metricNames.alerts);
|
|
858
138
|
/**
|
|
859
139
|
* Observes the duration since the provided start time and updates the summary.
|
|
860
140
|
* @param startTime - The start time.
|
|
861
141
|
* @param name - The metrics summary to increment.
|
|
862
142
|
*/
|
|
863
|
-
observeEnd(startTime, name = this.#metricNames.mutate) {
|
|
143
|
+
observeEnd = (startTime, name = this.#metricNames.mutate) => {
|
|
864
144
|
this.#summaries.get(this.#getMetricName(name))?.observe(import_perf_hooks.performance.now() - startTime);
|
|
865
|
-
}
|
|
145
|
+
};
|
|
866
146
|
/**
|
|
867
147
|
* Fetches the current metrics from the registry.
|
|
868
148
|
* @returns The metrics.
|
|
869
149
|
*/
|
|
870
|
-
|
|
871
|
-
|
|
150
|
+
getMetrics = () => this.#registry.metrics();
|
|
151
|
+
/**
|
|
152
|
+
* Returns the current timestamp from performance.now() method. Useful for start timing an operation.
|
|
153
|
+
* @returns The timestamp.
|
|
154
|
+
*/
|
|
155
|
+
static observeStart() {
|
|
156
|
+
return import_perf_hooks.performance.now();
|
|
872
157
|
}
|
|
873
158
|
};
|
|
874
159
|
|
|
@@ -888,9 +173,20 @@ function ValidateError(error = "") {
|
|
|
888
173
|
}
|
|
889
174
|
}
|
|
890
175
|
|
|
176
|
+
// src/lib/k8s.ts
|
|
177
|
+
var import_kubernetes_fluent_client = require("kubernetes-fluent-client");
|
|
178
|
+
var PeprStore = class extends import_kubernetes_fluent_client.GenericKind {
|
|
179
|
+
};
|
|
180
|
+
var peprStoreGVK = {
|
|
181
|
+
kind: "PeprStore",
|
|
182
|
+
version: "v1",
|
|
183
|
+
group: "pepr.dev"
|
|
184
|
+
};
|
|
185
|
+
(0, import_kubernetes_fluent_client.RegisterKind)(PeprStore, peprStoreGVK);
|
|
186
|
+
|
|
891
187
|
// src/lib/filter.ts
|
|
892
188
|
function shouldSkipRequest(binding, req, capabilityNamespaces) {
|
|
893
|
-
const { group, kind, version } = binding.kind || {};
|
|
189
|
+
const { group, kind: kind2, version } = binding.kind || {};
|
|
894
190
|
const { namespaces, labels, annotations, name } = binding.filters || {};
|
|
895
191
|
const operation = req.operation.toUpperCase();
|
|
896
192
|
const srcObject = operation === "DELETE" /* DELETE */ ? req.oldObject : req.object;
|
|
@@ -902,7 +198,7 @@ function shouldSkipRequest(binding, req, capabilityNamespaces) {
|
|
|
902
198
|
if (name && name !== req.name) {
|
|
903
199
|
return true;
|
|
904
200
|
}
|
|
905
|
-
if (
|
|
201
|
+
if (kind2 !== req.kind.kind) {
|
|
906
202
|
return true;
|
|
907
203
|
}
|
|
908
204
|
if (group && group !== req.kind.group) {
|
|
@@ -941,7 +237,7 @@ function shouldSkipRequest(binding, req, capabilityNamespaces) {
|
|
|
941
237
|
}
|
|
942
238
|
|
|
943
239
|
// src/lib/mutate-request.ts
|
|
944
|
-
var
|
|
240
|
+
var import_ramda = require("ramda");
|
|
945
241
|
var PeprMutateRequest = class {
|
|
946
242
|
Raw;
|
|
947
243
|
#input;
|
|
@@ -975,17 +271,10 @@ var PeprMutateRequest = class {
|
|
|
975
271
|
*/
|
|
976
272
|
constructor(input) {
|
|
977
273
|
this.#input = input;
|
|
978
|
-
this.Merge = this.Merge.bind(this);
|
|
979
|
-
this.SetLabel = this.SetLabel.bind(this);
|
|
980
|
-
this.SetAnnotation = this.SetAnnotation.bind(this);
|
|
981
|
-
this.RemoveLabel = this.RemoveLabel.bind(this);
|
|
982
|
-
this.RemoveAnnotation = this.RemoveAnnotation.bind(this);
|
|
983
|
-
this.HasLabel = this.HasLabel.bind(this);
|
|
984
|
-
this.HasAnnotation = this.HasAnnotation.bind(this);
|
|
985
274
|
if (input.operation.toUpperCase() === "DELETE" /* DELETE */) {
|
|
986
|
-
this.Raw = (0,
|
|
275
|
+
this.Raw = (0, import_ramda.clone)(input.oldObject);
|
|
987
276
|
} else {
|
|
988
|
-
this.Raw = (0,
|
|
277
|
+
this.Raw = (0, import_ramda.clone)(input.object);
|
|
989
278
|
}
|
|
990
279
|
if (!this.Raw) {
|
|
991
280
|
throw new Error("unable to load the request object into PeprRequest.RawP");
|
|
@@ -996,75 +285,75 @@ var PeprMutateRequest = class {
|
|
|
996
285
|
*
|
|
997
286
|
* @param obj - The object to merge with the current resource.
|
|
998
287
|
*/
|
|
999
|
-
Merge(obj) {
|
|
1000
|
-
this.Raw = (0,
|
|
1001
|
-
}
|
|
288
|
+
Merge = (obj) => {
|
|
289
|
+
this.Raw = (0, import_ramda.mergeDeepRight)(this.Raw, obj);
|
|
290
|
+
};
|
|
1002
291
|
/**
|
|
1003
292
|
* Updates a label on the Kubernetes resource.
|
|
1004
293
|
* @param key - The key of the label to update.
|
|
1005
294
|
* @param value - The value of the label.
|
|
1006
295
|
* @returns The current action instance for method chaining.
|
|
1007
296
|
*/
|
|
1008
|
-
SetLabel(key, value) {
|
|
297
|
+
SetLabel = (key, value) => {
|
|
1009
298
|
const ref = this.Raw;
|
|
1010
299
|
ref.metadata = ref.metadata ?? {};
|
|
1011
300
|
ref.metadata.labels = ref.metadata.labels ?? {};
|
|
1012
301
|
ref.metadata.labels[key] = value;
|
|
1013
302
|
return this;
|
|
1014
|
-
}
|
|
303
|
+
};
|
|
1015
304
|
/**
|
|
1016
305
|
* Updates an annotation on the Kubernetes resource.
|
|
1017
306
|
* @param key - The key of the annotation to update.
|
|
1018
307
|
* @param value - The value of the annotation.
|
|
1019
308
|
* @returns The current action instance for method chaining.
|
|
1020
309
|
*/
|
|
1021
|
-
SetAnnotation(key, value) {
|
|
310
|
+
SetAnnotation = (key, value) => {
|
|
1022
311
|
const ref = this.Raw;
|
|
1023
312
|
ref.metadata = ref.metadata ?? {};
|
|
1024
313
|
ref.metadata.annotations = ref.metadata.annotations ?? {};
|
|
1025
314
|
ref.metadata.annotations[key] = value;
|
|
1026
315
|
return this;
|
|
1027
|
-
}
|
|
316
|
+
};
|
|
1028
317
|
/**
|
|
1029
318
|
* Removes a label from the Kubernetes resource.
|
|
1030
319
|
* @param key - The key of the label to remove.
|
|
1031
320
|
* @returns The current Action instance for method chaining.
|
|
1032
321
|
*/
|
|
1033
|
-
RemoveLabel(key) {
|
|
322
|
+
RemoveLabel = (key) => {
|
|
1034
323
|
if (this.Raw.metadata?.labels?.[key]) {
|
|
1035
324
|
delete this.Raw.metadata.labels[key];
|
|
1036
325
|
}
|
|
1037
326
|
return this;
|
|
1038
|
-
}
|
|
327
|
+
};
|
|
1039
328
|
/**
|
|
1040
329
|
* Removes an annotation from the Kubernetes resource.
|
|
1041
330
|
* @param key - The key of the annotation to remove.
|
|
1042
331
|
* @returns The current Action instance for method chaining.
|
|
1043
332
|
*/
|
|
1044
|
-
RemoveAnnotation(key) {
|
|
333
|
+
RemoveAnnotation = (key) => {
|
|
1045
334
|
if (this.Raw.metadata?.annotations?.[key]) {
|
|
1046
335
|
delete this.Raw.metadata.annotations[key];
|
|
1047
336
|
}
|
|
1048
337
|
return this;
|
|
1049
|
-
}
|
|
338
|
+
};
|
|
1050
339
|
/**
|
|
1051
340
|
* Check if a label exists on the Kubernetes resource.
|
|
1052
341
|
*
|
|
1053
342
|
* @param key the label key to check
|
|
1054
343
|
* @returns
|
|
1055
344
|
*/
|
|
1056
|
-
HasLabel(key) {
|
|
345
|
+
HasLabel = (key) => {
|
|
1057
346
|
return this.Raw.metadata?.labels?.[key] !== void 0;
|
|
1058
|
-
}
|
|
347
|
+
};
|
|
1059
348
|
/**
|
|
1060
349
|
* Check if an annotation exists on the Kubernetes resource.
|
|
1061
350
|
*
|
|
1062
351
|
* @param key the annotation key to check
|
|
1063
352
|
* @returns
|
|
1064
353
|
*/
|
|
1065
|
-
HasAnnotation(key) {
|
|
354
|
+
HasAnnotation = (key) => {
|
|
1066
355
|
return this.Raw.metadata?.annotations?.[key] !== void 0;
|
|
1067
|
-
}
|
|
356
|
+
};
|
|
1068
357
|
};
|
|
1069
358
|
|
|
1070
359
|
// src/lib/utils.ts
|
|
@@ -1193,7 +482,7 @@ async function mutateProcessor(config, capabilities, req, reqMetadata) {
|
|
|
1193
482
|
}
|
|
1194
483
|
|
|
1195
484
|
// src/lib/validate-request.ts
|
|
1196
|
-
var
|
|
485
|
+
var import_ramda2 = require("ramda");
|
|
1197
486
|
var PeprValidateRequest = class {
|
|
1198
487
|
Raw;
|
|
1199
488
|
#input;
|
|
@@ -1217,17 +506,13 @@ var PeprValidateRequest = class {
|
|
|
1217
506
|
*/
|
|
1218
507
|
constructor(input) {
|
|
1219
508
|
this.#input = input;
|
|
1220
|
-
this.HasLabel = this.HasLabel.bind(this);
|
|
1221
|
-
this.HasAnnotation = this.HasAnnotation.bind(this);
|
|
1222
|
-
this.Approve = this.Approve.bind(this);
|
|
1223
|
-
this.Deny = this.Deny.bind(this);
|
|
1224
509
|
if (input.operation.toUpperCase() === "DELETE" /* DELETE */) {
|
|
1225
|
-
this.Raw = (0,
|
|
510
|
+
this.Raw = (0, import_ramda2.clone)(input.oldObject);
|
|
1226
511
|
} else {
|
|
1227
|
-
this.Raw = (0,
|
|
512
|
+
this.Raw = (0, import_ramda2.clone)(input.object);
|
|
1228
513
|
}
|
|
1229
514
|
if (!this.Raw) {
|
|
1230
|
-
throw new Error("unable to load the request object into PeprRequest.
|
|
515
|
+
throw new Error("unable to load the request object into PeprRequest.Raw");
|
|
1231
516
|
}
|
|
1232
517
|
}
|
|
1233
518
|
/**
|
|
@@ -1236,28 +521,28 @@ var PeprValidateRequest = class {
|
|
|
1236
521
|
* @param key the label key to check
|
|
1237
522
|
* @returns
|
|
1238
523
|
*/
|
|
1239
|
-
HasLabel(key) {
|
|
524
|
+
HasLabel = (key) => {
|
|
1240
525
|
return this.Raw.metadata?.labels?.[key] !== void 0;
|
|
1241
|
-
}
|
|
526
|
+
};
|
|
1242
527
|
/**
|
|
1243
528
|
* Check if an annotation exists on the Kubernetes resource.
|
|
1244
529
|
*
|
|
1245
530
|
* @param key the annotation key to check
|
|
1246
531
|
* @returns
|
|
1247
532
|
*/
|
|
1248
|
-
HasAnnotation(key) {
|
|
533
|
+
HasAnnotation = (key) => {
|
|
1249
534
|
return this.Raw.metadata?.annotations?.[key] !== void 0;
|
|
1250
|
-
}
|
|
535
|
+
};
|
|
1251
536
|
/**
|
|
1252
537
|
* Create a validation response that allows the request.
|
|
1253
538
|
*
|
|
1254
539
|
* @returns The validation response.
|
|
1255
540
|
*/
|
|
1256
|
-
Approve() {
|
|
541
|
+
Approve = () => {
|
|
1257
542
|
return {
|
|
1258
543
|
allowed: true
|
|
1259
544
|
};
|
|
1260
|
-
}
|
|
545
|
+
};
|
|
1261
546
|
/**
|
|
1262
547
|
* Create a validation response that denies the request.
|
|
1263
548
|
*
|
|
@@ -1265,13 +550,13 @@ var PeprValidateRequest = class {
|
|
|
1265
550
|
* @param statusCode Optional status code to return to the user.
|
|
1266
551
|
* @returns The validation response.
|
|
1267
552
|
*/
|
|
1268
|
-
Deny(statusMessage, statusCode) {
|
|
553
|
+
Deny = (statusMessage, statusCode) => {
|
|
1269
554
|
return {
|
|
1270
555
|
allowed: false,
|
|
1271
556
|
statusCode,
|
|
1272
557
|
statusMessage
|
|
1273
558
|
};
|
|
1274
|
-
}
|
|
559
|
+
};
|
|
1275
560
|
};
|
|
1276
561
|
|
|
1277
562
|
// src/lib/validate-processor.ts
|
|
@@ -1322,7 +607,125 @@ async function validateProcessor(capabilities, req, reqMetadata) {
|
|
|
1322
607
|
return response;
|
|
1323
608
|
}
|
|
1324
609
|
|
|
1325
|
-
// src/lib/controller.ts
|
|
610
|
+
// src/lib/controller/store.ts
|
|
611
|
+
var import_kubernetes_fluent_client2 = require("kubernetes-fluent-client");
|
|
612
|
+
var import_ramda3 = require("ramda");
|
|
613
|
+
var namespace = "pepr-system";
|
|
614
|
+
var debounceBackoff = 5e3;
|
|
615
|
+
var PeprControllerStore = class {
|
|
616
|
+
#name;
|
|
617
|
+
#stores = {};
|
|
618
|
+
#sendDebounce;
|
|
619
|
+
#onReady;
|
|
620
|
+
constructor(config, capabilities, onReady) {
|
|
621
|
+
this.#onReady = onReady;
|
|
622
|
+
this.#name = `pepr-${config.uuid}-store`;
|
|
623
|
+
for (const { name, registerStore } of capabilities) {
|
|
624
|
+
const { store } = registerStore();
|
|
625
|
+
store.registerSender(this.#send(name));
|
|
626
|
+
this.#stores[name] = store;
|
|
627
|
+
}
|
|
628
|
+
setTimeout(
|
|
629
|
+
() => (0, import_kubernetes_fluent_client2.K8s)(PeprStore).InNamespace(namespace).Get(this.#name).then(this.#setupWatch).catch(this.#createStoreResource),
|
|
630
|
+
Math.random() * 3e3
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
#setupWatch = () => {
|
|
634
|
+
void (0, import_kubernetes_fluent_client2.K8s)(PeprStore, { name: this.#name, namespace }).Watch(this.#receive);
|
|
635
|
+
};
|
|
636
|
+
#receive = (store) => {
|
|
637
|
+
logger_default.debug(store, "Pepr Store update");
|
|
638
|
+
const debounced = () => {
|
|
639
|
+
const data = store.data || {};
|
|
640
|
+
for (const name of Object.keys(this.#stores)) {
|
|
641
|
+
const offset = `${name}-`.length;
|
|
642
|
+
const filtered = {};
|
|
643
|
+
for (const key of Object.keys(data)) {
|
|
644
|
+
if ((0, import_ramda3.startsWith)(name, key)) {
|
|
645
|
+
filtered[key.slice(offset)] = data[key];
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
this.#stores[name].receive(filtered);
|
|
649
|
+
}
|
|
650
|
+
if (this.#onReady) {
|
|
651
|
+
this.#onReady();
|
|
652
|
+
this.#onReady = void 0;
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
clearTimeout(this.#sendDebounce);
|
|
656
|
+
this.#sendDebounce = setTimeout(debounced, debounceBackoff);
|
|
657
|
+
};
|
|
658
|
+
#send = (capabilityName) => {
|
|
659
|
+
const sendCache = {};
|
|
660
|
+
const fillCache = (op, key, val) => {
|
|
661
|
+
if (op === "add") {
|
|
662
|
+
const path = `/data/${capabilityName}-${key}`;
|
|
663
|
+
const value = val || "";
|
|
664
|
+
const cacheIdx = [op, path, value].join(":");
|
|
665
|
+
sendCache[cacheIdx] = { op, path, value };
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
if (op === "remove") {
|
|
669
|
+
if (key.length < 1) {
|
|
670
|
+
throw new Error(`Key is required for REMOVE operation`);
|
|
671
|
+
}
|
|
672
|
+
for (const k of key) {
|
|
673
|
+
const path = `/data/${capabilityName}-${k}`;
|
|
674
|
+
const cacheIdx = [op, path].join(":");
|
|
675
|
+
sendCache[cacheIdx] = { op, path };
|
|
676
|
+
}
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
throw new Error(`Unsupported operation: ${op}`);
|
|
680
|
+
};
|
|
681
|
+
const flushCache = async () => {
|
|
682
|
+
const indexes = Object.keys(sendCache);
|
|
683
|
+
const payload = Object.values(sendCache);
|
|
684
|
+
for (const idx of indexes) {
|
|
685
|
+
delete sendCache[idx];
|
|
686
|
+
}
|
|
687
|
+
try {
|
|
688
|
+
await (0, import_kubernetes_fluent_client2.K8s)(PeprStore, { namespace, name: this.#name }).Patch(payload);
|
|
689
|
+
} catch (err) {
|
|
690
|
+
logger_default.error(err, "Pepr store update failure");
|
|
691
|
+
for (const idx of indexes) {
|
|
692
|
+
sendCache[idx] = payload[Number(idx)];
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
const sender = async (op, key, val) => {
|
|
697
|
+
fillCache(op, key, val);
|
|
698
|
+
};
|
|
699
|
+
setInterval(() => {
|
|
700
|
+
if (Object.keys(sendCache).length > 0) {
|
|
701
|
+
logger_default.debug(sendCache, "Sending updates to Pepr store");
|
|
702
|
+
void flushCache();
|
|
703
|
+
}
|
|
704
|
+
}, debounceBackoff);
|
|
705
|
+
return sender;
|
|
706
|
+
};
|
|
707
|
+
#createStoreResource = async (e) => {
|
|
708
|
+
logger_default.info(`Pepr store not found, creating...`);
|
|
709
|
+
logger_default.debug(e);
|
|
710
|
+
try {
|
|
711
|
+
await (0, import_kubernetes_fluent_client2.K8s)(PeprStore).Apply({
|
|
712
|
+
metadata: {
|
|
713
|
+
name: this.#name,
|
|
714
|
+
namespace
|
|
715
|
+
},
|
|
716
|
+
data: {
|
|
717
|
+
// JSON Patch will die if the data is empty, so we need to add a placeholder
|
|
718
|
+
__pepr_do_not_delete__: "k-thx-bye"
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
this.#setupWatch();
|
|
722
|
+
} catch (err) {
|
|
723
|
+
logger_default.error(err, "Failed to create Pepr store");
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
// src/lib/controller/index.ts
|
|
1326
729
|
var Controller = class _Controller {
|
|
1327
730
|
// Track whether the server is running
|
|
1328
731
|
#running = false;
|
|
@@ -1337,24 +740,27 @@ var Controller = class _Controller {
|
|
|
1337
740
|
#capabilities;
|
|
1338
741
|
#beforeHook;
|
|
1339
742
|
#afterHook;
|
|
1340
|
-
constructor(config, capabilities, beforeHook, afterHook) {
|
|
743
|
+
constructor(config, capabilities, beforeHook, afterHook, onReady) {
|
|
1341
744
|
this.#config = config;
|
|
1342
745
|
this.#capabilities = capabilities;
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
746
|
+
new PeprControllerStore(config, capabilities, () => {
|
|
747
|
+
this.#bindEndpoints();
|
|
748
|
+
onReady && onReady();
|
|
749
|
+
logger_default.info("\u2705 Controller startup complete");
|
|
750
|
+
});
|
|
1346
751
|
this.#app.use(_Controller.#logger);
|
|
1347
752
|
this.#app.use(import_express.default.json({ limit: "2mb" }));
|
|
1348
753
|
if (beforeHook) {
|
|
1349
754
|
logger_default.info(`Using beforeHook: ${beforeHook}`);
|
|
755
|
+
this.#beforeHook = beforeHook;
|
|
1350
756
|
}
|
|
1351
757
|
if (afterHook) {
|
|
1352
758
|
logger_default.info(`Using afterHook: ${afterHook}`);
|
|
759
|
+
this.#afterHook = afterHook;
|
|
1353
760
|
}
|
|
1354
|
-
this.#bindEndpoints();
|
|
1355
761
|
}
|
|
1356
762
|
/** Start the webhook server */
|
|
1357
|
-
startServer(port) {
|
|
763
|
+
startServer = (port) => {
|
|
1358
764
|
if (this.#running) {
|
|
1359
765
|
throw new Error("Cannot start Pepr module: Pepr module was not instantiated with deferStart=true");
|
|
1360
766
|
}
|
|
@@ -1362,7 +768,7 @@ var Controller = class _Controller {
|
|
|
1362
768
|
key: import_fs.default.readFileSync(process.env.SSL_KEY_PATH || "/etc/certs/tls.key"),
|
|
1363
769
|
cert: import_fs.default.readFileSync(process.env.SSL_CERT_PATH || "/etc/certs/tls.crt")
|
|
1364
770
|
};
|
|
1365
|
-
if (!isWatchMode) {
|
|
771
|
+
if (!isWatchMode()) {
|
|
1366
772
|
this.#token = process.env.PEPR_API_TOKEN || import_fs.default.readFileSync("/app/api-token/value").toString().trim();
|
|
1367
773
|
logger_default.info(`Using API token: ${this.#token}`);
|
|
1368
774
|
if (!this.#token) {
|
|
@@ -1392,11 +798,11 @@ var Controller = class _Controller {
|
|
|
1392
798
|
process.exit(0);
|
|
1393
799
|
});
|
|
1394
800
|
});
|
|
1395
|
-
}
|
|
801
|
+
};
|
|
1396
802
|
#bindEndpoints = () => {
|
|
1397
803
|
this.#app.get("/healthz", _Controller.#healthz);
|
|
1398
804
|
this.#app.get("/metrics", this.#metrics);
|
|
1399
|
-
if (isWatchMode) {
|
|
805
|
+
if (isWatchMode()) {
|
|
1400
806
|
return;
|
|
1401
807
|
}
|
|
1402
808
|
this.#app.use(["/mutate/:token", "/validate/:token"], this.#validateToken);
|
|
@@ -1444,16 +850,16 @@ var Controller = class _Controller {
|
|
|
1444
850
|
*/
|
|
1445
851
|
#admissionReq = (admissionKind) => {
|
|
1446
852
|
return async (req, res) => {
|
|
1447
|
-
const startTime =
|
|
853
|
+
const startTime = MetricsCollector.observeStart();
|
|
1448
854
|
try {
|
|
1449
855
|
const request = req.body?.request || {};
|
|
1450
856
|
this.#beforeHook && this.#beforeHook(request || {});
|
|
1451
857
|
const name = request?.name ? `/${request.name}` : "";
|
|
1452
|
-
const
|
|
858
|
+
const namespace2 = request?.namespace || "";
|
|
1453
859
|
const gvk = request?.kind || { group: "", version: "", kind: "" };
|
|
1454
860
|
const reqMetadata = {
|
|
1455
861
|
uid: request.uid,
|
|
1456
|
-
namespace,
|
|
862
|
+
namespace: namespace2,
|
|
1457
863
|
name
|
|
1458
864
|
};
|
|
1459
865
|
logger_default.info({ ...reqMetadata, gvk, operation: request.operation, admissionKind }, "Incoming request");
|
|
@@ -1517,7 +923,44 @@ var Controller = class _Controller {
|
|
|
1517
923
|
}
|
|
1518
924
|
};
|
|
1519
925
|
|
|
926
|
+
// src/lib/watch-processor.ts
|
|
927
|
+
var import_kubernetes_fluent_client3 = require("kubernetes-fluent-client");
|
|
928
|
+
var import_types2 = require("kubernetes-fluent-client/dist/fluent/types");
|
|
929
|
+
function setupWatch(capabilities) {
|
|
930
|
+
capabilities.flatMap((c) => c.bindings).filter((binding) => binding.isWatch).forEach(runBinding);
|
|
931
|
+
}
|
|
932
|
+
async function runBinding(binding) {
|
|
933
|
+
const eventToPhaseMap = {
|
|
934
|
+
["CREATE" /* Create */]: [import_types2.WatchPhase.Added],
|
|
935
|
+
["UPDATE" /* Update */]: [import_types2.WatchPhase.Modified],
|
|
936
|
+
["CREATEORUPDATE" /* CreateOrUpdate */]: [import_types2.WatchPhase.Added, import_types2.WatchPhase.Modified],
|
|
937
|
+
["DELETE" /* Delete */]: [import_types2.WatchPhase.Deleted],
|
|
938
|
+
["*" /* Any */]: [import_types2.WatchPhase.Added, import_types2.WatchPhase.Modified, import_types2.WatchPhase.Deleted]
|
|
939
|
+
};
|
|
940
|
+
const phaseMatch = eventToPhaseMap[binding.event] || eventToPhaseMap["*" /* Any */];
|
|
941
|
+
const watchCfg = {
|
|
942
|
+
retryMax: 3,
|
|
943
|
+
retryDelaySec: 5,
|
|
944
|
+
retryFail(e) {
|
|
945
|
+
logger_default.error(e, "Watch failed after 3 attempts, giving up");
|
|
946
|
+
process.exit(1);
|
|
947
|
+
}
|
|
948
|
+
};
|
|
949
|
+
await (0, import_kubernetes_fluent_client3.K8s)(binding.model, binding.filters).Watch((obj, type) => {
|
|
950
|
+
if (phaseMatch.includes(type)) {
|
|
951
|
+
try {
|
|
952
|
+
void binding.watchCallback?.(obj, type);
|
|
953
|
+
} catch (e) {
|
|
954
|
+
logger_default.error(e, "Error executing watch callback");
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}, watchCfg);
|
|
958
|
+
}
|
|
959
|
+
|
|
1520
960
|
// src/lib/module.ts
|
|
961
|
+
var isWatchMode = () => process.env.PEPR_WATCH_MODE === "true";
|
|
962
|
+
var isBuildMode = () => process.env.PEPR_MODE === "build";
|
|
963
|
+
var isDevMode = () => process.env.PEPR_MODE === "dev";
|
|
1521
964
|
var PeprModule = class {
|
|
1522
965
|
#controller;
|
|
1523
966
|
/**
|
|
@@ -1531,8 +974,10 @@ var PeprModule = class {
|
|
|
1531
974
|
const config = (0, import_ramda4.clone)(pepr);
|
|
1532
975
|
config.description = description;
|
|
1533
976
|
ValidateError(config.onError);
|
|
1534
|
-
|
|
1535
|
-
|
|
977
|
+
if (isBuildMode()) {
|
|
978
|
+
if (!process.send) {
|
|
979
|
+
throw new Error("process.send is not defined");
|
|
980
|
+
}
|
|
1536
981
|
const exportedCapabilities = [];
|
|
1537
982
|
for (const capability of capabilities) {
|
|
1538
983
|
exportedCapabilities.push({
|
|
@@ -1545,7 +990,11 @@ var PeprModule = class {
|
|
|
1545
990
|
process.send(exportedCapabilities);
|
|
1546
991
|
return;
|
|
1547
992
|
}
|
|
1548
|
-
this.#controller = new Controller(config, capabilities, opts.beforeHook, opts.afterHook)
|
|
993
|
+
this.#controller = new Controller(config, capabilities, opts.beforeHook, opts.afterHook, () => {
|
|
994
|
+
if (isWatchMode() || isDevMode()) {
|
|
995
|
+
setupWatch(capabilities);
|
|
996
|
+
}
|
|
997
|
+
});
|
|
1549
998
|
if (opts.deferStart) {
|
|
1550
999
|
return;
|
|
1551
1000
|
}
|
|
@@ -1557,13 +1006,242 @@ var PeprModule = class {
|
|
|
1557
1006
|
*
|
|
1558
1007
|
* @param port
|
|
1559
1008
|
*/
|
|
1560
|
-
start(port = 3e3) {
|
|
1009
|
+
start = (port = 3e3) => {
|
|
1561
1010
|
this.#controller.startServer(port);
|
|
1011
|
+
};
|
|
1012
|
+
};
|
|
1013
|
+
|
|
1014
|
+
// src/lib/storage.ts
|
|
1015
|
+
var import_ramda5 = require("ramda");
|
|
1016
|
+
var Storage = class {
|
|
1017
|
+
#store = {};
|
|
1018
|
+
#send;
|
|
1019
|
+
#subscribers = {};
|
|
1020
|
+
#subscriberId = 0;
|
|
1021
|
+
#readyHandlers = [];
|
|
1022
|
+
registerSender = (send) => {
|
|
1023
|
+
this.#send = send;
|
|
1024
|
+
};
|
|
1025
|
+
receive = (data) => {
|
|
1026
|
+
logger_default.debug(data, `Pepr store data received`);
|
|
1027
|
+
this.#store = data || {};
|
|
1028
|
+
this.#onReady();
|
|
1029
|
+
for (const idx in this.#subscribers) {
|
|
1030
|
+
this.#subscribers[idx]((0, import_ramda5.clone)(this.#store));
|
|
1031
|
+
}
|
|
1032
|
+
};
|
|
1033
|
+
getItem = (key) => {
|
|
1034
|
+
return this.#store[key] || null;
|
|
1035
|
+
};
|
|
1036
|
+
clear = () => {
|
|
1037
|
+
this.#dispatchUpdate("remove", Object.keys(this.#store));
|
|
1038
|
+
};
|
|
1039
|
+
removeItem = (key) => {
|
|
1040
|
+
this.#dispatchUpdate("remove", [key]);
|
|
1041
|
+
};
|
|
1042
|
+
setItem = (key, value) => {
|
|
1043
|
+
this.#dispatchUpdate("add", [key], value);
|
|
1044
|
+
};
|
|
1045
|
+
subscribe = (subscriber) => {
|
|
1046
|
+
const idx = this.#subscriberId++;
|
|
1047
|
+
this.#subscribers[idx] = subscriber;
|
|
1048
|
+
return () => this.unsubscribe(idx);
|
|
1049
|
+
};
|
|
1050
|
+
onReady = (callback) => {
|
|
1051
|
+
this.#readyHandlers.push(callback);
|
|
1052
|
+
};
|
|
1053
|
+
/**
|
|
1054
|
+
* Remove a subscriber from the list of subscribers.
|
|
1055
|
+
* @param idx - The index of the subscriber to remove.
|
|
1056
|
+
*/
|
|
1057
|
+
unsubscribe = (idx) => {
|
|
1058
|
+
delete this.#subscribers[idx];
|
|
1059
|
+
};
|
|
1060
|
+
#onReady = () => {
|
|
1061
|
+
for (const handler of this.#readyHandlers) {
|
|
1062
|
+
handler((0, import_ramda5.clone)(this.#store));
|
|
1063
|
+
}
|
|
1064
|
+
this.#onReady = () => {
|
|
1065
|
+
};
|
|
1066
|
+
};
|
|
1067
|
+
/**
|
|
1068
|
+
* Dispatch an update to the store and notify all subscribers.
|
|
1069
|
+
* @param op - The type of operation to perform.
|
|
1070
|
+
* @param keys - The keys to update.
|
|
1071
|
+
* @param [value] - The new value.
|
|
1072
|
+
*/
|
|
1073
|
+
#dispatchUpdate = (op, keys, value) => {
|
|
1074
|
+
this.#send(op, keys, value);
|
|
1075
|
+
};
|
|
1076
|
+
};
|
|
1077
|
+
|
|
1078
|
+
// src/lib/capability.ts
|
|
1079
|
+
var registerAdmission = isBuildMode() || !isWatchMode();
|
|
1080
|
+
var registerWatch = isBuildMode() || isWatchMode() || isDevMode();
|
|
1081
|
+
var Capability = class {
|
|
1082
|
+
#name;
|
|
1083
|
+
#description;
|
|
1084
|
+
#namespaces;
|
|
1085
|
+
#bindings = [];
|
|
1086
|
+
#store = new Storage();
|
|
1087
|
+
#registered = false;
|
|
1088
|
+
/**
|
|
1089
|
+
* Store is a key-value data store that can be used to persist data that should be shared
|
|
1090
|
+
* between requests. Each capability has its own store, and the data is persisted in Kubernetes
|
|
1091
|
+
* in the `pepr-system` namespace.
|
|
1092
|
+
*
|
|
1093
|
+
* Note: You should only access the store from within an action.
|
|
1094
|
+
*/
|
|
1095
|
+
Store = {
|
|
1096
|
+
clear: this.#store.clear,
|
|
1097
|
+
getItem: this.#store.getItem,
|
|
1098
|
+
removeItem: this.#store.removeItem,
|
|
1099
|
+
setItem: this.#store.setItem,
|
|
1100
|
+
subscribe: this.#store.subscribe,
|
|
1101
|
+
onReady: this.#store.onReady
|
|
1102
|
+
};
|
|
1103
|
+
get bindings() {
|
|
1104
|
+
return this.#bindings;
|
|
1105
|
+
}
|
|
1106
|
+
get name() {
|
|
1107
|
+
return this.#name;
|
|
1108
|
+
}
|
|
1109
|
+
get description() {
|
|
1110
|
+
return this.#description;
|
|
1562
1111
|
}
|
|
1112
|
+
get namespaces() {
|
|
1113
|
+
return this.#namespaces || [];
|
|
1114
|
+
}
|
|
1115
|
+
constructor(cfg) {
|
|
1116
|
+
this.#name = cfg.name;
|
|
1117
|
+
this.#description = cfg.description;
|
|
1118
|
+
this.#namespaces = cfg.namespaces;
|
|
1119
|
+
logger_default.info(`Capability ${this.#name} registered`);
|
|
1120
|
+
logger_default.debug(cfg);
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* Register the store with the capability. This is called automatically by the Pepr controller.
|
|
1124
|
+
*
|
|
1125
|
+
* @param store
|
|
1126
|
+
*/
|
|
1127
|
+
registerStore = () => {
|
|
1128
|
+
logger_default.info(`Registering store for ${this.#name}`);
|
|
1129
|
+
if (this.#registered) {
|
|
1130
|
+
throw new Error(`Store already registered for ${this.#name}`);
|
|
1131
|
+
}
|
|
1132
|
+
this.#registered = true;
|
|
1133
|
+
return {
|
|
1134
|
+
store: this.#store
|
|
1135
|
+
};
|
|
1136
|
+
};
|
|
1137
|
+
/**
|
|
1138
|
+
* The When method is used to register a action to be executed when a Kubernetes resource is
|
|
1139
|
+
* processed by Pepr. The action will be executed if the resource matches the specified kind and any
|
|
1140
|
+
* filters that are applied.
|
|
1141
|
+
*
|
|
1142
|
+
* @param model the KubernetesObject model to match
|
|
1143
|
+
* @param kind if using a custom KubernetesObject not available in `a.*`, specify the GroupVersionKind
|
|
1144
|
+
* @returns
|
|
1145
|
+
*/
|
|
1146
|
+
When = (model, kind2) => {
|
|
1147
|
+
const matchedKind = (0, import_kubernetes_fluent_client4.modelToGroupVersionKind)(model.name);
|
|
1148
|
+
if (!matchedKind && !kind2) {
|
|
1149
|
+
throw new Error(`Kind not specified for ${model.name}`);
|
|
1150
|
+
}
|
|
1151
|
+
const binding = {
|
|
1152
|
+
model,
|
|
1153
|
+
// If the kind is not specified, use the matched kind from the model
|
|
1154
|
+
kind: kind2 || matchedKind,
|
|
1155
|
+
event: "*" /* Any */,
|
|
1156
|
+
filters: {
|
|
1157
|
+
name: "",
|
|
1158
|
+
namespaces: [],
|
|
1159
|
+
labels: {},
|
|
1160
|
+
annotations: {}
|
|
1161
|
+
}
|
|
1162
|
+
};
|
|
1163
|
+
const bindings = this.#bindings;
|
|
1164
|
+
const prefix = `${this.#name}: ${model.name}`;
|
|
1165
|
+
const commonChain = { WithLabel, WithAnnotation, Mutate, Validate, Watch };
|
|
1166
|
+
const isNotEmpty = (value) => Object.keys(value).length > 0;
|
|
1167
|
+
const log = (message, cbString) => {
|
|
1168
|
+
const filteredObj = (0, import_ramda6.pickBy)(isNotEmpty, binding.filters);
|
|
1169
|
+
logger_default.info(`${message} configured for ${binding.event}`, prefix);
|
|
1170
|
+
logger_default.info(filteredObj, prefix);
|
|
1171
|
+
logger_default.debug(cbString, prefix);
|
|
1172
|
+
};
|
|
1173
|
+
function Validate(validateCallback) {
|
|
1174
|
+
if (registerAdmission) {
|
|
1175
|
+
log("Validate Action", validateCallback.toString());
|
|
1176
|
+
bindings.push({
|
|
1177
|
+
...binding,
|
|
1178
|
+
isValidate: true,
|
|
1179
|
+
validateCallback
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
return { Watch };
|
|
1183
|
+
}
|
|
1184
|
+
function Mutate(mutateCallback) {
|
|
1185
|
+
if (registerAdmission) {
|
|
1186
|
+
log("Mutate Action", mutateCallback.toString());
|
|
1187
|
+
bindings.push({
|
|
1188
|
+
...binding,
|
|
1189
|
+
isMutate: true,
|
|
1190
|
+
mutateCallback
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1193
|
+
return { Watch, Validate };
|
|
1194
|
+
}
|
|
1195
|
+
function Watch(watchCallback) {
|
|
1196
|
+
if (registerWatch) {
|
|
1197
|
+
log("Watch Action", watchCallback.toString());
|
|
1198
|
+
bindings.push({
|
|
1199
|
+
...binding,
|
|
1200
|
+
isWatch: true,
|
|
1201
|
+
watchCallback
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
function InNamespace(...namespaces) {
|
|
1206
|
+
logger_default.debug(`Add namespaces filter ${namespaces}`, prefix);
|
|
1207
|
+
binding.filters.namespaces.push(...namespaces);
|
|
1208
|
+
return { ...commonChain, WithName };
|
|
1209
|
+
}
|
|
1210
|
+
function WithName(name) {
|
|
1211
|
+
logger_default.debug(`Add name filter ${name}`, prefix);
|
|
1212
|
+
binding.filters.name = name;
|
|
1213
|
+
return commonChain;
|
|
1214
|
+
}
|
|
1215
|
+
function WithLabel(key, value = "") {
|
|
1216
|
+
logger_default.debug(`Add label filter ${key}=${value}`, prefix);
|
|
1217
|
+
binding.filters.labels[key] = value;
|
|
1218
|
+
return commonChain;
|
|
1219
|
+
}
|
|
1220
|
+
function WithAnnotation(key, value = "") {
|
|
1221
|
+
logger_default.debug(`Add annotation filter ${key}=${value}`, prefix);
|
|
1222
|
+
binding.filters.annotations[key] = value;
|
|
1223
|
+
return commonChain;
|
|
1224
|
+
}
|
|
1225
|
+
function bindEvent(event) {
|
|
1226
|
+
binding.event = event;
|
|
1227
|
+
return {
|
|
1228
|
+
...commonChain,
|
|
1229
|
+
InNamespace,
|
|
1230
|
+
WithName
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
return {
|
|
1234
|
+
IsCreatedOrUpdated: () => bindEvent("CREATEORUPDATE" /* CreateOrUpdate */),
|
|
1235
|
+
IsCreated: () => bindEvent("CREATE" /* Create */),
|
|
1236
|
+
IsUpdated: () => bindEvent("UPDATE" /* Update */),
|
|
1237
|
+
IsDeleted: () => bindEvent("DELETE" /* Delete */)
|
|
1238
|
+
};
|
|
1239
|
+
};
|
|
1563
1240
|
};
|
|
1564
1241
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1565
1242
|
0 && (module.exports = {
|
|
1566
1243
|
Capability,
|
|
1244
|
+
K8s,
|
|
1567
1245
|
Log,
|
|
1568
1246
|
PeprModule,
|
|
1569
1247
|
PeprMutateRequest,
|
|
@@ -1573,8 +1251,7 @@ var PeprModule = class {
|
|
|
1573
1251
|
RegisterKind,
|
|
1574
1252
|
a,
|
|
1575
1253
|
fetch,
|
|
1576
|
-
fetchRaw,
|
|
1577
1254
|
fetchStatus,
|
|
1578
|
-
|
|
1255
|
+
kind
|
|
1579
1256
|
});
|
|
1580
1257
|
//# sourceMappingURL=lib.js.map
|