pepr 0.13.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/cli.js +37 -28
  2. package/dist/controller.js +1 -1
  3. package/dist/lib/assets/index.d.ts +5 -6
  4. package/dist/lib/assets/index.d.ts.map +1 -1
  5. package/dist/lib/assets/loader.d.ts +2 -8
  6. package/dist/lib/assets/loader.d.ts.map +1 -1
  7. package/dist/lib/capability.d.ts +4 -7
  8. package/dist/lib/capability.d.ts.map +1 -1
  9. package/dist/lib/controller.d.ts +3 -49
  10. package/dist/lib/controller.d.ts.map +1 -1
  11. package/dist/lib/errors.d.ts +12 -0
  12. package/dist/lib/errors.d.ts.map +1 -0
  13. package/dist/lib/k8s/types.d.ts +5 -1
  14. package/dist/lib/k8s/types.d.ts.map +1 -1
  15. package/dist/lib/metrics.d.ts +6 -12
  16. package/dist/lib/metrics.d.ts.map +1 -1
  17. package/dist/lib/module.d.ts +2 -2
  18. package/dist/lib/module.d.ts.map +1 -1
  19. package/dist/lib/mutate-processor.d.ts.map +1 -1
  20. package/dist/lib/mutate-request.d.ts +2 -2
  21. package/dist/lib/mutate-request.d.ts.map +1 -1
  22. package/dist/lib/types.d.ts +3 -9
  23. package/dist/lib/types.d.ts.map +1 -1
  24. package/dist/lib/validate-processor.d.ts.map +1 -1
  25. package/dist/lib/validate-request.d.ts +2 -2
  26. package/dist/lib/validate-request.d.ts.map +1 -1
  27. package/dist/lib.js +247 -191
  28. package/dist/lib.js.map +3 -3
  29. package/jest.config.json +4 -0
  30. package/journey/before.ts +21 -0
  31. package/journey/k8s.ts +81 -0
  32. package/journey/pepr-build.ts +69 -0
  33. package/journey/pepr-deploy.ts +133 -0
  34. package/journey/pepr-dev.ts +155 -0
  35. package/journey/pepr-format.ts +13 -0
  36. package/journey/pepr-init.ts +12 -0
  37. package/package.json +20 -21
  38. package/src/lib/assets/index.ts +15 -8
  39. package/src/lib/assets/loader.ts +4 -12
  40. package/src/lib/assets/webhooks.ts +2 -2
  41. package/src/lib/capability.ts +34 -32
  42. package/src/lib/controller.ts +111 -93
  43. package/src/lib/errors.ts +20 -0
  44. package/src/lib/k8s/types.ts +5 -1
  45. package/src/lib/metrics.ts +45 -32
  46. package/src/lib/module.ts +24 -10
  47. package/src/lib/mutate-processor.ts +5 -3
  48. package/src/lib/mutate-request.ts +22 -9
  49. package/src/lib/types.ts +4 -10
  50. package/src/lib/validate-processor.ts +8 -0
  51. package/src/lib/validate-request.ts +19 -7
package/dist/lib.js CHANGED
@@ -611,27 +611,28 @@ var logger_default = Log;
611
611
 
612
612
  // src/lib/capability.ts
