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.
Files changed (106) hide show
  1. package/README.md +23 -4
  2. package/dist/cli.js +455 -266
  3. package/dist/controller.js +1 -1
  4. package/dist/lib/assets/deploy.d.ts.map +1 -1
  5. package/dist/lib/assets/destroy.d.ts +2 -0
  6. package/dist/lib/assets/destroy.d.ts.map +1 -0
  7. package/dist/lib/assets/index.d.ts +6 -5
  8. package/dist/lib/assets/index.d.ts.map +1 -1
  9. package/dist/lib/assets/networking.d.ts +6 -5
  10. package/dist/lib/assets/networking.d.ts.map +1 -1
  11. package/dist/lib/assets/pods.d.ts +84 -4
  12. package/dist/lib/assets/pods.d.ts.map +1 -1
  13. package/dist/lib/assets/rbac.d.ts +6 -4
  14. package/dist/lib/assets/rbac.d.ts.map +1 -1
  15. package/dist/lib/assets/store.d.ts +7 -0
  16. package/dist/lib/assets/store.d.ts.map +1 -0
  17. package/dist/lib/assets/webhooks.d.ts +2 -2
  18. package/dist/lib/assets/webhooks.d.ts.map +1 -1
  19. package/dist/lib/assets/yaml.d.ts.map +1 -1
  20. package/dist/lib/capability.d.ts +21 -4
  21. package/dist/lib/capability.d.ts.map +1 -1
  22. package/dist/lib/controller/index.d.ts +10 -0
  23. package/dist/lib/controller/index.d.ts.map +1 -0
  24. package/dist/lib/controller/store.d.ts +7 -0
  25. package/dist/lib/controller/store.d.ts.map +1 -0
  26. package/dist/lib/filter.d.ts +2 -2
  27. package/dist/lib/filter.d.ts.map +1 -1
  28. package/dist/lib/{k8s/types.d.ts → k8s.d.ts} +14 -25
  29. package/dist/lib/k8s.d.ts.map +1 -0
  30. package/dist/lib/metrics.d.ts +12 -12
  31. package/dist/lib/metrics.d.ts.map +1 -1
  32. package/dist/lib/module.d.ts +25 -4
  33. package/dist/lib/module.d.ts.map +1 -1
  34. package/dist/lib/mutate-processor.d.ts +3 -3
  35. package/dist/lib/mutate-processor.d.ts.map +1 -1
  36. package/dist/lib/mutate-request.d.ts +11 -10
  37. package/dist/lib/mutate-request.d.ts.map +1 -1
  38. package/dist/lib/storage.d.ts +56 -0
  39. package/dist/lib/storage.d.ts.map +1 -0
  40. package/dist/lib/tls.d.ts.map +1 -0
  41. package/dist/lib/types.d.ts +28 -48
  42. package/dist/lib/types.d.ts.map +1 -1
  43. package/dist/lib/validate-processor.d.ts +2 -2
  44. package/dist/lib/validate-processor.d.ts.map +1 -1
  45. package/dist/lib/validate-request.d.ts +9 -8
  46. package/dist/lib/validate-request.d.ts.map +1 -1
  47. package/dist/lib/watch-processor.d.ts +3 -0
  48. package/dist/lib/watch-processor.d.ts.map +1 -0
  49. package/dist/lib.d.ts +3 -7
  50. package/dist/lib.d.ts.map +1 -1
  51. package/dist/lib.js +484 -807
  52. package/dist/lib.js.map +4 -4
  53. package/package.json +16 -20
  54. package/src/lib/assets/deploy.ts +69 -127
  55. package/src/lib/assets/destroy.ts +33 -0
  56. package/src/lib/assets/index.ts +8 -14
  57. package/src/lib/assets/networking.ts +28 -5
  58. package/src/lib/assets/pods.ts +130 -11
  59. package/src/lib/assets/rbac.ts +42 -4
  60. package/src/lib/assets/store.ts +49 -0
  61. package/src/lib/assets/webhooks.ts +2 -2
  62. package/src/lib/assets/yaml.ts +13 -3
  63. package/src/lib/capability.ts +69 -14
  64. package/src/lib/{controller.ts → controller/index.ts} +25 -23
  65. package/src/lib/controller/store.ts +197 -0
  66. package/src/lib/filter.ts +2 -2
  67. package/src/lib/{k8s/types.ts → k8s.ts} +15 -26
  68. package/src/lib/metrics.ts +22 -38
  69. package/src/lib/module.ts +47 -10
  70. package/src/lib/mutate-processor.ts +6 -6
  71. package/src/lib/mutate-request.ts +18 -26
  72. package/src/lib/storage.ts +128 -0
  73. package/src/lib/types.ts +30 -53
  74. package/src/lib/validate-processor.ts +5 -4
  75. package/src/lib/validate-request.ts +15 -19
  76. package/src/lib/watch-processor.ts +55 -0
  77. package/src/lib.ts +4 -8
  78. package/src/templates/.eslintrc.template.json +18 -0
  79. package/src/templates/capabilities/hello-pepr.ts +54 -5
  80. package/src/templates/package.json +1 -0
  81. package/dist/lib/controller.d.ts +0 -10
  82. package/dist/lib/controller.d.ts.map +0 -1
  83. package/dist/lib/fetch.d.ts +0 -23
  84. package/dist/lib/fetch.d.ts.map +0 -1
  85. package/dist/lib/k8s/index.d.ts +0 -7
  86. package/dist/lib/k8s/index.d.ts.map +0 -1
  87. package/dist/lib/k8s/kinds.d.ts +0 -12
  88. package/dist/lib/k8s/kinds.d.ts.map +0 -1
  89. package/dist/lib/k8s/tls.d.ts.map +0 -1
  90. package/dist/lib/k8s/types.d.ts.map +0 -1
  91. package/dist/lib/k8s/upstream.d.ts +0 -4
  92. package/dist/lib/k8s/upstream.d.ts.map +0 -1
  93. package/jest.config.json +0 -4
  94. package/journey/before.ts +0 -21
  95. package/journey/k8s.ts +0 -100
  96. package/journey/pepr-build.ts +0 -69
  97. package/journey/pepr-deploy.ts +0 -174
  98. package/journey/pepr-dev.ts +0 -155
  99. package/journey/pepr-format.ts +0 -13
  100. package/journey/pepr-init.ts +0 -12
  101. package/src/lib/fetch.ts +0 -76
  102. package/src/lib/k8s/index.ts +0 -14
  103. package/src/lib/k8s/kinds.ts +0 -531
  104. package/src/lib/k8s/upstream.ts +0 -53
  105. /package/dist/lib/{k8s/tls.d.ts → tls.d.ts} +0 -0
  106. /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: () => upstream_exports,
