pepr 0.1.18 → 0.1.20

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.
@@ -0,0 +1,898 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ var types = require('./types-672dd6e4.js');
5
+ var dist = require('@kubernetes/client-node/dist');
6
+ var R = require('ramda');
7
+ var fastJsonPatch = require('fast-json-patch');
8
+
9
+ function _interopNamespaceDefault(e) {
10
+ var n = Object.create(null);
11
+ if (e) {
12
+ Object.keys(e).forEach(function (k) {
13
+ if (k !== 'default') {
14
+ var d = Object.getOwnPropertyDescriptor(e, k);
15
+ Object.defineProperty(n, k, d.get ? d : {
16
+ enumerable: true,
17
+ get: function () { return e[k]; }
18
+ });
19
+ }
20
+ });
21
+ }
22
+ n.default = e;
23
+ return Object.freeze(n);
24
+ }
25
+
26
+ var R__namespace = /*#__PURE__*/_interopNamespaceDefault(R);
27
+
28
+ // SPDX-License-Identifier: Apache-2.0
29
+
30
+ var upstream = /*#__PURE__*/Object.freeze({
31
+ __proto__: null,
32
+ APIService: dist.V1APIService,
33
+ CSIDriver: dist.V1CSIDriver,
34
+ CSIStorageCapacity: dist.V1CSIStorageCapacity,
35
+ CertificateSigningRequest: dist.V1CertificateSigningRequest,
36
+ ConfigMap: dist.V1ConfigMap,
37
+ ControllerRevision: dist.V1ControllerRevision,
38
+ CronJob: dist.V1CronJob,
39
+ CustomResourceDefinition: dist.V1CustomResourceDefinition,
40
+ DaemonSet: dist.V1DaemonSet,
41
+ Deployment: dist.V1Deployment,
42
+ Endpoint: dist.V1Endpoint,
43
+ EndpointSlice: dist.V1EndpointSlice,
44
+ HorizontalPodAutoscaler: dist.V1HorizontalPodAutoscaler,
45
+ Ingress: dist.V1Ingress,
46
+ IngressClass: dist.V1IngressClass,
47
+ Job: dist.V1Job,
48
+ LimitRange: dist.V1LimitRange,
49
+ LocalSubjectAccessReview: dist.V1LocalSubjectAccessReview,
50
+ MutatingWebhookConfiguration: dist.V1MutatingWebhookConfiguration,
51
+ Namespace: dist.V1Namespace,
52
+ NetworkPolicy: dist.V1NetworkPolicy,
53
+ Node: dist.V1Node,
54
+ PersistentVolume: dist.V1PersistentVolume,
55
+ PersistentVolumeClaim: dist.V1PersistentVolumeClaim,
56
+ Pod: dist.V1Pod,
57
+ PodDisruptionBudget: dist.V1PodDisruptionBudget,
58
+ PodTemplate: dist.V1PodTemplate,
59
+ ReplicaSet: dist.V1ReplicaSet,
60
+ ReplicationController: dist.V1ReplicationController,
61
+ ResourceQuota: dist.V1ResourceQuota,
62
+ RuntimeClass: dist.V1RuntimeClass,
63
+ Secret: dist.V1Secret,
64
+ SelfSubjectAccessReview: dist.V1SelfSubjectAccessReview,
65
+ SelfSubjectRulesReview: dist.V1SelfSubjectRulesReview,
66
+ Service: dist.V1Service,
67
+ ServiceAccount: dist.V1ServiceAccount,
68
+ StatefulSet: dist.V1StatefulSet,
69
+ StorageClass: dist.V1StorageClass,
70
+ SubjectAccessReview: dist.V1SubjectAccessReview,
71
+ TokenReview: dist.V1TokenReview,
72
+ ValidatingWebhookConfiguration: dist.V1ValidatingWebhookConfiguration,
73
+ VolumeAttachment: dist.V1VolumeAttachment
74
+ });
75
+
76
+ // SPDX-License-Identifier: Apache-2.0
77
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
78
+ const gvkMap = {
79
+ /**
80
+ * Represents a K8s ConfigMap resource.
81
+ * ConfigMap holds configuration data for pods to consume.
82
+ * @see {@link https://kubernetes.io/docs/concepts/configuration/configmap/}
83
+ */
84
+ V1ConfigMap: {
85
+ kind: "ConfigMap",
86
+ version: "v1",
87
+ group: "",
88
+ },
89
+ /**
90
+ * Represents a K8s Endpoints resource.
91
+ * Endpoints expose a service's IP addresses and ports to other resources.
92
+ * @see {@link https://kubernetes.io/docs/concepts/services-networking/service/#endpoints}
93
+ */
94
+ V1Endpoint: {
95
+ kind: "Endpoints",
96
+ version: "v1",
97
+ group: "",
98
+ },
99
+ /**
100
+ * Represents a K8s LimitRange resource.
101
+ * LimitRange enforces constraints on the resource consumption of objects in a namespace.
102
+ * @see {@link https://kubernetes.io/docs/concepts/policy/limit-range/}
103
+ */
104
+ V1LimitRange: {
105
+ kind: "LimitRange",
106
+ version: "v1",
107
+ group: "",
108
+ },
109
+ /**
110
+ * Represents a K8s Namespace resource.
111
+ * Namespace is a way to divide cluster resources between multiple users.
112
+ * @see {@link https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/}
113
+ */
114
+ V1Namespace: {
115
+ kind: "Namespace",
116
+ version: "v1",
117
+ group: "",
118
+ },
119
+ /**
120
+ * Represents a K8s Node resource.
121
+ * Node is a worker machine in Kubernetes.
122
+ * @see {@link https://kubernetes.io/docs/concepts/architecture/nodes/}
123
+ */
124
+ V1Node: {
125
+ kind: "Node",
126
+ version: "v1",
127
+ group: "",
128
+ },
129
+ /**
130
+ * Represents a K8s PersistentVolumeClaim resource.
131
+ * PersistentVolumeClaim is a user's request for and claim to a persistent volume.
132
+ * @see {@link https://kubernetes.io/docs/concepts/storage/persistent-volumes/#persistentvolumeclaims}
133
+ */
134
+ V1PersistentVolumeClaim: {
135
+ kind: "PersistentVolumeClaim",
136
+ version: "v1",
137
+ group: "",
138
+ },
139
+ /**
140
+ * Represents a K8s PersistentVolume resource.
141
+ * PersistentVolume is a piece of storage in the cluster that has been provisioned by an administrator.
142
+ * @see {@link https://kubernetes.io/docs/concepts/storage/persistent-volumes/}
143
+ */
144
+ V1PersistentVolume: {
145
+ kind: "PersistentVolume",
146
+ version: "v1",
147
+ group: "",
148
+ },
149
+ /**
150
+ * Represents a K8s Pod resource.
151
+ * Pod is the smallest and simplest unit in the Kubernetes object model.
152
+ * @see {@link https://kubernetes.io/docs/concepts/workloads/pods/}
153
+ */
154
+ V1Pod: {
155
+ kind: "Pod",
156
+ version: "v1",
157
+ group: "",
158
+ },
159
+ /**
160
+ * Represents a K8s PodTemplate resource.
161
+ * PodTemplate is an object that describes the pod that will be created from a higher level abstraction.
162
+ * @see {@link https://kubernetes.io/docs/concepts/workloads/controllers/#pod-template}
163
+ */
164
+ V1PodTemplate: {
165
+ kind: "PodTemplate",
166
+ version: "v1",
167
+ group: "",
168
+ },
169
+ /**
170
+ * Represents a K8s ReplicationController resource.
171
+ * ReplicationController ensures that a specified number of pod replicas are running at any given time.
172
+ * @see {@link https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/}
173
+ */
174
+ V1ReplicationController: {
175
+ kind: "ReplicationController",
176
+ version: "v1",
177
+ group: "",
178
+ },
179
+ /**
180
+ * Represents a K8s ResourceQuota resource.
181
+ * ResourceQuota provides constraints that limit resource consumption per namespace.
182
+ * @see {@link https://kubernetes.io/docs/concepts/policy/resource-quotas/}
183
+ */
184
+ V1ResourceQuota: {
185
+ kind: "ResourceQuota",
186
+ version: "v1",
187
+ group: "",
188
+ },
189
+ /**
190
+ * Represents a K8s Secret resource.
191
+ * Secret holds secret data of a certain type.
192
+ * @see {@link https://kubernetes.io/docs/concepts/configuration/secret/}
193
+ */
194
+ V1Secret: {
195
+ kind: "Secret",
196
+ version: "v1",
197
+ group: "",
198
+ },
199
+ /**
200
+ * Represents a K8s ServiceAccount resource.
201
+ * ServiceAccount is an identity that processes in a pod can use to access the Kubernetes API.
202
+ * @see {@link https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/}
203
+ */
204
+ V1ServiceAccount: {
205
+ kind: "ServiceAccount",
206
+ version: "v1",
207
+ group: "",
208
+ },
209
+ /**
210
+ * Represents a K8s Service resource.
211
+ * Service is an abstraction which defines a logical set of Pods and a policy by which to access them.
212
+ * @see {@link https://kubernetes.io/docs/concepts/services-networking/service/}
213
+ */
214
+ V1Service: {
215
+ kind: "Service",
216
+ version: "v1",
217
+ group: "",
218
+ },
219
+ /**
220
+ * Represents a K8s MutatingWebhookConfiguration resource.
221
+ * MutatingWebhookConfiguration configures a mutating admission webhook.
222
+ * @see {@link https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#configure-admission-webhooks-on-the-fly}
223
+ */
224
+ V1MutatingWebhookConfiguration: {
225
+ kind: "MutatingWebhookConfiguration",
226
+ version: "v1",
227
+ group: "admissionregistration.k8s.io",
228
+ },
229
+ /**
230
+ * Represents a K8s ValidatingWebhookConfiguration resource.
231
+ * ValidatingWebhookConfiguration configures a validating admission webhook.
232
+ * @see {@link https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#configure-admission-webhooks-on-the-fly}
233
+ */
234
+ V1ValidatingWebhookConfiguration: {
235
+ kind: "ValidatingWebhookConfiguration",
236
+ version: "v1",
237
+ group: "admissionregistration.k8s.io",
238
+ },
239
+ /**
240
+ * Represents a K8s CustomResourceDefinition resource.
241
+ * CustomResourceDefinition is a custom resource in a Kubernetes cluster.
242
+ * @see {@link https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/}
243
+ */
244
+ V1CustomResourceDefinition: {
245
+ kind: "CustomResourceDefinition",
246
+ version: "v1",
247
+ group: "apiextensions.k8s.io",
248
+ },
249
+ /**
250
+ * Represents a K8s APIService resource.
251
+ * APIService represents a server for a particular API version and group.
252
+ * @see {@link https://kubernetes.io/docs/tasks/access-kubernetes-api/setup-extension-api-server/}
253
+ */
254
+ V1APIService: {
255
+ kind: "APIService",
256
+ version: "v1",
257
+ group: "apiregistration.k8s.io",
258
+ },
259
+ /**
260
+ * Represents a K8s ControllerRevision resource.
261
+ * ControllerRevision is used to manage the history of a StatefulSet or DaemonSet.
262
+ * @see {@link https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#revision-history}
263
+ */
264
+ V1ControllerRevision: {
265
+ kind: "ControllerRevision",
266
+ version: "v1",
267
+ group: "apps",
268
+ },
269
+ /**
270
+ * Represents a K8s DaemonSet resource.
271
+ * DaemonSet ensures that all (or some) nodes run a copy of a Pod.
272
+ * @see {@link https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/}
273
+ */
274
+ V1DaemonSet: {
275
+ kind: "DaemonSet",
276
+ version: "v1",
277
+ group: "apps",
278
+ },
279
+ /**
280
+ * Represents a K8s Deployment resource.
281
+ * Deployment provides declarative updates for Pods and ReplicaSets.
282
+ * @see {@link https://kubernetes.io/docs/concepts/workloads/controllers/deployment/}
283
+ */
284
+ V1Deployment: {
285
+ kind: "Deployment",
286
+ version: "v1",
287
+ group: "apps",
288
+ },
289
+ /**
290
+ * Represents a K8s ReplicaSet resource.
291
+ * ReplicaSet ensures that a specified number of pod replicas are running at any given time.
292
+ * @see {@link https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/}
293
+ */
294
+ V1ReplicaSet: {
295
+ kind: "ReplicaSet",
296
+ version: "v1",
297
+ group: "apps",
298
+ },
299
+ /**
300
+ * Represents a K8s StatefulSet resource.
301
+ * StatefulSet is used to manage stateful applications.
302
+ * @see {@link https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/}
303
+ */
304
+ V1StatefulSet: {
305
+ kind: "StatefulSet",
306
+ version: "v1",
307
+ group: "apps",
308
+ },
309
+ /**
310
+ * Represents a K8s TokenReview resource.
311
+ * TokenReview attempts to authenticate a token to a known user.
312
+ * @see {@link https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#tokenreview-v1-authentication-k8s-io}
313
+ */
314
+ V1TokenReview: {
315
+ kind: "TokenReview",
316
+ version: "v1",
317
+ group: "authentication.k8s.io",
318
+ },
319
+ /**
320
+ * Represents a K8s LocalSubjectAccessReview resource.
321
+ * LocalSubjectAccessReview checks whether a specific user can perform a specific action in a specific namespace.
322
+ * @see {@link https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#localsubjectaccessreview-v1-authorization-k8s-io}
323
+ */
324
+ V1LocalSubjectAccessReview: {
325
+ kind: "LocalSubjectAccessReview",
326
+ version: "v1",
327
+ group: "authorization.k8s.io",
328
+ },
329
+ /**
330
+ * Represents a K8s SelfSubjectAccessReview resource.
331
+ * SelfSubjectAccessReview checks whether the current user can perform a specific action.
332
+ * @see {@link https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#selfsubjectaccessreview-v1-authorization-k8s-io}
333
+ */
334
+ V1SelfSubjectAccessReview: {
335
+ kind: "SelfSubjectAccessReview",
336
+ version: "v1",
337
+ group: "authorization.k8s.io",
338
+ },
339
+ /**
340
+ * Represents a K8s SelfSubjectRulesReview resource.
341
+ * SelfSubjectRulesReview lists the permissions a specific user has within a namespace.
342
+ * @see {@link https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#selfsubjectrulesreview-v1-authorization-k8s-io}
343
+ */
344
+ V1SelfSubjectRulesReview: {
345
+ kind: "SelfSubjectRulesReview",
346
+ version: "v1",
347
+ group: "authorization.k8s.io",
348
+ },
349
+ /**
350
+ * Represents a K8s SubjectAccessReview resource.
351
+ * SubjectAccessReview checks whether a specific user can perform a specific action.
352
+ * @see {@link https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#subjectaccessreview-v1-authorization-k8s-io}
353
+ */
354
+ V1SubjectAccessReview: {
355
+ kind: "SubjectAccessReview",
356
+ version: "v1",
357
+ group: "authorization.k8s.io",
358
+ },
359
+ /**
360
+ * Represents a K8s HorizontalPodAutoscaler resource.
361
+ * HorizontalPodAutoscaler automatically scales the number of Pods in a replication controller, deployment, or replica set.
362
+ * @see {@link https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/}
363
+ */
364
+ V1HorizontalPodAutoscaler: {
365
+ kind: "HorizontalPodAutoscaler",
366
+ version: "v2",
367
+ group: "autoscaling",
368
+ },
369
+ /**
370
+ * Represents a K8s CronJob resource.
371
+ * CronJob manages time-based jobs, specifically those that run periodically and complete after a successful execution.
372
+ * @see {@link https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/}
373
+ */
374
+ V1CronJob: {
375
+ kind: "CronJob",
376
+ version: "v1",
377
+ group: "batch",
378
+ },
379
+ /**
380
+ * Represents a K8s Job resource.
381
+ * Job represents the configuration of a single job.
382
+ * @see {@link https://kubernetes.io/docs/concepts/workloads/controllers/job/}
383
+ */
384
+ V1Job: {
385
+ kind: "Job",
386
+ version: "v1",
387
+ group: "batch",
388
+ },
389
+ /**
390
+ * Represents a K8s CertificateSigningRequest resource.
391
+ * CertificateSigningRequest represents a certificate signing request.
392
+ * @see {@link https://kubernetes.io/docs/reference/access-authn-authz/certificate-signing-requests/}
393
+ */
394
+ V1CertificateSigningRequest: {
395
+ kind: "CertificateSigningRequest",
396
+ version: "v1",
397
+ group: "certificates.k8s.io",
398
+ },
399
+ /**
400
+ * Represents a K8s EndpointSlice resource.
401
+ * EndpointSlice represents a scalable set of network endpoints for a Kubernetes Service.
402
+ * @see {@link https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/}
403
+ */
404
+ V1EndpointSlice: {
405
+ kind: "EndpointSlice",
406
+ version: "v1",
407
+ group: "discovery.k8s.io",
408
+ },
409
+ /**
410
+ * Represents a K8s IngressClass resource.
411
+ * IngressClass represents the class of the Ingress, referenced by the Ingress spec.
412
+ * @see {@link https://kubernetes.io/docs/concepts/services-networking/ingress/}
413
+ */
414
+ V1IngressClass: {
415
+ kind: "IngressClass",
416
+ version: "v1",
417
+ group: "networking.k8s.io",
418
+ },
419
+ /**
420
+ * Represents a K8s Ingress resource.
421
+ * Ingress exposes HTTP and HTTPS routes from outside the cluster to services within the cluster.
422
+ * @see {@link https://kubernetes.io/docs/concepts/services-networking/ingress/}
423
+ */
424
+ V1Ingress: {
425
+ kind: "Ingress",
426
+ version: "v1",
427
+ group: "networking.k8s.io",
428
+ },
429
+ /**
430
+ * Represents a K8s NetworkPolicy resource.
431
+ * NetworkPolicy defines a set of rules for how pods communicate with each other.
432
+ * @see {@link https://kubernetes.io/docs/concepts/services-networking/network-policies/}
433
+ */
434
+ V1NetworkPolicy: {
435
+ kind: "NetworkPolicy",
436
+ version: "v1",
437
+ group: "networking.k8s.io",
438
+ },
439
+ /**
440
+ * Represents a K8s RuntimeClass resource.
441
+ * RuntimeClass is a cluster-scoped resource that surfaces container runtime properties to the control plane.
442
+ * @see {@link https://kubernetes.io/docs/concepts/containers/runtime-class/}
443
+ */
444
+ V1RuntimeClass: {
445
+ kind: "RuntimeClass",
446
+ version: "v1",
447
+ group: "node.k8s.io",
448
+ },
449
+ /**
450
+ * Represents a K8s PodDisruptionBudget resource.
451
+ * PodDisruptionBudget is an API object that limits the number of pods of a replicated application that are down simultaneously.
452
+ * @see {@link https://kubernetes.io/docs/concepts/workloads/pods/disruptions/}
453
+ */
454
+ V1PodDisruptionBudget: {
455
+ kind: "PodDisruptionBudget",
456
+ version: "v1",
457
+ group: "policy",
458
+ },
459
+ /**
460
+ * Represents a K8s VolumeAttachment resource.
461
+ * VolumeAttachment captures the intent to attach or detach the specified volume to/from the specified node.
462
+ * @see {@link https://kubernetes.io/docs/concepts/storage/storage-classes/}
463
+ */
464
+ V1VolumeAttachment: {
465
+ kind: "VolumeAttachment",
466
+ version: "v1",
467
+ group: "storage.k8s.io",
468
+ },
469
+ /**
470
+ * Represents a K8s CSIDriver resource.
471
+ * CSIDriver captures information about a Container Storage Interface (CSI) volume driver.
472
+ * @see {@link https://kubernetes.io/docs/concepts/storage/volumes/}
473
+ */
474
+ V1CSIDriver: {
475
+ kind: "CSIDriver",
476
+ version: "v1",
477
+ group: "storage.k8s.io",
478
+ },
479
+ /**
480
+ * Represents a K8s CSIStorageCapacity resource.
481
+ * CSIStorageCapacity stores the reported storage capacity of a CSI node or storage class.
482
+ * @see {@link https://kubernetes.io/docs/concepts/storage/csi/}
483
+ */
484
+ V1CSIStorageCapacity: {
485
+ kind: "CSIStorageCapacity",
486
+ version: "v1",
487
+ group: "storage.k8s.io",
488
+ },
489
+ /**
490
+ * Represents a K8s StorageClass resource.
491
+ * StorageClass is a cluster-scoped resource that provides a way for administrators to describe the classes of storage they offer.
492
+ * @see {@link https://kubernetes.io/docs/concepts/storage/storage-classes/}
493
+ */
494
+ V1StorageClass: {
495
+ kind: "StorageClass",
496
+ version: "v1",
497
+ group: "storage.k8s.io",
498
+ },
499
+ };
500
+ function modelToGroupVersionKind(key) {
501
+ return gvkMap[key];
502
+ }
503
+
504
+ // SPDX-License-Identifier: Apache-2.0
505
+ /**
506
+ * A capability is a unit of functionality that can be registered with the Pepr runtime.
507
+ */
508
+ class Capability {
509
+ get bindings() {
510
+ return this._bindings;
511
+ }
512
+ get name() {
513
+ return this._name;
514
+ }
515
+ get description() {
516
+ return this._description;
517
+ }
518
+ get namespaces() {
519
+ return this._namespaces || [];
520
+ }
521
+ get mutateOrValidate() {
522
+ return this._mutateOrValidate;
523
+ }
524
+ constructor(cfg) {
525
+ // Currently everything is considered a mutation
526
+ this._mutateOrValidate = types.HookPhase.mutate;
527
+ this._bindings = [];
528
+ /**
529
+ * The Register method is used to register a capability with the Pepr runtime. This method is
530
+ * called in the order that the capabilities should be executed.
531
+ *
532
+ * @param callback the state register method to call, passing the capability as an argument
533
+ */
534
+ this.Register = (register) => register(this);
535
+ /**
536
+ * The When method is used to register a capability action to be executed when a Kubernetes resource is
537
+ * processed by Pepr. The action will be executed if the resource matches the specified kind and any
538
+ * filters that are applied.
539
+ *
540
+ * @param model if using a custom KubernetesObject not available in `a.*`, specify the GroupVersionKind
541
+ * @returns
542
+ */
543
+ this.When = (model) => {
544
+ const binding = {
545
+ // If the kind is not specified, use the default KubernetesObject
546
+ kind: modelToGroupVersionKind(model.name),
547
+ filters: {
548
+ name: "",
549
+ namespaces: [],
550
+ labels: {},
551
+ annotations: {},
552
+ },
553
+ callback: () => null,
554
+ };
555
+ const prefix = `${this._name}: ${model.name}`;
556
+ types.Log.info(`Binding created`, prefix);
557
+ const Then = (cb) => {
558
+ types.Log.info(`Binding action created`, prefix);
559
+ types.Log.debug(cb.toString(), prefix);
560
+ // Push the binding to the list of bindings for this capability as a new BindingAction
561
+ // with the callback function to preserve
562
+ this._bindings.push({
563
+ ...binding,
564
+ callback: cb,
565
+ });
566
+ // Now only allow adding actions to the same binding
567
+ return { Then };
568
+ };
569
+ const ThenSet = (merge) => {
570
+ // Add the new action to the binding
571
+ Then(req => req.Merge(merge));
572
+ return { Then };
573
+ };
574
+ function InNamespace(...namespaces) {
575
+ types.Log.debug(`Add namespaces filter ${namespaces}`, prefix);
576
+ binding.filters.namespaces.push(...namespaces);
577
+ return { WithLabel, WithAnnotation, WithName, Then, ThenSet };
578
+ }
579
+ function WithName(name) {
580
+ types.Log.debug(`Add name filter ${name}`, prefix);
581
+ binding.filters.name = name;
582
+ return { WithLabel, WithAnnotation, Then, ThenSet };
583
+ }
584
+ function WithLabel(key, value = "") {
585
+ types.Log.debug(`Add label filter ${key}=${value}`, prefix);
586
+ binding.filters.labels[key] = value;
587
+ return { WithLabel, WithAnnotation, Then, ThenSet };
588
+ }
589
+ const WithAnnotation = (key, value = "") => {
590
+ types.Log.debug(`Add annotation filter ${key}=${value}`, prefix);
591
+ binding.filters.annotations[key] = value;
592
+ return { WithLabel, WithAnnotation, Then, ThenSet };
593
+ };
594
+ const bindEvent = (event) => {
595
+ binding.event = event;
596
+ return {
597
+ InNamespace,
598
+ Then,
599
+ ThenSet,
600
+ WithAnnotation,
601
+ WithLabel,
602
+ WithName,
603
+ };
604
+ };
605
+ return {
606
+ IsCreatedOrUpdated: () => bindEvent(types.Event.CreateOrUpdate),
607
+ IsCreated: () => bindEvent(types.Event.Create),
608
+ IsUpdated: () => bindEvent(types.Event.Update),
609
+ IsDeleted: () => bindEvent(types.Event.Delete),
610
+ };
611
+ };
612
+ this._name = cfg.name;
613
+ this._description = cfg.description;
614
+ this._namespaces = cfg.namespaces;
615
+ types.Log.info(`Capability ${this._name} registered`);
616
+ types.Log.debug(cfg);
617
+ }
618
+ }
619
+
620
+ // SPDX-License-Identifier: Apache-2.0
621
+ /**
622
+ * The RequestWrapper class provides methods to modify Kubernetes objects in the context
623
+ * of a mutating webhook request.
624
+ */
625
+ class RequestWrapper {
626
+ get PermitSideEffects() {
627
+ return !this._input.dryRun;
628
+ }
629
+ /**
630
+ * Indicates whether the request is a dry run.
631
+ * @returns true if the request is a dry run, false otherwise.
632
+ */
633
+ get IsDryRun() {
634
+ return this._input.dryRun;
635
+ }
636
+ /**
637
+ * Provides access to the old resource in the request if available.
638
+ * @returns The old Kubernetes resource object or null if not available.
639
+ */
640
+ get OldResource() {
641
+ return this._input.oldObject;
642
+ }
643
+ /**
644
+ * Provides access to the request object.
645
+ * @returns The request object containing the Kubernetes resource.
646
+ */
647
+ get Request() {
648
+ return this._input;
649
+ }
650
+ /**
651
+ * Creates a new instance of the Action class.
652
+ * @param input - The request object containing the Kubernetes resource to modify.
653
+ */
654
+ constructor(input) {
655
+ // Deep clone the object to prevent mutation of the original object
656
+ this.Raw = R__namespace.clone(input.object);
657
+ // Store the input
658
+ this._input = input;
659
+ }
660
+ /**
661
+ * Deep merges the provided object with the current resource.
662
+ *
663
+ * @param obj - The object to merge with the current resource.
664
+ */
665
+ Merge(obj) {
666
+ this.Raw = R__namespace.mergeDeepRight(this.Raw, obj);
667
+ }
668
+ /**
669
+ * Updates a label on the Kubernetes resource.
670
+ * @param key - The key of the label to update.
671
+ * @param value - The value of the label.
672
+ * @returns The current Action instance for method chaining.
673
+ */
674
+ SetLabel(key, value) {
675
+ const ref = this.Raw;
676
+ ref.metadata = ref.metadata ?? {};
677
+ ref.metadata.labels = ref.metadata.labels ?? {};
678
+ ref.metadata.labels[key] = value;
679
+ return this;
680
+ }
681
+ /**
682
+ * Updates an annotation on the Kubernetes resource.
683
+ * @param key - The key of the annotation to update.
684
+ * @param value - The value of the annotation.
685
+ * @returns The current Action instance for method chaining.
686
+ */
687
+ SetAnnotation(key, value) {
688
+ const ref = this.Raw;
689
+ ref.metadata = ref.metadata ?? {};
690
+ ref.metadata.annotations = ref.metadata.annotations ?? {};
691
+ ref.metadata.annotations[key] = value;
692
+ return this;
693
+ }
694
+ /**
695
+ * Removes a label from the Kubernetes resource.
696
+ * @param key - The key of the label to remove.
697
+ * @returns The current Action instance for method chaining.
698
+ */
699
+ RemoveLabel(key) {
700
+ if (this.Raw.metadata?.labels?.[key]) {
701
+ delete this.Raw.metadata.labels[key];
702
+ }
703
+ return this;
704
+ }
705
+ /**
706
+ * Removes an annotation from the Kubernetes resource.
707
+ * @param key - The key of the annotation to remove.
708
+ * @returns The current Action instance for method chaining.
709
+ */
710
+ RemoveAnnotation(key) {
711
+ if (this.Raw.metadata?.annotations?.[key]) {
712
+ delete this.Raw.metadata.annotations[key];
713
+ }
714
+ return this;
715
+ }
716
+ /**
717
+ * Check if a label exists on the Kubernetes resource.
718
+ *
719
+ * @param key the label key to check
720
+ * @returns
721
+ */
722
+ HasLabel(key) {
723
+ return this.Raw?.metadata?.labels?.[key] !== undefined;
724
+ }
725
+ /**
726
+ * Check if an annotation exists on the Kubernetes resource.
727
+ *
728
+ * @param key the annotation key to check
729
+ * @returns
730
+ */
731
+ HasAnnotation(key) {
732
+ return this.Raw?.metadata?.annotations?.[key] !== undefined;
733
+ }
734
+ }
735
+
736
+ // SPDX-License-Identifier: Apache-2.0
737
+ /**
738
+ * shouldSkipRequest determines if a request should be skipped based on the binding filters.
739
+ *
740
+ * @param binding the capability action binding
741
+ * @param req the incoming request
742
+ * @returns
743
+ */
744
+ function shouldSkipRequest(binding, req) {
745
+ const { group, kind, version } = binding.kind;
746
+ const { namespaces, labels, annotations } = binding.filters;
747
+ const { metadata } = req.object;
748
+ if (kind !== req.kind.kind) {
749
+ types.Log.debug(`${req.kind.kind} does not match ${kind}`);
750
+ return true;
751
+ }
752
+ if (group && group !== req.kind.group) {
753
+ types.Log.debug(`${req.kind.group} does not match ${group}`);
754
+ return true;
755
+ }
756
+ if (version && version !== req.kind.version) {
757
+ types.Log.debug(`${req.kind.version} does not match ${version}`);
758
+ return true;
759
+ }
760
+ if (namespaces.length && !namespaces.includes(req.namespace || "")) {
761
+ types.Log.debug(`${req.namespace} is not in ${namespaces}`);
762
+ return true;
763
+ }
764
+ for (const [key, value] of Object.entries(labels)) {
765
+ if (metadata?.labels?.[key] !== value) {
766
+ types.Log.debug(`${metadata?.labels?.[key]} does not match ${value}`);
767
+ return true;
768
+ }
769
+ }
770
+ for (const [key, value] of Object.entries(annotations)) {
771
+ if (metadata?.annotations?.[key] !== value) {
772
+ types.Log.debug(`${metadata?.annotations?.[key]} does not match ${value}`);
773
+ return true;
774
+ }
775
+ }
776
+ return false;
777
+ }
778
+
779
+ // SPDX-License-Identifier: Apache-2.0
780
+ function processor(config, capabilities, req) {
781
+ const wrapped = new RequestWrapper(req);
782
+ const response = {
783
+ uid: req.uid,
784
+ patchType: "JSONPatch",
785
+ warnings: [],
786
+ allowed: false,
787
+ };
788
+ types.Log.info(`Processing '${req.uid}' for '${req.kind.kind}' '${req.name}'`);
789
+ for (const { name, bindings } of capabilities) {
790
+ const prefix = `${req.uid} ${req.name}: ${name}`;
791
+ types.Log.info(`Processing capability ${name}`, prefix);
792
+ for (const action of bindings) {
793
+ // Continue to the next action without doing anything if this one should be skipped
794
+ if (shouldSkipRequest(action, req)) {
795
+ continue;
796
+ }
797
+ types.Log.info(`Processing matched action ${action.kind.kind}`, prefix);
798
+ // Add annotations to the request to indicate that the capability started processing
799
+ // this will allow tracking of failed mutations that were permitted to continue
800
+ const { metadata } = wrapped.Raw;
801
+ const identifier = `pepr.dev/${config.uuid}/${name}`;
802
+ metadata.annotations = metadata.annotations || {};
803
+ metadata.annotations[identifier] = "started";
804
+ try {
805
+ // Run the action
806
+ action.callback(wrapped);
807
+ // Add annotations to the request to indicate that the capability succeeded
808
+ metadata.annotations[identifier] = "succeeded";
809
+ }
810
+ catch (e) {
811
+ response.warnings.push(`Action failed: ${e}`);
812
+ // If errors are not allowed, note the failure in the Reponse
813
+ if (config.onError) {
814
+ types.Log.error(`Action failed: ${e}`, prefix);
815
+ response.result = "Pepr module configured to reject on error";
816
+ return response;
817
+ }
818
+ else {
819
+ types.Log.warn(`Action failed: ${e}`, prefix);
820
+ metadata.annotations[identifier] = "warning";
821
+ }
822
+ }
823
+ }
824
+ }
825
+ // If we've made it this far, the request is allowed
826
+ response.allowed = true;
827
+ // Compare the original request to the modified request to get the patches
828
+ const patches = fastJsonPatch.compare(req.object, wrapped.Raw);
829
+ // Only add the patch if there are patches to apply
830
+ if (patches.length > 0) {
831
+ response.patch = JSON.stringify(patches);
832
+ }
833
+ // Remove the warnings array if it's empty
834
+ if (response.warnings.length < 1) {
835
+ delete response.warnings;
836
+ }
837
+ types.Log.debug(patches);
838
+ return response;
839
+ }
840
+
841
+ // SPDX-License-Identifier: Apache-2.0
842
+ const alwaysIgnore = {
843
+ namespaces: ["kube-system", "pepr-system"],
844
+ labels: [{ "pepr.dev": "ignore" }],
845
+ };
846
+ class PeprModule {
847
+ get kinds() {
848
+ return this._kinds;
849
+ }
850
+ get UUID() {
851
+ return this._config.uuid;
852
+ }
853
+ /**
854
+ * Create a new Pepr runtime
855
+ *
856
+ * @param config The configuration for the Pepr runtime
857
+ */
858
+ constructor({ description, pepr }) {
859
+ this._state = [];
860
+ this._kinds = [];
861
+ this.Register = (capability) => {
862
+ types.Log.info(`Registering capability ${capability.name}`);
863
+ // Add the kinds to the list of kinds (ignoring duplicates for now)
864
+ this._kinds = capability.bindings.map(({ kind }) => kind);
865
+ // Add the capability to the state
866
+ this._state.push(capability);
867
+ };
868
+ this.ProcessRequest = (req) => {
869
+ return processor(this._config, this._state, req);
870
+ };
871
+ pepr.description = description;
872
+ this._config = R.mergeDeepWith(R.concat, pepr, alwaysIgnore);
873
+ }
874
+ }
875
+
876
+ Object.defineProperty(exports, 'ErrorBehavior', {
877
+ enumerable: true,
878
+ get: function () { return types.ErrorBehavior; }
879
+ });
880
+ Object.defineProperty(exports, 'Event', {
881
+ enumerable: true,
882
+ get: function () { return types.Event; }
883
+ });
884
+ Object.defineProperty(exports, 'HookPhase', {
885
+ enumerable: true,
886
+ get: function () { return types.HookPhase; }
887
+ });
888
+ exports.Log = types.Log;
889
+ Object.defineProperty(exports, 'Operation', {
890
+ enumerable: true,
891
+ get: function () { return types.Operation; }
892
+ });
893
+ exports.Capability = Capability;
894
+ exports.PeprModule = PeprModule;
895
+ exports.RequestWrapper = RequestWrapper;
896
+ exports.a = upstream;
897
+ exports.gvkMap = gvkMap;
898
+ exports.modelToGroupVersionKind = modelToGroupVersionKind;