kubernetes-fluent-client 1.4.0 → 1.4.2
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/README.md +81 -2
- package/dist/fluent/index.js +2 -2
- package/dist/fluent/types.d.ts +4 -2
- package/dist/fluent/types.d.ts.map +1 -1
- package/dist/fluent/utils.test.js +35 -0
- package/package.json +2 -2
- package/src/fluent/index.ts +2 -2
- package/src/fluent/types.ts +4 -2
- package/src/fluent/utils.test.ts +39 -0
package/README.md
CHANGED
|
@@ -5,11 +5,90 @@
|
|
|
5
5
|
[](https://npmjs.com/package/kubernetes-fluent-client)
|
|
6
6
|
[](https://npmjs.com/package/kubernetes-fluent-client)
|
|
7
7
|
|
|
8
|
+
The Kubernetes Fluent Client for Node is a fluent API for the [Kubernetes JavaScript Client](https://github.com/kubernetes-client/javascript) with some additional logic for [Server Side Apply](https://kubernetes.io/docs/reference/using-api/server-side-apply/), [Watch](https://kubernetes.io/docs/reference/using-api/api-concepts/#efficient-detection-of-changes) with retry/signal control, and [Field Selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/). In addition to providing a human-friendly API, it also provides a simple way to create and manage resources in the cluster and integrate with K8s in a type-safe way.
|
|
9
|
+
|
|
10
|
+
See below for some example uses of the library.
|
|
11
|
+
|
|
8
12
|
```typescript
|
|
9
13
|
import { K8s, kind } from "kubernetes-fluent-client";
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
// Let's create a random namespace to work in
|
|
16
|
+
const namespace = "my-namespace" + Math.floor(Math.random() * 1000);
|
|
17
|
+
|
|
18
|
+
// This will be called after the resources are created in the cluster
|
|
19
|
+
async function demo() {
|
|
20
|
+
// Now, we can use the fluent API to query for the resources we just created
|
|
21
|
+
|
|
22
|
+
// You can use watch to monitor resources in the cluster and react to changes
|
|
23
|
+
// This will run until the process is terminated or the watch is aborted
|
|
24
|
+
const ctrl = await K8s(kind.Pod).Watch((pod, phase) => {
|
|
25
|
+
console.log(`Pod ${pod.metadata?.name} is ${phase}`);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Let's abort the watch after 5 seconds
|
|
29
|
+
setTimeout(ctrl.abort, 5 * 1000);
|
|
30
|
+
|
|
31
|
+
// Passing the name to Get() will return a single resource
|
|
32
|
+
const ns = await K8s(kind.Namespace).Get(namespace);
|
|
33
|
+
console.log(ns);
|
|
34
|
+
|
|
35
|
+
// This time we'll use the InNamespace() method to filter the results by namespace and name
|
|
36
|
+
const cm = await K8s(kind.ConfigMap).InNamespace(namespace).Get("my-configmap");
|
|
37
|
+
console.log(cm);
|
|
38
|
+
|
|
39
|
+
// If we don't pass a name to Get(), we'll get a list of resources as KubernetesListObject
|
|
40
|
+
// The matching resources will be in the items property
|
|
41
|
+
const pods = await K8s(kind.Pod).InNamespace(namespace).Get();
|
|
13
42
|
console.log(pods);
|
|
43
|
+
|
|
44
|
+
// Now let's delete the resources we created, you can pass the name to Delete() or the resource itself
|
|
45
|
+
await K8s(kind.Namespace).Delete(namespace);
|
|
46
|
+
|
|
47
|
+
// Let's use the field selector to find all the running pods in the cluster
|
|
48
|
+
const runningPods = await K8s(kind.Pod).WithField("status.phase", "Running").Get();
|
|
49
|
+
runningPods.items.forEach(pod => {
|
|
50
|
+
console.log(`${pod.metadata?.namespace}/${pod.metadata?.name} is running`);
|
|
51
|
+
});
|
|
14
52
|
}
|
|
53
|
+
|
|
54
|
+
// Create a few resources to work with: Namespace, ConfigMap, and Pod
|
|
55
|
+
Promise.all([
|
|
56
|
+
// Create the namespace
|
|
57
|
+
K8s(kind.Namespace).Apply({
|
|
58
|
+
metadata: {
|
|
59
|
+
name: namespace,
|
|
60
|
+
},
|
|
61
|
+
}),
|
|
62
|
+
|
|
63
|
+
// Create the ConfigMap in the namespace
|
|
64
|
+
K8s(kind.ConfigMap).Apply({
|
|
65
|
+
metadata: {
|
|
66
|
+
name: "my-configmap",
|
|
67
|
+
namespace,
|
|
68
|
+
},
|
|
69
|
+
data: {
|
|
70
|
+
"my-key": "my-value",
|
|
71
|
+
},
|
|
72
|
+
}),
|
|
73
|
+
|
|
74
|
+
// Create the Pod in the namespace
|
|
75
|
+
K8s(kind.Pod).Apply({
|
|
76
|
+
metadata: {
|
|
77
|
+
name: "my-pod",
|
|
78
|
+
namespace,
|
|
79
|
+
},
|
|
80
|
+
spec: {
|
|
81
|
+
containers: [
|
|
82
|
+
{
|
|
83
|
+
name: "my-container",
|
|
84
|
+
image: "nginx",
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
}),
|
|
89
|
+
])
|
|
90
|
+
.then(demo)
|
|
91
|
+
.catch(err => {
|
|
92
|
+
console.error(err);
|
|
93
|
+
});
|
|
15
94
|
```
|
package/dist/fluent/index.js
CHANGED
|
@@ -23,12 +23,12 @@ function K8s(model, filters = {}) {
|
|
|
23
23
|
filters.namespace = namespaces;
|
|
24
24
|
return withFilters;
|
|
25
25
|
}
|
|
26
|
-
function WithField(key, value
|
|
26
|
+
function WithField(key, value) {
|
|
27
27
|
filters.fields = filters.fields || {};
|
|
28
28
|
filters.fields[key] = value;
|
|
29
29
|
return withFilters;
|
|
30
30
|
}
|
|
31
|
-
function WithLabel(key, value
|
|
31
|
+
function WithLabel(key, value) {
|
|
32
32
|
filters.labels = filters.labels || {};
|
|
33
33
|
filters.labels[key] = value;
|
|
34
34
|
return withFilters;
|
package/dist/fluent/types.d.ts
CHANGED
|
@@ -81,12 +81,14 @@ export type K8sWithFilters<K extends KubernetesObject> = K8sFilteredActions<K> &
|
|
|
81
81
|
* ```
|
|
82
82
|
*
|
|
83
83
|
* Will only delete the Deployment if it has the `metadata.name=bar` and `metadata.namespace=qux` fields.
|
|
84
|
+
* Not all fields are supported, see https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/#supported-fields,
|
|
85
|
+
* but Typescript will limit to only fields that exist on the resource.
|
|
84
86
|
*
|
|
85
87
|
* @param key The field key
|
|
86
88
|
* @param value The field value
|
|
87
89
|
* @returns
|
|
88
90
|
*/
|
|
89
|
-
WithField: <P extends Paths<K>>(key: P, value
|
|
91
|
+
WithField: <P extends Paths<K>>(key: P, value: string) => K8sWithFilters<K>;
|
|
90
92
|
/**
|
|
91
93
|
* Filter the query by the given label. If no value is specified, the label simply must exist.
|
|
92
94
|
* Note multiple calls to this method will result in an AND condition. e.g.
|
|
@@ -103,7 +105,7 @@ export type K8sWithFilters<K extends KubernetesObject> = K8sFilteredActions<K> &
|
|
|
103
105
|
* @param key The label key
|
|
104
106
|
* @param value (optional) The label value
|
|
105
107
|
*/
|
|
106
|
-
WithLabel: (key: string, value
|
|
108
|
+
WithLabel: (key: string, value: string) => K8sWithFilters<K>;
|
|
107
109
|
};
|
|
108
110
|
export type K8sInit<K extends KubernetesObject> = K8sWithFilters<K> & K8sUnfilteredActions<K> & {
|
|
109
111
|
/**
|
|
@@ -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;AAEpD;;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,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAEhD;;;;;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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/fluent/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,7 @@ const globals_1 = require("@jest/globals");
|
|
|
6
6
|
const fetch_1 = require("../fetch");
|
|
7
7
|
const upstream_1 = require("../upstream");
|
|
8
8
|
const utils_1 = require("./utils");
|
|
9
|
+
const kinds_1 = require("../kinds");
|
|
9
10
|
globals_1.jest.mock("https");
|
|
10
11
|
globals_1.jest.mock("../fetch");
|
|
11
12
|
(0, globals_1.describe)("pathBuilder Function", () => {
|
|
@@ -15,6 +16,40 @@ globals_1.jest.mock("../fetch");
|
|
|
15
16
|
const filters = {};
|
|
16
17
|
(0, globals_1.expect)(() => (0, utils_1.pathBuilder)("", model, filters)).toThrow("Kind not specified for Unknown");
|
|
17
18
|
});
|
|
19
|
+
(0, globals_1.it)("should generate a path for core group kinds (with custom filters)", () => {
|
|
20
|
+
const filters = {
|
|
21
|
+
namespace: "default",
|
|
22
|
+
name: "mypod",
|
|
23
|
+
fields: { iamafield: "iamavalue" },
|
|
24
|
+
labels: { iamalabel: "iamalabelvalue" },
|
|
25
|
+
};
|
|
26
|
+
const result = (0, utils_1.pathBuilder)(serverUrl, upstream_1.Pod, filters);
|
|
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);
|
|
29
|
+
});
|
|
30
|
+
(0, globals_1.it)("Version not specified in a Kind", () => {
|
|
31
|
+
const filters = {
|
|
32
|
+
namespace: "default",
|
|
33
|
+
name: "mypod",
|
|
34
|
+
};
|
|
35
|
+
class Fake {
|
|
36
|
+
name;
|
|
37
|
+
constructor() {
|
|
38
|
+
this.name = "Fake";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
(0, kinds_1.RegisterKind)(Fake, {
|
|
42
|
+
kind: "Fake",
|
|
43
|
+
version: "",
|
|
44
|
+
group: "fake",
|
|
45
|
+
});
|
|
46
|
+
try {
|
|
47
|
+
(0, utils_1.pathBuilder)(serverUrl, Fake, filters);
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
(0, globals_1.expect)(e.message).toEqual(`Version not specified for Fake`);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
18
53
|
(0, globals_1.it)("should generate a path for core group kinds", () => {
|
|
19
54
|
const filters = { namespace: "default", name: "mypod" };
|
|
20
55
|
const result = (0, utils_1.pathBuilder)(serverUrl, upstream_1.Pod, filters);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kubernetes-fluent-client",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.2",
|
|
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",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"fast-json-patch": "3.1.1",
|
|
40
40
|
"http-status-codes": "2.3.0",
|
|
41
41
|
"node-fetch": "2.7.0",
|
|
42
|
-
"type-fest": "4.3.
|
|
42
|
+
"type-fest": "4.3.3"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@commitlint/cli": "17.7.2",
|
package/src/fluent/index.ts
CHANGED
|
@@ -34,13 +34,13 @@ export function K8s<T extends GenericClass, K extends KubernetesObject = Instanc
|
|
|
34
34
|
return withFilters;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
function WithField<P extends Paths<K>>(key: P, value
|
|
37
|
+
function WithField<P extends Paths<K>>(key: P, value: string) {
|
|
38
38
|
filters.fields = filters.fields || {};
|
|
39
39
|
filters.fields[key] = value;
|
|
40
40
|
return withFilters;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
function WithLabel(key: string, value
|
|
43
|
+
function WithLabel(key: string, value: string) {
|
|
44
44
|
filters.labels = filters.labels || {};
|
|
45
45
|
filters.labels[key] = value;
|
|
46
46
|
return withFilters;
|
package/src/fluent/types.ts
CHANGED
|
@@ -99,12 +99,14 @@ export type K8sWithFilters<K extends KubernetesObject> = K8sFilteredActions<K> &
|
|
|
99
99
|
* ```
|
|
100
100
|
*
|
|
101
101
|
* Will only delete the Deployment if it has the `metadata.name=bar` and `metadata.namespace=qux` fields.
|
|
102
|
+
* Not all fields are supported, see https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/#supported-fields,
|
|
103
|
+
* but Typescript will limit to only fields that exist on the resource.
|
|
102
104
|
*
|
|
103
105
|
* @param key The field key
|
|
104
106
|
* @param value The field value
|
|
105
107
|
* @returns
|
|
106
108
|
*/
|
|
107
|
-
WithField: <P extends Paths<K>>(key: P, value
|
|
109
|
+
WithField: <P extends Paths<K>>(key: P, value: string) => K8sWithFilters<K>;
|
|
108
110
|
|
|
109
111
|
/**
|
|
110
112
|
* Filter the query by the given label. If no value is specified, the label simply must exist.
|
|
@@ -122,7 +124,7 @@ export type K8sWithFilters<K extends KubernetesObject> = K8sFilteredActions<K> &
|
|
|
122
124
|
* @param key The label key
|
|
123
125
|
* @param value (optional) The label value
|
|
124
126
|
*/
|
|
125
|
-
WithLabel: (key: string, value
|
|
127
|
+
WithLabel: (key: string, value: string) => K8sWithFilters<K>;
|
|
126
128
|
};
|
|
127
129
|
|
|
128
130
|
export type K8sInit<K extends KubernetesObject> = K8sWithFilters<K> &
|
package/src/fluent/utils.test.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { GenericClass } from "../types";
|
|
|
8
8
|
import { ClusterRole, Ingress, Pod } from "../upstream";
|
|
9
9
|
import { Filters } from "./types";
|
|
10
10
|
import { k8sExec, pathBuilder } from "./utils";
|
|
11
|
+
import { RegisterKind } from "../kinds";
|
|
11
12
|
|
|
12
13
|
jest.mock("https");
|
|
13
14
|
jest.mock("../fetch");
|
|
@@ -20,6 +21,44 @@ describe("pathBuilder Function", () => {
|
|
|
20
21
|
expect(() => pathBuilder("", model, filters)).toThrow("Kind not specified for Unknown");
|
|
21
22
|
});
|
|
22
23
|
|
|
24
|
+
it("should generate a path for core group kinds (with custom filters)", () => {
|
|
25
|
+
const filters: Filters = {
|
|
26
|
+
namespace: "default",
|
|
27
|
+
name: "mypod",
|
|
28
|
+
fields: { iamafield: "iamavalue" },
|
|
29
|
+
labels: { iamalabel: "iamalabelvalue" },
|
|
30
|
+
};
|
|
31
|
+
const result = pathBuilder(serverUrl, Pod, filters);
|
|
32
|
+
const expected = new URL(
|
|
33
|
+
"/api/v1/namespaces/default/pods/mypod?fieldSelector=iamafield%3Diamavalue&labelSelector=iamalabel%3Diamalabelvalue",
|
|
34
|
+
serverUrl,
|
|
35
|
+
);
|
|
36
|
+
expect(result).toEqual(expected);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("Version not specified in a Kind", () => {
|
|
40
|
+
const filters: Filters = {
|
|
41
|
+
namespace: "default",
|
|
42
|
+
name: "mypod",
|
|
43
|
+
};
|
|
44
|
+
class Fake {
|
|
45
|
+
name: string;
|
|
46
|
+
constructor() {
|
|
47
|
+
this.name = "Fake";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
RegisterKind(Fake, {
|
|
51
|
+
kind: "Fake",
|
|
52
|
+
version: "",
|
|
53
|
+
group: "fake",
|
|
54
|
+
});
|
|
55
|
+
try {
|
|
56
|
+
pathBuilder(serverUrl, Fake, filters);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
expect(e.message).toEqual(`Version not specified for Fake`);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
23
62
|
it("should generate a path for core group kinds", () => {
|
|
24
63
|
const filters: Filters = { namespace: "default", name: "mypod" };
|
|
25
64
|
const result = pathBuilder(serverUrl, Pod, filters);
|