kubernetes-fluent-client 1.3.2 → 1.4.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.
@@ -92,8 +92,8 @@ function K8s(model, filters = {}) {
92
92
  }
93
93
  return (0, utils_1.k8sExec)(model, filters, "PATCH", payload);
94
94
  }
95
- async function Watch(callback) {
96
- return (0, watch_1.ExecWatch)(model, filters, callback);
95
+ async function Watch(callback, watchCfg) {
96
+ return (0, watch_1.ExecWatch)(model, filters, callback, watchCfg);
97
97
  }
98
98
  return { InNamespace, Apply, Create, Patch, ...withFilters };
99
99
  }
@@ -1,8 +1,8 @@
1
- /// <reference types="node" />
2
1
  import { KubernetesListObject, KubernetesObject } from "@kubernetes/client-node";
3
2
  import { Operation } from "fast-json-patch";
4
3
  import type { PartialDeep } from "type-fest";
5
4
  import { GenericClass, GroupVersionKind } from "../types";
5
+ import { WatchCfg, WatchController } from "./watch";
6
6
  /**
7
7
  * The Phase matched when using the K8s Watch API.
8
8
  */
@@ -41,7 +41,7 @@ export type K8sFilteredActions<K extends KubernetesObject> = {
41
41
  * @param callback
42
42
  * @returns
43
43
  */
44
- Watch: (callback: (payload: K, phase: WatchPhase) => void) => Promise<AbortController>;
44
+ Watch: (callback: (payload: K, phase: WatchPhase) => void, watchCfg?: WatchCfg) => Promise<WatchController>;
45
45
  };
46
46
  export type K8sUnfilteredActions<K extends KubernetesObject> = {
47
47
  /**
@@ -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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"}
@@ -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;;;GAqBhD;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;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"}
@@ -86,6 +86,8 @@ async function k8sCfg(method) {
86
86
  "User-Agent": `kubernetes-fluent-client`,
87
87
  },
88
88
  });
89
+ // Enable compression
90
+ opts.compress = true;
89
91
  return { opts, serverUrl: cluster.server };
90
92
  }
91
93
  exports.k8sCfg = k8sCfg;
@@ -1,8 +1,45 @@
1
1
  /// <reference types="node" />
2
- import { GenericClass } from "../types";
2
+ import { GenericClass, LogFn } from "../types";
3
3
  import { Filters, WatchAction } from "./types";
4
+ /**
5
+ * Wrapper for the AbortController to allow the watch to be aborted externally.
6
+ */
7
+ export type WatchController = {
8
+ /**
9
+ * Abort the watch.
10
+ * @param reason optional reason for aborting the watch
11
+ * @returns
12
+ */
13
+ abort: (reason?: string) => void;
14
+ /**
15
+ * Get the AbortSignal for the watch.
16
+ * @returns
17
+ */
18
+ signal: () => AbortSignal;
19
+ };
20
+ /**
21
+ * Configuration for the watch function.
22
+ */
23
+ export type WatchCfg = {
24
+ /**
25
+ * The maximum number of times to retry the watch, the retry count is reset on success.
26
+ */
27
+ retryMax?: number;
28
+ /**
29
+ * The delay between retries in seconds.
30
+ */
31
+ retryDelaySec?: number;
32
+ /**
33
+ * A function to log errors.
34
+ */
35
+ logFn?: LogFn;
36
+ /**
37
+ * A function to call when the watch fails after the maximum number of retries.
38
+ */
39
+ retryFail?: (e: Error) => void;
40
+ };
4
41
  /**
5
42
  * Execute a watch on the specified resource.
6
43
  */
7
- export declare function ExecWatch<T extends GenericClass>(model: T, filters: Filters, callback: WatchAction<T>): Promise<AbortController>;
44
+ export declare function ExecWatch<T extends GenericClass>(model: T, filters: Filters, callback: WatchAction<T>, watchCfg?: WatchCfg): Promise<WatchController>;
8
45
  //# sourceMappingURL=watch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/fluent/watch.ts"],"names":[],"mappings":";AAMA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAc,MAAM,SAAS,CAAC;AAG3D;;GAEG;AACH,wBAAsB,SAAS,CAAC,CAAC,SAAS,YAAY,EACpD,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,4BA6EzB"}
1
+ {"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/fluent/watch.ts"],"names":[],"mappings":";AAMA,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,WAAW,EAAc,MAAM,SAAS,CAAC;AAG3D;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B;;;;OAIG;IACH,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC;;;OAGG;IACH,MAAM,EAAE,MAAM,WAAW,CAAC;CAC3B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;IACd;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;CAChC,CAAC;AAEF;;GAEG;AACH,wBAAsB,SAAS,CAAC,CAAC,SAAS,YAAY,EACpD,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,EACxB,QAAQ,GAAE,QAAa,4BAwJxB"}
@@ -6,13 +6,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
6
6
  };
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.ExecWatch = void 0;
9
- const readline_1 = __importDefault(require("readline"));
9
+ const byline_1 = __importDefault(require("byline"));
10
10
  const node_fetch_1 = __importDefault(require("node-fetch"));
