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.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Configuration for the apply function.
3
+ */
4
+ export type ApplyCfg = {
5
+ /**
6
+ * Force the apply to be a create.
7
+ */
8
+ force?: boolean;
9
+ };
10
+ //# sourceMappingURL=apply.d.ts.map
@@ -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"}
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
4
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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;AAI/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,CAuGZ"}
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"}
@@ -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 = { metadata: { name: "fake", namespace: "default" } };
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);
@@ -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>) => Promise<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":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/fluent/types.ts"],"names":[],"mappings":"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"}
@@ -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;AAIhD;;;;;;;;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,cA8BtB"}
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"}
@@ -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.4.2",
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.3",
50
- "@typescript-eslint/parser": "6.7.3",
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",
@@ -0,0 +1,12 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
+
4
+ /**
5
+ * Configuration for the apply function.
6
+ */
7
+ export type ApplyCfg = {
8
+ /**
9
+ * Force the apply to be a create.
10
+ */
11
+ force?: boolean;
12
+ };
@@ -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 = { metadata: { name: "fake", namespace: "default" } };
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);
@@ -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(resource: PartialDeep<K>): Promise<K> {
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> {
@@ -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>) => Promise<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.
@@ -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
- expect(result).toEqual(expected);
36
+
37
+ expect(result.toString()).toEqual(expected.toString());
37
38
  });
38
39
 
39
40
  it("Version not specified in a Kind", () => {
@@ -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