pepr 0.35.0 → 0.37.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.
- package/dist/cli/init/index.d.ts.map +1 -1
- package/dist/cli/init/templates.d.ts +3 -1
- 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 +10 -3
- package/dist/cli/init/walkthrough.d.ts.map +1 -1
- package/dist/cli.js +253 -31
- package/dist/controller.js +138 -1
- package/dist/lib/adjudicators.d.ts +63 -0
- package/dist/lib/adjudicators.d.ts.map +1 -0
- package/dist/lib/adjudicators.test.d.ts +2 -0
- package/dist/lib/adjudicators.test.d.ts.map +1 -0
- package/dist/lib/assets/loader.d.ts.map +1 -1
- package/dist/lib/assets/pods.d.ts +1 -0
- package/dist/lib/assets/pods.d.ts.map +1 -1
- package/dist/lib/capability.d.ts +1 -0
- package/dist/lib/capability.d.ts.map +1 -1
- package/dist/lib/capability.test.d.ts +2 -0
- package/dist/lib/capability.test.d.ts.map +1 -0
- package/dist/lib/controller/index.d.ts.map +1 -1
- package/dist/lib/controller/store.d.ts +4 -0
- package/dist/lib/controller/store.d.ts.map +1 -1
- package/dist/lib/controller/store.test.d.ts +2 -0
- package/dist/lib/controller/store.test.d.ts.map +1 -0
- package/dist/lib/filter.d.ts +2 -3
- package/dist/lib/filter.d.ts.map +1 -1
- package/dist/lib/filter.test.d.ts +2 -1
- package/dist/lib/filter.test.d.ts.map +1 -1
- package/dist/lib/finalizer.d.ts +6 -0
- package/dist/lib/finalizer.d.ts.map +1 -0
- package/dist/lib/finalizer.test.d.ts +2 -0
- package/dist/lib/finalizer.test.d.ts.map +1 -0
- package/dist/lib/helpers.d.ts +2 -2
- package/dist/lib/helpers.d.ts.map +1 -1
- package/dist/lib/helpers.test.d.ts +1 -1
- package/dist/lib/helpers.test.d.ts.map +1 -1
- package/dist/lib/k8s.d.ts.map +1 -1
- package/dist/lib/logger.d.ts +1 -1
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/module.d.ts +2 -1
- package/dist/lib/module.d.ts.map +1 -1
- package/dist/lib/mutate-processor.d.ts +2 -1
- package/dist/lib/mutate-processor.d.ts.map +1 -1
- package/dist/lib/mutate-request.d.ts +1 -2
- package/dist/lib/mutate-request.d.ts.map +1 -1
- package/dist/lib/queue.d.ts +19 -3
- package/dist/lib/queue.d.ts.map +1 -1
- package/dist/lib/schedule.d.ts +1 -2
- package/dist/lib/schedule.d.ts.map +1 -1
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/types.d.ts +118 -6
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/validate-processor.d.ts +4 -2
- package/dist/lib/validate-processor.d.ts.map +1 -1
- package/dist/lib/validate-request.d.ts +1 -1
- package/dist/lib/validate-request.d.ts.map +1 -1
- package/dist/lib/watch-processor.d.ts +8 -6
- package/dist/lib/watch-processor.d.ts.map +1 -1
- package/dist/lib.js +467 -233
- package/dist/lib.js.map +4 -4
- package/dist/sdk/sdk.d.ts +5 -3
- package/dist/sdk/sdk.d.ts.map +1 -1
- package/package.json +13 -11
- package/src/cli/build.ts +3 -3
- package/src/cli/init/index.ts +20 -11
- package/src/cli/init/templates.ts +1 -1
- package/src/cli/init/utils.test.ts +11 -20
- package/src/cli/init/utils.ts +5 -0
- package/src/cli/init/walkthrough.test.ts +92 -11
- package/src/cli/init/walkthrough.ts +71 -16
- package/src/cli/monitor.ts +1 -1
- package/src/cli.ts +4 -2
- package/src/fixtures/data/create-pod.json +1 -1
- package/src/fixtures/data/delete-pod.json +1 -1
- package/src/lib/adjudicators.test.ts +1232 -0
- package/src/lib/adjudicators.ts +235 -0
- package/src/lib/assets/index.ts +1 -1
- package/src/lib/assets/loader.ts +1 -0
- package/src/lib/assets/webhooks.ts +1 -1
- package/src/lib/capability.test.ts +655 -0
- package/src/lib/capability.ts +112 -11
- package/src/lib/controller/index.ts +7 -4
- package/src/lib/controller/store.test.ts +131 -0
- package/src/lib/controller/store.ts +43 -5
- package/src/lib/filter.test.ts +279 -9
- package/src/lib/filter.ts +46 -98
- package/src/lib/finalizer.test.ts +236 -0
- package/src/lib/finalizer.ts +63 -0
- package/src/lib/helpers.test.ts +359 -65
- package/src/lib/helpers.ts +141 -95
- package/src/lib/k8s.ts +4 -0
- package/src/lib/module.ts +3 -3
- package/src/lib/mutate-processor.ts +5 -4
- package/src/lib/mutate-request.test.ts +1 -2
- package/src/lib/mutate-request.ts +1 -3
- package/src/lib/queue.test.ts +138 -44
- package/src/lib/queue.ts +48 -13
- package/src/lib/schedule.ts +1 -1
- package/src/lib/storage.ts +5 -6
- package/src/lib/types.ts +154 -5
- package/src/lib/validate-processor.ts +5 -2
- package/src/lib/validate-request.test.ts +1 -4
- package/src/lib/validate-request.ts +1 -1
- package/src/lib/watch-processor.test.ts +89 -124
- package/src/lib/watch-processor.ts +52 -35
- package/src/sdk/sdk.test.ts +46 -13
- package/src/sdk/sdk.ts +15 -6
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
|
-
import { afterAll, beforeEach,
|
|
3
|
+
import { afterAll, beforeEach, describe, expect, it, jest } from "@jest/globals";
|
|
4
4
|
import { GenericClass, K8s, KubernetesObject, kind } from "kubernetes-fluent-client";
|
|
5
5
|
import { K8sInit, WatchPhase } from "kubernetes-fluent-client/dist/fluent/types";
|
|
6
6
|
import { WatchCfg, WatchEvent, Watcher } from "kubernetes-fluent-client/dist/fluent/watch";
|
|
7
7
|
import { Capability } from "./capability";
|
|
8
|
-
import { setupWatch, logEvent,
|
|
8
|
+
import { setupWatch, logEvent, queueKey, getOrCreateQueue } from "./watch-processor";
|
|
9
9
|
import Log from "./logger";
|
|
10
10
|
import { metricsCollector } from "./metrics";
|
|
11
11
|
|
|
@@ -40,6 +40,7 @@ describe("WatchProcessor", () => {
|
|
|
40
40
|
bindings: [
|
|
41
41
|
{
|
|
42
42
|
isWatch: true,
|
|
43
|
+
isQueue: false,
|
|
43
44
|
model: "someModel",
|
|
44
45
|
filters: {},
|
|
45
46
|
event: "Create",
|
|
@@ -93,8 +94,15 @@ describe("WatchProcessor", () => {
|
|
|
93
94
|
|
|
94
95
|
capabilities.push({
|
|
95
96
|
bindings: [
|
|
96
|
-
{
|
|
97
|
-
|
|
97
|
+
{
|
|
98
|
+
isWatch: true,
|
|
99
|
+
isQueue: true,
|
|
100
|
+
model: "someModel",
|
|
101
|
+
filters: { name: "bleh" },
|
|
102
|
+
event: "Create",
|
|
103
|
+
watchCallback: jest.fn(),
|
|
104
|
+
},
|
|
105
|
+
{ isWatch: false, isQueue: false, model: "someModel", filters: {}, event: "Create", watchCallback: jest.fn() },
|
|
98
106
|
],
|
|
99
107
|
} as unknown as Capability);
|
|
100
108
|
|
|
@@ -321,133 +329,90 @@ describe("logEvent function", () => {
|
|
|
321
329
|
});
|
|
322
330
|
});
|
|
323
331
|
|
|
324
|
-
describe("
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
};
|
|
373
|
-
|
|
374
|
-
expect(queueRecordKey(obj)).toBe("Pod/Unnamed/cluster-scoped");
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
it("should handle objects with missing kind", () => {
|
|
378
|
-
const obj: KubernetesObject = {
|
|
379
|
-
metadata: {
|
|
380
|
-
name: "my-pod",
|
|
381
|
-
namespace: "my-namespace",
|
|
382
|
-
},
|
|
383
|
-
};
|
|
384
|
-
|
|
385
|
-
expect(queueRecordKey(obj)).toBe("UnknownKind/my-pod/my-namespace");
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
it("should handle completely empty objects", () => {
|
|
389
|
-
const obj: KubernetesObject = {};
|
|
390
|
-
|
|
391
|
-
expect(queueRecordKey(obj)).toBe("UnknownKind/Unnamed/cluster-scoped");
|
|
392
|
-
});
|
|
332
|
+
describe("queueKey", () => {
|
|
333
|
+
const withKindNsName = { kind: "Pod", metadata: { namespace: "my-ns", name: "my-name" } } as KubernetesObject;
|
|
334
|
+
const withKindNs = { kind: "Pod", metadata: { namespace: "my-ns" } } as KubernetesObject;
|
|
335
|
+
const withKindName = { kind: "Pod", metadata: { name: "my-name" } } as KubernetesObject;
|
|
336
|
+
const withNsName = { metadata: { namespace: "my-ns", name: "my-name" } } as KubernetesObject;
|
|
337
|
+
const withKind = { kind: "Pod" } as KubernetesObject;
|
|
338
|
+
const withNs = { metadata: { namespace: "my-ns" } } as KubernetesObject;
|
|
339
|
+
const withName = { metadata: { name: "my-name" } } as KubernetesObject;
|
|
340
|
+
const withNone = {} as KubernetesObject;
|
|
341
|
+
|
|
342
|
+
const original = process.env.PEPR_RECONCILE_STRATEGY;
|
|
343
|
+
|
|
344
|
+
it.each([
|
|
345
|
+
["kind", withKindNsName, "Pod"],
|
|
346
|
+
["kind", withKindNs, "Pod"],
|
|
347
|
+
["kind", withKindName, "Pod"],
|
|
348
|
+
["kind", withNsName, "UnknownKind"],
|
|
349
|
+
["kind", withKind, "Pod"],
|
|
350
|
+
["kind", withNs, "UnknownKind"],
|
|
351
|
+
["kind", withName, "UnknownKind"],
|
|
352
|
+
["kind", withNone, "UnknownKind"],
|
|
353
|
+
["kindNs", withKindNsName, "Pod/my-ns"],
|
|
354
|
+
["kindNs", withKindNs, "Pod/my-ns"],
|
|
355
|
+
["kindNs", withKindName, "Pod/cluster-scoped"],
|
|
356
|
+
["kindNs", withNsName, "UnknownKind/my-ns"],
|
|
357
|
+
["kindNs", withKind, "Pod/cluster-scoped"],
|
|
358
|
+
["kindNs", withNs, "UnknownKind/my-ns"],
|
|
359
|
+
["kindNs", withName, "UnknownKind/cluster-scoped"],
|
|
360
|
+
["kindNs", withNone, "UnknownKind/cluster-scoped"],
|
|
361
|
+
["kindNsName", withKindNsName, "Pod/my-ns/my-name"],
|
|
362
|
+
["kindNsName", withKindNs, "Pod/my-ns/Unnamed"],
|
|
363
|
+
["kindNsName", withKindName, "Pod/cluster-scoped/my-name"],
|
|
364
|
+
["kindNsName", withNsName, "UnknownKind/my-ns/my-name"],
|
|
365
|
+
["kindNsName", withKind, "Pod/cluster-scoped/Unnamed"],
|
|
366
|
+
["kindNsName", withNs, "UnknownKind/my-ns/Unnamed"],
|
|
367
|
+
["kindNsName", withName, "UnknownKind/cluster-scoped/my-name"],
|
|
368
|
+
["kindNsName", withNone, "UnknownKind/cluster-scoped/Unnamed"],
|
|
369
|
+
["global", withKindNsName, "global"],
|
|
370
|
+
["global", withKindNs, "global"],
|
|
371
|
+
["global", withKindName, "global"],
|
|
372
|
+
["global", withNsName, "global"],
|
|
373
|
+
["global", withKind, "global"],
|
|
374
|
+
["global", withNs, "global"],
|
|
375
|
+
["global", withName, "global"],
|
|
376
|
+
["global", withNone, "global"],
|
|
377
|
+
])("PEPR_RECONCILE_STRATEGY='%s' over '%j' becomes '%s'", (strat, obj, key) => {
|
|
378
|
+
process.env.PEPR_RECONCILE_STRATEGY = strat;
|
|
379
|
+
expect(queueKey(obj)).toBe(key);
|
|
393
380
|
});
|
|
394
381
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
process.env.PEPR_RECONCILE_STRATEGY = "singular";
|
|
400
|
-
});
|
|
401
|
-
afterAll(() => {
|
|
402
|
-
process.env.PEPR_RECONCILE_STRATEGY = original;
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
it("should return correct key for an object with 'kind/namespace'", () => {
|
|
406
|
-
const obj: KubernetesObject = {
|
|
407
|
-
kind: "Pod",
|
|
408
|
-
metadata: {
|
|
409
|
-
name: "my-pod",
|
|
410
|
-
namespace: "my-namespace",
|
|
411
|
-
},
|
|
412
|
-
};
|
|
413
|
-
|
|
414
|
-
expect(queueRecordKey(obj)).toBe("Pod/my-namespace");
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
it("should handle objects with missing namespace", () => {
|
|
418
|
-
const obj: KubernetesObject = {
|
|
419
|
-
kind: "Pod",
|
|
420
|
-
metadata: {
|
|
421
|
-
name: "my-pod",
|
|
422
|
-
},
|
|
423
|
-
};
|
|
424
|
-
|
|
425
|
-
expect(queueRecordKey(obj)).toBe("Pod/cluster-scoped");
|
|
426
|
-
});
|
|
382
|
+
afterAll(() => {
|
|
383
|
+
process.env.PEPR_RECONCILE_STRATEGY = original;
|
|
384
|
+
});
|
|
385
|
+
});
|
|
427
386
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
387
|
+
describe("getOrCreateQueue", () => {
|
|
388
|
+
it("creates a Queue instance on first call", () => {
|
|
389
|
+
const obj: KubernetesObject = {
|
|
390
|
+
kind: "queue",
|
|
391
|
+
metadata: {
|
|
392
|
+
name: "nm",
|
|
393
|
+
namespace: "ns",
|
|
394
|
+
},
|
|
395
|
+
};
|
|
432
396
|
|
|
433
|
-
|
|
434
|
-
|
|
397
|
+
const firstQueue = getOrCreateQueue(obj);
|
|
398
|
+
expect(firstQueue.label()).toBeDefined();
|
|
399
|
+
});
|
|
435
400
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
401
|
+
it("returns same Queue instance on subsequent calls", () => {
|
|
402
|
+
const obj: KubernetesObject = {
|
|
403
|
+
kind: "queue",
|
|
404
|
+
metadata: {
|
|
405
|
+
name: "nm",
|
|
406
|
+
namespace: "ns",
|
|
407
|
+
},
|
|
408
|
+
};
|
|
443
409
|
|
|
444
|
-
|
|
445
|
-
|
|
410
|
+
const firstQueue = getOrCreateQueue(obj);
|
|
411
|
+
expect(firstQueue.label()).toBeDefined();
|
|
446
412
|
|
|
447
|
-
|
|
448
|
-
|
|
413
|
+
const secondQueue = getOrCreateQueue(obj);
|
|
414
|
+
expect(secondQueue.label()).toBeDefined();
|
|
449
415
|
|
|
450
|
-
|
|
451
|
-
});
|
|
416
|
+
expect(firstQueue).toBe(secondQueue);
|
|
452
417
|
});
|
|
453
418
|
});
|
|
@@ -4,23 +4,24 @@ import { K8s, KubernetesObject, WatchCfg, WatchEvent } from "kubernetes-fluent-c
|
|
|
4
4
|
import { WatchPhase } from "kubernetes-fluent-client/dist/fluent/types";
|
|
5
5
|
import { Capability } from "./capability";
|
|
6
6
|
import { filterNoMatchReason } from "./helpers";
|
|
7
|
+
import { removeFinalizer } from "./finalizer";
|
|
7
8
|
import Log from "./logger";
|
|
8
9
|
import { Queue } from "./queue";
|
|
9
10
|
import { Binding, Event } from "./types";
|
|
10
11
|
import { metricsCollector } from "./metrics";
|
|
11
12
|
|
|
12
|
-
//
|
|
13
|
-
const
|
|
13
|
+
// stores Queue instances
|
|
14
|
+
const queues: Record<string, Queue<KubernetesObject>> = {};
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
|
-
* Get the key for
|
|
17
|
+
* Get the key for an entry in the queues
|
|
17
18
|
*
|
|
18
|
-
* @param obj The object to
|
|
19
|
-
* @returns The key
|
|
19
|
+
* @param obj The object to derive a key from
|
|
20
|
+
* @returns The key to a Queue in the list of queues
|
|
20
21
|
*/
|
|
21
|
-
export function
|
|
22
|
-
const options = ["
|
|
23
|
-
const d3fault = "
|
|
22
|
+
export function queueKey(obj: KubernetesObject) {
|
|
23
|
+
const options = ["kind", "kindNs", "kindNsName", "global"];
|
|
24
|
+
const d3fault = "kind";
|
|
24
25
|
|
|
25
26
|
let strat = process.env.PEPR_RECONCILE_STRATEGY || d3fault;
|
|
26
27
|
strat = options.includes(strat) ? strat : d3fault;
|
|
@@ -29,7 +30,21 @@ export function queueRecordKey(obj: KubernetesObject) {
|
|
|
29
30
|
const kind = obj.kind ?? "UnknownKind";
|
|
30
31
|
const name = obj.metadata?.name ?? "Unnamed";
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
const lookup: Record<string, string> = {
|
|
34
|
+
kind: `${kind}`,
|
|
35
|
+
kindNs: `${kind}/${ns}`,
|
|
36
|
+
kindNsName: `${kind}/${ns}/${name}`,
|
|
37
|
+
global: "global",
|
|
38
|
+
};
|
|
39
|
+
return lookup[strat];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getOrCreateQueue(obj: KubernetesObject) {
|
|
43
|
+
const key = queueKey(obj);
|
|
44
|
+
if (!queues[key]) {
|
|
45
|
+
queues[key] = new Queue<KubernetesObject>(key);
|
|
46
|
+
}
|
|
47
|
+
return queues[key];
|
|
33
48
|
}
|
|
34
49
|
|
|
35
50
|
// Watch configuration
|
|
@@ -58,11 +73,11 @@ const eventToPhaseMap = {
|
|
|
58
73
|
*
|
|
59
74
|
* @param capabilities The capabilities to load watches for
|
|
60
75
|
*/
|
|
61
|
-
export function setupWatch(capabilities: Capability[]) {
|
|
76
|
+
export function setupWatch(capabilities: Capability[], ignoredNamespaces?: string[]) {
|
|
62
77
|
capabilities.map(capability =>
|
|
63
78
|
capability.bindings
|
|
64
79
|
.filter(binding => binding.isWatch)
|
|
65
|
-
.forEach(bindingElement => runBinding(bindingElement, capability.namespaces)),
|
|
80
|
+
.forEach(bindingElement => runBinding(bindingElement, capability.namespaces, ignoredNamespaces)),
|
|
66
81
|
);
|
|
67
82
|
}
|
|
68
83
|
|
|
@@ -72,21 +87,34 @@ export function setupWatch(capabilities: Capability[]) {
|
|
|
72
87
|
* @param binding the binding to watch
|
|
73
88
|
* @param capabilityNamespaces list of namespaces to filter on
|
|
74
89
|
*/
|
|
75
|
-
async function runBinding(binding: Binding, capabilityNamespaces: string[]) {
|
|
90
|
+
async function runBinding(binding: Binding, capabilityNamespaces: string[], ignoredNamespaces?: string[]) {
|
|
76
91
|
// Get the phases to match, fallback to any
|
|
77
92
|
const phaseMatch: WatchPhase[] = eventToPhaseMap[binding.event] || eventToPhaseMap[Event.Any];
|
|
78
93
|
|
|
79
94
|
// The watch callback is run when an object is received or dequeued
|
|
80
|
-
|
|
81
95
|
Log.debug({ watchCfg }, "Effective WatchConfig");
|
|
82
|
-
|
|
96
|
+
|
|
97
|
+
const watchCallback = async (obj: KubernetesObject, phase: WatchPhase) => {
|
|
83
98
|
// First, filter the object based on the phase
|
|
84
|
-
if (phaseMatch.includes(
|
|
99
|
+
if (phaseMatch.includes(phase)) {
|
|
85
100
|
try {
|
|
86
101
|
// Then, check if the object matches the filter
|
|
87
|
-
const filterMatch = filterNoMatchReason(binding, obj, capabilityNamespaces);
|
|
102
|
+
const filterMatch = filterNoMatchReason(binding, obj, capabilityNamespaces, ignoredNamespaces);
|
|
88
103
|
if (filterMatch === "") {
|
|
89
|
-
|
|
104
|
+
if (binding.isFinalize) {
|
|
105
|
+
if (!obj.metadata?.deletionTimestamp) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
await binding.finalizeCallback?.(obj);
|
|
110
|
+
|
|
111
|
+
// irrespective of callback success / failure, remove pepr finalizer
|
|
112
|
+
} finally {
|
|
113
|
+
await removeFinalizer(binding, obj);
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
await binding.watchCallback?.(obj, phase);
|
|
117
|
+
}
|
|
90
118
|
} else {
|
|
91
119
|
Log.debug(filterMatch);
|
|
92
120
|
}
|
|
@@ -97,26 +125,15 @@ async function runBinding(binding: Binding, capabilityNamespaces: string[]) {
|
|
|
97
125
|
}
|
|
98
126
|
};
|
|
99
127
|
|
|
100
|
-
function getOrCreateQueue(key: string): Queue<KubernetesObject> {
|
|
101
|
-
if (!queueRecord[key]) {
|
|
102
|
-
queueRecord[key] = new Queue<KubernetesObject>();
|
|
103
|
-
queueRecord[key].setReconcile(watchCallback);
|
|
104
|
-
}
|
|
105
|
-
return queueRecord[key];
|
|
106
|
-
}
|
|
107
|
-
|
|
108
128
|
// Setup the resource watch
|
|
109
|
-
const watcher = K8s(binding.model, binding.filters).Watch(async (obj,
|
|
110
|
-
Log.debug(obj, `Watch event ${
|
|
111
|
-
|
|
112
|
-
const queue = getOrCreateQueue(queueRecordKey(obj));
|
|
129
|
+
const watcher = K8s(binding.model, binding.filters).Watch(async (obj, phase) => {
|
|
130
|
+
Log.debug(obj, `Watch event ${phase} received`);
|
|
113
131
|
|
|
114
|
-
// If the binding is a queue, enqueue the object
|
|
115
132
|
if (binding.isQueue) {
|
|
116
|
-
|
|
133
|
+
const queue = getOrCreateQueue(obj);
|
|
134
|
+
await queue.enqueue(obj, phase, watchCallback);
|
|
117
135
|
} else {
|
|
118
|
-
|
|
119
|
-
await watchCallback(obj, type);
|
|
136
|
+
await watchCallback(obj, phase);
|
|
120
137
|
}
|
|
121
138
|
}, watchCfg);
|
|
122
139
|
|
|
@@ -160,8 +177,8 @@ async function runBinding(binding: Binding, capabilityNamespaces: string[]) {
|
|
|
160
177
|
}
|
|
161
178
|
}
|
|
162
179
|
|
|
163
|
-
export function logEvent(
|
|
164
|
-
const logMessage = `Watch event ${
|
|
180
|
+
export function logEvent(event: WatchEvent, message: string = "", obj?: KubernetesObject) {
|
|
181
|
+
const logMessage = `Watch event ${event} received${message ? `. ${message}.` : "."}`;
|
|
165
182
|
if (obj) {
|
|
166
183
|
Log.debug(obj, logMessage);
|
|
167
184
|
} else {
|
package/src/sdk/sdk.test.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { beforeEach, describe, it, jest } from "@jest/globals";
|
|
|
11
11
|
import { GenericKind } from "kubernetes-fluent-client";
|
|
12
12
|
import { K8s, kind } from "kubernetes-fluent-client";
|
|
13
13
|
import { Mock } from "jest-mock";
|
|
14
|
+
import { V1OwnerReference } from "@kubernetes/client-node";
|
|
14
15
|
|
|
15
16
|
jest.mock("kubernetes-fluent-client", () => ({
|
|
16
17
|
K8s: jest.fn(),
|
|
@@ -163,23 +164,55 @@ describe("writeEvent", () => {
|
|
|
163
164
|
});
|
|
164
165
|
|
|
165
166
|
describe("getOwnerRefFrom", () => {
|
|
166
|
-
|
|
167
|
-
|
|
167
|
+
const customResource = {
|
|
168
|
+
apiVersion: "v1",
|
|
169
|
+
kind: "Package",
|
|
170
|
+
metadata: { name: "test", namespace: "default", uid: "1" },
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const ownerRef = [
|
|
174
|
+
{
|
|
168
175
|
apiVersion: "v1",
|
|
169
176
|
kind: "Package",
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
177
|
+
name: "test",
|
|
178
|
+
uid: "1",
|
|
179
|
+
},
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
const ownerRefWithController = ownerRef.map(item => ({
|
|
183
|
+
...item,
|
|
184
|
+
controller: true,
|
|
185
|
+
}));
|
|
186
|
+
const ownerRefWithBlockOwnerDeletion = ownerRef.map(item => ({
|
|
187
|
+
...item,
|
|
188
|
+
blockOwnerDeletion: false,
|
|
189
|
+
}));
|
|
190
|
+
const ownerRefWithAllFields = ownerRef.map(item => ({
|
|
191
|
+
...item,
|
|
192
|
+
blockOwnerDeletion: true,
|
|
193
|
+
controller: false,
|
|
194
|
+
}));
|
|
195
|
+
|
|
196
|
+
test.each([
|
|
197
|
+
[true, false, ownerRefWithAllFields],
|
|
198
|
+
[false, undefined, ownerRefWithBlockOwnerDeletion],
|
|
199
|
+
[undefined, true, ownerRefWithController],
|
|
200
|
+
[undefined, undefined, ownerRef],
|
|
201
|
+
])(
|
|
202
|
+
"should return owner reference for the CRD for combinations of V1OwnerReference fields - Optionals: blockOwnerDeletion (%s), controller (%s)",
|
|
203
|
+
(blockOwnerDeletion, controller, expected) => {
|
|
204
|
+
const result = getOwnerRefFrom(customResource, blockOwnerDeletion, controller);
|
|
205
|
+
expect(result).toStrictEqual(expected);
|
|
206
|
+
},
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
it("should support all defined fields in the V1OwnerReference type", () => {
|
|
210
|
+
const V1OwnerReferenceFieldCount = Object.getOwnPropertyNames(V1OwnerReference).length;
|
|
211
|
+
const result = getOwnerRefFrom(customResource, false, true);
|
|
212
|
+
expect(Object.keys(result[0]).length).toEqual(V1OwnerReferenceFieldCount);
|
|
181
213
|
});
|
|
182
214
|
});
|
|
215
|
+
|
|
183
216
|
describe("sanitizeResourceName Fuzzing Tests", () => {
|
|
184
217
|
test("should handle any random string input", () => {
|
|
185
218
|
fc.assert(
|
package/src/sdk/sdk.ts
CHANGED
|
@@ -79,18 +79,27 @@ export async function writeEvent(
|
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
81
|
* Get the owner reference for a custom resource
|
|
82
|
-
* @param
|
|
83
|
-
* @
|
|
82
|
+
* @param customResource the custom resource to get the owner reference for
|
|
83
|
+
* @param blockOwnerDeletion if true, AND if the owner has the "foregroundDeletion" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed.
|
|
84
|
+
* @param controller if true, this reference points to the managing controller.
|
|
85
|
+
* @returns the owner reference array for the custom resource
|
|
84
86
|
*/
|
|
85
|
-
export function getOwnerRefFrom(
|
|
86
|
-
|
|
87
|
+
export function getOwnerRefFrom(
|
|
88
|
+
customResource: GenericKind,
|
|
89
|
+
blockOwnerDeletion?: boolean,
|
|
90
|
+
controller?: boolean,
|
|
91
|
+
): V1OwnerReference[] {
|
|
92
|
+
const { apiVersion, kind, metadata } = customResource;
|
|
93
|
+
const { name, uid } = metadata!;
|
|
87
94
|
|
|
88
95
|
return [
|
|
89
96
|
{
|
|
90
|
-
apiVersion:
|
|
91
|
-
kind:
|
|
97
|
+
apiVersion: apiVersion!,
|
|
98
|
+
kind: kind!,
|
|
92
99
|
uid: uid!,
|
|
93
100
|
name: name!,
|
|
101
|
+
...(blockOwnerDeletion !== undefined && { blockOwnerDeletion }),
|
|
102
|
+
...(controller !== undefined && { controller }),
|
|
94
103
|
},
|
|
95
104
|
];
|
|
96
105
|
}
|