1 | "use strict";
|
2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
4 | return new (P || (P = Promise))(function (resolve, reject) {
|
5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
8 | step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9 | });
|
10 | };
|
11 | Object.defineProperty(exports, "__esModule", { value: true });
|
12 | const client_1 = require("./client");
|
13 | const prom_client_1 = require("prom-client");
|
14 | const debug = require("debug")("kubernetes:resource");
|
15 | const sleep = (ms) => new Promise(res => setTimeout(res, ms));
|
16 | class CustomResourceClient {
|
17 | constructor(inner, kind, apiVersion) {
|
18 | this.inner = inner;
|
19 | this.kind = kind;
|
20 | this.apiVersion = apiVersion;
|
21 | }
|
22 | list(listOptions) {
|
23 | return this.inner.list(listOptions);
|
24 | }
|
25 | get(name) {
|
26 | return this.inner.get(name);
|
27 | }
|
28 | apply(resource) {
|
29 | return this.inner.apply(Object.assign(Object.assign({}, resource), { kind: this.kind, apiVersion: this.apiVersion }));
|
30 | }
|
31 | put(resource) {
|
32 | return this.inner.put(Object.assign(Object.assign({}, resource), { kind: this.kind, apiVersion: this.apiVersion }));
|
33 | }
|
34 | post(resource) {
|
35 | return this.inner.post(Object.assign(Object.assign({}, resource), { kind: this.kind, apiVersion: this.apiVersion }));
|
36 | }
|
37 | delete(resourceOrName, deleteOptions) {
|
38 | return this.inner.delete(resourceOrName, deleteOptions);
|
39 | }
|
40 | deleteMany(opts) {
|
41 | return this.inner.deleteMany(opts);
|
42 | }
|
43 | watch(handler, errorHandler, opts) {
|
44 | return this.inner.watch(handler, errorHandler, opts);
|
45 | }
|
46 | listWatch(handler, errorHandler, opts) {
|
47 | return this.inner.listWatch(handler, errorHandler, opts);
|
48 | }
|
49 | patchJSON(resourceOrName, patch) {
|
50 | return this.inner.patchJSON(resourceOrName, patch);
|
51 | }
|
52 | patchStrategic(resourceOrName, patch) {
|
53 | return this.inner.patchStrategic(resourceOrName, patch);
|
54 | }
|
55 | patchMerge(resourceOrName, patch) {
|
56 | return this.inner.patchMerge(resourceOrName, patch);
|
57 | }
|
58 | namespace(ns) {
|
59 | return new CustomResourceClient(this.inner.namespace(ns), this.kind, this.apiVersion);
|
60 | }
|
61 | allNamespaces() {
|
62 | return new CustomResourceClient(this.inner.allNamespaces(), this.kind, this.apiVersion);
|
63 | }
|
64 | }
|
65 | exports.CustomResourceClient = CustomResourceClient;
|
66 | const resourceMetricLabels = ["baseURL"];
|
67 | class ResourceClient {
|
68 | constructor(client, apiBaseURL, resourceBaseURL, registry) {
|
69 | this.client = client;
|
70 | this.apiBaseURL = apiBaseURL;
|
71 | this.resourceBaseURL = resourceBaseURL;
|
72 | this.supportsCollectionDeletion = true;
|
73 | this.apiBaseURL = apiBaseURL.replace(/\/$/, "");
|
74 | this.resourceBaseURL = resourceBaseURL.replace(/^\//, "").replace(/\/$/, "");
|
75 | this.baseURL = apiBaseURL + "/" + resourceBaseURL;
|
76 |
|
77 |
|
78 | if (!ResourceClient.watchResyncErrorCount) {
|
79 | ResourceClient.watchResyncErrorCount = new prom_client_1.Counter({
|
80 | name: "kubernetes_listwatch_resync_errors",
|
81 | help: "Amount of resync errors while running listwatches",
|
82 | registers: [registry],
|
83 | labelNames: resourceMetricLabels,
|
84 | });
|
85 | }
|
86 | if (!ResourceClient.watchOpenCount) {
|
87 | ResourceClient.watchOpenCount = new prom_client_1.Gauge({
|
88 | name: "kubernetes_listwatch_open",
|
89 | help: "Amount of currently open listwatches",
|
90 | registers: [registry],
|
91 | labelNames: resourceMetricLabels,
|
92 | });
|
93 | }
|
94 | }
|
95 | urlForResource(r) {
|
96 | return this.baseURL + "/" + r.metadata.name;
|
97 | }
|
98 | urlForResourceOrName(r) {
|
99 | return (typeof r === "string") ? this.baseURL + "/" + r : this.urlForResource(r);
|
100 | }
|
101 | list(opts) {
|
102 | return __awaiter(this, void 0, void 0, function* () {
|
103 | const list = yield this.client.get(this.baseURL, opts);
|
104 | return list.items || [];
|
105 | });
|
106 | }
|
107 | get(name) {
|
108 | return __awaiter(this, void 0, void 0, function* () {
|
109 | return yield this.client.get(this.baseURL + "/" + name);
|
110 | });
|
111 | }
|
112 | watch(handler, errorHandler, opts = {}) {
|
113 | errorHandler = errorHandler || (() => { });
|
114 | return this.client.watch(this.baseURL, handler, errorHandler, opts);
|
115 | }
|
116 | listWatch(handler, errorHandler, opts = {}) {
|
117 | let resourceVersion = 0;
|
118 | let running = true;
|
119 | ResourceClient.watchOpenCount.inc({ baseURL: this.baseURL });
|
120 | debug("starting list-watch on %o", this.resourceBaseURL);
|
121 | const { resyncAfterIterations = 10 } = opts;
|
122 | const resync = () => this.client.get(this.baseURL, opts)
|
123 | .then((list) => {
|
124 | resourceVersion = parseInt(list.metadata.resourceVersion, 10);
|
125 | if (opts.onResync) {
|
126 | opts.onResync(list.items || []);
|
127 | }
|
128 | if (!opts.skipAddEventsOnResync) {
|
129 | for (const i of list.items || []) {
|
130 | const event = { type: "ADDED", object: i };
|
131 | handler(event);
|
132 | }
|
133 | }
|
134 | });
|
135 | const initialized = resync();
|
136 | const done = initialized.then(() => __awaiter(this, void 0, void 0, function* () {
|
137 | errorHandler = errorHandler || (() => { });
|
138 | let errorCount = 0;
|
139 | let successCount = 0;
|
140 | debug("initial list for list-watch on %o completed", this.resourceBaseURL);
|
141 | while (running) {
|
142 | try {
|
143 | if (successCount > resyncAfterIterations) {
|
144 | debug(`resyncing after ${resyncAfterIterations} successful WATCH iterations`);
|
145 | yield resync();
|
146 | }
|
147 | const result = yield this.client.watch(this.baseURL, handler, errorHandler, Object.assign(Object.assign({}, opts), { resourceVersion }));
|
148 | if (result.resyncRequired) {
|
149 | debug(`resyncing listwatch`);
|
150 | yield resync();
|
151 | continue;
|
152 | }
|
153 | resourceVersion = Math.max(resourceVersion, result.resourceVersion);
|
154 | errorCount = 0;
|
155 | successCount++;
|
156 | }
|
157 | catch (err) {
|
158 | errorCount++;
|
159 | ResourceClient.watchResyncErrorCount.inc({ baseURL: this.baseURL });
|
160 | if (opts.onError) {
|
161 | opts.onError(err);
|
162 | }
|
163 | if (opts.abortAfterErrorCount && errorCount > opts.abortAfterErrorCount) {
|
164 | ResourceClient.watchOpenCount.dec({ baseURL: this.baseURL });
|
165 | throw new Error(`more than ${opts.abortAfterErrorCount} consecutive errors when watching ${this.baseURL}`);
|
166 | }
|
167 | debug("resuming watch after back-off of %o ms", 10000);
|
168 | yield sleep(10000);
|
169 | debug("resuming watch with resync after error: %o", err);
|
170 | yield resync();
|
171 | }
|
172 | }
|
173 | ResourceClient.watchOpenCount.dec({ baseURL: this.baseURL });
|
174 | }));
|
175 | return {
|
176 | initialized,
|
177 | done,
|
178 | stop() {
|
179 | running = false;
|
180 | },
|
181 | };
|
182 | }
|
183 | apply(resource) {
|
184 | return __awaiter(this, void 0, void 0, function* () {
|
185 | const existing = yield this.client.get(this.urlForResource(resource));
|
186 | if (existing) {
|
187 | return yield this.put(resource);
|
188 | }
|
189 | else {
|
190 | return yield this.post(resource);
|
191 | }
|
192 | });
|
193 | }
|
194 | put(resource) {
|
195 | return __awaiter(this, void 0, void 0, function* () {
|
196 | return yield this.client.put(this.urlForResource(resource), resource);
|
197 | });
|
198 | }
|
199 | post(resource) {
|
200 | return __awaiter(this, void 0, void 0, function* () {
|
201 | return yield this.client.post(this.baseURL, resource);
|
202 | });
|
203 | }
|
204 | patchStrategic(resourceOrName, patch) {
|
205 | return __awaiter(this, void 0, void 0, function* () {
|
206 | return yield this.client.patch(this.urlForResourceOrName(resourceOrName), patch, client_1.patchKindStrategicMergePatch);
|
207 | });
|
208 | }
|
209 | patchMerge(resourceOrName, patch) {
|
210 | return __awaiter(this, void 0, void 0, function* () {
|
211 | return yield this.client.patch(this.urlForResourceOrName(resourceOrName), patch, client_1.patchKindMergePatch);
|
212 | });
|
213 | }
|
214 | patchJSON(resourceOrName, patch) {
|
215 | return __awaiter(this, void 0, void 0, function* () {
|
216 | return yield this.client.patch(this.urlForResourceOrName(resourceOrName), patch, client_1.patchKindJSONPatch);
|
217 | });
|
218 | }
|
219 | delete(resourceOrName, deleteOptions) {
|
220 | return __awaiter(this, void 0, void 0, function* () {
|
221 | let url;
|
222 | if (typeof resourceOrName === "string") {
|
223 | url = this.baseURL + "/" + resourceOrName;
|
224 | }
|
225 | else {
|
226 | url = this.urlForResource(resourceOrName);
|
227 | }
|
228 | return yield this.client.delete(url, deleteOptions);
|
229 | });
|
230 | }
|
231 | deleteMany(opts) {
|
232 | return __awaiter(this, void 0, void 0, function* () {
|
233 | if (this.supportsCollectionDeletion) {
|
234 | return yield this.client.delete(this.baseURL, opts);
|
235 | }
|
236 | const resources = yield this.list(opts);
|
237 | yield Promise.all(resources.map(r => this.delete(r)));
|
238 | });
|
239 | }
|
240 | }
|
241 | exports.ResourceClient = ResourceClient;
|
242 | class NamespacedResourceClient extends ResourceClient {
|
243 | constructor(client, apiBaseURL, resourceBaseURL, registry, ns) {
|
244 | super(client, apiBaseURL, resourceBaseURL, registry);
|
245 | this.registry = registry;
|
246 | apiBaseURL = apiBaseURL.replace(/\/$/, "");
|
247 | resourceBaseURL = resourceBaseURL.replace(/^\//, "").replace(/\/$/, "");
|
248 | this.ns = ns;
|
249 | if (ns) {
|
250 | this.baseURL = apiBaseURL + "/namespaces/" + ns + "/" + resourceBaseURL;
|
251 | }
|
252 | else {
|
253 | this.baseURL = apiBaseURL + "/" + resourceBaseURL;
|
254 | }
|
255 | }
|
256 | urlForResource(r) {
|
257 | const namespace = r.metadata.namespace || this.ns;
|
258 | if (namespace) {
|
259 | return this.apiBaseURL + "/namespaces/" + namespace + "/" + this.resourceBaseURL + "/" + r.metadata.name;
|
260 | }
|
261 | return this.apiBaseURL + "/" + this.resourceBaseURL + "/" + r.metadata.name;
|
262 | }
|
263 | namespace(ns) {
|
264 | const n = new NamespacedResourceClient(this.client, this.apiBaseURL, this.resourceBaseURL, this.registry, ns);
|
265 | n.supportsCollectionDeletion = this.supportsCollectionDeletion;
|
266 | return n;
|
267 | }
|
268 | allNamespaces() {
|
269 | const n = new NamespacedResourceClient(this.client, this.apiBaseURL, this.resourceBaseURL, this.registry);
|
270 | n.supportsCollectionDeletion = this.supportsCollectionDeletion;
|
271 | return n;
|
272 | }
|
273 | post(resource) {
|
274 | return __awaiter(this, void 0, void 0, function* () {
|
275 | let url = this.baseURL;
|
276 | if (resource.metadata.namespace) {
|
277 | url = this.apiBaseURL + "/namespaces/" + resource.metadata.namespace + "/" + this.resourceBaseURL;
|
278 | }
|
279 | return yield this.client.post(url, resource);
|
280 | });
|
281 | }
|
282 | }
|
283 | exports.NamespacedResourceClient = NamespacedResourceClient;
|
284 |
|
\ | No newline at end of file |