11
11
  const utils_1 = require("./utils");
12
12
  /**
13
13
  * Execute a watch on the specified resource.
14
14
  */
15
- async function ExecWatch(model, filters, callback) {
15
+ async function ExecWatch(model, filters, callback, watchCfg = {}) {
16
+ watchCfg.logFn?.({ model, filters, watchCfg }, "ExecWatch");
16
17
  // Build the path and query params for the resource, excluding the name
17
18
  const { opts, serverUrl } = await (0, utils_1.k8sCfg)("GET");
18
19
  const url = (0, utils_1.pathBuilder)(serverUrl, model, filters, true);
@@ -24,55 +25,110 @@ async function ExecWatch(model, filters, callback) {
24
25
  if (filters.name) {
25
26
  url.searchParams.set("fieldSelector", `metadata.name=${filters.name}`);
26
27
  }
27
- // Add abort controller to the long-running request
28
- const controller = new AbortController();
29
- opts.signal = controller.signal;
30
- // Close the connection and make the callback function no-op
31
- let close = (err) => {
32
- controller.abort();
33
- close = () => { };
34
- if (err) {
35
- throw err;
36
- }
37
- };
38
- try {
39
- // Make the actual request
40
- const response = await (0, node_fetch_1.default)(url, opts);
41
- // If the request is successful, start listening for events
42
- if (response.ok) {
43
- const { body } = response;
44
- // Bind connection events to the close function
45
- body.on("error", close);
46
- body.on("close", close);
47
- body.on("finish", close);
48
- // Create a readline interface to parse the stream
49
- const rl = readline_1.default.createInterface({
50
- input: response.body,
51
- terminal: false,
52
- });
53
- // Listen for events and call the callback function
54
- rl.on("line", line => {
55
- try {
56
- // Parse the event payload
57
- const { object: payload, type: phase } = JSON.parse(line);
58
- // Call the callback function with the parsed payload
59
- void callback(payload, phase);
28
+ // Set the initial timeout to 15 seconds
29
+ opts.timeout = 15 * 1000;
30
+ // Enable keep alive
31
+ opts.agent.keepAlive = true;
32
+ // Track the number of retries
33
+ let retryCount = 0;
34
+ // Set the maximum number of retries to 5 if not specified
35
+ watchCfg.retryMax ??= 5;
36
+ // Set the retry delay to 5 seconds if not specified
37
+ watchCfg.retryDelaySec ??= 5;
38
+ // Create a throwaway AbortController to setup the wrapped AbortController
39
+ let abortController;
40
+ // Create a wrapped AbortController to allow the watch to be aborted externally
41
+ const abortWrapper = {};
42
+ function bindAbortController() {
43
+ // Create a new AbortController
44
+ abortController = new AbortController();
45
+ // Update the abort wrapper
46
+ abortWrapper.abort = reason => abortController.abort(reason);
47
+ abortWrapper.signal = () => abortController.signal;
48
+ // Add the abort signal to the request options
49
+ opts.signal = abortController.signal;
50
+ }
51
+ async function runner() {
52
+ let doneCalled = false;
53
+ bindAbortController();
54
+ // Create a stream to read the response body
55
+ const stream = byline_1.default.createStream();
56
+ const onError = (err) => {
57
+ stream.removeAllListeners();
58
+ if (!doneCalled) {
59
+ doneCalled = true;
60
+ // If the error is not an AbortError, reload the watch
61
+ if (err.name !== "AbortError") {
62
+ watchCfg.logFn?.(err, "stream error");
63
+ void reload(err);
60
64
  }
61
- catch (ignore) {
62
- // ignore parse errors
65
+ else {
66
+ watchCfg.logFn?.("watch aborted via WatchController.abort()");
63
67
  }
64
- });
68
+ }
69
+ };
70
+ const cleanup = () => {
71
+ if (!doneCalled) {
72
+ doneCalled = true;
73
+ stream.removeAllListeners();
74
+ }
75
+ };
76
+ try {
77
+ // Make the actual request
78
+ const response = await (0, node_fetch_1.default)(url, { ...opts });
79
+ // If the request is successful, start listening for events
80
+ if (response.ok) {
81
+ const { body } = response;
82
+ // Reset the retry count
83
+ retryCount = 0;
84
+ stream.on("error", onError);
85
+ stream.on("close", cleanup);
86
+ stream.on("finish", cleanup);
87
+ // Listen for events and call the callback function
88
+ stream.on("data", line => {
89
+ try {
90
+ // Parse the event payload
91
+ const { object: payload, type: phase } = JSON.parse(line);
92
+ // Call the callback function with the parsed payload
93
+ void callback(payload, phase);
94
+ }
95
+ catch (err) {
96
+ watchCfg.logFn?.(err, "watch callback error");
97
+ }
98
+ });
99
+ body.on("error", onError);
100
+ body.on("close", cleanup);
101
+ body.on("finish", cleanup);
102
+ // Pipe the response body to the stream
103
+ body.pipe(stream);
104
+ }
105
+ else {
106
+ throw new Error(`watch failed: ${response.status} ${response.statusText}`);
107
+ }
65
108
  }
66
- else {
67
- // If the request fails, throw an error
68
- const error = new Error(response.statusText);
69
- error.statusCode = response.status;
70
- throw error;
109
+ catch (e) {
110
+ onError(e);
111
+ }
112
+ // On unhandled errors, retry the watch
113
+ async function reload(e) {
114
+ // If there are more attempts, retry the watch
115
+ if (watchCfg.retryMax > retryCount) {
116
+ retryCount++;
117
+ watchCfg.logFn?.(`retrying watch ${retryCount}/${watchCfg.retryMax}`);
118
+ // Sleep for the specified delay or 5 seconds
119
+ await new Promise(r => setTimeout(r, watchCfg.retryDelaySec * 1000));
120
+ // Retry the watch after the delay
121
+ await runner();
122
+ }
123
+ else {
124
+ // Otherwise, call the finally function if it exists
125
+ if (watchCfg.retryFail) {
126
+ watchCfg.retryFail(e);
127
+ }
128
+ }
71
129
  }
72
130
  }
73
- catch (e) {
74
- close(e);
75
- }
76
- return controller;
131
+ await runner();
132
+ return abortWrapper;
77
133
  }
78
134
  exports.ExecWatch = ExecWatch;
@@ -1 +1 @@
1
- {"version":3,"file":"kinds.d.ts","sourceRoot":"","sources":["../src/kinds.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AA0fzD,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAErE;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,UAAW,YAAY,oBAAoB,gBAAgB,SAUnF,CAAC"}
1
+ {"version":3,"file":"kinds.d.ts","sourceRoot":"","sources":["../src/kinds.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAwgBzD,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAErE;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,UAAW,YAAY,oBAAoB,gBAAgB,SAUnF,CAAC"}
package/dist/kinds.js CHANGED
@@ -4,6 +4,19 @@
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.RegisterKind = exports.modelToGroupVersionKind = void 0;
6
6
  const gvkMap = {
7
+ /**
8
+ * Represents a K8s Event resource.
9
+ * Event is a report of an event somewhere in the cluster. It generally denotes some state change in the system.
10
+ * Events have a limited retention time and triggers and messages may evolve with time. Event consumers should not
11
+ * rely on the timing of an event with a given Reason reflecting a consistent underlying trigger, or the continued
12
+ * existence of events with that Reason. Events should be treated as informative, best-effort, supplemental data.
13
+ * @see {@link https://kubernetes.io/docs/reference/kubernetes-api/cluster-resources/event-v1/}
14
+ */
15
+ CoreV1Event: {
16
+ kind: "Event",
17
+ version: "v1",
18
+ group: "events.k8s.io",
19
+ },
7
20
  /**
8
21
  * Represents a K8s ClusterRole resource.
9
22
  * ClusterRole is a set of permissions that can be bound to a user or group in a cluster-wide scope.
@@ -6,6 +6,10 @@ const globals_1 = require("@jest/globals");
6
6
  const index_1 = require("./index");
7
7
  const kinds_1 = require("./kinds");
8
8
  const testCases = [
9
+ {
10
+ name: index_1.kind.Event,
11
+ expected: { group: "events.k8s.io", version: "v1", kind: "Event" },
12
+ },
9
13
  {
10
14
  name: index_1.kind.ClusterRole,
11
15
  expected: { group: "rbac.authorization.k8s.io", version: "v1", kind: "ClusterRole" },
package/dist/types.d.ts CHANGED
@@ -24,4 +24,9 @@ export interface GroupVersionKind {
24
24
  /** Optional, override the plural name for use in Webhook rules generation */
25
25
  readonly plural?: string;
26
26
  }
27
+ export interface LogFn {
28
+ <T extends object>(obj: T, msg?: string, ...args: never[]): void;
29
+ (obj: unknown, msg?: string, ...args: never[]): void;
30
+ (msg: string, ...args: never[]): void;
31
+ }
27
32
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEzE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAGjF,MAAM,MAAM,YAAY,GAAG,QAAQ,WAAW,GAAG,CAAC;AAElD;;;;GAIG;AACH,qBAAa,WAAY,YAAW,gBAAgB;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,YAAY,CAAC;IAExB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED;;;IAGI;AACJ,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEzE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAGjF,MAAM,MAAM,YAAY,GAAG,QAAQ,WAAW,GAAG,CAAC;AAElD;;;;GAIG;AACH,qBAAa,WAAY,YAAW,gBAAgB;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,YAAY,CAAC;IAExB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED;;;IAGI;AACJ,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,KAAK;IAEpB,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IACjE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IACrD,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;CACvC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kubernetes-fluent-client",
3
- "version": "1.3.2",
3
+ "version": "1.4.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",
@@ -35,15 +35,17 @@
35
35
  "homepage": "https://github.com/defenseunicorns/kubernetes-fluent-client#readme",
36
36
  "dependencies": {
37
37
  "@kubernetes/client-node": "1.0.0-rc3",
38
+ "byline": "5.0.0",
38
39
  "fast-json-patch": "3.1.1",
39
40
  "http-status-codes": "2.3.0",
40
41
  "node-fetch": "2.7.0",
41
42
  "type-fest": "4.3.2"
42
43
  },
43
44
  "devDependencies": {
44
- "@commitlint/cli": "17.7.1",
45
+ "@commitlint/cli": "17.7.2",
45
46
  "@commitlint/config-conventional": "17.7.0",
46
47
  "@jest/globals": "29.7.0",
48
+ "@types/byline": "4.2.34",
47
49
  "@typescript-eslint/eslint-plugin": "6.7.3",
48
50
  "@typescript-eslint/parser": "6.7.3",
49
51
  "jest": "29.7.0",
@@ -10,7 +10,7 @@ import { modelToGroupVersionKind } from "../kinds";
10
10
  import { GenericClass } from "../types";
11
11
  import { Filters, K8sInit, Paths, WatchAction } from "./types";
12
12
  import { k8sExec } from "./utils";
13
- import { ExecWatch } from "./watch";
13
+ import { ExecWatch, WatchCfg } from "./watch";
14
14
 
15
15
  /**
16
16
  * Kubernetes fluent API inspired by Kubectl. Pass in a model, then call filters and actions on it.
@@ -119,8 +119,8 @@ export function K8s<T extends GenericClass, K extends KubernetesObject = Instanc
119
119
  return k8sExec<T, K>(model, filters, "PATCH", payload);
120
120
  }
121
121
 
122
- async function Watch(callback: WatchAction<T>): Promise<AbortController> {
123
- return ExecWatch(model, filters, callback);
122
+ async function Watch(callback: WatchAction<T>, watchCfg?: WatchCfg) {
123
+ return ExecWatch(model, filters, callback, watchCfg);
124
124
  }
125
125
 
126
126
  return { InNamespace, Apply, Create, Patch, ...withFilters };
@@ -6,6 +6,7 @@ import { Operation } from "fast-json-patch";
6
6
  import type { PartialDeep } from "type-fest";
7
7
 
8
8
  import { GenericClass, GroupVersionKind } from "../types";
9
+ import { WatchCfg, WatchController } from "./watch";
9
10
 
10
11
  /**
11
12
  * The Phase matched when using the K8s Watch API.
@@ -51,7 +52,10 @@ export type K8sFilteredActions<K extends KubernetesObject> = {
51
52
  * @param callback
52
53
  * @returns
53
54
  */
54
- Watch: (callback: (payload: K, phase: WatchPhase) => void) => Promise<AbortController>;
55
+ Watch: (
56
+ callback: (payload: K, phase: WatchPhase) => void,
57
+ watchCfg?: WatchCfg,
58
+ ) => Promise<WatchController>;
55
59
  };
56
60
 
57
61
  export type K8sUnfilteredActions<K extends KubernetesObject> = {
@@ -112,6 +112,9 @@ export async function k8sCfg(method: FetchMethods) {
112
112
  },
113
113
  });
114
114
 
115
+ // Enable compression
116
+ opts.compress = true;
117
+
115
118
  return { opts, serverUrl: cluster.server };
116
119
  }
117
120
 
@@ -1,13 +1,52 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
- import readline from "readline";
5
-
4
+ import byline from "byline";
6
5
  import fetch from "node-fetch";
7
- import { GenericClass } from "../types";
6
+
7
+ import { GenericClass, LogFn } from "../types";
8
8
  import { Filters, WatchAction, WatchPhase } from "./types";
9
9
  import { k8sCfg, pathBuilder } from "./utils";
10
10
 
11
+ /**
12
+ * Wrapper for the AbortController to allow the watch to be aborted externally.
13
+ */
14
+ export type WatchController = {
15
+ /**
16
+ * Abort the watch.
17
+ * @param reason optional reason for aborting the watch
18
+ * @returns
19
+ */
20
+ abort: (reason?: string) => void;
21
+ /**
22
+ * Get the AbortSignal for the watch.
23
+ * @returns
24
+ */
25
+ signal: () => AbortSignal;
26
+ };
27
+
28
+ /**
29
+ * Configuration for the watch function.
30
+ */
31
+ export type WatchCfg = {
32
+ /**
33
+ * The maximum number of times to retry the watch, the retry count is reset on success.
34
+ */
35
+ retryMax?: number;
36
+ /**
37
+ * The delay between retries in seconds.
38
+ */
39
+ retryDelaySec?: number;
40
+ /**
41
+ * A function to log errors.
42
+ */
43
+ logFn?: LogFn;
44
+ /**
45
+ * A function to call when the watch fails after the maximum number of retries.
46
+ */
47
+ retryFail?: (e: Error) => void;
48
+ };
49
+
11
50
  /**
12
51
  * Execute a watch on the specified resource.
13
52
  */
@@ -15,7 +54,10 @@ export async function ExecWatch<T extends GenericClass>(
15
54
  model: T,
16
55
  filters: Filters,
17
56
  callback: WatchAction<T>,
57
+ watchCfg: WatchCfg = {},
18
58
  ) {
59
+ watchCfg.logFn?.({ model, filters, watchCfg }, "ExecWatch");
60
+
19
61
  // Build the path and query params for the resource, excluding the name
20
62
  const { opts, serverUrl } = await k8sCfg("GET");
21
63
  const url = pathBuilder(serverUrl, model, filters, true);
@@ -31,64 +73,137 @@ export async function ExecWatch<T extends GenericClass>(
31
73
  url.searchParams.set("fieldSelector", `metadata.name=${filters.name}`);
32
74
  }
33
75
 
34
- // Add abort controller to the long-running request
35
- const controller = new AbortController();
36
- opts.signal = controller.signal;
76
+ // Set the initial timeout to 15 seconds
77
+ opts.timeout = 15 * 1000;
78
+
79
+ // Enable keep alive
80
+ (opts.agent as unknown as { keepAlive: boolean }).keepAlive = true;
81
+
82
+ // Track the number of retries
83
+ let retryCount = 0;
84
+
85
+ // Set the maximum number of retries to 5 if not specified
86
+ watchCfg.retryMax ??= 5;
87
+
88
+ // Set the retry delay to 5 seconds if not specified
89
+ watchCfg.retryDelaySec ??= 5;
90
+
91
+ // Create a throwaway AbortController to setup the wrapped AbortController
92
+ let abortController: AbortController;
93
+
94
+ // Create a wrapped AbortController to allow the watch to be aborted externally
95
+ const abortWrapper = {} as WatchController;
96
+
97
+ function bindAbortController() {
98
+ // Create a new AbortController
99
+ abortController = new AbortController();
100
+
101
+ // Update the abort wrapper
102
+ abortWrapper.abort = reason => abortController.abort(reason);
103
+ abortWrapper.signal = () => abortController.signal;
104
+
105
+ // Add the abort signal to the request options
106
+ opts.signal = abortController.signal;
107
+ }
108
+
109
+ async function runner() {
110
+ let doneCalled = false;
37
111
 
38
- // Close the connection and make the callback function no-op
39
- let close = (err?: Error) => {
40
- controller.abort();
41
- close = () => {};
42
- if (err) {
43
- throw err;
112
+ bindAbortController();
113
+
114
+ // Create a stream to read the response body
115
+ const stream = byline.createStream();
116
+
117
+ const onError = (err: Error) => {
118
+ stream.removeAllListeners();
119
+
120
+ if (!doneCalled) {
121
+ doneCalled = true;
122
+
123
+ // If the error is not an AbortError, reload the watch
124
+ if (err.name !== "AbortError") {
125
+ watchCfg.logFn?.(err, "stream error");
126
+ void reload(err);
127
+ } else {
128
+ watchCfg.logFn?.("watch aborted via WatchController.abort()");
129
+ }
130
+ }
131
+ };
132
+
133
+ const cleanup = () => {
134
+ if (!doneCalled) {
135
+ doneCalled = true;
136
+ stream.removeAllListeners();
137
+ }
138
+ };
139
+
140
+ try {
141
+ // Make the actual request
142
+ const response = await fetch(url, { ...opts });
143
+
144
+ // If the request is successful, start listening for events
145
+ if (response.ok) {
146
+ const { body } = response;
147
+
148
+ // Reset the retry count
149
+ retryCount = 0;
150
+
151
+ stream.on("error", onError);
152
+ stream.on("close", cleanup);
153
+ stream.on("finish", cleanup);
154
+
155
+ // Listen for events and call the callback function
156
+ stream.on("data", line => {
157
+ try {
158
+ // Parse the event payload
159
+ const { object: payload, type: phase } = JSON.parse(line) as {
160
+ type: WatchPhase;
161
+ object: InstanceType<T>;
162
+ };
163
+
164
+ // Call the callback function with the parsed payload
165
+ void callback(payload, phase as WatchPhase);
166
+ } catch (err) {
167
+ watchCfg.logFn?.(err, "watch callback error");
168
+ }
169
+ });
170
+
171
+ body.on("error", onError);
172
+ body.on("close", cleanup);
173
+ body.on("finish", cleanup);
174
+
175
+ // Pipe the response body to the stream
176
+ body.pipe(stream);
177
+ } else {
178
+ throw new Error(`watch failed: ${response.status} ${response.statusText}`);
179
+ }
180
+ } catch (e) {
181
+ onError(e);
44
182
  }
45
- };
46
-
47
- try {
48
- // Make the actual request
49
- const response = await fetch(url, opts);
50
-
51
- // If the request is successful, start listening for events
52
- if (response.ok) {
53
- const { body } = response;
54
-
55
- // Bind connection events to the close function
56
- body.on("error", close);
57
- body.on("close", close);
58
- body.on("finish", close);
59
-
60
- // Create a readline interface to parse the stream
61
- const rl = readline.createInterface({
62
- input: response.body!,
63
- terminal: false,
64
- });
65
-
66
- // Listen for events and call the callback function
67
- rl.on("line", line => {
68
- try {
69
- // Parse the event payload
70
- const { object: payload, type: phase } = JSON.parse(line) as {
71
- type: WatchPhase;
72
- object: InstanceType<T>;
73
- };
74
-
75
- // Call the callback function with the parsed payload
76
- void callback(payload, phase as WatchPhase);
77
- } catch (ignore) {
78
- // ignore parse errors
183
+
184
+ // On unhandled errors, retry the watch
185
+ async function reload(e: Error) {
186
+ // If there are more attempts, retry the watch
187
+ if (watchCfg.retryMax! > retryCount) {
188
+ retryCount++;
189
+
190
+ watchCfg.logFn?.(`retrying watch ${retryCount}/${watchCfg.retryMax}`);
191
+
192
+ // Sleep for the specified delay or 5 seconds
193
+ await new Promise(r => setTimeout(r, watchCfg.retryDelaySec! * 1000));
194
+
195
+ // Retry the watch after the delay
196
+ await runner();
197
+ } else {
198
+ // Otherwise, call the finally function if it exists
199
+ if (watchCfg.retryFail) {
200
+ watchCfg.retryFail(e);
79
201
  }
80
- });
81
- } else {
82
- // If the request fails, throw an error
83
- const error = new Error(response.statusText) as Error & {
84
- statusCode: number | undefined;
85
- };
86
- error.statusCode = response.status;
87
- throw error;
202
+ }
88
203
  }
89
- } catch (e) {
90
- close(e);
91
204
  }
92
205
 
93
- return controller;
206
+ await runner();
207
+
208
+ return abortWrapper;
94
209
  }
package/src/kinds.test.ts CHANGED
@@ -8,6 +8,10 @@ import { RegisterKind } from "./kinds";
8
8
  import { GroupVersionKind } from "./types";
9
9
 
10
10
  const testCases = [
11
+ {
12
+ name: kind.Event,
13
+ expected: { group: "events.k8s.io", version: "v1", kind: "Event" },
14
+ },
11
15
  {
12
16
  name: kind.ClusterRole,
13
17
  expected: { group: "rbac.authorization.k8s.io", version: "v1", kind: "ClusterRole" },
package/src/kinds.ts CHANGED
@@ -4,6 +4,20 @@
4
4
  import { GenericClass, GroupVersionKind } from "./types";
5
5
 
6
6
  const gvkMap: Record<string, GroupVersionKind> = {
7
+ /**
8
+ * Represents a K8s Event resource.
9
+ * Event is a report of an event somewhere in the cluster. It generally denotes some state change in the system.
10
+ * Events have a limited retention time and triggers and messages may evolve with time. Event consumers should not
11
+ * rely on the timing of an event with a given Reason reflecting a consistent underlying trigger, or the continued
12
+ * existence of events with that Reason. Events should be treated as informative, best-effort, supplemental data.
13
+ * @see {@link https://kubernetes.io/docs/reference/kubernetes-api/cluster-resources/event-v1/}
14
+ */
15
+ CoreV1Event: {
16
+ kind: "Event",
17
+ version: "v1",
18
+ group: "events.k8s.io",
19
+ },
20
+
7
21
  /**
8
22
  * Represents a K8s ClusterRole resource.
9
23
  * ClusterRole is a set of permissions that can be bound to a user or group in a cluster-wide scope.
package/src/types.ts CHANGED
@@ -33,3 +33,10 @@ export interface GroupVersionKind {
33
33
  /** Optional, override the plural name for use in Webhook rules generation */
34
34
  readonly plural?: string;
35
35
  }
36
+
37
+ export interface LogFn {
38
+ /* tslint:disable:no-unnecessary-generics */
39
+ <T extends object>(obj: T, msg?: string, ...args: never[]): void;
40
+ (obj: unknown, msg?: string, ...args: never[]): void;
41
+ (msg: string, ...args: never[]): void;
42
+ }
@@ -1,22 +0,0 @@
1
- // __mocks__/@kubernetes/client-node.ts
2
-
3
- import { jest } from "@jest/globals";
4
-
5
- const actual = jest.requireActual("@kubernetes/client-node") as any;
6
-
7
- const cloned = { ...actual };
8
-
9
- cloned.KubeConfig = class MockedKubeConfig {
10
- loadFromDefault = jest.fn();
11
-
12
- applyToFetchOptions = jest.fn(data => data);
13
-
14
- getCurrentCluster() {
15
- return {
16
- server: "http://jest-test:8080",
17
- };
18
- }
19
- };
20
-
21
- // export all elements of the mocked module
22
- module.exports = cloned;
@@ -1 +0,0 @@
1
- module.exports = { extends: ["@commitlint/config-conventional"] };