pepr 0.42.0 → 0.42.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/build.d.ts +1 -0
- package/dist/cli/build.d.ts.map +1 -1
- package/dist/cli/build.helpers.d.ts +66 -0
- package/dist/cli/build.helpers.d.ts.map +1 -1
- package/dist/cli/deploy.d.ts +15 -0
- package/dist/cli/deploy.d.ts.map +1 -1
- package/dist/cli/dev.d.ts.map +1 -1
- package/dist/cli/format.d.ts.map +1 -1
- package/dist/cli/format.helpers.d.ts +3 -0
- package/dist/cli/format.helpers.d.ts.map +1 -0
- package/dist/cli/init/enums.d.ts +10 -0
- package/dist/cli/init/enums.d.ts.map +1 -0
- package/dist/cli/init/index.d.ts.map +1 -1
- package/dist/cli/init/templates.d.ts +15 -11
- package/dist/cli/init/templates.d.ts.map +1 -1
- package/dist/cli/init/utils.d.ts.map +1 -1
- package/dist/cli/init/walkthrough.d.ts +3 -2
- package/dist/cli/init/walkthrough.d.ts.map +1 -1
- package/dist/cli/kfc.d.ts.map +1 -1
- package/dist/cli/monitor.d.ts +23 -0
- package/dist/cli/monitor.d.ts.map +1 -1
- package/dist/cli/root.d.ts.map +1 -1
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/uuid.d.ts.map +1 -1
- package/dist/cli.js +483 -586
- package/dist/controller.js +1 -195
- package/dist/fixtures/loader.d.ts.map +1 -1
- package/dist/lib/assets/deploy.d.ts.map +1 -1
- package/dist/lib/assets/destroy.d.ts.map +1 -1
- package/dist/lib/assets/helm.d.ts +1 -1
- package/dist/lib/assets/helm.d.ts.map +1 -1
- package/dist/lib/assets/index.d.ts +1 -1
- package/dist/lib/assets/index.d.ts.map +1 -1
- package/dist/lib/assets/pods.d.ts +6 -22
- package/dist/lib/assets/pods.d.ts.map +1 -1
- package/dist/lib/assets/rbac.d.ts.map +1 -1
- package/dist/lib/assets/webhooks.d.ts.map +1 -1
- package/dist/lib/assets/yaml.d.ts.map +1 -1
- package/dist/lib/controller/index.d.ts +2 -2
- package/dist/lib/controller/index.d.ts.map +1 -1
- package/dist/lib/controller/store.d.ts +1 -2
- package/dist/lib/controller/store.d.ts.map +1 -1
- package/dist/lib/controller/storeCache.d.ts +2 -1
- package/dist/lib/controller/storeCache.d.ts.map +1 -1
- package/dist/lib/{capability.d.ts → core/capability.d.ts} +1 -1
- package/dist/lib/core/capability.d.ts.map +1 -0
- package/dist/lib/{module.d.ts → core/module.d.ts} +2 -2
- package/dist/lib/core/module.d.ts.map +1 -0
- package/dist/lib/core/queue.d.ts.map +1 -0
- package/dist/lib/{schedule.d.ts → core/schedule.d.ts} +0 -1
- package/dist/lib/core/schedule.d.ts.map +1 -0
- package/dist/lib/{storage.d.ts → core/storage.d.ts} +4 -4
- package/dist/lib/core/storage.d.ts.map +1 -0
- package/dist/lib/deploymentChecks.d.ts.map +1 -1
- package/dist/lib/errors.d.ts +0 -5
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/filesystemService.d.ts.map +1 -1
- package/dist/lib/filter/adjudicators/adjudicators.d.ts +5 -4
- package/dist/lib/filter/adjudicators/adjudicators.d.ts.map +1 -1
- package/dist/lib/filter/filter.d.ts +33 -1
- package/dist/lib/filter/filter.d.ts.map +1 -1
- package/dist/lib/finalizer.d.ts.map +1 -1
- package/dist/lib/helpers.d.ts +4 -9
- package/dist/lib/helpers.d.ts.map +1 -1
- package/dist/lib/included-files.d.ts.map +1 -1
- package/dist/lib/mutate-request.d.ts +2 -2
- package/dist/lib/mutate-request.d.ts.map +1 -1
- package/dist/lib/processors/mutate-processor.d.ts +28 -0
- package/dist/lib/processors/mutate-processor.d.ts.map +1 -0
- package/dist/lib/processors/validate-processor.d.ts +9 -0
- package/dist/lib/processors/validate-processor.d.ts.map +1 -0
- package/dist/lib/{watch-processor.d.ts → processors/watch-processor.d.ts} +2 -2
- package/dist/lib/processors/watch-processor.d.ts.map +1 -0
- package/dist/lib/telemetry/logger.d.ts.map +1 -1
- package/dist/lib/telemetry/metrics.d.ts.map +1 -1
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/validate-request.d.ts +2 -2
- package/dist/lib/validate-request.d.ts.map +1 -1
- package/dist/lib.d.ts +2 -2
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +508 -341
- package/dist/lib.js.map +4 -4
- package/dist/sdk/heredoc.d.ts.map +1 -1
- package/package.json +9 -9
- package/src/cli/build.helpers.ts +180 -0
- package/src/cli/build.ts +85 -133
- package/src/cli/deploy.ts +113 -74
- package/src/cli/dev.ts +2 -2
- package/src/cli/format.helpers.ts +27 -0
- package/src/cli/format.ts +4 -18
- package/src/cli/init/enums.ts +9 -0
- package/src/cli/init/index.ts +4 -3
- package/src/cli/init/templates.ts +30 -2
- package/src/cli/init/utils.ts +3 -3
- package/src/cli/init/walkthrough.ts +7 -8
- package/src/cli/kfc.ts +1 -1
- package/src/cli/monitor.ts +108 -65
- package/src/cli/root.ts +1 -1
- package/src/cli/update.ts +1 -1
- package/src/cli/uuid.ts +1 -1
- package/src/fixtures/loader.ts +2 -2
- package/src/lib/assets/deploy.ts +11 -11
- package/src/lib/assets/destroy.ts +1 -1
- package/src/lib/assets/helm.ts +6 -6
- package/src/lib/assets/index.ts +23 -23
- package/src/lib/assets/pods.ts +11 -6
- package/src/lib/assets/webhooks.ts +31 -46
- package/src/lib/assets/yaml.ts +12 -9
- package/src/lib/controller/index.ts +13 -11
- package/src/lib/controller/store.ts +25 -12
- package/src/lib/controller/storeCache.ts +16 -3
- package/src/lib/{capability.ts → core/capability.ts} +25 -14
- package/src/lib/{module.ts → core/module.ts} +10 -10
- package/src/lib/{queue.ts → core/queue.ts} +13 -5
- package/src/lib/{storage.ts → core/storage.ts} +33 -24
- package/src/lib/deploymentChecks.ts +2 -2
- package/src/lib/errors.ts +3 -8
- package/src/lib/filesystemService.ts +1 -1
- package/src/lib/filter/adjudicators/adjudicators.ts +40 -9
- package/src/lib/filter/filter.ts +204 -111
- package/src/lib/finalizer.ts +2 -2
- package/src/lib/helpers.ts +20 -133
- package/src/lib/included-files.ts +1 -1
- package/src/lib/mutate-request.ts +11 -11
- package/src/lib/processors/mutate-processor.ts +225 -0
- package/src/lib/processors/validate-processor.ts +93 -0
- package/src/lib/{watch-processor.ts → processors/watch-processor.ts} +19 -15
- package/src/lib/telemetry/logger.ts +3 -1
- package/src/lib/tls.ts +5 -1
- package/src/lib/utils.ts +5 -5
- package/src/lib/validate-request.ts +4 -4
- package/src/lib.ts +2 -2
- package/src/runtime/controller.ts +2 -2
- package/src/sdk/cosign.ts +4 -4
- package/src/sdk/heredoc.ts +1 -1
- package/dist/lib/capability.d.ts.map +0 -1
- package/dist/lib/module.d.ts.map +0 -1
- package/dist/lib/mutate-processor.d.ts +0 -6
- package/dist/lib/mutate-processor.d.ts.map +0 -1
- package/dist/lib/queue.d.ts.map +0 -1
- package/dist/lib/schedule.d.ts.map +0 -1
- package/dist/lib/storage.d.ts.map +0 -1
- package/dist/lib/validate-processor.d.ts +0 -6
- package/dist/lib/validate-processor.d.ts.map +0 -1
- package/dist/lib/watch-processor.d.ts.map +0 -1
- package/src/lib/mutate-processor.ts +0 -165
- package/src/lib/validate-processor.ts +0 -85
- /package/dist/lib/{queue.d.ts → core/queue.d.ts} +0 -0
- /package/src/lib/{schedule.ts → core/schedule.ts} +0 -0
|
@@ -5,14 +5,15 @@ import { Operation } from "fast-json-patch";
|
|
|
5
5
|
import { K8s } from "kubernetes-fluent-client";
|
|
6
6
|
import { startsWith } from "ramda";
|
|
7
7
|
|
|
8
|
-
import { Capability } from "../capability";
|
|
8
|
+
import { Capability } from "../core/capability";
|
|
9
9
|
import { Store } from "../k8s";
|
|
10
10
|
import Log, { redactedPatch, redactedStore } from "../telemetry/logger";
|
|
11
|
-
import { DataOp, DataSender, DataStore, Storage } from "../storage";
|
|
11
|
+
import { DataOp, DataSender, DataStore, Storage } from "../core/storage";
|
|
12
12
|
import { fillStoreCache, sendUpdatesAndFlushCache } from "./storeCache";
|
|
13
13
|
|
|
14
14
|
const namespace = "pepr-system";
|
|
15
|
-
|
|
15
|
+
const debounceBackoffReceive = 1000;
|
|
16
|
+
const debounceBackoffSend = 4000;
|
|
16
17
|
|
|
17
18
|
export class StoreController {
|
|
18
19
|
#name: string;
|
|
@@ -25,7 +26,7 @@ export class StoreController {
|
|
|
25
26
|
|
|
26
27
|
this.#name = name;
|
|
27
28
|
|
|
28
|
-
const setStorageInstance = (registrationFunction: () => Storage, name: string) => {
|
|
29
|
+
const setStorageInstance = (registrationFunction: () => Storage, name: string): void => {
|
|
29
30
|
const scheduleStore = registrationFunction();
|
|
30
31
|
|
|
31
32
|
// Bind the store sender to the capability
|
|
@@ -61,13 +62,22 @@ export class StoreController {
|
|
|
61
62
|
);
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
#setupWatch = () => {
|
|
65
|
+
#setupWatch = (): void => {
|
|
65
66
|
const watcher = K8s(Store, { name: this.#name, namespace }).Watch(this.#receive);
|
|
66
67
|
watcher.start().catch(e => Log.error(e, "Error starting Pepr store watch"));
|
|
67
68
|
};
|
|
68
69
|
|
|
69
|
-
#migrateAndSetupWatch = async (store: Store) => {
|
|
70
|
+
#migrateAndSetupWatch = async (store: Store): Promise<void> => {
|
|
70
71
|
Log.debug(redactedStore(store), "Pepr Store migration");
|
|
72
|
+
// Add cacheID label to store
|
|
73
|
+
await K8s(Store, { namespace, name: this.#name }).Patch([
|
|
74
|
+
{
|
|
75
|
+
op: "add",
|
|
76
|
+
path: "/metadata/labels/pepr.dev-cacheID",
|
|
77
|
+
value: `${Date.now()}`,
|
|
78
|
+
},
|
|
79
|
+
]);
|
|
80
|
+
|
|
71
81
|
const data: DataStore = store.data || {};
|
|
72
82
|
let storeCache: Record<string, Operation> = {};
|
|
73
83
|
|
|
@@ -96,11 +106,11 @@ export class StoreController {
|
|
|
96
106
|
this.#setupWatch();
|
|
97
107
|
};
|
|
98
108
|
|
|
99
|
-
#receive = (store: Store) => {
|
|
109
|
+
#receive = (store: Store): void => {
|
|
100
110
|
Log.debug(redactedStore(store), "Pepr Store update");
|
|
101
111
|
|
|
102
112
|
// Wrap the update in a debounced function
|
|
103
|
-
const debounced = () => {
|
|
113
|
+
const debounced = (): void => {
|
|
104
114
|
// Base64 decode the data
|
|
105
115
|
const data: DataStore = store.data || {};
|
|
106
116
|
|
|
@@ -134,10 +144,10 @@ export class StoreController {
|
|
|
134
144
|
|
|
135
145
|
// Debounce the update to 1 second to avoid multiple rapid calls
|
|
136
146
|
clearTimeout(this.#sendDebounce);
|
|
137
|
-
this.#sendDebounce = setTimeout(debounced, this.#onReady ? 0 :
|
|
147
|
+
this.#sendDebounce = setTimeout(debounced, this.#onReady ? 0 : debounceBackoffReceive);
|
|
138
148
|
};
|
|
139
149
|
|
|
140
|
-
#send = (capabilityName: string) => {
|
|
150
|
+
#send = (capabilityName: string): DataSender => {
|
|
141
151
|
let storeCache: Record<string, Operation> = {};
|
|
142
152
|
|
|
143
153
|
// Create a sender function for the capability to add/remove data from the store
|
|
@@ -151,12 +161,12 @@ export class StoreController {
|
|
|
151
161
|
Log.debug(redactedPatch(storeCache), "Sending updates to Pepr store");
|
|
152
162
|
void sendUpdatesAndFlushCache(storeCache, namespace, this.#name);
|
|
153
163
|
}
|
|
154
|
-
},
|
|
164
|
+
}, debounceBackoffSend);
|
|
155
165
|
|
|
156
166
|
return sender;
|
|
157
167
|
};
|
|
158
168
|
|
|
159
|
-
#createStoreResource = async (e: unknown) => {
|
|
169
|
+
#createStoreResource = async (e: unknown): Promise<void> => {
|
|
160
170
|
Log.info(`Pepr store not found, creating...`);
|
|
161
171
|
Log.debug(e);
|
|
162
172
|
|
|
@@ -165,6 +175,9 @@ export class StoreController {
|
|
|
165
175
|
metadata: {
|
|
166
176
|
name: this.#name,
|
|
167
177
|
namespace,
|
|
178
|
+
labels: {
|
|
179
|
+
"pepr.dev-cacheID": `${Date.now()}`,
|
|
180
|
+
},
|
|
168
181
|
},
|
|
169
182
|
data: {
|
|
170
183
|
// JSON Patch will die if the data is empty, so we need to add a placeholder
|
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
import { DataOp } from "../storage";
|
|
1
|
+
import { DataOp } from "../core/storage";
|
|
2
2
|
import Log from "../telemetry/logger";
|
|
3
3
|
import { K8s } from "kubernetes-fluent-client";
|
|
4
4
|
import { Store } from "../k8s";
|
|
5
5
|
import { StatusCodes } from "http-status-codes";
|
|
6
6
|
import { Operation } from "fast-json-patch";
|
|
7
7
|
|
|
8
|
-
export const sendUpdatesAndFlushCache = async (
|
|
8
|
+
export const sendUpdatesAndFlushCache = async (
|
|
9
|
+
cache: Record<string, Operation>,
|
|
10
|
+
namespace: string,
|
|
11
|
+
name: string,
|
|
12
|
+
): Promise<Record<string, Operation>> => {
|
|
9
13
|
const indexes = Object.keys(cache);
|
|
10
14
|
const payload = Object.values(cache);
|
|
11
15
|
|
|
12
16
|
try {
|
|
13
17
|
if (payload.length > 0) {
|
|
14
|
-
await K8s(Store, { namespace, name }).Patch(payload); // Send patch to cluster
|
|
18
|
+
await K8s(Store, { namespace, name }).Patch(updateCacheID(payload)); // Send patch to cluster
|
|
15
19
|
Object.keys(cache).forEach(key => delete cache[key]);
|
|
16
20
|
}
|
|
17
21
|
} catch (err) {
|
|
@@ -61,3 +65,12 @@ export const fillStoreCache = (
|
|
|
61
65
|
}
|
|
62
66
|
return cache;
|
|
63
67
|
};
|
|
68
|
+
|
|
69
|
+
export function updateCacheID(payload: Operation[]): Operation[] {
|
|
70
|
+
payload.push({
|
|
71
|
+
op: "replace",
|
|
72
|
+
path: "/metadata/labels/pepr.dev-cacheID",
|
|
73
|
+
value: `${Date.now()}`,
|
|
74
|
+
});
|
|
75
|
+
return payload;
|
|
76
|
+
}
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
import { GenericClass, GroupVersionKind, modelToGroupVersionKind } from "kubernetes-fluent-client";
|
|
5
5
|
import { pickBy } from "ramda";
|
|
6
|
-
import Log from "
|
|
6
|
+
import Log from "../telemetry/logger";
|
|
7
7
|
import { isBuildMode, isDevMode, isWatchMode } from "./module";
|
|
8
8
|
import { PeprStore, Storage } from "./storage";
|
|
9
9
|
import { OnSchedule, Schedule } from "./schedule";
|
|
10
|
-
import { Event } from "
|
|
10
|
+
import { Event } from "../enums";
|
|
11
11
|
import {
|
|
12
12
|
Binding,
|
|
13
13
|
BindingFilter,
|
|
@@ -22,8 +22,8 @@ import {
|
|
|
22
22
|
FinalizeAction,
|
|
23
23
|
FinalizeActionChain,
|
|
24
24
|
WhenSelector,
|
|
25
|
-
} from "
|
|
26
|
-
import { addFinalizer } from "
|
|
25
|
+
} from "../types";
|
|
26
|
+
import { addFinalizer } from "../finalizer";
|
|
27
27
|
|
|
28
28
|
const registerAdmission = isBuildMode() || !isWatchMode();
|
|
29
29
|
const registerWatch = isBuildMode() || isWatchMode() || isDevMode();
|
|
@@ -71,7 +71,7 @@ export class Capability implements CapabilityExport {
|
|
|
71
71
|
}
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
-
public getScheduleStore() {
|
|
74
|
+
public getScheduleStore(): Storage {
|
|
75
75
|
return this.#scheduleStore;
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -111,19 +111,19 @@ export class Capability implements CapabilityExport {
|
|
|
111
111
|
onReady: this.#scheduleStore.onReady,
|
|
112
112
|
};
|
|
113
113
|
|
|
114
|
-
get bindings() {
|
|
114
|
+
get bindings(): Binding[] {
|
|
115
115
|
return this.#bindings;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
get name() {
|
|
118
|
+
get name(): string {
|
|
119
119
|
return this.#name;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
get description() {
|
|
122
|
+
get description(): string {
|
|
123
123
|
return this.#description;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
get namespaces() {
|
|
126
|
+
get namespaces(): string[] {
|
|
127
127
|
return this.#namespaces || [];
|
|
128
128
|
}
|
|
129
129
|
|
|
@@ -207,8 +207,19 @@ export class Capability implements CapabilityExport {
|
|
|
207
207
|
const bindings = this.#bindings;
|
|
208
208
|
const prefix = `${this.#name}: ${model.name}`;
|
|
209
209
|
const commonChain = { WithLabel, WithAnnotation, WithDeletionTimestamp, Mutate, Validate, Watch, Reconcile, Alias };
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
|
|
211
|
+
type CommonChainType = typeof commonChain;
|
|
212
|
+
type ExtendedCommonChainType = CommonChainType & {
|
|
213
|
+
Alias: (alias: string) => CommonChainType;
|
|
214
|
+
InNamespace: (...namespaces: string[]) => BindingWithName<T>;
|
|
215
|
+
InNamespaceRegex: (...namespaces: RegExp[]) => BindingWithName<T>;
|
|
216
|
+
WithName: (name: string) => BindingFilter<T>;
|
|
217
|
+
WithNameRegex: (regexName: RegExp) => BindingFilter<T>;
|
|
218
|
+
WithDeletionTimestamp: () => BindingFilter<T>;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const isNotEmpty = (value: object): boolean => Object.keys(value).length > 0;
|
|
222
|
+
const log = (message: string, cbString: string): void => {
|
|
212
223
|
const filteredObj = pickBy(isNotEmpty, binding.filters);
|
|
213
224
|
|
|
214
225
|
Log.info(`${message} configured for ${binding.event}`, prefix);
|
|
@@ -329,7 +340,7 @@ export class Capability implements CapabilityExport {
|
|
|
329
340
|
isWatch: true,
|
|
330
341
|
isFinalize: true,
|
|
331
342
|
event: Event.UPDATE,
|
|
332
|
-
finalizeCallback: async (update: InstanceType<T>, logger = aliasLogger) => {
|
|
343
|
+
finalizeCallback: async (update: InstanceType<T>, logger = aliasLogger): Promise<boolean | void> => {
|
|
333
344
|
Log.info(`Executing finalize action with alias: ${binding.alias || "no alias provided"}`);
|
|
334
345
|
return await finalizeCallback(update, logger);
|
|
335
346
|
},
|
|
@@ -380,13 +391,13 @@ export class Capability implements CapabilityExport {
|
|
|
380
391
|
return commonChain;
|
|
381
392
|
}
|
|
382
393
|
|
|
383
|
-
function Alias(alias: string) {
|
|
394
|
+
function Alias(alias: string): CommonChainType {
|
|
384
395
|
Log.debug(`Adding prefix alias ${alias}`, prefix);
|
|
385
396
|
binding.alias = alias;
|
|
386
397
|
return commonChain;
|
|
387
398
|
}
|
|
388
399
|
|
|
389
|
-
function bindEvent(event: Event) {
|
|
400
|
+
function bindEvent(event: Event): ExtendedCommonChainType {
|
|
390
401
|
binding.event = event;
|
|
391
402
|
return {
|
|
392
403
|
...commonChain,
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
3
|
import { clone } from "ramda";
|
|
4
4
|
import { Capability } from "./capability";
|
|
5
|
-
import { Controller } from "
|
|
6
|
-
import { ValidateError } from "
|
|
7
|
-
import { MutateResponse, ValidateResponse, WebhookIgnore } from "
|
|
8
|
-
import { CapabilityExport, AdmissionRequest } from "
|
|
9
|
-
import { setupWatch } from "
|
|
10
|
-
import { Log } from "
|
|
5
|
+
import { Controller } from "../controller";
|
|
6
|
+
import { ValidateError } from "../errors";
|
|
7
|
+
import { MutateResponse, ValidateResponse, WebhookIgnore } from "../k8s";
|
|
8
|
+
import { CapabilityExport, AdmissionRequest } from "../types";
|
|
9
|
+
import { setupWatch } from "../processors/watch-processor";
|
|
10
|
+
import { Log } from "../../lib";
|
|
11
11
|
import { V1PolicyRule as PolicyRule } from "@kubernetes/client-node";
|
|
12
12
|
|
|
13
13
|
/** Custom Labels Type for package.json */
|
|
@@ -58,12 +58,12 @@ export type PeprModuleOptions = {
|
|
|
58
58
|
};
|
|
59
59
|
|
|
60
60
|
// Track if this is a watch mode controller
|
|
61
|
-
export const isWatchMode = () => process.env.PEPR_WATCH_MODE === "true";
|
|
61
|
+
export const isWatchMode = (): boolean => process.env.PEPR_WATCH_MODE === "true";
|
|
62
62
|
|
|
63
63
|
// Track if Pepr is running in build mode
|
|
64
|
-
export const isBuildMode = () => process.env.PEPR_MODE === "build";
|
|
64
|
+
export const isBuildMode = (): boolean => process.env.PEPR_MODE === "build";
|
|
65
65
|
|
|
66
|
-
export const isDevMode = () => process.env.PEPR_MODE === "dev";
|
|
66
|
+
export const isDevMode = (): boolean => process.env.PEPR_MODE === "dev";
|
|
67
67
|
|
|
68
68
|
export class PeprModule {
|
|
69
69
|
#controller!: Controller;
|
|
@@ -135,7 +135,7 @@ export class PeprModule {
|
|
|
135
135
|
*
|
|
136
136
|
* @param port
|
|
137
137
|
*/
|
|
138
|
-
start = (port = 3000) => {
|
|
138
|
+
start = (port = 3000): void => {
|
|
139
139
|
this.#controller.startServer(port);
|
|
140
140
|
};
|
|
141
141
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { KubernetesObject } from "@kubernetes/client-node";
|
|
4
4
|
import { WatchPhase } from "kubernetes-fluent-client/dist/fluent/types";
|
|
5
5
|
import { randomBytes } from "node:crypto";
|
|
6
|
-
import Log from "
|
|
6
|
+
import Log from "../telemetry/logger";
|
|
7
7
|
|
|
8
8
|
type WatchCallback = (obj: KubernetesObject, phase: WatchPhase) => Promise<void>;
|
|
9
9
|
|
|
@@ -29,11 +29,19 @@ export class Queue<K extends KubernetesObject> {
|
|
|
29
29
|
this.#uid = `${Date.now()}-${randomBytes(2).toString("hex")}`;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
label() {
|
|
32
|
+
label(): { name: string; uid: string } {
|
|
33
33
|
return { name: this.#name, uid: this.#uid };
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
stats() {
|
|
36
|
+
stats(): {
|
|
37
|
+
queue: {
|
|
38
|
+
name: string;
|
|
39
|
+
uid: string;
|
|
40
|
+
};
|
|
41
|
+
stats: {
|
|
42
|
+
length: number;
|
|
43
|
+
};
|
|
44
|
+
} {
|
|
37
45
|
return {
|
|
38
46
|
queue: this.label(),
|
|
39
47
|
stats: {
|
|
@@ -51,7 +59,7 @@ export class Queue<K extends KubernetesObject> {
|
|
|
51
59
|
* @param reconcile The callback to enqueue for reconcile
|
|
52
60
|
* @returns A promise that resolves when the object is reconciled
|
|
53
61
|
*/
|
|
54
|
-
enqueue(item: K, phase: WatchPhase, reconcile: WatchCallback) {
|
|
62
|
+
enqueue(item: K, phase: WatchPhase, reconcile: WatchCallback): Promise<void> {
|
|
55
63
|
const note = {
|
|
56
64
|
queue: this.label(),
|
|
57
65
|
item: {
|
|
@@ -73,7 +81,7 @@ export class Queue<K extends KubernetesObject> {
|
|
|
73
81
|
*
|
|
74
82
|
* @returns A promise that resolves when the webapp is reconciled
|
|
75
83
|
*/
|
|
76
|
-
async #dequeue() {
|
|
84
|
+
async #dequeue(): Promise<false | undefined> {
|
|
77
85
|
// If there is a pending promise, do nothing
|
|
78
86
|
if (this.#pendingPromise) {
|
|
79
87
|
Log.debug("Pending promise, not dequeuing");
|
|
@@ -12,6 +12,11 @@ export type Unsubscribe = () => void;
|
|
|
12
12
|
const MAX_WAIT_TIME = 15000;
|
|
13
13
|
const STORE_VERSION_PREFIX = "v2";
|
|
14
14
|
|
|
15
|
+
interface WaitRecord {
|
|
16
|
+
timeout?: ReturnType<typeof setTimeout>;
|
|
17
|
+
unsubscribe?: () => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
export function v2StoreKey(key: string): string {
|
|
16
21
|
return `${STORE_VERSION_PREFIX}-${pointer.escape(key)}`;
|
|
17
22
|
}
|
|
@@ -58,13 +63,13 @@ export interface PeprStore {
|
|
|
58
63
|
* Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
|
|
59
64
|
* Resolves when the key/value show up in the store.
|
|
60
65
|
*/
|
|
61
|
-
setItemAndWait(key: string, value: string): Promise<
|
|
66
|
+
setItemAndWait(key: string, value: string): Promise<string>;
|
|
62
67
|
|
|
63
68
|
/**
|
|
64
69
|
* Remove the value of the key.
|
|
65
70
|
* Resolves when the key does not show up in the store.
|
|
66
71
|
*/
|
|
67
|
-
removeItemAndWait(key: string): Promise<
|
|
72
|
+
removeItemAndWait(key: string): Promise<string>;
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
/**
|
|
@@ -128,22 +133,24 @@ export class Storage implements PeprStore {
|
|
|
128
133
|
* @param value - The value of the key
|
|
129
134
|
* @returns
|
|
130
135
|
*/
|
|
131
|
-
setItemAndWait = (key: string, value: string): Promise<
|
|
136
|
+
setItemAndWait = (key: string, value: string): Promise<string> => {
|
|
132
137
|
this.#dispatchUpdate("add", [v2StoreKey(key)], value);
|
|
138
|
+
const record: WaitRecord = {};
|
|
133
139
|
|
|
134
|
-
return new Promise<
|
|
135
|
-
|
|
140
|
+
return new Promise<string>((resolve, reject) => {
|
|
141
|
+
// If promise has not resolved before MAX_WAIT_TIME reject
|
|
142
|
+
record.timeout = setTimeout(() => {
|
|
143
|
+
record.unsubscribe!();
|
|
144
|
+
return reject(`MAX_WAIT_TIME elapsed: Key ${key} not seen in ${MAX_WAIT_TIME / 1000}s`);
|
|
145
|
+
}, MAX_WAIT_TIME);
|
|
146
|
+
|
|
147
|
+
record.unsubscribe = this.subscribe(data => {
|
|
136
148
|
if (data[`${v2UnescapedStoreKey(key)}`] === value) {
|
|
137
|
-
unsubscribe();
|
|
138
|
-
|
|
149
|
+
record.unsubscribe!();
|
|
150
|
+
clearTimeout(record.timeout);
|
|
151
|
+
resolve("ok");
|
|
139
152
|
}
|
|
140
153
|
});
|
|
141
|
-
|
|
142
|
-
// If promise has not resolved before MAX_WAIT_TIME reject
|
|
143
|
-
setTimeout(() => {
|
|
144
|
-
unsubscribe();
|
|
145
|
-
return reject();
|
|
146
|
-
}, MAX_WAIT_TIME);
|
|
147
154
|
});
|
|
148
155
|
};
|
|
149
156
|
|
|
@@ -154,21 +161,23 @@ export class Storage implements PeprStore {
|
|
|
154
161
|
* @param key - The key to add into the store
|
|
155
162
|
* @returns
|
|
156
163
|
*/
|
|
157
|
-
removeItemAndWait = (key: string): Promise<
|
|
164
|
+
removeItemAndWait = (key: string): Promise<string> => {
|
|
158
165
|
this.#dispatchUpdate("remove", [v2StoreKey(key)]);
|
|
159
|
-
|
|
160
|
-
|
|
166
|
+
const record: WaitRecord = {};
|
|
167
|
+
return new Promise<string>((resolve, reject) => {
|
|
168
|
+
// If promise has not resolved before MAX_WAIT_TIME reject
|
|
169
|
+
record.timeout = setTimeout(() => {
|
|
170
|
+
record.unsubscribe!();
|
|
171
|
+
return reject(`MAX_WAIT_TIME elapsed: Key ${key} still seen after ${MAX_WAIT_TIME / 1000}s`);
|
|
172
|
+
}, MAX_WAIT_TIME);
|
|
173
|
+
|
|
174
|
+
record.unsubscribe = this.subscribe(data => {
|
|
161
175
|
if (!Object.hasOwn(data, `${v2UnescapedStoreKey(key)}`)) {
|
|
162
|
-
unsubscribe();
|
|
163
|
-
|
|
176
|
+
record.unsubscribe!();
|
|
177
|
+
clearTimeout(record.timeout);
|
|
178
|
+
resolve("ok");
|
|
164
179
|
}
|
|
165
180
|
});
|
|
166
|
-
|
|
167
|
-
// If promise has not resolved before MAX_WAIT_TIME reject
|
|
168
|
-
setTimeout(() => {
|
|
169
|
-
unsubscribe();
|
|
170
|
-
return reject();
|
|
171
|
-
}, MAX_WAIT_TIME);
|
|
172
181
|
});
|
|
173
182
|
};
|
|
174
183
|
|
|
@@ -4,7 +4,7 @@ import { K8s, kind } from "kubernetes-fluent-client";
|
|
|
4
4
|
import Log from "./telemetry/logger";
|
|
5
5
|
|
|
6
6
|
// returns true if all deployments are ready, false otherwise
|
|
7
|
-
export async function checkDeploymentStatus(namespace: string) {
|
|
7
|
+
export async function checkDeploymentStatus(namespace: string): Promise<boolean> {
|
|
8
8
|
const deployments = await K8s(kind.Deployment).InNamespace(namespace).Get();
|
|
9
9
|
let status = false;
|
|
10
10
|
let readyCount = 0;
|
|
@@ -29,7 +29,7 @@ export async function checkDeploymentStatus(namespace: string) {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// wait for all deployments in the pepr-system namespace to be ready
|
|
32
|
-
export async function namespaceDeploymentsReady(namespace: string = "pepr-system") {
|
|
32
|
+
export async function namespaceDeploymentsReady(namespace: string = "pepr-system"): Promise<true | undefined> {
|
|
33
33
|
Log.info(`Checking ${namespace} deployments status...`);
|
|
34
34
|
let ready = false;
|
|
35
35
|
while (!ready) {
|
package/src/lib/errors.ts
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
audit: "audit",
|
|
6
|
-
ignore: "ignore",
|
|
7
|
-
reject: "reject",
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export const ErrorList = Object.values(Errors);
|
|
4
|
+
import { OnError } from "../cli/init/enums";
|
|
11
5
|
|
|
6
|
+
export const ErrorList = Object.values(OnError) as string[];
|
|
12
7
|
/**
|
|
13
8
|
* Validate the error or throw an error
|
|
14
9
|
* @param error
|
|
15
10
|
*/
|
|
16
|
-
export function ValidateError(error = "") {
|
|
11
|
+
export function ValidateError(error: string = ""): void {
|
|
17
12
|
if (!ErrorList.includes(error)) {
|
|
18
13
|
throw new Error(`Invalid error: ${error}. Must be one of: ${ErrorList.join(", ")}`);
|
|
19
14
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
3
|
|
|
4
4
|
import { Event, Operation } from "../../enums";
|
|
5
|
-
import { AdmissionRequest, Binding } from "../../types";
|
|
5
|
+
import { AdmissionRequest, Binding, FinalizeAction, WatchLogAction, MutateAction, ValidateAction } from "../../types";
|
|
6
6
|
import {
|
|
7
7
|
__,
|
|
8
8
|
allPass,
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
nthArg,
|
|
20
20
|
pipe,
|
|
21
21
|
} from "ramda";
|
|
22
|
-
import { KubernetesObject } from "kubernetes-fluent-client";
|
|
22
|
+
import { GenericClass, KubernetesObject } from "kubernetes-fluent-client";
|
|
23
23
|
|
|
24
24
|
/*
|
|
25
25
|
Naming scheme:
|
|
@@ -77,6 +77,7 @@ export const carriedNamespace = pipe(
|
|
|
77
77
|
(kubernetesObject: KubernetesObject): string | undefined => kubernetesObject?.metadata?.namespace,
|
|
78
78
|
defaultTo(""),
|
|
79
79
|
);
|
|
80
|
+
|
|
80
81
|
export const carriesNamespace = pipe(carriedNamespace, equals(""), not);
|
|
81
82
|
|
|
82
83
|
export const carriedAnnotations = pipe(
|
|
@@ -144,7 +145,7 @@ export const definesVersion = pipe(definedVersion, equals(""), not);
|
|
|
144
145
|
export const definedKind = pipe((binding): string => binding?.kind?.kind, defaultTo(""));
|
|
145
146
|
export const definesKind = pipe(definedKind, equals(""), not);
|
|
146
147
|
|
|
147
|
-
export const definedCategory = (binding: Partial<Binding>) => {
|
|
148
|
+
export const definedCategory = (binding: Partial<Binding>): string => {
|
|
148
149
|
// Ordering matters, finalize is a "watch"
|
|
149
150
|
// prettier-ignore
|
|
150
151
|
return binding.isFinalize ? "Finalize" :
|
|
@@ -153,8 +154,15 @@ export const definedCategory = (binding: Partial<Binding>) => {
|
|
|
153
154
|
binding.isValidate ? "Validate" :
|
|
154
155
|
"";
|
|
155
156
|
};
|
|
156
|
-
|
|
157
|
-
|
|
157
|
+
export type DefinedCallbackReturnType =
|
|
158
|
+
| FinalizeAction<GenericClass, InstanceType<GenericClass>>
|
|
159
|
+
| WatchLogAction<GenericClass, InstanceType<GenericClass>>
|
|
160
|
+
| MutateAction<GenericClass, InstanceType<GenericClass>>
|
|
161
|
+
| ValidateAction<GenericClass, InstanceType<GenericClass>>
|
|
162
|
+
| null
|
|
163
|
+
| undefined;
|
|
164
|
+
|
|
165
|
+
export const definedCallback = (binding: Partial<Binding>): DefinedCallbackReturnType => {
|
|
158
166
|
// Ordering matters, finalize is a "watch"
|
|
159
167
|
// prettier-ignore
|
|
160
168
|
return binding.isFinalize ? binding.finalizeCallback :
|
|
@@ -241,10 +249,21 @@ export const mismatchedLabels = allPass([
|
|
|
241
249
|
pipe((binding, kubernetesObject) => metasMismatch(definedLabels(binding), carriedLabels(kubernetesObject))),
|
|
242
250
|
]);
|
|
243
251
|
|
|
252
|
+
/*
|
|
253
|
+
* If the object does not have a namespace, and it is not a namespace,
|
|
254
|
+
* then we must return false because it cannot be uncarryable
|
|
255
|
+
*/
|
|
244
256
|
export const uncarryableNamespace = allPass([
|
|
245
257
|
pipe(nthArg(0), length, gt(__, 0)),
|
|
246
|
-
pipe(
|
|
247
|
-
|
|
258
|
+
pipe((namespaceSelector, kubernetesObject) => {
|
|
259
|
+
if (kubernetesObject?.kind === "Namespace") {
|
|
260
|
+
return namespaceSelector.includes(kubernetesObject?.metadata?.name);
|
|
261
|
+
}
|
|
262
|
+
if (carriesNamespace(kubernetesObject)) {
|
|
263
|
+
return namespaceSelector.includes(carriedNamespace(kubernetesObject));
|
|
264
|
+
}
|
|
265
|
+
return true;
|
|
266
|
+
}, not),
|
|
248
267
|
]);
|
|
249
268
|
|
|
250
269
|
export const missingCarriableNamespace = allPass([
|
|
@@ -256,10 +275,22 @@ export const missingCarriableNamespace = allPass([
|
|
|
256
275
|
),
|
|
257
276
|
]);
|
|
258
277
|
|
|
278
|
+
/*
|
|
279
|
+
* If the object does not have a namespace, and it is not a namespace,
|
|
280
|
+
* then we must return false because it cannot be ignored
|
|
281
|
+
*/
|
|
259
282
|
export const carriesIgnoredNamespace = allPass([
|
|
260
283
|
pipe(nthArg(0), length, gt(__, 0)),
|
|
261
|
-
pipe(
|
|
262
|
-
|
|
284
|
+
pipe((namespaceSelector, kubernetesObject) => {
|
|
285
|
+
if (kubernetesObject?.kind === "Namespace") {
|
|
286
|
+
return namespaceSelector.includes(kubernetesObject?.metadata?.name);
|
|
287
|
+
}
|
|
288
|
+
if (carriesNamespace(kubernetesObject)) {
|
|
289
|
+
return namespaceSelector.includes(carriedNamespace(kubernetesObject));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return false;
|
|
293
|
+
}),
|
|
263
294
|
]);
|
|
264
295
|
|
|
265
296
|
export const unbindableNamespaces = allPass([
|