kubernetes-fluent-client 1.4.2 → 1.5.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/fluent/apply.d.ts +10 -0
- package/dist/fluent/apply.d.ts.map +1 -0
- package/dist/fluent/apply.js +4 -0
- package/dist/fluent/index.d.ts.map +1 -1
- package/dist/fluent/index.js +2 -2
- package/dist/fluent/index.test.js +50 -1
- package/dist/fluent/types.d.ts +2 -1
- package/dist/fluent/types.d.ts.map +1 -1
- package/dist/fluent/utils.d.ts +2 -1
- package/dist/fluent/utils.d.ts.map +1 -1
- package/dist/fluent/utils.js +2 -2
- package/dist/fluent/utils.test.js +1 -1
- package/package.json +3 -3
- package/src/fluent/apply.ts +12 -0
- package/src/fluent/index.test.ts +55 -1
- package/src/fluent/index.ts +6 -2
- package/src/fluent/types.ts +2 -1
- package/src/fluent/utils.test.ts +2 -1
- package/src/fluent/utils.ts +3 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/fluent/apply.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fluent/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAwB,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAMjF,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAsB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fluent/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAwB,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAMjF,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAsB,MAAM,SAAS,CAAC;AAK/D;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,CAAC,SAAS,YAAY,EAAE,CAAC,SAAS,gBAAgB,GAAG,YAAY,CAAC,CAAC,CAAC,EACtF,KAAK,EAAE,CAAC,EACR,OAAO,GAAE,OAAY,GACpB,OAAO,CAAC,CAAC,CAAC,CA0GZ"}
|
package/dist/fluent/index.js
CHANGED
|
@@ -77,9 +77,9 @@ function K8s(model, filters = {}) {
|
|
|
77
77
|
throw e;
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
async function Apply(resource) {
|
|
80
|
+
async function Apply(resource, applyCfg = { force: false }) {
|
|
81
81
|
syncFilters(resource);
|
|
82
|
-
return (0, utils_1.k8sExec)(model, filters, "APPLY", resource);
|
|
82
|
+
return (0, utils_1.k8sExec)(model, filters, "APPLY", resource, applyCfg);
|
|
83
83
|
}
|
|
84
84
|
async function Create(resource) {
|
|
85
85
|
syncFilters(resource);
|
|
@@ -6,8 +6,49 @@ const _1 = require(".");
|
|
|
6
6
|
const utils_1 = require("./utils");
|
|
7
7
|
// Setup mocks
|
|
8
8
|
globals_1.jest.mock("./utils");
|
|
9
|
+
const generateFakePodManagedFields = (manager) => {
|
|
10
|
+
return [
|
|
11
|
+
{
|
|
12
|
+
apiVersion: "v1",
|
|
13
|
+
fieldsType: "FieldsV1",
|
|
14
|
+
fieldsV1: {
|
|
15
|
+
"f:metadata": {
|
|
16
|
+
"f:labels": {
|
|
17
|
+
"f:fake": {},
|
|
18
|
+
},
|
|
19
|
+
"f:spec": {
|
|
20
|
+
"f:containers": {
|
|
21
|
+
'k:{"name":"fake"}': {
|
|
22
|
+
"f:image": {},
|
|
23
|
+
"f:name": {},
|
|
24
|
+
"f:resources": {
|
|
25
|
+
"f:limits": {
|
|
26
|
+
"f:cpu": {},
|
|
27
|
+
"f:memory": {},
|
|
28
|
+
},
|
|
29
|
+
"f:requests": {
|
|
30
|
+
"f:cpu": {},
|
|
31
|
+
"f:memory": {},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
manager: manager,
|
|
40
|
+
operation: "Apply",
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
};
|
|
9
44
|
(0, globals_1.describe)("Kube", () => {
|
|
10
|
-
const fakeResource = {
|
|
45
|
+
const fakeResource = {
|
|
46
|
+
metadata: {
|
|
47
|
+
name: "fake",
|
|
48
|
+
namespace: "default",
|
|
49
|
+
managedFields: generateFakePodManagedFields("pepr"),
|
|
50
|
+
},
|
|
51
|
+
};
|
|
11
52
|
const mockedKubeExec = globals_1.jest.mocked(utils_1.k8sExec).mockResolvedValue(fakeResource);
|
|
12
53
|
(0, globals_1.beforeEach)(() => {
|
|
13
54
|
// Clear all instances and calls to constructor and all methods:
|
|
@@ -94,6 +135,14 @@ globals_1.jest.mock("./utils");
|
|
|
94
135
|
const result = await kube.Apply({ metadata: { name: "fake" }, spec: { priority: 3 } });
|
|
95
136
|
(0, globals_1.expect)(result).toEqual(fakeResource);
|
|
96
137
|
});
|
|
138
|
+
(0, globals_1.it)("should allow force apply to resolve FieldManagerConflict", async () => {
|
|
139
|
+
const kube = (0, _1.K8s)(upstream_1.Pod);
|
|
140
|
+
const result = await kube.Apply({
|
|
141
|
+
metadata: { name: "fake", managedFields: generateFakePodManagedFields("kubectl") },
|
|
142
|
+
spec: { priority: 3 },
|
|
143
|
+
}, { force: true });
|
|
144
|
+
(0, globals_1.expect)(result).toEqual(fakeResource);
|
|
145
|
+
});
|
|
97
146
|
(0, globals_1.it)("should throw an error if a Delete failed for a reason other than Not Found", async () => {
|
|
98
147
|
mockedKubeExec.mockRejectedValueOnce({ status: 500 }); // Internal Server Error on first call
|
|
99
148
|
const kube = (0, _1.K8s)(upstream_1.Pod);
|
package/dist/fluent/types.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Operation } from "fast-json-patch";
|
|
|
3
3
|
import type { PartialDeep } from "type-fest";
|
|
4
4
|
import { GenericClass, GroupVersionKind } from "../types";
|
|
5
5
|
import { WatchCfg, WatchController } from "./watch";
|
|
6
|
+
import { ApplyCfg } from "./apply";
|
|
6
7
|
/**
|
|
7
8
|
* The Phase matched when using the K8s Watch API.
|
|
8
9
|
*/
|
|
@@ -50,7 +51,7 @@ export type K8sUnfilteredActions<K extends KubernetesObject> = {
|
|
|
50
51
|
* @param resource
|
|
51
52
|
* @returns
|
|
52
53
|
*/
|
|
53
|
-
Apply: (resource: PartialDeep<K
|
|
54
|
+
Apply: (resource: PartialDeep<K>, applyCfg?: ApplyCfg) => Promise<K>;
|
|
54
55
|
/**
|
|
55
56
|
* Create the provided K8s resource or throw an error if it already exists.
|
|
56
57
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/fluent/types.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/fluent/types.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC;;GAEG;AACH,oBAAY,UAAU;IACpB,KAAK,UAAU;IACf,QAAQ,aAAa;IACrB,OAAO,YAAY;CACpB;AAED,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;AAE3F,MAAM,WAAW,OAAO;IACtB,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,gBAAgB,IAAI;IACpD,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,gBAAgB,IAAI;IAC3D;;;;OAIG;IACH,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IAEpB;;;;OAIG;IACH,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/C;;;;OAIG;IACH,KAAK,EAAE,CACL,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,UAAU,KAAK,IAAI,EACjD,QAAQ,CAAC,EAAE,QAAQ,KAChB,OAAO,CAAC,eAAe,CAAC,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,gBAAgB,IAAI;IAC7D;;;;;OAKG;IACH,KAAK,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAErE;;;;;OAKG;IACH,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAEpC;;;;;;;OAOG;IACH,KAAK,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,gBAAgB,IAAI,kBAAkB,CAAC,CAAC,CAAC,GAAG;IAC/E;;;;;;;;;;;;;;;;;;OAkBG;IACH,SAAS,EAAE,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,cAAc,CAAC,CAAC,CAAC,CAAC;IAE5E;;;;;;;;;;;;;;;OAeG;IACH,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,cAAc,CAAC,CAAC,CAAC,CAAC;CAC9D,CAAC;AAEF,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,gBAAgB,IAAI,cAAc,CAAC,CAAC,CAAC,GACjE,oBAAoB,CAAC,CAAC,CAAC,GAAG;IACxB;;;;;OAKG;IACH,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,cAAc,CAAC,CAAC,CAAC,CAAC;CACvD,CAAC;AAEJ,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,YAAY,EAAE,CAAC,SAAS,gBAAgB,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,CAC9F,MAAM,EAAE,CAAC,EACT,KAAK,EAAE,UAAU,KACd,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAG1B,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,MAAM,GAAG,MAAM,GACvC,CAAC,SAAS,MAAM,GAAG,MAAM,GACvB,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,CAAC,EAAE,GACpC,KAAK,GACP,KAAK,CAAC;AAEV,MAAM,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAC7D,KAAK,GACL,CAAC,SAAS,MAAM,GAChB;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,MAAM,GAAG,MAAM,GAAG,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK;CAAE,CAAC,MAAM,CAAC,CAAC,GAChG,EAAE,CAAC"}
|
package/dist/fluent/utils.d.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { URL } from "url";
|
|
3
3
|
import { GenericClass } from "../types";
|
|
4
4
|
import { FetchMethods, Filters } from "./types";
|
|
5
|
+
import { ApplyCfg } from "./apply";
|
|
5
6
|
/**
|
|
6
7
|
* Generate a path to a Kubernetes resource
|
|
7
8
|
*
|
|
@@ -27,5 +28,5 @@ export declare function k8sCfg(method: FetchMethods): Promise<{
|
|
|
27
28
|
opts: import("node-fetch").RequestInit;
|
|
28
29
|
serverUrl: string;
|
|
29
30
|
}>;
|
|
30
|
-
export declare function k8sExec<T extends GenericClass, K>(model: T, filters: Filters, method: FetchMethods, payload?: K | unknown): Promise<K>;
|
|
31
|
+
export declare function k8sExec<T extends GenericClass, K>(model: T, filters: Filters, method: FetchMethods, payload?: K | unknown, applyCfg?: ApplyCfg): Promise<K>;
|
|
31
32
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/fluent/utils.ts"],"names":[],"mappings":";AAMA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAG1B,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/fluent/utils.ts"],"names":[],"mappings":";AAMA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAG1B,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAInC;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,YAAY,EAChD,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,OAAO,EAChB,WAAW,UAAQ,OAsDpB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,MAAM,CAAC,MAAM,EAAE,YAAY;;;GAwBhD;AAED,wBAAsB,OAAO,CAAC,CAAC,SAAS,YAAY,EAAE,CAAC,EACrD,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE,CAAC,GAAG,OAAO,EACrB,QAAQ,GAAE,QAA2B,cA8BtC"}
|
package/dist/fluent/utils.js
CHANGED
|
@@ -91,7 +91,7 @@ async function k8sCfg(method) {
|
|
|
91
91
|
return { opts, serverUrl: cluster.server };
|
|
92
92
|
}
|
|
93
93
|
exports.k8sCfg = k8sCfg;
|
|
94
|
-
async function k8sExec(model, filters, method, payload) {
|
|
94
|
+
async function k8sExec(model, filters, method, payload, applyCfg = { force: false }) {
|
|
95
95
|
const { opts, serverUrl } = await k8sCfg(method);
|
|
96
96
|
const url = pathBuilder(serverUrl, model, filters, method === "POST");
|
|
97
97
|
switch (opts.method) {
|
|
@@ -103,7 +103,7 @@ async function k8sExec(model, filters, method, payload) {
|
|
|
103
103
|
opts.method = "PATCH";
|
|
104
104
|
url.searchParams.set("fieldManager", "pepr");
|
|
105
105
|
url.searchParams.set("fieldValidation", "Strict");
|
|
106
|
-
url.searchParams.set("force", "false");
|
|
106
|
+
url.searchParams.set("force", applyCfg.force ? "true" : "false");
|
|
107
107
|
break;
|
|
108
108
|
}
|
|
109
109
|
if (payload) {
|
|
@@ -25,7 +25,7 @@ globals_1.jest.mock("../fetch");
|
|
|
25
25
|
};
|
|
26
26
|
const result = (0, utils_1.pathBuilder)(serverUrl, upstream_1.Pod, filters);
|
|
27
27
|
const expected = new URL("/api/v1/namespaces/default/pods/mypod?fieldSelector=iamafield%3Diamavalue&labelSelector=iamalabel%3Diamalabelvalue", serverUrl);
|
|
28
|
-
(0, globals_1.expect)(result).toEqual(expected);
|
|
28
|
+
(0, globals_1.expect)(result.toString()).toEqual(expected.toString());
|
|
29
29
|
});
|
|
30
30
|
(0, globals_1.it)("Version not specified in a Kind", () => {
|
|
31
31
|
const filters = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kubernetes-fluent-client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "A @kubernetes/client-node fluent API wrapper that leverages K8s Server Side Apply",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"@commitlint/config-conventional": "17.7.0",
|
|
47
47
|
"@jest/globals": "29.7.0",
|
|
48
48
|
"@types/byline": "4.2.34",
|
|
49
|
-
"@typescript-eslint/eslint-plugin": "6.7.
|
|
50
|
-
"@typescript-eslint/parser": "6.7.
|
|
49
|
+
"@typescript-eslint/eslint-plugin": "6.7.4",
|
|
50
|
+
"@typescript-eslint/parser": "6.7.4",
|
|
51
51
|
"jest": "29.7.0",
|
|
52
52
|
"nock": "13.3.3",
|
|
53
53
|
"prettier": "3.0.3",
|
package/src/fluent/index.test.ts
CHANGED
|
@@ -8,8 +8,50 @@ import { k8sExec } from "./utils";
|
|
|
8
8
|
// Setup mocks
|
|
9
9
|
jest.mock("./utils");
|
|
10
10
|
|
|
11
|
+
const generateFakePodManagedFields = (manager: string) => {
|
|
12
|
+
return [
|
|
13
|
+
{
|
|
14
|
+
apiVersion: "v1",
|
|
15
|
+
fieldsType: "FieldsV1",
|
|
16
|
+
fieldsV1: {
|
|
17
|
+
"f:metadata": {
|
|
18
|
+
"f:labels": {
|
|
19
|
+
"f:fake": {},
|
|
20
|
+
},
|
|
21
|
+
"f:spec": {
|
|
22
|
+
"f:containers": {
|
|
23
|
+
'k:{"name":"fake"}': {
|
|
24
|
+
"f:image": {},
|
|
25
|
+
"f:name": {},
|
|
26
|
+
"f:resources": {
|
|
27
|
+
"f:limits": {
|
|
28
|
+
"f:cpu": {},
|
|
29
|
+
"f:memory": {},
|
|
30
|
+
},
|
|
31
|
+
"f:requests": {
|
|
32
|
+
"f:cpu": {},
|
|
33
|
+
"f:memory": {},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
manager: manager,
|
|
42
|
+
operation: "Apply",
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
};
|
|
11
46
|
describe("Kube", () => {
|
|
12
|
-
const fakeResource = {
|
|
47
|
+
const fakeResource = {
|
|
48
|
+
metadata: {
|
|
49
|
+
name: "fake",
|
|
50
|
+
namespace: "default",
|
|
51
|
+
managedFields: generateFakePodManagedFields("pepr"),
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
13
55
|
const mockedKubeExec = jest.mocked(k8sExec).mockResolvedValue(fakeResource);
|
|
14
56
|
|
|
15
57
|
beforeEach(() => {
|
|
@@ -140,6 +182,18 @@ describe("Kube", () => {
|
|
|
140
182
|
expect(result).toEqual(fakeResource);
|
|
141
183
|
});
|
|
142
184
|
|
|
185
|
+
it("should allow force apply to resolve FieldManagerConflict", async () => {
|
|
186
|
+
const kube = K8s(Pod);
|
|
187
|
+
const result = await kube.Apply(
|
|
188
|
+
{
|
|
189
|
+
metadata: { name: "fake", managedFields: generateFakePodManagedFields("kubectl") },
|
|
190
|
+
spec: { priority: 3 },
|
|
191
|
+
},
|
|
192
|
+
{ force: true },
|
|
193
|
+
);
|
|
194
|
+
expect(result).toEqual(fakeResource);
|
|
195
|
+
});
|
|
196
|
+
|
|
143
197
|
it("should throw an error if a Delete failed for a reason other than Not Found", async () => {
|
|
144
198
|
mockedKubeExec.mockRejectedValueOnce({ status: 500 }); // Internal Server Error on first call
|
|
145
199
|
const kube = K8s(Pod);
|
package/src/fluent/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { GenericClass } from "../types";
|
|
|
11
11
|
import { Filters, K8sInit, Paths, WatchAction } from "./types";
|
|
12
12
|
import { k8sExec } from "./utils";
|
|
13
13
|
import { ExecWatch, WatchCfg } from "./watch";
|
|
14
|
+
import { ApplyCfg } from "./apply";
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Kubernetes fluent API inspired by Kubectl. Pass in a model, then call filters and actions on it.
|
|
@@ -100,9 +101,12 @@ export function K8s<T extends GenericClass, K extends KubernetesObject = Instanc
|
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
async function Apply(
|
|
104
|
+
async function Apply(
|
|
105
|
+
resource: PartialDeep<K>,
|
|
106
|
+
applyCfg: ApplyCfg = { force: false },
|
|
107
|
+
): Promise<K> {
|
|
104
108
|
syncFilters(resource as K);
|
|
105
|
-
return k8sExec(model, filters, "APPLY", resource);
|
|
109
|
+
return k8sExec(model, filters, "APPLY", resource, applyCfg);
|
|
106
110
|
}
|
|
107
111
|
|
|
108
112
|
async function Create(resource: K): Promise<K> {
|
package/src/fluent/types.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type { PartialDeep } from "type-fest";
|
|
|
7
7
|
|
|
8
8
|
import { GenericClass, GroupVersionKind } from "../types";
|
|
9
9
|
import { WatchCfg, WatchController } from "./watch";
|
|
10
|
+
import { ApplyCfg } from "./apply";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* The Phase matched when using the K8s Watch API.
|
|
@@ -65,7 +66,7 @@ export type K8sUnfilteredActions<K extends KubernetesObject> = {
|
|
|
65
66
|
* @param resource
|
|
66
67
|
* @returns
|
|
67
68
|
*/
|
|
68
|
-
Apply: (resource: PartialDeep<K
|
|
69
|
+
Apply: (resource: PartialDeep<K>, applyCfg?: ApplyCfg) => Promise<K>;
|
|
69
70
|
|
|
70
71
|
/**
|
|
71
72
|
* Create the provided K8s resource or throw an error if it already exists.
|
package/src/fluent/utils.test.ts
CHANGED
|
@@ -33,7 +33,8 @@ describe("pathBuilder Function", () => {
|
|
|
33
33
|
"/api/v1/namespaces/default/pods/mypod?fieldSelector=iamafield%3Diamavalue&labelSelector=iamalabel%3Diamalabelvalue",
|
|
34
34
|
serverUrl,
|
|
35
35
|
);
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
expect(result.toString()).toEqual(expected.toString());
|
|
37
38
|
});
|
|
38
39
|
|
|
39
40
|
it("Version not specified in a Kind", () => {
|
package/src/fluent/utils.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { fetch } from "../fetch";
|
|
|
9
9
|
import { modelToGroupVersionKind } from "../kinds";
|
|
10
10
|
import { GenericClass } from "../types";
|
|
11
11
|
import { FetchMethods, Filters } from "./types";
|
|
12
|
+
import { ApplyCfg } from "./apply";
|
|
12
13
|
|
|
13
14
|
const SSA_CONTENT_TYPE = "application/apply-patch+yaml";
|
|
14
15
|
|
|
@@ -123,6 +124,7 @@ export async function k8sExec<T extends GenericClass, K>(
|
|
|
123
124
|
filters: Filters,
|
|
124
125
|
method: FetchMethods,
|
|
125
126
|
payload?: K | unknown,
|
|
127
|
+
applyCfg: ApplyCfg = { force: false },
|
|
126
128
|
) {
|
|
127
129
|
const { opts, serverUrl } = await k8sCfg(method);
|
|
128
130
|
const url = pathBuilder(serverUrl, model, filters, method === "POST");
|
|
@@ -137,7 +139,7 @@ export async function k8sExec<T extends GenericClass, K>(
|
|
|
137
139
|
opts.method = "PATCH";
|
|
138
140
|
url.searchParams.set("fieldManager", "pepr");
|
|
139
141
|
url.searchParams.set("fieldValidation", "Strict");
|
|
140
|
-
url.searchParams.set("force", "false");
|
|
142
|
+
url.searchParams.set("force", applyCfg.force ? "true" : "false");
|
|
141
143
|
break;
|
|
142
144
|
}
|
|
143
145
|
|