UNPKG

12.1 kBJavaScriptView Raw
1"use strict";
2var __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};
11Object.defineProperty(exports, "__esModule", { value: true });
12const client_1 = require("./client");
13const prom_client_1 = require("prom-client");
14const debug = require("debug")("kubernetes:resource");
15const sleep = (ms) => new Promise(res => setTimeout(res, ms));
16class 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}
65exports.CustomResourceClient = CustomResourceClient;
66const resourceMetricLabels = ["baseURL"];
67class 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 // Metrics need to be static, because there can be multiple ResourceClients, but
77 // metrics may exist only _once_.
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) => __awaiter(this, void 0, void 0, function* () {
124 resourceVersion = parseInt(list.metadata.resourceVersion, 10);
125 if (opts.onResync) {
126 yield opts.onResync(list.items || []);
127 }
128 if (!opts.skipAddEventsOnResync) {
129 for (const i of list.items || []) {
130 const event = { type: "ADDED", object: i };
131 yield 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 yield 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}
241exports.ResourceClient = ResourceClient;
242class 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}
283exports.NamespacedResourceClient = NamespacedResourceClient;
284//# sourceMappingURL=resource.js.map
\No newline at end of file