pepr 0.14.2 → 0.16.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 (85) hide show
  1. package/BEST_PRACTICES.md +37 -0
  2. package/dist/cli.js +100 -47
  3. package/dist/controller.js +1 -1
  4. package/dist/lib/assets/deploy.d.ts.map +1 -1
  5. package/dist/lib/assets/index.d.ts +1 -1
  6. package/dist/lib/assets/index.d.ts.map +1 -1
  7. package/dist/lib/assets/rbac.d.ts +2 -1
  8. package/dist/lib/assets/rbac.d.ts.map +1 -1
  9. package/dist/lib/assets/yaml.d.ts +1 -1
  10. package/dist/lib/assets/yaml.d.ts.map +1 -1
  11. package/dist/lib/capability.d.ts +25 -0
  12. package/dist/lib/capability.d.ts.map +1 -1
  13. package/dist/lib/controller/index.d.ts.map +1 -1
  14. package/dist/lib/controller/store.d.ts +2 -1
  15. package/dist/lib/controller/store.d.ts.map +1 -1
  16. package/dist/lib/helpers.d.ts +12 -0
  17. package/dist/lib/helpers.d.ts.map +1 -0
  18. package/dist/lib/module.d.ts.map +1 -1
  19. package/dist/lib/schedule.d.ts +76 -0
  20. package/dist/lib/schedule.d.ts.map +1 -0
  21. package/dist/lib/storage.d.ts +14 -0
  22. package/dist/lib/storage.d.ts.map +1 -1
  23. package/dist/lib/types.d.ts +1 -0
  24. package/dist/lib/types.d.ts.map +1 -1
  25. package/dist/lib.d.ts +3 -6
  26. package/dist/lib.d.ts.map +1 -1
  27. package/dist/lib.js +236 -9
  28. package/dist/lib.js.map +4 -4
  29. package/package.json +16 -16
  30. package/src/lib/assets/deploy.ts +4 -3
  31. package/src/lib/assets/index.ts +2 -2
  32. package/src/lib/assets/rbac.ts +27 -11
  33. package/src/lib/assets/yaml.ts +2 -2
  34. package/src/lib/capability.ts +72 -0
  35. package/src/lib/controller/index.ts +5 -1
  36. package/src/lib/controller/store.ts +29 -11
  37. package/src/lib/helpers.ts +52 -0
  38. package/src/lib/module.ts +1 -0
  39. package/src/lib/schedule.ts +175 -0
  40. package/src/lib/storage.ts +33 -0
  41. package/src/lib/types.ts +1 -0
  42. package/src/lib.ts +9 -16
  43. package/src/templates/capabilities/hello-pepr.ts +16 -11
  44. package/website/.linkinator.config.json +8 -0
  45. package/website/.markdownlint.json +6 -0
  46. package/website/.prettierignore +12 -0
  47. package/website/LICENSE +201 -0
  48. package/website/README.md +50 -0
  49. package/website/archetypes/default.md +6 -0
  50. package/website/assets/img/doug.svg +345 -0
  51. package/website/assets/img/pepr.svg +212 -0
  52. package/website/assets/scss/_styles_project.scss +3 -0
  53. package/website/assets/scss/_variables_project.scss +1 -0
  54. package/website/content/en/docs/OnSchedule.md +86 -0
  55. package/website/content/en/docs/_index.md +9 -0
  56. package/website/content/en/docs/cli.md +64 -0
  57. package/website/content/en/docs/codeSample.txt +31 -0
  58. package/website/content/en/docs/concepts.md +238 -0
  59. package/website/content/en/docs/customresources.md +167 -0
  60. package/website/content/en/docs/diagrams.txt +18 -0
  61. package/website/content/en/docs/metrics.md +74 -0
  62. package/website/content/en/docs/rbac.md +153 -0
  63. package/website/content/en/docs/store.md +48 -0
  64. package/website/content/en/docs/webassembly.md +189 -0
  65. package/website/go.mod +8 -0
  66. package/website/go.sum +4 -0
  67. package/website/package-lock.json +3907 -0
  68. package/website/package.json +30 -0
  69. package/website/renovate.json +16 -0
  70. package/website/static/favicons/android-144x144.png +0 -0
  71. package/website/static/favicons/android-192x192.png +0 -0
  72. package/website/static/favicons/android-36x36.png +0 -0
  73. package/website/static/favicons/android-48x48.png +0 -0
  74. package/website/static/favicons/android-72x72.png +0 -0
  75. package/website/static/favicons/android-96x96.png +0 -0
  76. package/website/static/favicons/android-chrome-192x192.png +0 -0
  77. package/website/static/favicons/android-chrome-512x512.png +0 -0
  78. package/website/static/favicons/android-chrome-maskable-192x192.png +0 -0
  79. package/website/static/favicons/android-chrome-maskable-512x512.png +0 -0
  80. package/website/static/favicons/apple-touch-icon-180x180.png +0 -0
  81. package/website/static/favicons/apple-touch-icon.png +0 -0
  82. package/website/static/favicons/favicon-16x16.png +0 -0
  83. package/website/static/favicons/favicon-32x32.png +0 -0
  84. package/website/static/favicons/favicon.ico +0 -0
  85. package/website/static/img/how-to-use.png +0 -0
