kubernetes-fluent-client 3.0.3 → 4.0.0-rc-http2-watch
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/.prettierignore +4 -0
- package/README.md +24 -0
- package/dist/cli.js +21 -1
- package/dist/fileSystem.d.ts +11 -0
- package/dist/fileSystem.d.ts.map +1 -0
- package/dist/fileSystem.js +42 -0
- package/dist/fileSystem.test.d.ts +2 -0
- package/dist/fileSystem.test.d.ts.map +1 -0
- package/dist/fileSystem.test.js +75 -0
- package/dist/fluent/watch.d.ts +2 -0
- package/dist/fluent/watch.d.ts.map +1 -1
- package/dist/fluent/watch.js +147 -27
- package/dist/generate.d.ts +71 -11
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +130 -117
- package/dist/generate.test.js +293 -346
- package/dist/postProcessing.d.ts +246 -0
- package/dist/postProcessing.d.ts.map +1 -0
- package/dist/postProcessing.js +497 -0
- package/dist/postProcessing.test.d.ts +2 -0
- package/dist/postProcessing.test.d.ts.map +1 -0
- package/dist/postProcessing.test.js +550 -0
- package/e2e/cli.e2e.test.ts +127 -0
- package/e2e/crds/policyreports.default.expected/policyreport-v1alpha1.ts +332 -0
- package/e2e/crds/policyreports.default.expected/policyreport-v1alpha2.ts +360 -0
- package/e2e/crds/policyreports.default.expected/policyreport-v1beta1.ts +360 -0
- package/e2e/crds/policyreports.no.post.expected/policyreport-v1alpha1.ts +331 -0
- package/e2e/crds/policyreports.no.post.expected/policyreport-v1alpha2.ts +360 -0
- package/e2e/crds/policyreports.no.post.expected/policyreport-v1beta1.ts +360 -0
- package/e2e/crds/test.yaml/policyreports.test.yaml +1008 -0
- package/e2e/crds/test.yaml/uds-podmonitors.test.yaml +1245 -0
- package/e2e/crds/uds-podmonitors.default.expected/podmonitor-v1.ts +1333 -0
- package/e2e/crds/uds-podmonitors.no.post.expected/podmonitor-v1.ts +1360 -0
- package/package.json +6 -5
- package/src/cli.ts +25 -1
- package/src/fileSystem.test.ts +67 -0
- package/src/fileSystem.ts +25 -0
- package/src/fluent/watch.ts +174 -35
- package/src/generate.test.ts +368 -358
- package/src/generate.ts +173 -154
- package/src/postProcessing.test.ts +742 -0
- package/src/postProcessing.ts +568 -0
package/.prettierignore
ADDED
package/README.md
CHANGED
|
@@ -105,6 +105,30 @@ Promise.all([
|
|
|
105
105
|
});
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
+
### Generating TypeScript Definitions from CRDs
|
|
109
|
+
|
|
110
|
+
The Kubernetes Fluent Client can generate TypeScript definitions from Custom Resource Definitions (CRDs) using the `generate` command. This command will generate TypeScript interfaces for the CRDs in the cluster and save them to a file.
|
|
111
|
+
|
|
112
|
+
To generate TypeScript definitions from CRDs, run the following command:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
kubernetes-fluent-client crd /path/to/input.yaml /path/to/output/folder
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
If you have a CRD in a file named `crd.yaml` and you want to generate TypeScript definitions in a folder named `types`, you can run the following command:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
kubernetes-fluent-client crd crd.yaml types
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
This will generate TypeScript interfaces for the CRD in the `crd.yaml` file and save them to the `types` folder.
|
|
125
|
+
|
|
126
|
+
By default, the generated TypeScript interfaces will be post-processed to make them more user-friendly. If you want to disable this post-processing, you can use the `--noPost` flag:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
kubernetes-fluent-client crd crd.yaml types --noPost
|
|
130
|
+
```
|
|
131
|
+
|
|
108
132
|
### Community
|
|
109
133
|
|
|
110
134
|
To chat with other users & see some examples of the fluent client in active use, go to [Kubernetes Slack](https://communityinviter.com/apps/kubernetes/community) and join `#pepr` channel.
|
package/dist/cli.js
CHANGED
|
@@ -10,6 +10,8 @@ const helpers_1 = require("yargs/helpers");
|
|
|
10
10
|
const yargs_1 = __importDefault(require("yargs/yargs"));
|
|
11
11
|
const generate_1 = require("./generate");
|
|
12
12
|
const package_json_1 = require("../package.json");
|
|
13
|
+
const postProcessing_1 = require("./postProcessing");
|
|
14
|
+
const fileSystem_1 = require("./fileSystem"); // Import your new file system
|
|
13
15
|
void (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
14
16
|
.version("version", "Display version number", `kubernetes-fluent-client v${package_json_1.version}`)
|
|
15
17
|
.alias("version", "V")
|
|
@@ -33,13 +35,31 @@ void (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
|
33
35
|
type: "string",
|
|
34
36
|
default: "ts",
|
|
35
37
|
description: "the language to generate types in, see https://github.com/glideapps/quicktype#target-languages for a list of supported languages",
|
|
38
|
+
})
|
|
39
|
+
.option("noPost", {
|
|
40
|
+
alias: "x",
|
|
41
|
+
type: "boolean",
|
|
42
|
+
default: false,
|
|
43
|
+
description: "disable post-processing after generating the types",
|
|
36
44
|
})
|
|
37
45
|
.demandOption(["source", "directory"]);
|
|
38
46
|
}, async (argv) => {
|
|
39
47
|
const opts = argv;
|
|
40
48
|
opts.logFn = console.log;
|
|
49
|
+
// Pass the `post` flag to opts
|
|
50
|
+
opts.noPost = argv.noPost;
|
|
51
|
+
// Use NodeFileSystem as the file system for post-processing
|
|
52
|
+
const fileSystem = new fileSystem_1.NodeFileSystem(); // Create an instance of NodeFileSystem
|
|
53
|
+
if (!opts.noPost) {
|
|
54
|
+
console.log("\n✅ Post-processing has been enabled.\n");
|
|
55
|
+
}
|
|
41
56
|
try {
|
|
42
|
-
|
|
57
|
+
// Capture the results returned by generate
|
|
58
|
+
const allResults = await (0, generate_1.generate)(opts);
|
|
59
|
+
// If noPost is false, run post-processing
|
|
60
|
+
if (!opts.noPost) {
|
|
61
|
+
await (0, postProcessing_1.postProcessing)(allResults, opts, fileSystem); // Pass the file system to postProcessing
|
|
62
|
+
}
|
|
43
63
|
}
|
|
44
64
|
catch (e) {
|
|
45
65
|
console.log(`\n❌ ${e.message}`);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface FileSystem {
|
|
2
|
+
readFile(filePath: string): string;
|
|
3
|
+
writeFile(filePath: string, content: string): void;
|
|
4
|
+
readdirSync(directory: string): string[];
|
|
5
|
+
}
|
|
6
|
+
export declare class NodeFileSystem implements FileSystem {
|
|
7
|
+
readFile(filePath: string): string;
|
|
8
|
+
writeFile(filePath: string, content: string): void;
|
|
9
|
+
readdirSync(directory: string): string[];
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=fileSystem.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileSystem.d.ts","sourceRoot":"","sources":["../src/fileSystem.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACnD,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1C;AAGD,qBAAa,cAAe,YAAW,UAAU;IAC/C,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAIlC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAIlD,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;CAGzC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
// SPDX-FileCopyrightText: 2023-Present The Kubernetes Fluent Client Authors
|
|
4
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
5
|
+
if (k2 === undefined) k2 = k;
|
|
6
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
7
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
8
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
9
|
+
}
|
|
10
|
+
Object.defineProperty(o, k2, desc);
|
|
11
|
+
}) : (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
o[k2] = m[k];
|
|
14
|
+
}));
|
|
15
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
16
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
17
|
+
}) : function(o, v) {
|
|
18
|
+
o["default"] = v;
|
|
19
|
+
});
|
|
20
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
21
|
+
if (mod && mod.__esModule) return mod;
|
|
22
|
+
var result = {};
|
|
23
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
24
|
+
__setModuleDefault(result, mod);
|
|
25
|
+
return result;
|
|
26
|
+
};
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.NodeFileSystem = void 0;
|
|
29
|
+
const fs = __importStar(require("fs"));
|
|
30
|
+
/* eslint class-methods-use-this: "off" */
|
|
31
|
+
class NodeFileSystem {
|
|
32
|
+
readFile(filePath) {
|
|
33
|
+
return fs.readFileSync(filePath, "utf8");
|
|
34
|
+
}
|
|
35
|
+
writeFile(filePath, content) {
|
|
36
|
+
fs.writeFileSync(filePath, content, "utf8");
|
|
37
|
+
}
|
|
38
|
+
readdirSync(directory) {
|
|
39
|
+
return fs.readdirSync(directory);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.NodeFileSystem = NodeFileSystem;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileSystem.test.d.ts","sourceRoot":"","sources":["../src/fileSystem.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
// SPDX-FileCopyrightText: 2023-Present The Kubernetes Fluent Client Authors
|
|
4
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
5
|
+
if (k2 === undefined) k2 = k;
|
|
6
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
7
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
8
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
9
|
+
}
|
|
10
|
+
Object.defineProperty(o, k2, desc);
|
|
11
|
+
}) : (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
o[k2] = m[k];
|
|
14
|
+
}));
|
|
15
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
16
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
17
|
+
}) : function(o, v) {
|
|
18
|
+
o["default"] = v;
|
|
19
|
+
});
|
|
20
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
21
|
+
if (mod && mod.__esModule) return mod;
|
|
22
|
+
var result = {};
|
|
23
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
24
|
+
__setModuleDefault(result, mod);
|
|
25
|
+
return result;
|
|
26
|
+
};
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
const fs = __importStar(require("fs"));
|
|
29
|
+
const fileSystem_1 = require("./fileSystem");
|
|
30
|
+
const globals_1 = require("@jest/globals");
|
|
31
|
+
// Mock the fs module
|
|
32
|
+
globals_1.jest.mock("fs");
|
|
33
|
+
(0, globals_1.describe)("NodeFileSystem", () => {
|
|
34
|
+
let nodeFileSystem;
|
|
35
|
+
(0, globals_1.beforeEach)(() => {
|
|
36
|
+
nodeFileSystem = new fileSystem_1.NodeFileSystem();
|
|
37
|
+
globals_1.jest.clearAllMocks(); // Clear all mocks before each test
|
|
38
|
+
});
|
|
39
|
+
(0, globals_1.describe)("readFile", () => {
|
|
40
|
+
(0, globals_1.test)("should call fs.readFileSync with correct arguments", () => {
|
|
41
|
+
const mockFilePath = "test-file.txt";
|
|
42
|
+
const mockFileContent = "This is a test file";
|
|
43
|
+
// Mock the fs.readFileSync method to return the mock file content
|
|
44
|
+
fs.readFileSync.mockReturnValue(mockFileContent);
|
|
45
|
+
const result = nodeFileSystem.readFile(mockFilePath);
|
|
46
|
+
// Assert that fs.readFileSync was called with the correct file path and encoding
|
|
47
|
+
(0, globals_1.expect)(fs.readFileSync).toHaveBeenCalledWith(mockFilePath, "utf8");
|
|
48
|
+
// Assert that the returned content matches the mock file content
|
|
49
|
+
(0, globals_1.expect)(result).toBe(mockFileContent);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
(0, globals_1.describe)("writeFile", () => {
|
|
53
|
+
(0, globals_1.test)("should call fs.writeFileSync with correct arguments", () => {
|
|
54
|
+
const mockFilePath = "test-file.txt";
|
|
55
|
+
const mockFileContent = "This is a test file";
|
|
56
|
+
// Call the writeFile method
|
|
57
|
+
nodeFileSystem.writeFile(mockFilePath, mockFileContent);
|
|
58
|
+
// Assert that fs.writeFileSync was called with the correct arguments
|
|
59
|
+
(0, globals_1.expect)(fs.writeFileSync).toHaveBeenCalledWith(mockFilePath, mockFileContent, "utf8");
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
(0, globals_1.describe)("readdirSync", () => {
|
|
63
|
+
(0, globals_1.test)("should call fs.readdirSync with correct arguments and return file list", () => {
|
|
64
|
+
const mockDirectoryPath = "test-directory";
|
|
65
|
+
const mockFileList = ["file1.txt", "file2.txt"];
|
|
66
|
+
// Mock the fs.readdirSync method to return the mock file list
|
|
67
|
+
fs.readdirSync.mockReturnValue(mockFileList);
|
|
68
|
+
const result = nodeFileSystem.readdirSync(mockDirectoryPath);
|
|
69
|
+
// Assert that fs.readdirSync was called with the correct directory path
|
|
70
|
+
(0, globals_1.expect)(fs.readdirSync).toHaveBeenCalledWith(mockDirectoryPath);
|
|
71
|
+
// Assert that the returned file list matches the mock file list
|
|
72
|
+
(0, globals_1.expect)(result).toEqual(mockFileList);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
});
|
package/dist/fluent/watch.d.ts
CHANGED
|
@@ -41,6 +41,8 @@ export type WatchCfg = {
|
|
|
41
41
|
relistIntervalSec?: number;
|
|
42
42
|
/** Max amount of seconds to go without receiving an event before reconciliation starts. Defaults to 300 (5 minutes). */
|
|
43
43
|
lastSeenLimitSeconds?: number;
|
|
44
|
+
/** Use http2 for the Watch */
|
|
45
|
+
useHTTP2?: boolean;
|
|
44
46
|
};
|
|
45
47
|
/** A wrapper around the Kubernetes watch API. */
|
|
46
48
|
export declare class Watcher<T extends GenericClass> {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/fluent/watch.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/fluent/watch.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAKtC,OAAO,EAAE,YAAY,EAAwB,MAAM,UAAU,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,WAAW,EAAc,MAAM,SAAS,CAAC;AAI3D,oBAAY,UAAU;IACpB,sCAAsC;IACtC,OAAO,YAAY;IACnB,2BAA2B;IAC3B,aAAa,kBAAkB;IAC/B,kDAAkD;IAClD,UAAU,eAAe;IACzB,0BAA0B;IAC1B,SAAS,cAAc;IACvB,8BAA8B;IAC9B,OAAO,YAAY;IACnB,sBAAsB;IACtB,KAAK,UAAU;IACf,mCAAmC;IACnC,IAAI,SAAS;IACb,wCAAwC;IACxC,oBAAoB,yBAAyB;IAC7C,qCAAqC;IACrC,iBAAiB,sBAAsB;IACvC,kCAAkC;IAClC,IAAI,SAAS;IACb,2BAA2B;IAC3B,UAAU,eAAe;IACzB,mBAAmB;IACnB,UAAU,eAAe;IACzB,qCAAqC;IACrC,wBAAwB,6BAA6B;IACrD,iCAAiC;IACjC,eAAe,oBAAoB;CACpC;AAED,4CAA4C;AAC5C,MAAM,MAAM,QAAQ,GAAG;IACrB,+HAA+H;IAC/H,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,wDAAwD;IACxD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+FAA+F;IAC/F,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wHAAwH;IACxH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAKF,iDAAiD;AACjD,qBAAa,OAAO,CAAC,CAAC,SAAS,YAAY;;IA0BzC,YAAY,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;IAgB9B;;;;;;;;;;;OAWG;gBACS,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,QAAQ,GAAE,QAAa;IA6CzF;;;;OAIG;IACU,KAAK,IAAI,OAAO,CAAC,eAAe,CAAC;IAU9C,gGAAgG;IACzF,KAAK;IAOZ;;;;;OAKG;IACI,UAAU;IAWjB;;;;;;OAMG;IACH,IAAW,MAAM,IAAI,YAAY,CAEhC;CA8bF"}
|
package/dist/fluent/watch.js
CHANGED
|
@@ -9,10 +9,13 @@ exports.Watcher = exports.WatchEvent = void 0;
|
|
|
9
9
|
const byline_1 = __importDefault(require("byline"));
|
|
10
10
|
const crypto_1 = require("crypto");
|
|
11
11
|
const events_1 = require("events");
|
|
12
|
+
const https_1 = __importDefault(require("https"));
|
|
13
|
+
const http2_1 = __importDefault(require("http2"));
|
|
12
14
|
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
13
15
|
const fetch_1 = require("../fetch");
|
|
14
16
|
const types_1 = require("./types");
|
|
15
17
|
const utils_1 = require("./utils");
|
|
18
|
+
const fs_1 = __importDefault(require("fs"));
|
|
16
19
|
var WatchEvent;
|
|
17
20
|
(function (WatchEvent) {
|
|
18
21
|
/** Watch is connected successfully */
|
|
@@ -54,6 +57,7 @@ class Watcher {
|
|
|
54
57
|
#callback;
|
|
55
58
|
#watchCfg;
|
|
56
59
|
#latestRelistWindow = "";
|
|
60
|
+
#useHTTP2 = false;
|
|
57
61
|
// Track the last time data was received
|
|
58
62
|
#lastSeenTime = NONE;
|
|
59
63
|
#lastSeenLimit;
|
|
@@ -75,6 +79,8 @@ class Watcher {
|
|
|
75
79
|
#resourceVersion;
|
|
76
80
|
// Track the list of items in the cache
|
|
77
81
|
#cache = new Map();
|
|
82
|
+
// Token Path
|
|
83
|
+
#TOKEN_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token";
|
|
78
84
|
/**
|
|
79
85
|
* Setup a Kubernetes watcher for the specified model and filters. The callback function will be called for each event received.
|
|
80
86
|
* The watch can be aborted by calling {@link Watcher.close} or by calling abort() on the AbortController returned by {@link Watcher.start}.
|
|
@@ -98,6 +104,8 @@ class Watcher {
|
|
|
98
104
|
this.#lastSeenLimit = watchCfg.lastSeenLimitSeconds * 1000;
|
|
99
105
|
// Set the latest relist interval to now
|
|
100
106
|
this.#latestRelistWindow = new Date().toISOString();
|
|
107
|
+
// Set the latest relist interval to now
|
|
108
|
+
this.#useHTTP2 = watchCfg.useHTTP2 ?? false;
|
|
101
109
|
// Add random jitter to the relist/resync intervals (up to 1 second)
|
|
102
110
|
const jitter = Math.floor(Math.random() * 1000);
|
|
103
111
|
// Check every relist interval for cache staleness
|
|
@@ -123,7 +131,12 @@ class Watcher {
|
|
|
123
131
|
*/
|
|
124
132
|
async start() {
|
|
125
133
|
this.#events.emit(WatchEvent.INIT_CACHE_MISS, this.#latestRelistWindow);
|
|
126
|
-
|
|
134
|
+
if (this.#useHTTP2) {
|
|
135
|
+
await this.#http2Watch();
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
await this.#watch();
|
|
139
|
+
}
|
|
127
140
|
return this.#abortController;
|
|
128
141
|
}
|
|
129
142
|
/** Close the watch. Also available on the AbortController returned by {@link Watcher.start}. */
|
|
@@ -158,6 +171,19 @@ class Watcher {
|
|
|
158
171
|
get events() {
|
|
159
172
|
return this.#events;
|
|
160
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Read the serviceAccount Token
|
|
176
|
+
*
|
|
177
|
+
* @returns token or null
|
|
178
|
+
*/
|
|
179
|
+
async #getToken() {
|
|
180
|
+
try {
|
|
181
|
+
return (await fs_1.default.promises.readFile(this.#TOKEN_PATH, "utf8")).trim();
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
161
187
|
/**
|
|
162
188
|
* Build the URL and request options for the watch.
|
|
163
189
|
*
|
|
@@ -285,6 +311,32 @@ class Watcher {
|
|
|
285
311
|
this.#events.emit(WatchEvent.DATA_ERROR, err);
|
|
286
312
|
}
|
|
287
313
|
};
|
|
314
|
+
// process a line from the chunk
|
|
315
|
+
#processLine = async (line, process) => {
|
|
316
|
+
try {
|
|
317
|
+
// Parse the event payload
|
|
318
|
+
const { object: payload, type: phase } = JSON.parse(line);
|
|
319
|
+
// Update the last seen time
|
|
320
|
+
this.#lastSeenTime = Date.now();
|
|
321
|
+
// If the watch is too old, remove the resourceVersion and reload the watch
|
|
322
|
+
if (phase === types_1.WatchPhase.Error && payload.code === 410) {
|
|
323
|
+
throw {
|
|
324
|
+
name: "TooOld",
|
|
325
|
+
message: this.#resourceVersion,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
// Process the event payload, do not update the resource version as that is handled by the list operation
|
|
329
|
+
await process(payload, phase);
|
|
330
|
+
}
|
|
331
|
+
catch (err) {
|
|
332
|
+
if (err.name === "TooOld") {
|
|
333
|
+
// Reload the watch
|
|
334
|
+
void this.#errHandler(err);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
this.#events.emit(WatchEvent.DATA_ERROR, err);
|
|
338
|
+
}
|
|
339
|
+
};
|
|
288
340
|
/**
|
|
289
341
|
* Watch for changes to the resource.
|
|
290
342
|
*/
|
|
@@ -313,31 +365,7 @@ class Watcher {
|
|
|
313
365
|
this.#events.emit(WatchEvent.INC_RESYNC_FAILURE_COUNT, this.#resyncFailureCount);
|
|
314
366
|
// Listen for events and call the callback function
|
|
315
367
|
this.#stream.on("data", async (line) => {
|
|
316
|
-
|
|
317
|
-
// Parse the event payload
|
|
318
|
-
const { object: payload, type: phase } = JSON.parse(line);
|
|
319
|
-
// Update the last seen time
|
|
320
|
-
this.#lastSeenTime = Date.now();
|
|
321
|
-
// If the watch is too old, remove the resourceVersion and reload the watch
|
|
322
|
-
if (phase === types_1.WatchPhase.Error && payload.code === 410) {
|
|
323
|
-
throw {
|
|
324
|
-
name: "TooOld",
|
|
325
|
-
message: this.#resourceVersion,
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
// Process the event payload, do not update the resource version as that is handled by the list operation
|
|
329
|
-
await this.#process(payload, phase);
|
|
330
|
-
}
|
|
331
|
-
catch (err) {
|
|
332
|
-
if (err.name === "TooOld") {
|
|
333
|
-
// Prevent any body events from firing
|
|
334
|
-
body.removeAllListeners();
|
|
335
|
-
// Reload the watch
|
|
336
|
-
void this.#errHandler(err);
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
this.#events.emit(WatchEvent.DATA_ERROR, err);
|
|
340
|
-
}
|
|
368
|
+
await this.#processLine(line, this.#process);
|
|
341
369
|
});
|
|
342
370
|
// Bind the body events
|
|
343
371
|
body.on("error", this.#errHandler);
|
|
@@ -354,6 +382,93 @@ class Watcher {
|
|
|
354
382
|
void this.#errHandler(e);
|
|
355
383
|
}
|
|
356
384
|
};
|
|
385
|
+
/**
|
|
386
|
+
* Watch for changes to the resource.
|
|
387
|
+
*/
|
|
388
|
+
#http2Watch = async () => {
|
|
389
|
+
try {
|
|
390
|
+
// Start with a list operation
|
|
391
|
+
await this.#list();
|
|
392
|
+
// Build the URL and request options
|
|
393
|
+
const { opts, url } = await this.#buildURL(true, this.#resourceVersion);
|
|
394
|
+
let agentOptions;
|
|
395
|
+
if (opts.agent && opts.agent instanceof https_1.default.Agent) {
|
|
396
|
+
agentOptions = {
|
|
397
|
+
key: opts.agent.options.key,
|
|
398
|
+
cert: opts.agent.options.cert,
|
|
399
|
+
ca: opts.agent.options.ca,
|
|
400
|
+
rejectUnauthorized: false,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
// HTTP/2 client connection setup
|
|
404
|
+
const client = http2_1.default.connect(url.origin, {
|
|
405
|
+
ca: agentOptions?.ca,
|
|
406
|
+
cert: agentOptions?.cert,
|
|
407
|
+
key: agentOptions?.key,
|
|
408
|
+
rejectUnauthorized: agentOptions?.rejectUnauthorized,
|
|
409
|
+
});
|
|
410
|
+
// Set up headers for the HTTP/2 request
|
|
411
|
+
const token = await this.#getToken();
|
|
412
|
+
const headers = {
|
|
413
|
+
":method": "GET",
|
|
414
|
+
":path": url.pathname + url.search,
|
|
415
|
+
"content-type": "application/json",
|
|
416
|
+
"user-agent": "kubernetes-fluent-client",
|
|
417
|
+
};
|
|
418
|
+
if (token) {
|
|
419
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
420
|
+
}
|
|
421
|
+
// Make the HTTP/2 request
|
|
422
|
+
const req = client.request(headers);
|
|
423
|
+
req.setEncoding("utf8");
|
|
424
|
+
let buffer = "";
|
|
425
|
+
// Handle response data
|
|
426
|
+
req.on("response", headers => {
|
|
427
|
+
const statusCode = headers[":status"];
|
|
428
|
+
if (statusCode && statusCode >= 200 && statusCode < 300) {
|
|
429
|
+
this.#pendingReconnect = false;
|
|
430
|
+
this.#events.emit(WatchEvent.CONNECT, url.pathname);
|
|
431
|
+
// Reset the retry count
|
|
432
|
+
this.#resyncFailureCount = 0;
|
|
433
|
+
this.#events.emit(WatchEvent.INC_RESYNC_FAILURE_COUNT, this.#resyncFailureCount);
|
|
434
|
+
req.on("data", async (chunk) => {
|
|
435
|
+
try {
|
|
436
|
+
buffer += chunk;
|
|
437
|
+
const lines = buffer.split("\n");
|
|
438
|
+
// Avoid Watch event data_error received. Unexpected end of JSON input.
|
|
439
|
+
buffer = lines.pop();
|
|
440
|
+
for (const line of lines) {
|
|
441
|
+
await this.#processLine(line, this.#process);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
catch (err) {
|
|
445
|
+
void this.#errHandler(err);
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
req.on("end", () => {
|
|
449
|
+
this.#streamCleanup();
|
|
450
|
+
client.close();
|
|
451
|
+
});
|
|
452
|
+
req.on("close", () => {
|
|
453
|
+
this.#streamCleanup();
|
|
454
|
+
client.close();
|
|
455
|
+
});
|
|
456
|
+
req.on("error", err => {
|
|
457
|
+
void this.#errHandler(err);
|
|
458
|
+
client.close();
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
const statusMessage = headers[":status-text"] || "Unknown";
|
|
463
|
+
throw new Error(`watch connect failed: ${statusCode} ${statusMessage}`);
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
req.end();
|
|
467
|
+
}
|
|
468
|
+
catch (e) {
|
|
469
|
+
void this.#errHandler(e);
|
|
470
|
+
}
|
|
471
|
+
};
|
|
357
472
|
/** Clear the resync timer and schedule a new one. */
|
|
358
473
|
#checkResync = () => {
|
|
359
474
|
// Ignore if the last seen time is not set
|
|
@@ -379,7 +494,9 @@ class Watcher {
|
|
|
379
494
|
this.#pendingReconnect = true;
|
|
380
495
|
this.#events.emit(WatchEvent.RECONNECT, this.#resyncFailureCount);
|
|
381
496
|
this.#streamCleanup();
|
|
382
|
-
|
|
497
|
+
if (!this.#useHTTP2) {
|
|
498
|
+
void this.#watch();
|
|
499
|
+
}
|
|
383
500
|
}
|
|
384
501
|
}
|
|
385
502
|
else {
|
|
@@ -420,6 +537,9 @@ class Watcher {
|
|
|
420
537
|
this.#stream.removeAllListeners();
|
|
421
538
|
this.#stream.destroy();
|
|
422
539
|
}
|
|
540
|
+
if (this.#useHTTP2) {
|
|
541
|
+
void this.#http2Watch();
|
|
542
|
+
}
|
|
423
543
|
};
|
|
424
544
|
}
|
|
425
545
|
exports.Watcher = Watcher;
|
package/dist/generate.d.ts
CHANGED
|
@@ -1,24 +1,84 @@
|
|
|
1
|
-
import { TargetLanguage } from "quicktype-core";
|
|
1
|
+
import { InputData, TargetLanguage } from "quicktype-core";
|
|
2
|
+
import { CustomResourceDefinition } from "./upstream";
|
|
2
3
|
import { LogFn } from "./types";
|
|
3
4
|
export interface GenerateOptions {
|
|
4
|
-
/** The source URL, yaml file path or K8s CRD name */
|
|
5
5
|
source: string;
|
|
6
|
-
/** The output directory path */
|
|
7
6
|
directory?: string;
|
|
8
|
-
/** Disable kubernetes-fluent-client wrapping */
|
|
9
7
|
plain?: boolean;
|
|
10
|
-
/** The language to generate types in */
|
|
11
8
|
language?: string | TargetLanguage;
|
|
12
|
-
/** Override the NPM package to import when generating formatted Typescript */
|
|
13
9
|
npmPackage?: string;
|
|
14
|
-
/** Log function callback */
|
|
15
10
|
logFn: LogFn;
|
|
11
|
+
noPost?: boolean;
|
|
16
12
|
}
|
|
17
13
|
/**
|
|
18
|
-
*
|
|
14
|
+
* Converts a CustomResourceDefinition to TypeScript types
|
|
19
15
|
*
|
|
20
|
-
* @param
|
|
21
|
-
* @
|
|
16
|
+
* @param crd - The CustomResourceDefinition object to convert.
|
|
17
|
+
* @param opts - The options for generating the TypeScript types.
|
|
18
|
+
* @returns A promise that resolves to a record of generated TypeScript types.
|
|
22
19
|
*/
|
|
23
|
-
export declare function
|
|
20
|
+
export declare function convertCRDtoTS(crd: CustomResourceDefinition, opts: GenerateOptions): Promise<{
|
|
21
|
+
results: Record<string, string[]>;
|
|
22
|
+
name: string;
|
|
23
|
+
crd: CustomResourceDefinition;
|
|
24
|
+
version: string;
|
|
25
|
+
}[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Prepares the input data for quicktype from the provided schema.
|
|
28
|
+
*
|
|
29
|
+
* @param name - The name of the schema.
|
|
30
|
+
* @param schema - The JSON schema as a string.
|
|
31
|
+
* @returns A promise that resolves to the input data for quicktype.
|
|
32
|
+
*/
|
|
33
|
+
export declare function prepareInputData(name: string, schema: string): Promise<InputData>;
|
|
34
|
+
/**
|
|
35
|
+
* Generates TypeScript types using quicktype.
|
|
36
|
+
*
|
|
37
|
+
* @param inputData - The input data for quicktype.
|
|
38
|
+
* @param opts - The options for generating the TypeScript types.
|
|
39
|
+
* @returns A promise that resolves to an array of generated TypeScript type lines.
|
|
40
|
+
*/
|
|
41
|
+
export declare function generateTypes(inputData: InputData, opts: GenerateOptions): Promise<string[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Writes the processed lines to the output file.
|
|
44
|
+
*
|
|
45
|
+
* @param fileName - The name of the file to write.
|
|
46
|
+
* @param directory - The directory where the file will be written.
|
|
47
|
+
* @param content - The content to write to the file.
|
|
48
|
+
* @param language - The programming language of the file.
|
|
49
|
+
*/
|
|
50
|
+
export declare function writeGeneratedFile(fileName: string, directory: string, content: string[], language: string | TargetLanguage): void;
|
|
51
|
+
/**
|
|
52
|
+
* Reads or fetches a CustomResourceDefinition from a file, URL, or the cluster.
|
|
53
|
+
*
|
|
54
|
+
* @param opts - The options for generating the TypeScript types.
|
|
55
|
+
* @returns A promise that resolves to an array of CustomResourceDefinition objects.
|
|
56
|
+
*/
|
|
57
|
+
export declare function readOrFetchCrd(opts: GenerateOptions): Promise<CustomResourceDefinition[]>;
|
|
58
|
+
/**
|
|
59
|
+
* Resolves the source file path, treating relative paths as local files.
|
|
60
|
+
*
|
|
61
|
+
* @param source - The source path to resolve.
|
|
62
|
+
* @returns The resolved file path.
|
|
63
|
+
*/
|
|
64
|
+
export declare function resolveFilePath(source: string): string;
|
|
65
|
+
/**
|
|
66
|
+
* Tries to parse the source as a URL.
|
|
67
|
+
*
|
|
68
|
+
* @param source - The source string to parse as a URL.
|
|
69
|
+
* @returns The parsed URL object or null if parsing fails.
|
|
70
|
+
*/
|
|
71
|
+
export declare function tryParseUrl(source: string): URL | null;
|
|
72
|
+
/**
|
|
73
|
+
* Main generate function to convert CRDs to TypeScript types.
|
|
74
|
+
*
|
|
75
|
+
* @param opts - The options for generating the TypeScript types.
|
|
76
|
+
* @returns A promise that resolves to a record of generated TypeScript types.
|
|
77
|
+
*/
|
|
78
|
+
export declare function generate(opts: GenerateOptions): Promise<{
|
|
79
|
+
results: Record<string, string[]>;
|
|
80
|
+
name: string;
|
|
81
|
+
crd: CustomResourceDefinition;
|
|
82
|
+
version: string;
|
|
83
|
+
}[]>;
|
|
24
84
|
//# sourceMappingURL=generate.d.ts.map
|
package/dist/generate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAMA,OAAO,
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAMA,OAAO,EAEL,SAAS,EAET,cAAc,EAEf,MAAM,gBAAgB,CAAC;AAIxB,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,wBAAwB,EAC7B,IAAI,EAAE,eAAe,GACpB,OAAO,CACR;IACE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,wBAAwB,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;CACjB,EAAE,CACJ,CAuCA;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAYvF;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,MAAM,EAAE,CAAC,CAYnB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EAAE,EACjB,QAAQ,EAAE,MAAM,GAAG,cAAc,GAChC,IAAI,CAON;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,wBAAwB,EAAE,CAAC,CA0B/F;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAMtD;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAC5D;IACE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,wBAAwB,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;CACjB,EAAE,CACJ,CA8BA"}
|