pepr 0.36.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/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/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 +115 -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 +1 -1
- package/dist/lib/watch-processor.d.ts.map +1 -1
- package/dist/lib.js +383 -204
- package/dist/lib.js.map +4 -4
- package/package.json +9 -7
- 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 +104 -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 +194 -8
- package/src/lib/filter.ts +46 -107
- package/src/lib/finalizer.test.ts +236 -0
- package/src/lib/finalizer.ts +63 -0
- package/src/lib/helpers.test.ts +329 -69
- package/src/lib/helpers.ts +141 -100
- 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/schedule.ts +1 -1
- package/src/lib/storage.ts +5 -6
- package/src/lib/types.ts +151 -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.ts +19 -5
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
|
+
|
|
4
|
+
import { beforeEach, describe, expect, it, jest } from "@jest/globals";
|
|
5
|
+
import { addFinalizer, removeFinalizer } from "./finalizer";
|
|
6
|
+
import { KubernetesObject, K8s, GenericClass, RegisterKind } from "kubernetes-fluent-client";
|
|
7
|
+
import { K8sInit } from "kubernetes-fluent-client/dist/fluent/types";
|
|
8
|
+
import { AdmissionRequest, Operation } from "./types";
|
|
9
|
+
import { PeprMutateRequest } from "./mutate-request";
|
|
10
|
+
import { Binding } from "./types";
|
|
11
|
+
|
|
12
|
+
jest.mock("kubernetes-fluent-client");
|
|
13
|
+
const mockK8s = jest.mocked(K8s);
|
|
14
|
+
const mockRegisterKind = jest.mocked(RegisterKind);
|
|
15
|
+
|
|
16
|
+
const PEPR_FINALIZER = "pepr.dev/finalizer";
|
|
17
|
+
|
|
18
|
+
describe("addFinalizer", () => {
|
|
19
|
+
let request: PeprMutateRequest<KubernetesObject>;
|
|
20
|
+
|
|
21
|
+
const fakeAdmissionRequest = (op: Operation): AdmissionRequest<KubernetesObject> => {
|
|
22
|
+
const obj = {
|
|
23
|
+
apiVersion: "v1",
|
|
24
|
+
kind: "Pod",
|
|
25
|
+
metadata: {
|
|
26
|
+
name: "test-pod",
|
|
27
|
+
labels: {
|
|
28
|
+
"existing-label": "true",
|
|
29
|
+
},
|
|
30
|
+
annotations: {
|
|
31
|
+
"existing-annotation": "true",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
operation: op,
|
|
38
|
+
object: obj,
|
|
39
|
+
oldObject: obj,
|
|
40
|
+
dryRun: false,
|
|
41
|
+
uid: "uid",
|
|
42
|
+
name: "name",
|
|
43
|
+
kind: { group: "", version: "v1", kind: "Pod" },
|
|
44
|
+
resource: { group: "", version: "v1", resource: "pods" },
|
|
45
|
+
userInfo: {},
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
describe("on create", () => {
|
|
50
|
+
const op = Operation.CREATE;
|
|
51
|
+
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
request = new PeprMutateRequest(fakeAdmissionRequest(op));
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("adds pepr finalizer when no other finalizers are present", () => {
|
|
57
|
+
expect(request.Raw.metadata?.finalizers).toBeUndefined();
|
|
58
|
+
|
|
59
|
+
// impure function (acts via side effect instead of return result) --
|
|
60
|
+
// i.e. it directly modifies the passed-in request object
|
|
61
|
+
addFinalizer(request);
|
|
62
|
+
|
|
63
|
+
expect(request.Raw.metadata!.finalizers).toContain(PEPR_FINALIZER);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("adds pepr finalizer when other finalizers are present", () => {
|
|
67
|
+
const original = ["i.am.a/macguffin", "i.am.one/too"];
|
|
68
|
+
request.Raw.metadata!.finalizers = [...original];
|
|
69
|
+
|
|
70
|
+
addFinalizer(request);
|
|
71
|
+
|
|
72
|
+
const expected = [...original, PEPR_FINALIZER];
|
|
73
|
+
expect(request.Raw.metadata!.finalizers).toHaveLength(expected.length);
|
|
74
|
+
expect(request.Raw.metadata!.finalizers).toEqual(expect.arrayContaining(expected));
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("does not add another pepr finalizer when one is already present", () => {
|
|
78
|
+
const original = ["i.am.a/macguffin", "i.am.one/too", PEPR_FINALIZER];
|
|
79
|
+
request.Raw.metadata!.finalizers = [...original];
|
|
80
|
+
|
|
81
|
+
addFinalizer(request);
|
|
82
|
+
|
|
83
|
+
const expected = [...original];
|
|
84
|
+
expect(request.Raw.metadata!.finalizers).toHaveLength(expected.length);
|
|
85
|
+
expect(request.Raw.metadata!.finalizers).toEqual(expect.arrayContaining(expected));
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe("on update", () => {
|
|
90
|
+
const op = Operation.UPDATE;
|
|
91
|
+
|
|
92
|
+
beforeEach(() => {
|
|
93
|
+
request = new PeprMutateRequest(fakeAdmissionRequest(op));
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("adds pepr finalizer when no other finalizers are present", () => {
|
|
97
|
+
expect(request.Raw.metadata?.finalizers).toBeUndefined();
|
|
98
|
+
|
|
99
|
+
addFinalizer(request);
|
|
100
|
+
|
|
101
|
+
expect(request.Raw.metadata!.finalizers).toContain(PEPR_FINALIZER);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("adds pepr finalizer when other finalizers are present", () => {
|
|
105
|
+
const original = ["i.am.a/macguffin", "i.am.one/too"];
|
|
106
|
+
request.Raw.metadata!.finalizers = [...original];
|
|
107
|
+
|
|
108
|
+
addFinalizer(request);
|
|
109
|
+
|
|
110
|
+
const expected = [...original, PEPR_FINALIZER];
|
|
111
|
+
expect(request.Raw.metadata!.finalizers).toHaveLength(expected.length);
|
|
112
|
+
expect(request.Raw.metadata!.finalizers).toEqual(expect.arrayContaining(expected));
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("does not add another pepr finalizer when one is already present", () => {
|
|
116
|
+
const original = ["i.am.a/macguffin", "i.am.one/too", PEPR_FINALIZER];
|
|
117
|
+
request.Raw.metadata!.finalizers = [...original];
|
|
118
|
+
|
|
119
|
+
addFinalizer(request);
|
|
120
|
+
|
|
121
|
+
const expected = [...original];
|
|
122
|
+
expect(request.Raw.metadata!.finalizers).toHaveLength(expected.length);
|
|
123
|
+
expect(request.Raw.metadata!.finalizers).toEqual(expect.arrayContaining(expected));
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("does not add pepr finalizer if a deletetionTimestamp is present", () => {
|
|
127
|
+
const original = ["i.am.a/macguffin", "i.am.one/too"];
|
|
128
|
+
request.Raw.metadata!.finalizers = [...original];
|
|
129
|
+
request.Raw.metadata!.deletionTimestamp = new Date();
|
|
130
|
+
|
|
131
|
+
addFinalizer(request);
|
|
132
|
+
|
|
133
|
+
const expected = [...original];
|
|
134
|
+
expect(request.Raw.metadata!.finalizers).toHaveLength(expected.length);
|
|
135
|
+
expect(request.Raw.metadata!.finalizers).toEqual(expect.arrayContaining(expected));
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe("on delete", () => {
|
|
140
|
+
const op = Operation.DELETE;
|
|
141
|
+
|
|
142
|
+
beforeEach(() => {
|
|
143
|
+
request = new PeprMutateRequest(fakeAdmissionRequest(op));
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("does not add pepr finalizer", () => {
|
|
147
|
+
const original = ["i.am.a/macguffin", "i.am.one/too"];
|
|
148
|
+
request.Raw.metadata!.finalizers = [...original];
|
|
149
|
+
|
|
150
|
+
addFinalizer(request);
|
|
151
|
+
|
|
152
|
+
const expected = [...original];
|
|
153
|
+
expect(request.Raw.metadata!.finalizers).toHaveLength(expected.length);
|
|
154
|
+
expect(request.Raw.metadata!.finalizers).toEqual(expect.arrayContaining(expected));
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe("removeFinalizer", () => {
|
|
160
|
+
const mockPatch = jest.fn();
|
|
161
|
+
|
|
162
|
+
const fakeBinding = () =>
|
|
163
|
+
({
|
|
164
|
+
model: {},
|
|
165
|
+
kind: {},
|
|
166
|
+
}) as unknown as Binding;
|
|
167
|
+
|
|
168
|
+
const fakeObject = () =>
|
|
169
|
+
({
|
|
170
|
+
metadata: {
|
|
171
|
+
namespace: "test-namespace",
|
|
172
|
+
name: "test-name",
|
|
173
|
+
},
|
|
174
|
+
}) as KubernetesObject;
|
|
175
|
+
|
|
176
|
+
describe("when RegisterKind fails", () => {
|
|
177
|
+
beforeEach(() => {
|
|
178
|
+
jest.resetAllMocks();
|
|
179
|
+
|
|
180
|
+
mockK8s.mockImplementation(<T extends GenericClass, K extends KubernetesObject>() => {
|
|
181
|
+
return { Patch: mockPatch } as unknown as K8sInit<T, K>;
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
mockRegisterKind.mockImplementation(() => {
|
|
185
|
+
throw "oops";
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("does NOT send a JSON Patch to remove pepr finalizers", async () => {
|
|
190
|
+
const object = fakeObject();
|
|
191
|
+
const binding = fakeBinding();
|
|
192
|
+
|
|
193
|
+
await removeFinalizer(binding, object);
|
|
194
|
+
|
|
195
|
+
expect(mockPatch.mock.calls).toHaveLength(0);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe("when RegisterKind succeeds", () => {
|
|
200
|
+
beforeEach(() => {
|
|
201
|
+
jest.resetAllMocks();
|
|
202
|
+
|
|
203
|
+
mockPatch.mockImplementation(ops => {
|
|
204
|
+
return { ops };
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
mockK8s.mockImplementation(<T extends GenericClass, K extends KubernetesObject>() => {
|
|
208
|
+
return { Patch: mockPatch } as unknown as K8sInit<T, K>;
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
mockRegisterKind.mockImplementation(() => {});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("sends a JSON Patch to remove pepr finalizers", async () => {
|
|
215
|
+
const originalFinalizers = ["ignore.me/now", "ignore.me/too", PEPR_FINALIZER];
|
|
216
|
+
const adjustedFinalizers = originalFinalizers.filter(f => f !== PEPR_FINALIZER);
|
|
217
|
+
|
|
218
|
+
const object = fakeObject();
|
|
219
|
+
object.metadata!.finalizers = [...originalFinalizers];
|
|
220
|
+
const binding = fakeBinding();
|
|
221
|
+
|
|
222
|
+
await removeFinalizer(binding, object);
|
|
223
|
+
|
|
224
|
+
const expected = [
|
|
225
|
+
{
|
|
226
|
+
op: "replace",
|
|
227
|
+
path: `/metadata/finalizers`,
|
|
228
|
+
value: adjustedFinalizers,
|
|
229
|
+
},
|
|
230
|
+
];
|
|
231
|
+
expect(mockPatch.mock.calls).toHaveLength(1);
|
|
232
|
+
expect(mockPatch.mock.calls[0]).toHaveLength(1);
|
|
233
|
+
expect(mockPatch.mock.calls[0][0]).toEqual(expected);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
|
+
|
|
4
|
+
import { K8s, KubernetesObject, RegisterKind } from "kubernetes-fluent-client";
|
|
5
|
+
import Log from "./logger";
|
|
6
|
+
import { Binding, DeepPartial } from "./types";
|
|
7
|
+
import { Operation } from "./types";
|
|
8
|
+
import { PeprMutateRequest } from "./mutate-request";
|
|
9
|
+
|
|
10
|
+
export function addFinalizer<K extends KubernetesObject>(request: PeprMutateRequest<K>) {
|
|
11
|
+
// if a DELETE is being processed, don't add a finalizer
|
|
12
|
+
if (request.Request.operation === Operation.DELETE) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// if an UPDATE is being processed and it HAS a deletionTimestamp, the
|
|
17
|
+
// resource is going through a pre-delete flow so don't (re-)add a finalizer
|
|
18
|
+
if (request.Request.operation === Operation.UPDATE && request.Raw.metadata?.deletionTimestamp) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const peprFinal = "pepr.dev/finalizer";
|
|
23
|
+
const finalizers = request.Raw.metadata?.finalizers || [];
|
|
24
|
+
if (!finalizers.includes(peprFinal)) {
|
|
25
|
+
finalizers.push(peprFinal);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
request.Merge({ metadata: { finalizers } } as DeepPartial<K>);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function removeFinalizer(binding: Binding, obj: KubernetesObject) {
|
|
32
|
+
const peprFinal = "pepr.dev/finalizer";
|
|
33
|
+
const meta = obj.metadata!;
|
|
34
|
+
const resource = `${meta.namespace || "ClusterScoped"}/${meta.name}`;
|
|
35
|
+
|
|
36
|
+
Log.debug({ obj }, `Removing finalizer '${peprFinal}' from '${resource}'`);
|
|
37
|
+
|
|
38
|
+
// ensure request model is registerd with KFC (for non-built in CRD's, etc.)
|
|
39
|
+
const { model, kind } = binding;
|
|
40
|
+
try {
|
|
41
|
+
RegisterKind(model, kind);
|
|
42
|
+
} catch (e) {
|
|
43
|
+
const expected = e.message === `GVK ${model.name} already registered`;
|
|
44
|
+
if (!expected) {
|
|
45
|
+
Log.error({ model, kind, error: e }, `Error registering "${kind}" during finalization.`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// remove pepr finalizers
|
|
51
|
+
const finalizers = meta.finalizers?.filter(f => f !== peprFinal) || [];
|
|
52
|
+
|
|
53
|
+
// JSON Patch - replace a key
|
|
54
|
+
// https://datatracker.ietf.org/doc/html/rfc6902/#section-4.3
|
|
55
|
+
obj = await K8s(model, meta).Patch([
|
|
56
|
+
{
|
|
57
|
+
op: "replace",
|
|
58
|
+
path: `/metadata/finalizers`,
|
|
59
|
+
value: finalizers,
|
|
60
|
+
},
|
|
61
|
+
]);
|
|
62
|
+
Log.debug({ obj }, `Removed finalizer '${peprFinal}' from '${resource}'`);
|
|
63
|
+
}
|