@@ -2,26 +2,42 @@
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
4
  import { kind } from "kubernetes-fluent-client";
5
-
5
+ import { CapabilityExport } from "../types";
6
+ import { createRBACMap } from "../helpers";
6
7
  /**
7
8
  * Grants the controller access to cluster resources beyond the mutating webhook.
8
9
  *
9
10
  * @todo: should dynamically generate this based on resources used by the module. will also need to explore how this should work for multiple modules.
10
11
  * @returns
11
12
  */
12
- export function clusterRole(name: string): kind.ClusterRole {
13
+ export function clusterRole(name: string, capabilities: CapabilityExport[], rbacMode: string = ""): kind.ClusterRole {
14
+ const rbacMap = createRBACMap(capabilities);
13
15
  return {
14
16
  apiVersion: "rbac.authorization.k8s.io/v1",
15
17
  kind: "ClusterRole",
16
18
  metadata: { name },
17
- rules: [
18
- {
19
- // @todo: make this configurable
20
- apiGroups: ["*"],
21
- resources: ["*"],
22
- verbs: ["create", "delete", "get", "list", "patch", "update", "watch"],
23
- },
24
- ],
19
+ rules:
20
+ rbacMode === "scoped"
21
+ ? [
22
+ ...Object.keys(rbacMap).map(key => {
23
+ // let group:string, version:string, kind:string;
24
+ let group: string;
25
+ key.split("/").length < 3 ? (group = "") : (group = key.split("/")[0]);
26
+
27
+ return {
28
+ apiGroups: [group],
29
+ resources: [rbacMap[key].plural],
30
+ verbs: rbacMap[key].verbs,
31
+ };
32
+ }),
33
+ ]
34
+ : [
35
+ {
36
+ apiGroups: ["*"],
37
+ resources: ["*"],
38
+ verbs: ["create", "delete", "get", "list", "patch", "update", "watch"],
39
+ },
40
+ ],
25
41
  };
26
42
  }
27
43
 
@@ -64,7 +80,7 @@ export function storeRole(name: string): kind.Role {
64
80
  metadata: { name, namespace: "pepr-system" },
65
81
  rules: [
66
82
  {
67
- apiGroups: ["pepr.dev/*"],
83
+ apiGroups: ["pepr.dev"],
68
84
  resources: ["peprstores"],
69
85
  resourceNames: [""],
70
86
  verbs: ["create", "get", "patch", "watch"],
@@ -40,7 +40,7 @@ export function zarfYaml({ name, image, config }: Assets, path: string) {
40
40
  return dumpYaml(zarfCfg, { noRefs: true });
41
41
  }
42
42
 
43
- export async function allYaml(assets: Assets) {
43
+ export async function allYaml(assets: Assets, rbacMode: string) {
44
44
  const { name, tls, apiToken, path } = assets;
45
45
 
46
46
  const code = await fs.readFile(path);
@@ -54,7 +54,7 @@ export async function allYaml(assets: Assets) {
54
54
 
55
55
  const resources = [
56
56
  namespace,
57
- clusterRole(name),
57
+ clusterRole(name, assets.capabilities, rbacMode),
58
58
  clusterRoleBinding(name),
59
59
  serviceAccount(name),
60
60
  apiTokenSecret(name, apiToken),
@@ -8,6 +8,7 @@ import { pickBy } from "ramda";
8
8
  import Log from "./logger";
9
9
  import { isBuildMode, isDevMode, isWatchMode } from "./module";
10
10
  import { PeprStore, Storage } from "./storage";
11
+ import { OnSchedule, Schedule } from "./schedule";
11
12
  import {
12
13
  Binding,
13
14
  BindingFilter,
@@ -34,7 +35,39 @@ export class Capability implements CapabilityExport {
34
35
  #namespaces?: string[] | undefined;
35
36
  #bindings: Binding[] = [];
36
37
  #store = new Storage();
38
+ #scheduleStore = new Storage();
37
39
  #registered = false;
40
+ #scheduleRegistered = false;
41
+ hasSchedule: boolean;
42
+
43
+ /**
44
+ * Run code on a schedule with the capability.
45
+ *
46
+ * @param schedule The schedule to run the code on
47
+ * @returns
48
+ */
49
+ OnSchedule: (schedule: Schedule) => void = (schedule: Schedule) => {
50
+ const { name, every, unit, run, startTime, completions } = schedule;
51
+
52
+ if (process.env.PEPR_WATCH_MODE === "true") {
53
+ // Only create/watch schedule store if necessary
54
+ this.hasSchedule = true;
55
+
56
+ // Create a new schedule
57
+ const newSchedule: Schedule = {
58
+ name,
59
+ every,
60
+ unit,
61
+ run,
62
+ startTime,
63
+ completions,
64
+ };
65
+
66
+ this.#scheduleStore.onReady(() => {
67
+ new OnSchedule(newSchedule).setStore(this.#scheduleStore);
68
+ });
69
+ }
70
+ };
38
71
 
39
72
  /**
40
73
  * Store is a key-value data store that can be used to persist data that should be shared
@@ -50,6 +83,24 @@ export class Capability implements CapabilityExport {
50
83
  setItem: this.#store.setItem,
51
84
  subscribe: this.#store.subscribe,
52
85
  onReady: this.#store.onReady,
86
+ setItemAndWait: this.#store.setItemAndWait,
87
+ };
88
+
89
+ /**
90
+ * ScheduleStore is a key-value data store used to persist schedule data that should be shared
91
+ * between intervals. Each Schedule shares store, and the data is persisted in Kubernetes
92
+ * in the `pepr-system` namespace.
93
+ *
94
+ * Note: There is no direct access to schedule store
95
+ */
96
+ ScheduleStore: PeprStore = {
97
+ clear: this.#scheduleStore.clear,
98
+ getItem: this.#scheduleStore.getItem,
99
+ removeItem: this.#scheduleStore.removeItem,
100
+ setItemAndWait: this.#scheduleStore.setItemAndWait,
101
+ setItem: this.#scheduleStore.setItem,
102
+ subscribe: this.#scheduleStore.subscribe,
103
+ onReady: this.#scheduleStore.onReady,
53
104
  };
54
105
 
55
106
  get bindings() {
@@ -72,11 +123,32 @@ export class Capability implements CapabilityExport {
72
123
  this.#name = cfg.name;
73
124
  this.#description = cfg.description;
74
125
  this.#namespaces = cfg.namespaces;
126
+ this.hasSchedule = false;
75
127
 
76
128
  Log.info(`Capability ${this.#name} registered`);
77
129
  Log.debug(cfg);
78
130
  }
79
131
 
132
+ /**
133
+ * Register the store with the capability. This is called automatically by the Pepr controller.
134
+ *
135
+ * @param store
136
+ */
137
+ registerScheduleStore = () => {
138
+ Log.info(`Registering schedule store for ${this.#name}`);
139
+
140
+ if (this.#scheduleRegistered) {
141
+ throw new Error(`Schedule store already registered for ${this.#name}`);
142
+ }
143
+
144
+ this.#scheduleRegistered = true;
145
+
146
+ // Pass back any ready callback to the controller
147
+ return {
148
+ scheduleStore: this.#scheduleStore,
149
+ };
150
+ };
151
+
80
152
  /**
81
153
  * Register the store with the capability. This is called automatically by the Pepr controller.
82
154
  *
@@ -44,10 +44,14 @@ export class Controller {
44
44
  this.#capabilities = capabilities;
45
45
 
46
46
  // Initialize the Pepr store for each capability
47
- new PeprControllerStore(config, capabilities, () => {
47
+ new PeprControllerStore(config, capabilities, `pepr-${config.uuid}-store`, () => {
48
48
  this.#bindEndpoints();
49
49
  onReady && onReady();
50
50
  Log.info("✅ Controller startup complete");
51
+ // Initialize the schedule store for each capability
52
+ new PeprControllerStore(config, capabilities, `pepr-${config.uuid}-schedule`, () => {
53
+ Log.info("✅ Scheduling processed");
54
+ });
51
55
  });
52
56
 
53
57
  // Middleware for logging requests
@@ -12,7 +12,7 @@ import { ModuleConfig } from "../module";
12
12
  import { DataOp, DataSender, DataStore, Storage } from "../storage";
13
13
 
14
14
  const namespace = "pepr-system";
15
- const debounceBackoff = 5000;
15
+ export const debounceBackoff = 5000;
16
16
 
17
17
  export class PeprControllerStore {
18
18
  #name: string;
@@ -20,22 +20,40 @@ export class PeprControllerStore {
20
20
  #sendDebounce: NodeJS.Timeout | undefined;
21
21
  #onReady?: () => void;
22
22
 
23
- constructor(config: ModuleConfig, capabilities: Capability[], onReady?: () => void) {
23
+ constructor(config: ModuleConfig, capabilities: Capability[], name: string, onReady?: () => void) {
24
24
  this.#onReady = onReady;
25
25
 
26
26
  // Setup Pepr State bindings
27
- this.#name = `pepr-${config.uuid}-store`;
27
+ this.#name = name;
28
+
29
+ if (name.includes("schedule")) {
30
+ // Establish the store for each capability
31
+ for (const { name, registerScheduleStore, hasSchedule } of capabilities) {
32
+ // Guard Clause to exit early
33
+ if (hasSchedule !== true) {
34
+ return;
35
+ }
36
+ // Register the scheduleStore with the capability
37
+ const { scheduleStore } = registerScheduleStore();
38
+
39
+ // Bind the store sender to the capability
40
+ scheduleStore.registerSender(this.#send(name));
28
41
 
29
- // Establish the store for each capability
30
- for (const { name, registerStore } of capabilities) {
31
- // Register the store with the capability
32
- const { store } = registerStore();
42
+ // Store the storage instance
43
+ this.#stores[name] = scheduleStore;
44
+ }
45
+ } else {
46
+ // Establish the store for each capability
47
+ for (const { name, registerStore } of capabilities) {
48
+ // Register the store with the capability
49
+ const { store } = registerStore();
33
50
 
34
- // Bind the store sender to the capability
35
- store.registerSender(this.#send(name));
51
+ // Bind the store sender to the capability
52
+ store.registerSender(this.#send(name));
36
53
 
37
- // Store the storage instance
38
- this.#stores[name] = store;
54
+ // Store the storage instance
55
+ this.#stores[name] = store;
56
+ }
39
57
  }
40
58
 
41
59
  // Add a jitter to the Store creation to avoid collisions
@@ -0,0 +1,52 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { CapabilityExport } from "./types";
5
+ import { promises as fs } from "fs";
6
+
7
+ type RBACMap = {
8
+ [key: string]: {
9
+ verbs: string[];
10
+ plural: string;
11
+ };
12
+ };
13
+
14
+ export const addVerbIfNotExists = (verbs: string[], verb: string) => {
15
+ if (!verbs.includes(verb)) {
16
+ verbs.push(verb);
17
+ }
18
+ };
19
+
20
+ export const createRBACMap = (capabilities: CapabilityExport[]): RBACMap => {
21
+ return capabilities.reduce((acc: RBACMap, capability: CapabilityExport) => {
22
+ capability.bindings.forEach(binding => {
23
+ const key = `${binding.kind.group}/${binding.kind.version}/${binding.kind.kind}`;
24
+
25
+ acc["pepr.dev/v1/peprstore"] = {
26
+ verbs: ["create", "get", "patch", "watch"],
27
+ plural: "peprstores",
28
+ };
29
+
30
+ if (!acc[key] && binding.isWatch) {
31
+ acc[key] = {
32
+ verbs: ["watch"],
33
+ plural: binding.kind.plural || `${binding.kind.kind.toLowerCase()}s`,
34
+ };
35
+ }
36
+ });
37
+
38
+ return acc;
39
+ }, {});
40
+ };
41
+
42
+ export async function createDirectoryIfNotExists(path: string) {
43
+ try {
44
+ await fs.access(path);
45
+ } catch (error) {
46
+ if (error.code === "ENOENT") {
47
+ await fs.mkdir(path, { recursive: true });
48
+ } else {
49
+ throw error;
50
+ }
51
+ }
52
+ }
package/src/lib/module.ts CHANGED
@@ -87,6 +87,7 @@ export class PeprModule {
87
87
  description: capability.description,
88
88
  namespaces: capability.namespaces,
89
89
  bindings: capability.bindings,
90
+ hasSchedule: capability.hasSchedule,
90
91
  });
91
92
  }
92
93
 
@@ -0,0 +1,175 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ import { PeprStore } from "./storage";
5
+
6
+ type Unit = "seconds" | "second" | "minute" | "minutes" | "hours" | "hour";
7
+
8
+ export interface Schedule {
9
+ /**
10
+ * * The name of the store
11
+ */
12
+ name: string;
13
+ /**
14
+ * The value associated with a unit of time
15
+ */
16
+ every: number;
17
+ /**
18
+ * The unit of time
19
+ */
20
+ unit: Unit;
21
+ /**
22
+ * The code to run
23
+ */
24
+ run: () => void;
25
+ /**
26
+ * The start time of the schedule
27
+ */
28
+ startTime?: Date | undefined;
29
+
30
+ /**
31
+ * The number of times the schedule has run
32
+ */
33
+ completions?: number | undefined;
34
+ /**
35
+ * Tje intervalID to clear the interval
36
+ */
37
+ intervalID?: NodeJS.Timeout;
38
+ }
39
+
40
+ export class OnSchedule implements Schedule {
41
+ intervalId: NodeJS.Timeout | null = null;
42
+ store: PeprStore | undefined;
43
+ name!: string;
44
+ completions?: number | undefined;
45
+ every: number;
46
+ unit: Unit;
47
+ run!: () => void;
48
+ startTime?: Date | undefined;
49
+ duration: number | undefined;
50
+ lastTimestamp: Date | undefined;
51
+
52
+ constructor(schedule: Schedule) {
53
+ this.name = schedule.name;
54
+ this.run = schedule.run;
55
+ this.every = schedule.every;
56
+ this.unit = schedule.unit;
57
+ this.startTime = schedule?.startTime;
58
+ this.completions = schedule?.completions;
59
+ }
60
+ setStore(store: PeprStore) {
61
+ this.store = store;
62
+ this.startInterval();
63
+ }
64
+ startInterval() {
65
+ this.checkStore();
66
+ this.getDuration();
67
+ this.setupInterval();
68
+ }
69
+ /**
70
+ * Checks the store for this schedule and sets the values if it exists
71
+ * @returns
72
+ */
73
+ checkStore() {
74
+ const result = this.store && this.store.getItem(this.name);
75
+ if (result) {
76
+ const storedSchedule = JSON.parse(result);
77
+ this.completions = storedSchedule?.completions;
78
+ this.startTime = storedSchedule?.startTime;
79
+ this.lastTimestamp = storedSchedule?.lastTimestamp;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Saves the schedule to the store
85
+ * @returns
86
+ */
87
+ saveToStore() {
88
+ const schedule = {
89
+ completions: this.completions,
90
+ startTime: this.startTime,
91
+ lastTimestamp: new Date(),
92
+ name: this.name,
93
+ };
94
+ this.store && this.store.setItem(this.name, JSON.stringify(schedule));
95
+ }
96
+
97
+ /**
98
+ * Gets the durations in milliseconds
99
+ */
100
+ getDuration() {
101
+ switch (this.unit) {
102
+ case "seconds":
103
+ if (this.every < 10) throw new Error("10 Seconds in the smallest interval allowed");
104
+ this.duration = 1000 * this.every;
105
+ break;
106
+ case "minutes":
107
+ case "minute":
108
+ this.duration = 1000 * 60 * this.every;
109
+ break;
110
+ case "hours":
111
+ case "hour":
112
+ this.duration = 1000 * 60 * 60 * this.every;
113
+ break;
114
+ default:
115
+ throw new Error("Invalid time unit");
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Sets up the interval
121
+ */
122
+ setupInterval() {
123
+ const now = new Date();
124
+ let delay: number | undefined;
125
+
126
+ if (this.lastTimestamp && this.startTime) {
127
+ this.startTime = undefined;
128
+ }
129
+
130
+ if (this.startTime) {
131
+ delay = this.startTime.getTime() - now.getTime();
132
+ } else if (this.lastTimestamp && this.duration) {
133
+ const lastTimestamp = new Date(this.lastTimestamp);
134
+ delay = this.duration - (now.getTime() - lastTimestamp.getTime());
135
+ }
136
+
137
+ if (delay === undefined || delay <= 0) {
138
+ this.start();
139
+ } else {
140
+ setTimeout(() => {
141
+ this.start();
142
+ }, delay);
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Starts the interval
148
+ */
149
+ start() {
150
+ this.intervalId = setInterval(() => {
151
+ if (this.completions === 0) {
152
+ this.stop();
153
+ return;
154
+ } else {
155
+ this.run();
156
+
157
+ if (this.completions && this.completions !== 0) {
158
+ this.completions -= 1;
159
+ }
160
+ this.saveToStore();
161
+ }
162
+ }, this.duration);
163
+ }
164
+
165
+ /**
166
+ * Stops the interval
167
+ */
168
+ stop() {
169
+ if (this.intervalId) {
170
+ clearInterval(this.intervalId);
171
+ this.intervalId = null;
172
+ }
173
+ this.store && this.store.removeItem(this.name);
174
+ }
175
+ }
@@ -10,6 +10,7 @@ export type DataSender = (op: DataOp, keys: string[], value?: string) => void;
10
10
  export type DataReceiver = (data: DataStore) => void;
11
11
  export type Unsubscribe = () => void;
12
12
 
13
+ const MAX_WAIT_TIME = 15000;
13
14
  export interface PeprStore {
14
15
  /**
15
16
  * Returns the current value associated with the given key, or null if the given key does not exist.
@@ -40,6 +41,12 @@ export interface PeprStore {
40
41
  * Register a function to be called when the store is ready.
41
42
  */
42
43
  onReady(callback: DataReceiver): void;
44
+
45
+ /**
46
+ * Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
47
+ * Resolves when the key/value show up in the store.
48
+ */
49
+ setItemAndWait(key: string, value: string): Promise<void>;
43
50
  }
44
51
 
45
52
  /**
@@ -88,6 +95,32 @@ export class Storage implements PeprStore {
88
95
  this.#dispatchUpdate("add", [key], value);
89
96
  };
90
97
 
98
+ /**
99
+ * Creates a promise and subscribes to the store, the promise resolves when
100
+ * the key and value are seen in the store.
101
+ *
102
+ * @param key - The key to add into the store
103
+ * @param value - The value of the key
104
+ * @returns
105
+ */
106
+ setItemAndWait = (key: string, value: string) => {
107
+ this.#dispatchUpdate("add", [key], value);
108
+ return new Promise<void>((resolve, reject) => {
109
+ const unsubscribe = this.subscribe(data => {
110
+ if (data[key] === value) {
111
+ unsubscribe();
112
+ resolve();
113
+ }
114
+ });
115
+
116
+ // If promise has not resolved before MAX_WAIT_TIME reject
117
+ setTimeout(() => {
118
+ unsubscribe();
119
+ return reject();
120
+ }, MAX_WAIT_TIME);
121
+ });
122
+ };
123
+
91
124
  subscribe = (subscriber: DataReceiver) => {
92
125
  const idx = this.#subscriberId++;
93
126
  this.#subscribers[idx] = subscriber;
package/src/lib/types.ts CHANGED
@@ -43,6 +43,7 @@ export interface CapabilityCfg {
43
43
 
44
44
  export interface CapabilityExport extends CapabilityCfg {
45
45
  bindings: Binding[];
46
+ hasSchedule: boolean;
46
47
  }
47
48
 
48
49
  export type WhenSelector<T extends GenericClass> = {
package/src/lib.ts CHANGED
@@ -1,32 +1,25 @@
1
- import { K8s, RegisterKind, fetch, kind, kind as a, fetchStatus } from "kubernetes-fluent-client";
1
+ import { K8s, RegisterKind, kind as a, fetch, fetchStatus, kind } from "kubernetes-fluent-client";
2
2
  import * as R from "ramda";
3
3
 
4
4
  import { Capability } from "./lib/capability";
5
5
  import Log from "./lib/logger";
6
6
  import { PeprModule } from "./lib/module";
7
7
  import { PeprMutateRequest } from "./lib/mutate-request";
8
- import { PeprValidateRequest } from "./lib/validate-request";
9
8
  import * as PeprUtils from "./lib/utils";
10
-
11
- // Import type information for external packages
12
- import type * as RTypes from "ramda";
9
+ import { PeprValidateRequest } from "./lib/validate-request";
13
10
 
14
11
  export {
15
- a,
16
- kind,
17
- /** PeprModule is used to setup a complete Pepr Module: `new PeprModule(cfg, {...capabilities})` */
12
+ Capability,
13
+ K8s,
14
+ Log,
18
15
  PeprModule,
19
16
  PeprMutateRequest,
20
- PeprValidateRequest,
21
17
  PeprUtils,
22
- RegisterKind,
23
- K8s,
24
- Capability,
25
- Log,
18
+ PeprValidateRequest,
26
19
  R,
20
+ RegisterKind,
21
+ a,
27
22
  fetch,
28
23
  fetchStatus,
29
-
30
- // Export the imported type information for external packages
31
- RTypes,
24
+ kind,
32
25
  };
@@ -52,19 +52,24 @@ When(a.Namespace)
52
52
  .Watch(async ns => {
53
53
  Log.info("Namespace pepr-demo-2 was created.");
54
54
 
55
+ try {
56
+ // Apply the ConfigMap using K8s server-side apply
57
+ await K8s(kind.ConfigMap).Apply({
58
+ metadata: {
59
+ name: "pepr-ssa-demo",
60
+ namespace: "pepr-demo-2",
61
+ },
62
+ data: {
63
+ "ns-uid": ns.metadata.uid,
64
+ },
65
+ });
66
+ } catch (error) {
67
+ // You can use the Log object to log messages to the Pepr controller pod
68
+ Log.error(error, "Failed to apply ConfigMap using server-side apply.");
69
+ }
70
+
55
71
  // You can share data between actions using the Store, including between different types of actions
56
72
  Store.setItem("watch-data", "This data was stored by a Watch Action.");
57
-
58
- // Apply the ConfigMap using K8s server-side apply
59
- await K8s(kind.ConfigMap).Apply({
60
- metadata: {
61
- name: "pepr-ssa-demo",
62
- namespace: "pepr-demo-2",
63
- },
64
- data: {
65
- "ns-uid": ns.metadata.uid,
66
- },
67
- });
68
73
  });
69
74
 
70
75
  /**
@@ -0,0 +1,8 @@
1
+ {
2
+ "recurse": true,
3
+ "skip": "^(https://example.com), ^(https://github.com), ^(https://twitter.com)",
4
+ "verbosity": "error",
5
+ "timeout": 0,
6
+ "markdown": true,
7
+ "directoryListing": true
8
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "default": true,
3
+ "MD033": false,
4
+ "MD013": false,
5
+ "MD036": false
6
+ }
@@ -0,0 +1,12 @@
1
+ public/
2
+ node_modules/
3
+ .cache
4
+ .history
5
+ dist
6
+ coverage
7
+ docs/app
8
+ __mocks__
9
+ userguide/
10
+ .release-please-manifest.json
11
+ CHANGELOG.md
12
+ .vscode/