42
- fetch: () => fetch,
43
- fetchRaw: () => fetchRaw,
44
- fetchStatus: () => import_http_status_codes2.StatusCodes,
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 k8s = __toESM(require("@kubernetes/client-node"));
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 import_ramda = require("ramda");
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
- return `${this.#prefix}_${name}`;
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
- async getMetrics() {
871
- return this.#registry.metrics();
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 (kind !== req.kind.kind) {
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 import_ramda2 = require("ramda");
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, import_ramda2.clone)(input.oldObject);
275
+ this.Raw = (0, import_ramda.clone)(input.oldObject);
987
276
  } else {
988
- this.Raw = (0, import_ramda2.clone)(input.object);
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, import_ramda2.mergeDeepRight)(this.Raw, obj);
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 import_ramda3 = require("ramda");
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, import_ramda3.clone)(input.oldObject);
510
+ this.Raw = (0, import_ramda2.clone)(input.oldObject);
1226
511
  } else {
1227
- this.Raw = (0, import_ramda3.clone)(input.object);
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.RawP");
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
- this.#beforeHook = beforeHook;
1344
- this.#afterHook = afterHook;
1345
- this.startServer = this.startServer.bind(this);
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 = this.#metricsCollector.observeStart();
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 namespace = request?.namespace || "";
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
- this.start = this.start.bind(this);
1535
- if (process.env.PEPR_MODE === "build" && process.send) {
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
- k8s
1255
+ kind
1579
1256
  });
1580
1257
  //# sourceMappingURL=lib.js.map