kubernetes-fluent-client 1.8.4 → 1.10.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/index.d.ts.map +1 -1
- package/dist/fluent/index.js +15 -1
- package/dist/fluent/index.test.js +42 -29
- package/dist/fluent/types.d.ts +18 -0
- package/dist/fluent/types.d.ts.map +1 -1
- package/dist/helpers.d.ts +8 -0
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +33 -1
- package/dist/helpers.test.js +6 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/patch.d.ts +7 -0
- package/dist/patch.d.ts.map +1 -0
- package/dist/patch.js +2 -0
- package/package.json +14 -17
- package/src/fluent/index.test.ts +58 -30
- package/src/fluent/index.ts +20 -3
- package/src/fluent/types.ts +23 -2
- package/src/helpers.test.ts +9 -2
- package/src/helpers.ts +35 -0
- package/src/index.ts +5 -2
- package/src/patch.ts +8 -0
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fluent/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAwB,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAOjF,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAsB,MAAM,SAAS,CAAC;AAI/D;;;;;;GAMG;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,CAmKZ"}
|
package/dist/fluent/index.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
5
|
exports.K8s = void 0;
|
|
6
6
|
const http_status_codes_1 = require("http-status-codes");
|
|
7
|
+
const fetch_1 = require("../fetch");
|
|
7
8
|
const kinds_1 = require("../kinds");
|
|
8
9
|
const utils_1 = require("./utils");
|
|
9
10
|
const watch_1 = require("./watch");
|
|
@@ -137,6 +138,19 @@ function K8s(model, filters = {}) {
|
|
|
137
138
|
async function Watch(callback, watchCfg) {
|
|
138
139
|
return (0, watch_1.ExecWatch)(model, filters, callback, watchCfg);
|
|
139
140
|
}
|
|
140
|
-
|
|
141
|
+
/**
|
|
142
|
+
* @inheritdoc
|
|
143
|
+
* @see {@link K8sInit.Raw}
|
|
144
|
+
*/
|
|
145
|
+
async function Raw(url) {
|
|
146
|
+
const thing = await (0, utils_1.k8sCfg)("GET");
|
|
147
|
+
const { opts, serverUrl } = thing;
|
|
148
|
+
const resp = await (0, fetch_1.fetch)(`${serverUrl}${url}`, opts);
|
|
149
|
+
if (resp.ok) {
|
|
150
|
+
return resp.data;
|
|
151
|
+
}
|
|
152
|
+
throw resp;
|
|
153
|
+
}
|
|
154
|
+
return { InNamespace, Apply, Create, Patch, Raw, ...withFilters };
|
|
141
155
|
}
|
|
142
156
|
exports.K8s = K8s;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const globals_1 = require("@jest/globals");
|
|
4
|
-
const
|
|
4
|
+
const client_node_1 = require("@kubernetes/client-node");
|
|
5
5
|
const _1 = require(".");
|
|
6
|
+
const fetch_1 = require("../fetch");
|
|
7
|
+
const upstream_1 = require("../upstream");
|
|
6
8
|
const utils_1 = require("./utils");
|
|
7
9
|
// Setup mocks
|
|
8
10
|
globals_1.jest.mock("./utils");
|
|
11
|
+
globals_1.jest.mock("../fetch");
|
|
9
12
|
const generateFakePodManagedFields = (manager) => {
|
|
10
13
|
return [
|
|
11
14
|
{
|
|
@@ -49,14 +52,14 @@ const generateFakePodManagedFields = (manager) => {
|
|
|
49
52
|
managedFields: generateFakePodManagedFields("pepr"),
|
|
50
53
|
},
|
|
51
54
|
};
|
|
55
|
+
const mockedKubeCfg = globals_1.jest.mocked(utils_1.k8sCfg);
|
|
52
56
|
const mockedKubeExec = globals_1.jest.mocked(utils_1.k8sExec).mockResolvedValue(fakeResource);
|
|
53
57
|
(0, globals_1.beforeEach)(() => {
|
|
54
58
|
// Clear all instances and calls to constructor and all methods:
|
|
55
59
|
mockedKubeExec.mockClear();
|
|
56
60
|
});
|
|
57
61
|
(0, globals_1.it)("should create a resource", async () => {
|
|
58
|
-
const
|
|
59
|
-
const result = await kube.Create(fakeResource);
|
|
62
|
+
const result = await (0, _1.K8s)(upstream_1.Pod).Create(fakeResource);
|
|
60
63
|
(0, globals_1.expect)(result).toEqual(fakeResource);
|
|
61
64
|
(0, globals_1.expect)(mockedKubeExec).toHaveBeenCalledWith(upstream_1.Pod, globals_1.expect.objectContaining({
|
|
62
65
|
name: "fake",
|
|
@@ -64,8 +67,7 @@ const generateFakePodManagedFields = (manager) => {
|
|
|
64
67
|
}), "POST", fakeResource);
|
|
65
68
|
});
|
|
66
69
|
(0, globals_1.it)("should delete a resource", async () => {
|
|
67
|
-
|
|
68
|
-
await kube.Delete(fakeResource);
|
|
70
|
+
await (0, _1.K8s)(upstream_1.Pod).Delete(fakeResource);
|
|
69
71
|
(0, globals_1.expect)(mockedKubeExec).toHaveBeenCalledWith(upstream_1.Pod, globals_1.expect.objectContaining({
|
|
70
72
|
name: "fake",
|
|
71
73
|
namespace: "default",
|
|
@@ -75,14 +77,12 @@ const generateFakePodManagedFields = (manager) => {
|
|
|
75
77
|
const patchOperations = [
|
|
76
78
|
{ op: "replace", path: "/metadata/name", value: "new-fake" },
|
|
77
79
|
];
|
|
78
|
-
const
|
|
79
|
-
const result = await kube.Patch(patchOperations);
|
|
80
|
+
const result = await (0, _1.K8s)(upstream_1.Pod).Patch(patchOperations);
|
|
80
81
|
(0, globals_1.expect)(result).toEqual(fakeResource);
|
|
81
82
|
(0, globals_1.expect)(mockedKubeExec).toHaveBeenCalledWith(upstream_1.Pod, {}, "PATCH", patchOperations);
|
|
82
83
|
});
|
|
83
84
|
(0, globals_1.it)("should filter with WithField", async () => {
|
|
84
|
-
|
|
85
|
-
await kube.Get();
|
|
85
|
+
await (0, _1.K8s)(upstream_1.Pod).WithField("metadata.name", "fake").Get();
|
|
86
86
|
(0, globals_1.expect)(mockedKubeExec).toHaveBeenCalledWith(upstream_1.Pod, globals_1.expect.objectContaining({
|
|
87
87
|
fields: {
|
|
88
88
|
"metadata.name": "fake",
|
|
@@ -90,8 +90,7 @@ const generateFakePodManagedFields = (manager) => {
|
|
|
90
90
|
}), "GET");
|
|
91
91
|
});
|
|
92
92
|
(0, globals_1.it)("should filter with WithLabel", async () => {
|
|
93
|
-
|
|
94
|
-
await kube.Get();
|
|
93
|
+
await (0, _1.K8s)(upstream_1.Pod).WithLabel("app", "fakeApp").Get();
|
|
95
94
|
(0, globals_1.expect)(mockedKubeExec).toHaveBeenCalledWith(upstream_1.Pod, globals_1.expect.objectContaining({
|
|
96
95
|
labels: {
|
|
97
96
|
app: "fakeApp",
|
|
@@ -99,45 +98,37 @@ const generateFakePodManagedFields = (manager) => {
|
|
|
99
98
|
}), "GET");
|
|
100
99
|
});
|
|
101
100
|
(0, globals_1.it)("should use InNamespace", async () => {
|
|
102
|
-
|
|
103
|
-
await kube.Get();
|
|
101
|
+
await (0, _1.K8s)(upstream_1.Pod).InNamespace("fakeNamespace").Get();
|
|
104
102
|
(0, globals_1.expect)(mockedKubeExec).toHaveBeenCalledWith(upstream_1.Pod, globals_1.expect.objectContaining({
|
|
105
103
|
namespace: "fakeNamespace",
|
|
106
104
|
}), "GET");
|
|
107
105
|
});
|
|
108
106
|
(0, globals_1.it)("should throw an error if namespace is already specified", async () => {
|
|
109
|
-
|
|
110
|
-
(0, globals_1.expect)(() => kube.InNamespace("fakeNamespace")).toThrow("Namespace already specified: default");
|
|
107
|
+
(0, globals_1.expect)(() => (0, _1.K8s)(upstream_1.Pod, { namespace: "default" }).InNamespace("fakeNamespace")).toThrow("Namespace already specified: default");
|
|
111
108
|
});
|
|
112
109
|
(0, globals_1.it)("should handle Delete when the resource doesn't exist", async () => {
|
|
113
110
|
mockedKubeExec.mockRejectedValueOnce({ status: 404 }); // Not Found on first call
|
|
114
|
-
|
|
115
|
-
await (0, globals_1.expect)(kube.Delete("fakeResource")).resolves.toBeUndefined();
|
|
111
|
+
await (0, globals_1.expect)((0, _1.K8s)(upstream_1.Pod).Delete("fakeResource")).resolves.toBeUndefined();
|
|
116
112
|
});
|
|
117
113
|
(0, globals_1.it)("should handle Get", async () => {
|
|
118
|
-
const
|
|
119
|
-
const result = await kube.Get("fakeResource");
|
|
114
|
+
const result = await (0, _1.K8s)(upstream_1.Pod).Get("fakeResource");
|
|
120
115
|
(0, globals_1.expect)(result).toEqual(fakeResource);
|
|
121
116
|
(0, globals_1.expect)(mockedKubeExec).toHaveBeenCalledWith(upstream_1.Pod, globals_1.expect.objectContaining({
|
|
122
117
|
name: "fakeResource",
|
|
123
118
|
}), "GET");
|
|
124
119
|
});
|
|
125
120
|
(0, globals_1.it)("should thrown an error if Get is called with a name and filters are already specified a name", async () => {
|
|
126
|
-
|
|
127
|
-
await (0, globals_1.expect)(kube.Get("fakeResource")).rejects.toThrow("Name already specified: fake");
|
|
121
|
+
await (0, globals_1.expect)((0, _1.K8s)(upstream_1.Pod, { name: "fake" }).Get("fakeResource")).rejects.toThrow("Name already specified: fake");
|
|
128
122
|
});
|
|
129
123
|
(0, globals_1.it)("should throw an error if no patch operations provided", async () => {
|
|
130
|
-
|
|
131
|
-
await (0, globals_1.expect)(kube.Patch([])).rejects.toThrow("No operations specified");
|
|
124
|
+
await (0, globals_1.expect)((0, _1.K8s)(upstream_1.Pod).Patch([])).rejects.toThrow("No operations specified");
|
|
132
125
|
});
|
|
133
126
|
(0, globals_1.it)("should allow Apply of deep partials", async () => {
|
|
134
|
-
const
|
|
135
|
-
const result = await kube.Apply({ metadata: { name: "fake" }, spec: { priority: 3 } });
|
|
127
|
+
const result = await (0, _1.K8s)(upstream_1.Pod).Apply({ metadata: { name: "fake" }, spec: { priority: 3 } });
|
|
136
128
|
(0, globals_1.expect)(result).toEqual(fakeResource);
|
|
137
129
|
});
|
|
138
130
|
(0, globals_1.it)("should allow force apply to resolve FieldManagerConflict", async () => {
|
|
139
|
-
const
|
|
140
|
-
const result = await kube.Apply({
|
|
131
|
+
const result = await (0, _1.K8s)(upstream_1.Pod).Apply({
|
|
141
132
|
metadata: { name: "fake", managedFields: generateFakePodManagedFields("kubectl") },
|
|
142
133
|
spec: { priority: 3 },
|
|
143
134
|
}, { force: true });
|
|
@@ -145,7 +136,29 @@ const generateFakePodManagedFields = (manager) => {
|
|
|
145
136
|
});
|
|
146
137
|
(0, globals_1.it)("should throw an error if a Delete failed for a reason other than Not Found", async () => {
|
|
147
138
|
mockedKubeExec.mockRejectedValueOnce({ status: 500 }); // Internal Server Error on first call
|
|
148
|
-
|
|
149
|
-
|
|
139
|
+
await (0, globals_1.expect)((0, _1.K8s)(upstream_1.Pod).Delete("fakeResource")).rejects.toEqual(globals_1.expect.objectContaining({ status: 500 }));
|
|
140
|
+
});
|
|
141
|
+
(0, globals_1.it)("should create a raw api request", async () => {
|
|
142
|
+
mockedKubeCfg.mockReturnValue(new Promise(r => r({
|
|
143
|
+
serverUrl: "http://localhost:8080",
|
|
144
|
+
opts: {},
|
|
145
|
+
})));
|
|
146
|
+
const mockResp = {
|
|
147
|
+
kind: "APIVersions",
|
|
148
|
+
versions: ["v1"],
|
|
149
|
+
serverAddressByClientCIDRs: [
|
|
150
|
+
{
|
|
151
|
+
serverAddress: "172.27.0.3:6443",
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
globals_1.jest.mocked(fetch_1.fetch).mockResolvedValue({
|
|
156
|
+
ok: true,
|
|
157
|
+
data: mockResp,
|
|
158
|
+
status: 200,
|
|
159
|
+
statusText: "OK",
|
|
160
|
+
});
|
|
161
|
+
const result = await (0, _1.K8s)(client_node_1.V1APIGroup).Raw("/api");
|
|
162
|
+
(0, globals_1.expect)(result).toEqual(mockResp);
|
|
150
163
|
});
|
|
151
164
|
});
|
package/dist/fluent/types.d.ts
CHANGED
|
@@ -79,6 +79,24 @@ export type K8sUnfilteredActions<K extends KubernetesObject> = {
|
|
|
79
79
|
* @returns The patched resource
|
|
80
80
|
*/
|
|
81
81
|
Patch: (payload: Operation[]) => Promise<K>;
|
|
82
|
+
/**
|
|
83
|
+
* Perform a raw GET request to the Kubernetes API. This is useful for calling endpoints that are not supported by the fluent API.
|
|
84
|
+
* This command mirrors the `kubectl get --raw` command.
|
|
85
|
+
*
|
|
86
|
+
* E.g.
|
|
87
|
+
*
|
|
88
|
+
* ```ts
|
|
89
|
+
* import { V1APIGroup } from "@kubernetes/client-node";
|
|
90
|
+
*
|
|
91
|
+
* K8s(V1APIGroup).Raw("/api")
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* will call the `/api` endpoint and is equivalent to `kubectl get --raw /api`.
|
|
95
|
+
*
|
|
96
|
+
* @param url the URL to call (e.g. /api)
|
|
97
|
+
* @returns
|
|
98
|
+
*/
|
|
99
|
+
Raw: (url: string) => Promise<K>;
|
|
82
100
|
};
|
|
83
101
|
export type K8sWithFilters<K extends KubernetesObject> = K8sFilteredActions<K> & {
|
|
84
102
|
/**
|
|
@@ -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;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;;;;;;;GAOG;AACH,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;;;;;;OAMG;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;;;;;;OAMG;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;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/fluent/types.ts"],"names":[],"mappings":"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"}
|
package/dist/helpers.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Cluster } from "@kubernetes/client-node";
|
|
1
2
|
/**
|
|
2
3
|
* Get an environment variable (Node, Deno or Bun), or throw an error if it's not set.
|
|
3
4
|
*
|
|
@@ -15,4 +16,11 @@
|
|
|
15
16
|
* @throws An error if the environment variable is not set.
|
|
16
17
|
*/
|
|
17
18
|
export declare function fromEnv(name: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Wait for the Kubernetes cluster to be ready.
|
|
21
|
+
*
|
|
22
|
+
* @param seconds The number of seconds to wait for the cluster to be ready.
|
|
23
|
+
* @returns The current cluster.
|
|
24
|
+
*/
|
|
25
|
+
export declare function waitForCluster(seconds?: number): Promise<Cluster>;
|
|
18
26
|
//# sourceMappingURL=helpers.d.ts.map
|
package/dist/helpers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAc,MAAM,yBAAyB,CAAC;AAkB9D;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAqB5C;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,OAAO,SAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAenE"}
|
package/dist/helpers.js
CHANGED
|
@@ -2,7 +2,17 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
// SPDX-FileCopyrightText: 2023-Present The Kubernetes Fluent Client Authors
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.fromEnv = void 0;
|
|
5
|
+
exports.waitForCluster = exports.fromEnv = void 0;
|
|
6
|
+
const client_node_1 = require("@kubernetes/client-node");
|
|
7
|
+
/**
|
|
8
|
+
* Sleep for a number of seconds.
|
|
9
|
+
*
|
|
10
|
+
* @param seconds The number of seconds to sleep.
|
|
11
|
+
* @returns A promise that resolves after the specified number of seconds.
|
|
12
|
+
*/
|
|
13
|
+
function sleep(seconds) {
|
|
14
|
+
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
|
|
15
|
+
}
|
|
6
16
|
/**
|
|
7
17
|
* Get an environment variable (Node, Deno or Bun), or throw an error if it's not set.
|
|
8
18
|
*
|
|
@@ -39,3 +49,25 @@ function fromEnv(name) {
|
|
|
39
49
|
return envValue;
|
|
40
50
|
}
|
|
41
51
|
exports.fromEnv = fromEnv;
|
|
52
|
+
/**
|
|
53
|
+
* Wait for the Kubernetes cluster to be ready.
|
|
54
|
+
*
|
|
55
|
+
* @param seconds The number of seconds to wait for the cluster to be ready.
|
|
56
|
+
* @returns The current cluster.
|
|
57
|
+
*/
|
|
58
|
+
async function waitForCluster(seconds = 30) {
|
|
59
|
+
const kubeConfig = new client_node_1.KubeConfig();
|
|
60
|
+
kubeConfig.loadFromDefault();
|
|
61
|
+
const cluster = kubeConfig.getCurrentCluster();
|
|
62
|
+
if (!cluster) {
|
|
63
|
+
await sleep(1);
|
|
64
|
+
if (seconds > 0) {
|
|
65
|
+
return await waitForCluster(seconds - 1);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
throw new Error("Cluster not ready");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return cluster;
|
|
72
|
+
}
|
|
73
|
+
exports.waitForCluster = waitForCluster;
|
package/dist/helpers.test.js
CHANGED
|
@@ -14,3 +14,9 @@ const helpers_1 = require("./helpers");
|
|
|
14
14
|
delete process.env.MY_ENV_VAR;
|
|
15
15
|
});
|
|
16
16
|
});
|
|
17
|
+
(0, globals_1.describe)("Cluster Wait Function", () => {
|
|
18
|
+
(0, globals_1.it)("should resolve if the cluster is already ready", async () => {
|
|
19
|
+
const cluster = await (0, helpers_1.waitForCluster)(5);
|
|
20
|
+
(0, globals_1.expect)(cluster).toEqual({ server: "http://jest-test:8080" });
|
|
21
|
+
});
|
|
22
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "./patch";
|
|
1
2
|
import * as kind from "./upstream";
|
|
2
3
|
/** kind is a collection of K8s types to be used within a K8s call: `K8s(kind.Secret).Apply({})`. */
|
|
3
4
|
export { kind };
|
|
@@ -7,6 +8,6 @@ export { K8s } from "./fluent";
|
|
|
7
8
|
export { RegisterKind, modelToGroupVersionKind } from "./kinds";
|
|
8
9
|
export { GenericKind } from "./types";
|
|
9
10
|
export * from "./types";
|
|
10
|
-
export * as
|
|
11
|
-
export { fromEnv } from "./helpers";
|
|
11
|
+
export * as models from "@kubernetes/client-node/dist/gen/models/all";
|
|
12
|
+
export { fromEnv, waitForCluster } from "./helpers";
|
|
12
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,SAAS,CAAC;AAGjB,OAAO,KAAK,IAAI,MAAM,YAAY,CAAC;AAEnC,oGAAoG;AACpG,OAAO,EAAE,IAAI,EAAE,CAAC;AAGhB,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGhC,OAAO,EAAE,WAAW,IAAI,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAG/D,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAG/B,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAGhE,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC,cAAc,SAAS,CAAC;AAGxB,OAAO,KAAK,MAAM,MAAM,6CAA6C,CAAC;AAEtE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -28,7 +28,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
28
28
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
29
29
|
};
|
|
30
30
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
-
exports.fromEnv = exports.
|
|
31
|
+
exports.waitForCluster = exports.fromEnv = exports.models = exports.GenericKind = exports.modelToGroupVersionKind = exports.RegisterKind = exports.K8s = exports.fetchStatus = exports.fetch = exports.kind = void 0;
|
|
32
|
+
require("./patch");
|
|
32
33
|
// Export kinds as a single object
|
|
33
34
|
const kind = __importStar(require("./upstream"));
|
|
34
35
|
exports.kind = kind;
|
|
@@ -49,6 +50,8 @@ Object.defineProperty(exports, "modelToGroupVersionKind", { enumerable: true, ge
|
|
|
49
50
|
var types_1 = require("./types");
|
|
50
51
|
Object.defineProperty(exports, "GenericKind", { enumerable: true, get: function () { return types_1.GenericKind; } });
|
|
51
52
|
__exportStar(require("./types"), exports);
|
|
52
|
-
|
|
53
|
+
// Export the upstream raw models
|
|
54
|
+
exports.models = __importStar(require("@kubernetes/client-node/dist/gen/models/all"));
|
|
53
55
|
var helpers_1 = require("./helpers");
|
|
54
56
|
Object.defineProperty(exports, "fromEnv", { enumerable: true, get: function () { return helpers_1.fromEnv; } });
|
|
57
|
+
Object.defineProperty(exports, "waitForCluster", { enumerable: true, get: function () { return helpers_1.waitForCluster; } });
|
package/dist/patch.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["../src/patch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE9D,OAAO,QAAQ,yBAAyB,CAAC;IACvC,UAAU,0BAA0B;QAClC,IAAI,CAAC,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC;KAEnC;CACF"}
|
package/dist/patch.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kubernetes-fluent-client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "A @kubernetes/client-node fluent API wrapper that leverages K8s Server Side Apply",
|
|
5
5
|
"bin": "./dist/cli.js",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -40,29 +40,26 @@
|
|
|
40
40
|
"fast-json-patch": "3.1.1",
|
|
41
41
|
"http-status-codes": "2.3.0",
|
|
42
42
|
"node-fetch": "2.7.0",
|
|
43
|
-
"quicktype-core": "
|
|
44
|
-
"type-fest": "4.8.
|
|
43
|
+
"quicktype-core": "23.0.80",
|
|
44
|
+
"type-fest": "4.8.3",
|
|
45
45
|
"yargs": "17.7.2"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@commitlint/cli": "18.4.
|
|
49
|
-
"@commitlint/config-conventional": "18.4.
|
|
48
|
+
"@commitlint/cli": "18.4.3",
|
|
49
|
+
"@commitlint/config-conventional": "18.4.3",
|
|
50
50
|
"@jest/globals": "29.7.0",
|
|
51
51
|
"@types/byline": "4.2.36",
|
|
52
|
-
"@types/readable-stream": "4.0.
|
|
53
|
-
"@types/yargs": "17.0.
|
|
54
|
-
"@typescript-eslint/eslint-plugin": "6.
|
|
55
|
-
"@typescript-eslint/parser": "6.
|
|
56
|
-
"eslint-plugin-jsdoc": "46.9.
|
|
52
|
+
"@types/readable-stream": "4.0.10",
|
|
53
|
+
"@types/yargs": "17.0.32",
|
|
54
|
+
"@typescript-eslint/eslint-plugin": "6.15.0",
|
|
55
|
+
"@typescript-eslint/parser": "6.15.0",
|
|
56
|
+
"eslint-plugin-jsdoc": "46.9.1",
|
|
57
57
|
"jest": "29.7.0",
|
|
58
|
-
"nock": "13.
|
|
59
|
-
"prettier": "3.
|
|
60
|
-
"semantic-release": "22.0.
|
|
58
|
+
"nock": "13.4.0",
|
|
59
|
+
"prettier": "3.1.1",
|
|
60
|
+
"semantic-release": "22.0.12",
|
|
61
61
|
"ts-jest": "29.1.1",
|
|
62
|
-
"typescript": "5.
|
|
63
|
-
},
|
|
64
|
-
"overrides": {
|
|
65
|
-
"browser-or-node": "github:defenseunicorns/browser-or-node#master"
|
|
62
|
+
"typescript": "5.3.3"
|
|
66
63
|
},
|
|
67
64
|
"release": {
|
|
68
65
|
"branches": [
|
package/src/fluent/index.test.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, jest } from "@jest/globals";
|
|
2
|
+
import { V1APIGroup } from "@kubernetes/client-node";
|
|
2
3
|
import { Operation } from "fast-json-patch";
|
|
3
4
|
|
|
4
|
-
import { Pod } from "../upstream";
|
|
5
5
|
import { K8s } from ".";
|
|
6
|
-
import {
|
|
6
|
+
import { fetch } from "../fetch";
|
|
7
|
+
import { Pod } from "../upstream";
|
|
8
|
+
import { k8sCfg, k8sExec } from "./utils";
|
|
7
9
|
|
|
8
10
|
// Setup mocks
|
|
9
11
|
jest.mock("./utils");
|
|
12
|
+
jest.mock("../fetch");
|
|
10
13
|
|
|
11
14
|
const generateFakePodManagedFields = (manager: string) => {
|
|
12
15
|
return [
|
|
@@ -52,6 +55,7 @@ describe("Kube", () => {
|
|
|
52
55
|
},
|
|
53
56
|
};
|
|
54
57
|
|
|
58
|
+
const mockedKubeCfg = jest.mocked(k8sCfg);
|
|
55
59
|
const mockedKubeExec = jest.mocked(k8sExec).mockResolvedValue(fakeResource);
|
|
56
60
|
|
|
57
61
|
beforeEach(() => {
|
|
@@ -60,8 +64,7 @@ describe("Kube", () => {
|
|
|
60
64
|
});
|
|
61
65
|
|
|
62
66
|
it("should create a resource", async () => {
|
|
63
|
-
const
|
|
64
|
-
const result = await kube.Create(fakeResource);
|
|
67
|
+
const result = await K8s(Pod).Create(fakeResource);
|
|
65
68
|
|
|
66
69
|
expect(result).toEqual(fakeResource);
|
|
67
70
|
expect(mockedKubeExec).toHaveBeenCalledWith(
|
|
@@ -76,8 +79,7 @@ describe("Kube", () => {
|
|
|
76
79
|
});
|
|
77
80
|
|
|
78
81
|
it("should delete a resource", async () => {
|
|
79
|
-
|
|
80
|
-
await kube.Delete(fakeResource);
|
|
82
|
+
await K8s(Pod).Delete(fakeResource);
|
|
81
83
|
|
|
82
84
|
expect(mockedKubeExec).toHaveBeenCalledWith(
|
|
83
85
|
Pod,
|
|
@@ -94,16 +96,15 @@ describe("Kube", () => {
|
|
|
94
96
|
{ op: "replace", path: "/metadata/name", value: "new-fake" },
|
|
95
97
|
];
|
|
96
98
|
|
|
97
|
-
const
|
|
98
|
-
const result = await kube.Patch(patchOperations);
|
|
99
|
+
const result = await K8s(Pod).Patch(patchOperations);
|
|
99
100
|
|
|
100
101
|
expect(result).toEqual(fakeResource);
|
|
101
102
|
expect(mockedKubeExec).toHaveBeenCalledWith(Pod, {}, "PATCH", patchOperations);
|
|
102
103
|
});
|
|
103
104
|
|
|
104
105
|
it("should filter with WithField", async () => {
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
await K8s(Pod).WithField("metadata.name", "fake").Get();
|
|
107
|
+
|
|
107
108
|
expect(mockedKubeExec).toHaveBeenCalledWith(
|
|
108
109
|
Pod,
|
|
109
110
|
expect.objectContaining({
|
|
@@ -116,8 +117,8 @@ describe("Kube", () => {
|
|
|
116
117
|
});
|
|
117
118
|
|
|
118
119
|
it("should filter with WithLabel", async () => {
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
await K8s(Pod).WithLabel("app", "fakeApp").Get();
|
|
121
|
+
|
|
121
122
|
expect(mockedKubeExec).toHaveBeenCalledWith(
|
|
122
123
|
Pod,
|
|
123
124
|
expect.objectContaining({
|
|
@@ -130,8 +131,8 @@ describe("Kube", () => {
|
|
|
130
131
|
});
|
|
131
132
|
|
|
132
133
|
it("should use InNamespace", async () => {
|
|
133
|
-
|
|
134
|
-
|
|
134
|
+
await K8s(Pod).InNamespace("fakeNamespace").Get();
|
|
135
|
+
|
|
135
136
|
expect(mockedKubeExec).toHaveBeenCalledWith(
|
|
136
137
|
Pod,
|
|
137
138
|
expect.objectContaining({
|
|
@@ -142,19 +143,18 @@ describe("Kube", () => {
|
|
|
142
143
|
});
|
|
143
144
|
|
|
144
145
|
it("should throw an error if namespace is already specified", async () => {
|
|
145
|
-
|
|
146
|
-
|
|
146
|
+
expect(() => K8s(Pod, { namespace: "default" }).InNamespace("fakeNamespace")).toThrow(
|
|
147
|
+
"Namespace already specified: default",
|
|
148
|
+
);
|
|
147
149
|
});
|
|
148
150
|
|
|
149
151
|
it("should handle Delete when the resource doesn't exist", async () => {
|
|
150
152
|
mockedKubeExec.mockRejectedValueOnce({ status: 404 }); // Not Found on first call
|
|
151
|
-
|
|
152
|
-
await expect(kube.Delete("fakeResource")).resolves.toBeUndefined();
|
|
153
|
+
await expect(K8s(Pod).Delete("fakeResource")).resolves.toBeUndefined();
|
|
153
154
|
});
|
|
154
155
|
|
|
155
156
|
it("should handle Get", async () => {
|
|
156
|
-
const
|
|
157
|
-
const result = await kube.Get("fakeResource");
|
|
157
|
+
const result = await K8s(Pod).Get("fakeResource");
|
|
158
158
|
|
|
159
159
|
expect(result).toEqual(fakeResource);
|
|
160
160
|
expect(mockedKubeExec).toHaveBeenCalledWith(
|
|
@@ -167,24 +167,22 @@ describe("Kube", () => {
|
|
|
167
167
|
});
|
|
168
168
|
|
|
169
169
|
it("should thrown an error if Get is called with a name and filters are already specified a name", async () => {
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
await expect(K8s(Pod, { name: "fake" }).Get("fakeResource")).rejects.toThrow(
|
|
171
|
+
"Name already specified: fake",
|
|
172
|
+
);
|
|
172
173
|
});
|
|
173
174
|
|
|
174
175
|
it("should throw an error if no patch operations provided", async () => {
|
|
175
|
-
|
|
176
|
-
await expect(kube.Patch([])).rejects.toThrow("No operations specified");
|
|
176
|
+
await expect(K8s(Pod).Patch([])).rejects.toThrow("No operations specified");
|
|
177
177
|
});
|
|
178
178
|
|
|
179
179
|
it("should allow Apply of deep partials", async () => {
|
|
180
|
-
const
|
|
181
|
-
const result = await kube.Apply({ metadata: { name: "fake" }, spec: { priority: 3 } });
|
|
180
|
+
const result = await K8s(Pod).Apply({ metadata: { name: "fake" }, spec: { priority: 3 } });
|
|
182
181
|
expect(result).toEqual(fakeResource);
|
|
183
182
|
});
|
|
184
183
|
|
|
185
184
|
it("should allow force apply to resolve FieldManagerConflict", async () => {
|
|
186
|
-
const
|
|
187
|
-
const result = await kube.Apply(
|
|
185
|
+
const result = await K8s(Pod).Apply(
|
|
188
186
|
{
|
|
189
187
|
metadata: { name: "fake", managedFields: generateFakePodManagedFields("kubectl") },
|
|
190
188
|
spec: { priority: 3 },
|
|
@@ -196,9 +194,39 @@ describe("Kube", () => {
|
|
|
196
194
|
|
|
197
195
|
it("should throw an error if a Delete failed for a reason other than Not Found", async () => {
|
|
198
196
|
mockedKubeExec.mockRejectedValueOnce({ status: 500 }); // Internal Server Error on first call
|
|
199
|
-
|
|
200
|
-
await expect(kube.Delete("fakeResource")).rejects.toEqual(
|
|
197
|
+
await expect(K8s(Pod).Delete("fakeResource")).rejects.toEqual(
|
|
201
198
|
expect.objectContaining({ status: 500 }),
|
|
202
199
|
);
|
|
203
200
|
});
|
|
201
|
+
|
|
202
|
+
it("should create a raw api request", async () => {
|
|
203
|
+
mockedKubeCfg.mockReturnValue(
|
|
204
|
+
new Promise(r =>
|
|
205
|
+
r({
|
|
206
|
+
serverUrl: "http://localhost:8080",
|
|
207
|
+
opts: {},
|
|
208
|
+
}),
|
|
209
|
+
),
|
|
210
|
+
);
|
|
211
|
+
const mockResp = {
|
|
212
|
+
kind: "APIVersions",
|
|
213
|
+
versions: ["v1"],
|
|
214
|
+
serverAddressByClientCIDRs: [
|
|
215
|
+
{
|
|
216
|
+
serverAddress: "172.27.0.3:6443",
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
jest.mocked(fetch).mockResolvedValue({
|
|
222
|
+
ok: true,
|
|
223
|
+
data: mockResp,
|
|
224
|
+
status: 200,
|
|
225
|
+
statusText: "OK",
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const result = await K8s(V1APIGroup).Raw("/api");
|
|
229
|
+
|
|
230
|
+
expect(result).toEqual(mockResp);
|
|
231
|
+
});
|
|
204
232
|
});
|
package/src/fluent/index.ts
CHANGED
|
@@ -6,11 +6,12 @@ import { Operation } from "fast-json-patch";
|
|
|
6
6
|
import { StatusCodes } from "http-status-codes";
|
|
7
7
|
import type { PartialDeep } from "type-fest";
|
|
8
8
|
|
|
9
|
+
import { fetch } from "../fetch";
|
|
9
10
|
import { modelToGroupVersionKind } from "../kinds";
|
|
10
11
|
import { GenericClass } from "../types";
|
|
11
12
|
import { ApplyCfg } from "./apply";
|
|
12
13
|
import { Filters, K8sInit, Paths, WatchAction } from "./types";
|
|
13
|
-
import { k8sExec } from "./utils";
|
|
14
|
+
import { k8sCfg, k8sExec } from "./utils";
|
|
14
15
|
import { ExecWatch, WatchCfg } from "./watch";
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -158,7 +159,7 @@ export function K8s<T extends GenericClass, K extends KubernetesObject = Instanc
|
|
|
158
159
|
throw new Error("No operations specified");
|
|
159
160
|
}
|
|
160
161
|
|
|
161
|
-
return k8sExec
|
|
162
|
+
return k8sExec(model, filters, "PATCH", payload);
|
|
162
163
|
}
|
|
163
164
|
|
|
164
165
|
/**
|
|
@@ -169,5 +170,21 @@ export function K8s<T extends GenericClass, K extends KubernetesObject = Instanc
|
|
|
169
170
|
return ExecWatch(model, filters, callback, watchCfg);
|
|
170
171
|
}
|
|
171
172
|
|
|
172
|
-
|
|
173
|
+
/**
|
|
174
|
+
* @inheritdoc
|
|
175
|
+
* @see {@link K8sInit.Raw}
|
|
176
|
+
*/
|
|
177
|
+
async function Raw(url: string) {
|
|
178
|
+
const thing = await k8sCfg("GET");
|
|
179
|
+
const { opts, serverUrl } = thing;
|
|
180
|
+
const resp = await fetch<K>(`${serverUrl}${url}`, opts);
|
|
181
|
+
|
|
182
|
+
if (resp.ok) {
|
|
183
|
+
return resp.data;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
throw resp;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return { InNamespace, Apply, Create, Patch, Raw, ...withFilters };
|
|
173
190
|
}
|
package/src/fluent/types.ts
CHANGED
|
@@ -96,6 +96,25 @@ export type K8sUnfilteredActions<K extends KubernetesObject> = {
|
|
|
96
96
|
* @returns The patched resource
|
|
97
97
|
*/
|
|
98
98
|
Patch: (payload: Operation[]) => Promise<K>;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Perform a raw GET request to the Kubernetes API. This is useful for calling endpoints that are not supported by the fluent API.
|
|
102
|
+
* This command mirrors the `kubectl get --raw` command.
|
|
103
|
+
*
|
|
104
|
+
* E.g.
|
|
105
|
+
*
|
|
106
|
+
* ```ts
|
|
107
|
+
* import { V1APIGroup } from "@kubernetes/client-node";
|
|
108
|
+
*
|
|
109
|
+
* K8s(V1APIGroup).Raw("/api")
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* will call the `/api` endpoint and is equivalent to `kubectl get --raw /api`.
|
|
113
|
+
*
|
|
114
|
+
* @param url the URL to call (e.g. /api)
|
|
115
|
+
* @returns
|
|
116
|
+
*/
|
|
117
|
+
Raw: (url: string) => Promise<K>;
|
|
99
118
|
};
|
|
100
119
|
|
|
101
120
|
export type K8sWithFilters<K extends KubernetesObject> = K8sFilteredActions<K> & {
|
|
@@ -167,5 +186,7 @@ type Join<K, P> = K extends string | number
|
|
|
167
186
|
export type Paths<T, D extends number = 10> = [D] extends [never]
|
|
168
187
|
? never
|
|
169
188
|
: T extends object
|
|
170
|
-
|
|
171
|
-
|
|
189
|
+
? {
|
|
190
|
+
[K in keyof T]-?: K extends string | number ? `${K}` | Join<K, Paths<T[K]>> : never;
|
|
191
|
+
}[keyof T]
|
|
192
|
+
: "";
|
package/src/helpers.test.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// SPDX-FileCopyrightText: 2023-Present The Kubernetes Fluent Client Authors
|
|
3
3
|
|
|
4
|
-
import { describe, expect, test } from "@jest/globals";
|
|
4
|
+
import { describe, expect, it, test } from "@jest/globals";
|
|
5
5
|
|
|
6
|
-
import { fromEnv } from "./helpers";
|
|
6
|
+
import { fromEnv, waitForCluster } from "./helpers";
|
|
7
7
|
|
|
8
8
|
describe("helpers", () => {
|
|
9
9
|
test("fromEnv for NodeJS", () => {
|
|
@@ -16,3 +16,10 @@ describe("helpers", () => {
|
|
|
16
16
|
delete process.env.MY_ENV_VAR;
|
|
17
17
|
});
|
|
18
18
|
});
|
|
19
|
+
|
|
20
|
+
describe("Cluster Wait Function", () => {
|
|
21
|
+
it("should resolve if the cluster is already ready", async () => {
|
|
22
|
+
const cluster = await waitForCluster(5);
|
|
23
|
+
expect(cluster).toEqual({ server: "http://jest-test:8080" });
|
|
24
|
+
});
|
|
25
|
+
});
|
package/src/helpers.ts
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// SPDX-FileCopyrightText: 2023-Present The Kubernetes Fluent Client Authors
|
|
3
3
|
|
|
4
|
+
import { Cluster, KubeConfig } from "@kubernetes/client-node";
|
|
5
|
+
|
|
4
6
|
declare const Deno: {
|
|
5
7
|
env: {
|
|
6
8
|
get(name: string): string | undefined;
|
|
7
9
|
};
|
|
8
10
|
};
|
|
9
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Sleep for a number of seconds.
|
|
14
|
+
*
|
|
15
|
+
* @param seconds The number of seconds to sleep.
|
|
16
|
+
* @returns A promise that resolves after the specified number of seconds.
|
|
17
|
+
*/
|
|
18
|
+
function sleep(seconds: number): Promise<void> {
|
|
19
|
+
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
|
|
20
|
+
}
|
|
21
|
+
|
|
10
22
|
/**
|
|
11
23
|
* Get an environment variable (Node, Deno or Bun), or throw an error if it's not set.
|
|
12
24
|
*
|
|
@@ -45,3 +57,26 @@ export function fromEnv(name: string): string {
|
|
|
45
57
|
|
|
46
58
|
return envValue;
|
|
47
59
|
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Wait for the Kubernetes cluster to be ready.
|
|
63
|
+
*
|
|
64
|
+
* @param seconds The number of seconds to wait for the cluster to be ready.
|
|
65
|
+
* @returns The current cluster.
|
|
66
|
+
*/
|
|
67
|
+
export async function waitForCluster(seconds = 30): Promise<Cluster> {
|
|
68
|
+
const kubeConfig = new KubeConfig();
|
|
69
|
+
kubeConfig.loadFromDefault();
|
|
70
|
+
|
|
71
|
+
const cluster = kubeConfig.getCurrentCluster();
|
|
72
|
+
if (!cluster) {
|
|
73
|
+
await sleep(1);
|
|
74
|
+
if (seconds > 0) {
|
|
75
|
+
return await waitForCluster(seconds - 1);
|
|
76
|
+
} else {
|
|
77
|
+
throw new Error("Cluster not ready");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return cluster;
|
|
82
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// SPDX-FileCopyrightText: 2023-Present The Kubernetes Fluent Client Authors
|
|
3
3
|
|
|
4
|
+
import "./patch";
|
|
5
|
+
|
|
4
6
|
// Export kinds as a single object
|
|
5
7
|
import * as kind from "./upstream";
|
|
6
8
|
|
|
@@ -24,6 +26,7 @@ export { GenericKind } from "./types";
|
|
|
24
26
|
|
|
25
27
|
export * from "./types";
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
// Export the upstream raw models
|
|
30
|
+
export * as models from "@kubernetes/client-node/dist/gen/models/all";
|
|
28
31
|
|
|
29
|
-
export { fromEnv } from "./helpers";
|
|
32
|
+
export { fromEnv, waitForCluster } from "./helpers";
|
package/src/patch.ts
ADDED