613
613
  var Capability = class {
614
- _name;
615
- _description;
616
- _namespaces;
617
- _bindings = [];
614
+ #name;
615
+ #description;
616
+ #namespaces;
617
+ #bindings = [];
618
618
  get bindings() {
619
- return this._bindings;
619
+ return this.#bindings;
620
620
  }
621
621
  get name() {
622
- return this._name;
622
+ return this.#name;
623
623
  }
624
624
  get description() {
625
- return this._description;
625
+ return this.#description;
626
626
  }
627
627
  get namespaces() {
628
- return this._namespaces || [];
628
+ return this.#namespaces || [];
629
629
  }
630
630
  constructor(cfg) {
631
- this._name = cfg.name;
632
- this._description = cfg.description;
633
- this._namespaces = cfg.namespaces;
634
- logger_default.info(`Capability ${this._name} registered`);
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`);
635
636
  logger_default.debug(cfg);
636
637
  }
637
638
  /**
@@ -643,7 +644,7 @@ var Capability = class {
643
644
  * @param kind if using a custom KubernetesObject not available in `a.*`, specify the GroupVersionKind
644
645
  * @returns
645
646
  */
646
- When = (model, kind) => {
647
+ When(model, kind) {
647
648
  const matchedKind = modelToGroupVersionKind(model.name);
648
649
  if (!matchedKind && !kind) {
649
650
  throw new Error(`Kind not specified for ${model.name}`);
@@ -659,7 +660,9 @@ var Capability = class {
659
660
  annotations: {}
660
661
  }
661
662
  };
662
- const prefix = `${this._name}: ${model.name}`;
663
+ const bindings = this.#bindings;
664
+ const prefix = `${this.#name}: ${model.name}`;
665
+ const commonChain = { WithLabel, WithAnnotation, Mutate, Validate };
663
666
  const isNotEmpty = (value) => Object.keys(value).length > 0;
664
667
  const log = (message, cbString) => {
665
668
  const filteredObj = (0, import_ramda.pickBy)(isNotEmpty, binding.filters);
@@ -667,27 +670,27 @@ var Capability = class {
667
670
  logger_default.info(filteredObj, prefix);
668
671
  logger_default.debug(cbString, prefix);
669
672
  };
670
- const Validate = (validateCallback) => {
673
+ function Validate(validateCallback) {
671
674
  if (!isWatchMode) {
672
675
  log("Validate Action", validateCallback.toString());
673
- this._bindings.push({
676
+ bindings.push({
674
677
  ...binding,
675
678
  isValidate: true,
676
679
  validateCallback
677
680
  });
678
681
  }
679
- };
680
- const Mutate = (mutateCallback) => {
682
+ }
683
+ function Mutate(mutateCallback) {
681
684
  if (!isWatchMode) {
682
685
  log("Mutate Action", mutateCallback.toString());
683
- this._bindings.push({
686
+ bindings.push({
684
687
  ...binding,
685
688
  isMutate: true,
686
689
  mutateCallback
687
690
  });
688
691
  }
689
692
  return { Validate };
690
- };
693
+ }
691
694
  function InNamespace(...namespaces) {
692
695
  logger_default.debug(`Add namespaces filter ${namespaces}`, prefix);
693
696
  binding.filters.namespaces.push(...namespaces);
@@ -703,27 +706,26 @@ var Capability = class {
703
706
  binding.filters.labels[key] = value;
704
707
  return commonChain;
705
708
  }
706
- const WithAnnotation = (key, value = "") => {
709
+ function WithAnnotation(key, value = "") {
707
710
  logger_default.debug(`Add annotation filter ${key}=${value}`, prefix);
708
711
  binding.filters.annotations[key] = value;
709
712
  return commonChain;
710
- };
711
- const commonChain = { WithLabel, WithAnnotation, Mutate, Validate };
712
- const bindEvent = (event) => {
713
+ }
714
+ function bindEvent(event) {
713
715
  binding.event = event;
714
716
  return {
715
717
  ...commonChain,
716
718
  InNamespace,
717
719
  WithName
718
720
  };
719
- };
721
+ }
720
722
  return {
721
723
  IsCreatedOrUpdated: () => bindEvent("CREATEORUPDATE" /* CreateOrUpdate */),
722
724
  IsCreated: () => bindEvent("CREATE" /* Create */),
723
725
  IsUpdated: () => bindEvent("UPDATE" /* Update */),
724
726
  IsDeleted: () => bindEvent("DELETE" /* Delete */)
725
727
  };
726
- };
728
+ }
727
729
  };
728
730
 
729
731
  // src/lib/fetch.ts
@@ -778,15 +780,15 @@ var import_fs = __toESM(require("fs"));
778
780
  var import_https = __toESM(require("https"));
779
781
 
780
782
  // src/lib/metrics.ts
781
- var import_prom_client = __toESM(require("prom-client"));
782
783
  var import_perf_hooks = require("perf_hooks");
784
+ var import_prom_client = __toESM(require("prom-client"));
783
785
  var loggingPrefix = "MetricsCollector";
784
786
  var MetricsCollector = class {
785
- _registry;
786
- _counters = /* @__PURE__ */ new Map();
787
- _summaries = /* @__PURE__ */ new Map();
788
- _prefix;
789
- _metricNames = {
787
+ #registry;
788
+ #counters = /* @__PURE__ */ new Map();
789
+ #summaries = /* @__PURE__ */ new Map();
790
+ #prefix;
791
+ #metricNames = {
790
792
  errors: "errors",
791
793
  alerts: "alerts",
792
794
  mutate: "Mutate",
@@ -794,79 +796,98 @@ var MetricsCollector = class {
794
796
  };
795
797
  /**
796
798
  * Creates a MetricsCollector instance with prefixed metrics.
797
- * @param {string} [prefix='pepr'] - The prefix for the metric names.
799
+ * @param [prefix='pepr'] - The prefix for the metric names.
798
800
  */
799
801
  constructor(prefix = "pepr") {
800
- this._registry = new import_prom_client.Registry();
801
- this._prefix = prefix;
802
- this.addCounter(this._metricNames.errors, "Mutation/Validate errors encountered");
803
- this.addCounter(this._metricNames.alerts, "Mutation/Validate bad api token received");
804
- this.addSummary(this._metricNames.mutate, "Mutation operation summary");
805
- this.addSummary(this._metricNames.validate, "Validation operation summary");
806
- }
807
- getMetricName(name) {
808
- return `${this._prefix}_${name}`;
809
- }
810
- addMetric(collection, MetricType, name, help) {
811
- if (collection.has(this.getMetricName(name))) {
802
+ this.#registry = new import_prom_client.Registry();
803
+ this.#prefix = prefix;
804
+ this.addCounter(this.#metricNames.errors, "Mutation/Validate errors encountered");
805
+ this.addCounter(this.#metricNames.alerts, "Mutation/Validate bad api token received");
806
+ this.addSummary(this.#metricNames.mutate, "Mutation operation summary");
807
+ this.addSummary(this.#metricNames.validate, "Validation operation summary");
808
+ }
809
+ #getMetricName(name) {
810
+ return `${this.#prefix}_${name}`;
811
+ }
812
+ #addMetric(collection, MetricType, name, help) {
813
+ if (collection.has(this.#getMetricName(name))) {
812
814
  logger_default.debug(`Metric for ${name} already exists`, loggingPrefix);
813
815
  return;
814
816
  }
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);
815
823
  const metric = new MetricType({
816
- name: this.getMetricName(name),
824
+ name: this.#getMetricName(name),
817
825
  help,
818
- registers: [this._registry]
826
+ registers: [this.#registry]
819
827
  });
820
- collection.set(this.getMetricName(name), metric);
828
+ collection.set(this.#getMetricName(name), metric);
821
829
  }
822
830
  addCounter(name, help) {
823
- this.addMetric(this._counters, import_prom_client.default.Counter, name, help);
831
+ this.#addMetric(this.#counters, import_prom_client.default.Counter, name, help);
824
832
  }
825
833
  addSummary(name, help) {
826
- this.addMetric(this._summaries, import_prom_client.default.Summary, name, help);
834
+ this.#addMetric(this.#summaries, import_prom_client.default.Summary, name, help);
827
835
  }
828
836
  incCounter(name) {
829
- this._counters.get(this.getMetricName(name))?.inc();
837
+ this.#counters.get(this.#getMetricName(name))?.inc();
830
838
  }
831
839
  /**
832
840
  * Increments the error counter.
833
841
  */
834
842
  error() {
835
- this.incCounter(this._metricNames.errors);
843
+ this.incCounter(this.#metricNames.errors);
836
844
  }
837
845
  /**
838
846
  * Increments the alerts counter.
839
847
  */
840
848
  alert() {
841
- this.incCounter(this._metricNames.alerts);
849
+ this.incCounter(this.#metricNames.alerts);
842
850
  }
843
851
  /**
844
852
  * Returns the current timestamp from performance.now() method. Useful for start timing an operation.
845
- * @returns {number} The timestamp.
853
+ * @returns The timestamp.
846
854
  */
847
855
  observeStart() {
848
856
  return import_perf_hooks.performance.now();
849
857
  }
850
858
  /**
851
859
  * Observes the duration since the provided start time and updates the summary.
852
- * @param {number} startTime - The start time.
853
- * @param {string} name - The metrics summary to increment.
860
+ * @param startTime - The start time.
861
+ * @param name - The metrics summary to increment.
854
862
  */
855
- observeEnd(startTime, name = this._metricNames.mutate) {
856
- this._summaries.get(this.getMetricName(name))?.observe(import_perf_hooks.performance.now() - startTime);
863
+ observeEnd(startTime, name = this.#metricNames.mutate) {
864
+ this.#summaries.get(this.#getMetricName(name))?.observe(import_perf_hooks.performance.now() - startTime);
857
865
  }
858
866
  /**
859
867
  * Fetches the current metrics from the registry.
860
- * @returns {Promise<string>} The metrics.
868
+ * @returns The metrics.
861
869
  */
862
870
  async getMetrics() {
863
- return this._registry.metrics();
871
+ return this.#registry.metrics();
864
872
  }
865
873
  };
866
874
 
867
875
  // src/lib/mutate-processor.ts
868
876
  var import_fast_json_patch = __toESM(require("fast-json-patch"));
869
877
 
878
+ // src/lib/errors.ts
879
+ var Errors = {
880
+ audit: "audit",
881
+ ignore: "ignore",
882
+ reject: "reject"
883
+ };
884
+ var ErrorList = Object.values(Errors);
885
+ function ValidateError(error = "") {
886
+ if (!ErrorList.includes(error)) {
887
+ throw new Error(`Invalid error: ${error}. Must be one of: ${ErrorList.join(", ")}`);
888
+ }
889
+ }
890
+
870
891
  // src/lib/filter.ts
871
892
  function shouldSkipRequest(binding, req) {
872
893
  const { group, kind, version } = binding.kind || {};
@@ -921,45 +942,53 @@ function shouldSkipRequest(binding, req) {
921
942
  // src/lib/mutate-request.ts
922
943
  var import_ramda2 = require("ramda");
923
944
  var PeprMutateRequest = class {
924
- /**
925
- * Creates a new instance of the action class.
926
- * @param input - The request object containing the Kubernetes resource to modify.
927
- */
928
- constructor(_input) {
929
- this._input = _input;
930
- if (_input.operation.toUpperCase() === "DELETE" /* DELETE */) {
931
- this.Raw = (0, import_ramda2.clone)(_input.oldObject);
932
- } else {
933
- this.Raw = (0, import_ramda2.clone)(_input.object);
934
- }
935
- if (!this.Raw) {
936
- throw new Error("unable to load the request object into PeprRequest.RawP");
937
- }
938
- }
939
945
  Raw;
946
+ #input;
940
947
  get PermitSideEffects() {
941
- return !this._input.dryRun;
948
+ return !this.#input.dryRun;
942
949
  }
943
950
  /**
944
951
  * Indicates whether the request is a dry run.
945
952
  * @returns true if the request is a dry run, false otherwise.
946
953
  */
947
954
  get IsDryRun() {
948
- return this._input.dryRun;
955
+ return this.#input.dryRun;
949
956
  }
950
957
  /**
951
958
  * Provides access to the old resource in the request if available.
952
959
  * @returns The old Kubernetes resource object or null if not available.
953
960
  */
954
961
  get OldResource() {
955
- return this._input.oldObject;
962
+ return this.#input.oldObject;
956
963
  }
957
964
  /**
958
965
  * Provides access to the request object.
959
966
  * @returns The request object containing the Kubernetes resource.
960
967
  */
961
968
  get Request() {
962
- return this._input;
969
+ return this.#input;
970
+ }
971
+ /**
972
+ * Creates a new instance of the action class.
973
+ * @param input - The request object containing the Kubernetes resource to modify.
974
+ */
975
+ constructor(input) {
976
+ this.#input = input;
977
+ this.Merge = this.Merge.bind(this);
978
+ this.SetLabel = this.SetLabel.bind(this);
979
+ this.SetAnnotation = this.SetAnnotation.bind(this);
980
+ this.RemoveLabel = this.RemoveLabel.bind(this);
981
+ this.RemoveAnnotation = this.RemoveAnnotation.bind(this);
982
+ this.HasLabel = this.HasLabel.bind(this);
983
+ this.HasAnnotation = this.HasAnnotation.bind(this);
984
+ if (input.operation.toUpperCase() === "DELETE" /* DELETE */) {
985
+ this.Raw = (0, import_ramda2.clone)(input.oldObject);
986
+ } else {
987
+ this.Raw = (0, import_ramda2.clone)(input.object);
988
+ }
989
+ if (!this.Raw) {
990
+ throw new Error("unable to load the request object into PeprRequest.RawP");
991
+ }
963
992
  }
964
993
  /**
965
994
  * Deep merges the provided object with the current resource.
@@ -1126,11 +1155,13 @@ async function mutateProcessor(config, capabilities, req, reqMetadata) {
1126
1155
  response.warnings = response.warnings || [];
1127
1156
  response.warnings.push(`Action failed: ${e}`);
1128
1157
  switch (config.onError) {
1129
- case "reject":
1158
+ case Errors.reject:
1130
1159
  logger_default.error(actionMetadata, `Action failed: ${e}`);
1131
1160
  response.result = "Pepr module configured to reject on error";
1132
1161
  return response;
1133
- case "audit":
1162
+ case Errors.audit:
1163
+ response.auditAnnotations = response.auditAnnotations || {};
1164
+ response.auditAnnotations[Date.now()] = e;
1134
1165
  break;
1135
1166
  }
1136
1167
  }
@@ -1163,35 +1194,40 @@ async function mutateProcessor(config, capabilities, req, reqMetadata) {
1163
1194
  // src/lib/validate-request.ts
1164
1195
  var import_ramda3 = require("ramda");
1165
1196
  var PeprValidateRequest = class {
1166
- /**
1167
- * Creates a new instance of the Action class.
1168
- * @param input - The request object containing the Kubernetes resource to modify.
1169
- */
1170
- constructor(_input) {
1171
- this._input = _input;
1172
- if (_input.operation.toUpperCase() === "DELETE" /* DELETE */) {
1173
- this.Raw = (0, import_ramda3.clone)(_input.oldObject);
1174
- } else {
1175
- this.Raw = (0, import_ramda3.clone)(_input.object);
1176
- }
1177
- if (!this.Raw) {
1178
- throw new Error("unable to load the request object into PeprRequest.RawP");
1179
- }
1180
- }
1181
1197
  Raw;
1198
+ #input;
1182
1199
  /**
1183
1200
  * Provides access to the old resource in the request if available.
1184
1201
  * @returns The old Kubernetes resource object or null if not available.
1185
1202
  */
1186
1203
  get OldResource() {
1187
- return this._input.oldObject;
1204
+ return this.#input.oldObject;
1188
1205
  }
1189
1206
  /**
1190
1207
  * Provides access to the request object.
1191
1208
  * @returns The request object containing the Kubernetes resource.
1192
1209
  */
1193
1210
  get Request() {
1194
- return this._input;
1211
+ return this.#input;
1212
+ }
1213
+ /**
1214
+ * Creates a new instance of the Action class.
1215
+ * @param input - The request object containing the Kubernetes resource to modify.
1216
+ */
1217
+ constructor(input) {
1218
+ this.#input = input;
1219
+ this.HasLabel = this.HasLabel.bind(this);
1220
+ this.HasAnnotation = this.HasAnnotation.bind(this);
1221
+ this.Approve = this.Approve.bind(this);
1222
+ this.Deny = this.Deny.bind(this);
1223
+ if (input.operation.toUpperCase() === "DELETE" /* DELETE */) {
1224
+ this.Raw = (0, import_ramda3.clone)(input.oldObject);
1225
+ } else {
1226
+ this.Raw = (0, import_ramda3.clone)(input.object);
1227
+ }
1228
+ if (!this.Raw) {
1229
+ throw new Error("unable to load the request object into PeprRequest.RawP");
1230
+ }
1195
1231
  }
1196
1232
  /**
1197
1233
  * Check if a label exists on the Kubernetes resource.
@@ -1245,6 +1281,10 @@ async function validateProcessor(capabilities, req, reqMetadata) {
1245
1281
  allowed: true
1246
1282
  // Assume it's allowed until a validation check fails
1247
1283
  };
1284
+ const isSecret = req.kind.version == "v1" && req.kind.kind == "Secret";
1285
+ if (isSecret) {
1286
+ convertFromBase64Map(wrapped.Raw);
1287
+ }
1248
1288
  logger_default.info(reqMetadata, `Processing validation request`);
1249
1289
  for (const { name, bindings } of capabilities) {
1250
1290
  const actionMetadata = { ...reqMetadata, name };
@@ -1282,40 +1322,39 @@ async function validateProcessor(capabilities, req, reqMetadata) {
1282
1322
  }
1283
1323
 
1284
1324
  // src/lib/controller.ts
1285
- var Controller = class {
1286
- constructor(_config, _capabilities, _beforeHook, _afterHook) {
1287
- this._config = _config;
1288
- this._capabilities = _capabilities;
1289
- this._beforeHook = _beforeHook;
1290
- this._afterHook = _afterHook;
1291
- this._app.use(this.logger);
1292
- this._app.use(import_express.default.json({ limit: "2mb" }));
1293
- if (_beforeHook) {
1294
- logger_default.info(`Using beforeHook: ${_beforeHook}`);
1325
+ var Controller = class _Controller {
1326
+ // Track whether the server is running
1327
+ #running = false;
1328
+ // Metrics collector
1329
+ #metricsCollector = new MetricsCollector("pepr");
1330
+ // The token used to authenticate requests
1331
+ #token = "";
1332
+ // The express app instance
1333
+ #app = (0, import_express.default)();
1334
+ // Initialized with the constructor
1335
+ #config;
1336
+ #capabilities;
1337
+ #beforeHook;
1338
+ #afterHook;
1339
+ constructor(config, capabilities, beforeHook, afterHook) {
1340
+ this.#config = config;
1341
+ this.#capabilities = capabilities;
1342
+ this.#beforeHook = beforeHook;
1343
+ this.#afterHook = afterHook;
1344
+ this.startServer = this.startServer.bind(this);
1345
+ this.#app.use(_Controller.#logger);
1346
+ this.#app.use(import_express.default.json({ limit: "2mb" }));
1347
+ if (beforeHook) {
1348
+ logger_default.info(`Using beforeHook: ${beforeHook}`);
1295
1349
  }
1296
- if (_afterHook) {
1297
- logger_default.info(`Using afterHook: ${_afterHook}`);
1350
+ if (afterHook) {
1351
+ logger_default.info(`Using afterHook: ${afterHook}`);
1298
1352
  }
1299
- this.bindEndpoints();
1353
+ this.#bindEndpoints();
1300
1354
  }
1301
- _app = (0, import_express.default)();
1302
- _running = false;
1303
- metricsCollector = new MetricsCollector("pepr");
1304
- // The token used to authenticate requests
1305
- _token = "";
1306
- bindEndpoints = () => {
1307
- this._app.get("/healthz", this.healthz);
1308
- this._app.get("/metrics", this.metrics);
1309
- if (isWatchMode) {
1310
- return;
1311
- }
1312
- this._app.use(["/mutate/:token", "/validate/:token"], this.validateToken);
1313
- this._app.post("/mutate/:token", this.admissionReq("Mutate"));
1314
- this._app.post("/validate/:token", this.admissionReq("Validate"));
1315
- };
1316
1355
  /** Start the webhook server */
1317
- startServer = (port) => {
1318
- if (this._running) {
1356
+ startServer(port) {
1357
+ if (this.#running) {
1319
1358
  throw new Error("Cannot start Pepr module: Pepr module was not instantiated with deferStart=true");
1320
1359
  }
1321
1360
  const options = {
@@ -1323,16 +1362,16 @@ var Controller = class {
1323
1362
  cert: import_fs.default.readFileSync(process.env.SSL_CERT_PATH || "/etc/certs/tls.crt")
1324
1363
  };
1325
1364
  if (!isWatchMode) {
1326
- this._token = process.env.PEPR_API_TOKEN || import_fs.default.readFileSync("/app/api-token/value").toString().trim();
1327
- logger_default.info(`Using API token: ${this._token}`);
1328
- if (!this._token) {
1365
+ this.#token = process.env.PEPR_API_TOKEN || import_fs.default.readFileSync("/app/api-token/value").toString().trim();
1366
+ logger_default.info(`Using API token: ${this.#token}`);
1367
+ if (!this.#token) {
1329
1368
  throw new Error("API token not found");
1330
1369
  }
1331
1370
  }
1332
- const server = import_https.default.createServer(options, this._app).listen(port);
1371
+ const server = import_https.default.createServer(options, this.#app).listen(port);
1333
1372
  server.on("listening", () => {
1334
1373
  logger_default.info(`Server listening on port ${port}`);
1335
- this._running = true;
1374
+ this.#running = true;
1336
1375
  });
1337
1376
  server.on("error", (e) => {
1338
1377
  if (e.code === "EADDRINUSE") {
@@ -1352,28 +1391,16 @@ var Controller = class {
1352
1391
  process.exit(0);
1353
1392
  });
1354
1393
  });
1355
- };
1356
- /**
1357
- * Middleware for logging requests
1358
- *
1359
- * @param req the incoming request
1360
- * @param res the outgoing response
1361
- * @param next the next middleware function
1362
- */
1363
- logger = (req, res, next) => {
1364
- const startTime = Date.now();
1365
- res.on("finish", () => {
1366
- const elapsedTime = Date.now() - startTime;
1367
- const message = {
1368
- uid: req.body?.request?.uid,
1369
- method: req.method,
1370
- url: req.originalUrl,
1371
- status: res.statusCode,
1372
- duration: `${elapsedTime} ms`
1373
- };
1374
- res.statusCode >= 300 ? logger_default.warn(message) : logger_default.info(message);
1375
- });
1376
- next();
1394
+ }
1395
+ #bindEndpoints = () => {
1396
+ this.#app.get("/healthz", _Controller.#healthz);
1397
+ this.#app.get("/metrics", this.#metrics);
1398
+ if (isWatchMode) {
1399
+ return;
1400
+ }
1401
+ this.#app.use(["/mutate/:token", "/validate/:token"], this.#validateToken);
1402
+ this.#app.post("/mutate/:token", this.#admissionReq("Mutate"));
1403
+ this.#app.post("/validate/:token", this.#admissionReq("Validate"));
1377
1404
  };
1378
1405
  /**
1379
1406
  * Validate the token in the request path
@@ -1383,40 +1410,26 @@ var Controller = class {
1383
1410
  * @param next The next middleware function
1384
1411
  * @returns
1385
1412
  */
1386
- validateToken = (req, res, next) => {
1413
+ #validateToken = (req, res, next) => {
1387
1414
  const { token } = req.params;
1388
- if (token !== this._token) {
1415
+ if (token !== this.#token) {
1389
1416
  const err = `Unauthorized: invalid token '${token.replace(/[^\w]/g, "_")}'`;
1390
1417
  logger_default.warn(err);
1391
1418
  res.status(401).send(err);
1392
- this.metricsCollector.alert();
1419
+ this.#metricsCollector.alert();
1393
1420
  return;
1394
1421
  }
1395
1422
  next();
1396
1423
  };
1397
- /**
1398
- * Health check endpoint handler
1399
- *
1400
- * @param req the incoming request
1401
- * @param res the outgoing response
1402
- */
1403
- healthz = (req, res) => {
1404
- try {
1405
- res.send("OK");
1406
- } catch (err) {
1407
- logger_default.error(err);
1408
- res.status(500).send("Internal Server Error");
1409
- }
1410
- };
1411
1424
  /**
1412
1425
  * Metrics endpoint handler
1413
1426
  *
1414
1427
  * @param req the incoming request
1415
1428
  * @param res the outgoing response
1416
1429
  */
1417
- metrics = async (req, res) => {
1430
+ #metrics = async (req, res) => {
1418
1431
  try {
1419
- res.send(await this.metricsCollector.getMetrics());
1432
+ res.send(await this.#metricsCollector.getMetrics());
1420
1433
  } catch (err) {
1421
1434
  logger_default.error(err);
1422
1435
  res.status(500).send("Internal Server Error");
@@ -1428,12 +1441,12 @@ var Controller = class {
1428
1441
  * @param admissionKind the type of admission request
1429
1442
  * @returns the request handler
1430
1443
  */
1431
- admissionReq = (admissionKind) => {
1444
+ #admissionReq = (admissionKind) => {
1432
1445
  return async (req, res) => {
1433
- const startTime = this.metricsCollector.observeStart();
1446
+ const startTime = this.#metricsCollector.observeStart();
1434
1447
  try {
1435
1448
  const request = req.body?.request || {};
1436
- this._beforeHook && this._beforeHook(request || {});
1449
+ this.#beforeHook && this.#beforeHook(request || {});
1437
1450
  const name = request?.name ? `/${request.name}` : "";
1438
1451
  const namespace = request?.namespace || "";
1439
1452
  const gvk = request?.kind || { group: "", version: "", kind: "" };
@@ -1446,49 +1459,92 @@ var Controller = class {
1446
1459
  logger_default.debug({ ...reqMetadata, request }, "Incoming request body");
1447
1460
  let response;
1448
1461
  if (admissionKind === "Mutate") {
1449
- response = await mutateProcessor(this._config, this._capabilities, request, reqMetadata);
1462
+ response = await mutateProcessor(this.#config, this.#capabilities, request, reqMetadata);
1450
1463
  } else {
1451
- response = await validateProcessor(this._capabilities, request, reqMetadata);
1464
+ response = await validateProcessor(this.#capabilities, request, reqMetadata);
1452
1465
  }
1453
- this._afterHook && this._afterHook(response);
1466
+ this.#afterHook && this.#afterHook(response);
1454
1467
  logger_default.debug({ ...reqMetadata, response }, "Outgoing response");
1455
1468
  res.send({
1456
1469
  apiVersion: "admission.k8s.io/v1",
1457
1470
  kind: "AdmissionReview",
1458
1471
  response
1459
1472
  });
1460
- this.metricsCollector.observeEnd(startTime, admissionKind);
1473
+ this.#metricsCollector.observeEnd(startTime, admissionKind);
1461
1474
  } catch (err) {
1462
1475
  logger_default.error(err);
1463
1476
  res.status(500).send("Internal Server Error");
1464
- this.metricsCollector.error();
1477
+ this.#metricsCollector.error();
1465
1478
  }
1466
1479
  };
1467
1480
  };
1481
+ /**
1482
+ * Middleware for logging requests
1483
+ *
1484
+ * @param req the incoming request
1485
+ * @param res the outgoing response
1486
+ * @param next the next middleware function
1487
+ */
1488
+ static #logger(req, res, next) {
1489
+ const startTime = Date.now();
1490
+ res.on("finish", () => {
1491
+ const elapsedTime = Date.now() - startTime;
1492
+ const message = {
1493
+ uid: req.body?.request?.uid,
1494
+ method: req.method,
1495
+ url: req.originalUrl,
1496
+ status: res.statusCode,
1497
+ duration: `${elapsedTime} ms`
1498
+ };
1499
+ res.statusCode >= 300 ? logger_default.warn(message) : logger_default.info(message);
1500
+ });
1501
+ next();
1502
+ }
1503
+ /**
1504
+ * Health check endpoint handler
1505
+ *
1506
+ * @param req the incoming request
1507
+ * @param res the outgoing response
1508
+ */
1509
+ static #healthz(req, res) {
1510
+ try {
1511
+ res.send("OK");
1512
+ } catch (err) {
1513
+ logger_default.error(err);
1514
+ res.status(500).send("Internal Server Error");
1515
+ }
1516
+ }
1468
1517
  };
1469
1518
 
1470
1519
  // src/lib/module.ts
1471
1520
  var PeprModule = class {
1472
- _controller;
1521
+ #controller;
1473
1522
  /**
1474
1523
  * Create a new Pepr runtime
1475
1524
  *
1476
1525
  * @param config The configuration for the Pepr runtime
1477
1526
  * @param capabilities The capabilities to be loaded into the Pepr runtime
1478
- * @param _deferStart (optional) If set to `true`, the Pepr runtime will not be started automatically. This can be used to start the Pepr runtime manually with `start()`.
1527
+ * @param opts Options for the Pepr runtime
1479
1528
  */
1480
1529
  constructor({ description, pepr }, capabilities = [], opts = {}) {
1481
1530
  const config = (0, import_ramda4.clone)(pepr);
1482
1531
  config.description = description;
1483
- const validOnErrors = ["ignore", "warn", "fail"];
1484
- if (!validOnErrors.includes(config.onError || "")) {
1485
- throw new Error(`Invalid onErrors value: ${config.onError}`);
1486
- }
1532
+ ValidateError(config.onError);
1533
+ this.start = this.start.bind(this);
1487
1534
  if (process.env.PEPR_MODE === "build" && process.send) {
1488
- process.send(capabilities);
1535
+ const exportedCapabilities = [];
1536
+ for (const capability of capabilities) {
1537
+ exportedCapabilities.push({
1538
+ name: capability.name,
1539
+ description: capability.description,
1540
+ namespaces: capability.namespaces,
1541
+ bindings: capability.bindings
1542
+ });
1543
+ }
1544
+ process.send(exportedCapabilities);
1489
1545
  return;
1490
1546
  }
1491
- this._controller = new Controller(config, capabilities, opts.beforeHook, opts.afterHook);
1547
+ this.#controller = new Controller(config, capabilities, opts.beforeHook, opts.afterHook);
1492
1548
  if (opts.deferStart) {
1493
1549
  return;
1494
1550
  }
@@ -1501,7 +1557,7 @@ var PeprModule = class {
1501
1557
  * @param port
1502
1558
  */
1503
1559
  start(port = 3e3) {
1504
- this._controller.startServer(port);
1560
+ this.#controller.startServer(port);
1505
1561
  }
1506
1562
  };
1507
1563
  // Annotate the CommonJS export names for ESM import in node: