UNPKG

@mittwald/kubernetes

Version:

Kubernetes client library

277 lines 11.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const client_1 = require("./client"); const prom_client_1 = require("prom-client"); const debug = require("debug")("kubernetes:resource"); const sleep = (ms) => new Promise(res => setTimeout(res, ms)); class CustomResourceClient { constructor(inner, kind, apiVersion) { this.inner = inner; this.kind = kind; this.apiVersion = apiVersion; } list(listOptions) { return this.inner.list(listOptions); } get(name) { return this.inner.get(name); } apply(resource) { return this.inner.apply(Object.assign({}, resource, { kind: this.kind, apiVersion: this.apiVersion })); } put(resource) { return this.inner.put(Object.assign({}, resource, { kind: this.kind, apiVersion: this.apiVersion })); } post(resource) { return this.inner.post(Object.assign({}, resource, { kind: this.kind, apiVersion: this.apiVersion })); } delete(resourceOrName, deleteOptions) { return this.inner.delete(resourceOrName, deleteOptions); } deleteMany(opts) { return this.inner.deleteMany(opts); } watch(handler, errorHandler, opts) { return this.inner.watch(handler, errorHandler, opts); } listWatch(handler, errorHandler, opts) { return this.inner.listWatch(handler, errorHandler, opts); } patchJSON(resourceOrName, patch) { return this.inner.patchJSON(resourceOrName, patch); } patchStrategic(resourceOrName, patch) { return this.inner.patchStrategic(resourceOrName, patch); } patchMerge(resourceOrName, patch) { return this.inner.patchMerge(resourceOrName, patch); } namespace(ns) { return new CustomResourceClient(this.inner.namespace(ns), this.kind, this.apiVersion); } allNamespaces() { return new CustomResourceClient(this.inner.allNamespaces(), this.kind, this.apiVersion); } } exports.CustomResourceClient = CustomResourceClient; class ResourceClient { constructor(client, apiBaseURL, resourceBaseURL, registry) { this.client = client; this.apiBaseURL = apiBaseURL; this.resourceBaseURL = resourceBaseURL; this.supportsCollectionDeletion = true; this.apiBaseURL = apiBaseURL.replace(/\/$/, ""); this.resourceBaseURL = resourceBaseURL.replace(/^\//, "").replace(/\/$/, ""); this.baseURL = apiBaseURL + "/" + resourceBaseURL; // Metrics need to be static, because there can be multiple ResourceClients, but // metrics may exist only _once_. if (!ResourceClient.watchResyncErrorCount) { ResourceClient.watchResyncErrorCount = new prom_client_1.Counter({ name: "kubernetes_listwatch_resync_errors", help: "Amount of resync errors while running listwatches", registers: [registry], labelNames: ["baseURL"], }); } if (!ResourceClient.watchOpenCount) { ResourceClient.watchOpenCount = new prom_client_1.Gauge({ name: "kubernetes_listwatch_open", help: "Amount of currently open listwatches", registers: [registry], labelNames: ["baseURL"], }); } } urlForResource(r) { return this.baseURL + "/" + r.metadata.name; } urlForResourceOrName(r) { return (typeof r === "string") ? this.baseURL + "/" + r : this.urlForResource(r); } list(opts) { return __awaiter(this, void 0, void 0, function* () { const list = yield this.client.get(this.baseURL, opts); return list.items || []; }); } get(name) { return __awaiter(this, void 0, void 0, function* () { return yield this.client.get(this.baseURL + "/" + name); }); } watch(handler, errorHandler, opts = {}) { errorHandler = errorHandler || (() => { }); return this.client.watch(this.baseURL, handler, errorHandler, opts); } listWatch(handler, errorHandler, opts = {}) { let resourceVersion = 0; let running = true; ResourceClient.watchOpenCount.inc({ baseURL: this.baseURL }); debug("starting list-watch on %o", this.resourceBaseURL); const { resyncAfterIterations = 10 } = opts; const resync = () => this.client.get(this.baseURL, opts) .then((list) => { resourceVersion = parseInt(list.metadata.resourceVersion, 10); for (const i of list.items || []) { const event = { type: "ADDED", object: i }; handler(event); } }); const initialized = resync(); const done = initialized.then(() => __awaiter(this, void 0, void 0, function* () { errorHandler = errorHandler || (() => { }); let errorCount = 0; let successCount = 0; debug("initial list for list-watch on %o completed", this.resourceBaseURL); while (running) { try { if (successCount > resyncAfterIterations) { debug(`resyncing after ${resyncAfterIterations} successful WATCH iterations`); yield resync(); } const result = yield this.client.watch(this.baseURL, handler, errorHandler, Object.assign({}, opts, { resourceVersion })); if (result.resyncRequired) { debug(`resyncing listwatch`); yield resync(); continue; } resourceVersion = Math.max(resourceVersion, result.resourceVersion); errorCount = 0; successCount++; } catch (err) { errorCount++; ResourceClient.watchResyncErrorCount.inc({ baseURL: this.baseURL }); if (opts.onError) { opts.onError(err); } if (opts.abortAfterErrorCount && errorCount > opts.abortAfterErrorCount) { ResourceClient.watchOpenCount.dec({ baseURL: this.baseURL }); throw new Error(`more than ${opts.abortAfterErrorCount} consecutive errors when watching ${this.baseURL}`); } debug("resuming watch after back-off of %o ms", 10000); yield sleep(10000); debug("resuming watch with resync after error: %o", err); yield resync(); } } ResourceClient.watchOpenCount.dec({ baseURL: this.baseURL }); })); return { initialized, done, stop() { running = false; }, }; } apply(resource) { return __awaiter(this, void 0, void 0, function* () { const existing = yield this.client.get(this.urlForResource(resource)); if (existing) { return yield this.put(resource); } else { return yield this.post(resource); } }); } put(resource) { return __awaiter(this, void 0, void 0, function* () { return yield this.client.put(this.urlForResource(resource), resource); }); } post(resource) { return __awaiter(this, void 0, void 0, function* () { return yield this.client.post(this.baseURL, resource); }); } patchStrategic(resourceOrName, patch) { return __awaiter(this, void 0, void 0, function* () { return yield this.client.patch(this.urlForResourceOrName(resourceOrName), patch, client_1.patchKindStrategicMergePatch); }); } patchMerge(resourceOrName, patch) { return __awaiter(this, void 0, void 0, function* () { return yield this.client.patch(this.urlForResourceOrName(resourceOrName), patch, client_1.patchKindMergePatch); }); } patchJSON(resourceOrName, patch) { return __awaiter(this, void 0, void 0, function* () { return yield this.client.patch(this.urlForResourceOrName(resourceOrName), patch, client_1.patchKindJSONPatch); }); } delete(resourceOrName, deleteOptions) { return __awaiter(this, void 0, void 0, function* () { let url; if (typeof resourceOrName === "string") { url = this.baseURL + "/" + resourceOrName; } else { url = this.urlForResource(resourceOrName); } return yield this.client.delete(url, deleteOptions); }); } deleteMany(opts) { return __awaiter(this, void 0, void 0, function* () { if (this.supportsCollectionDeletion) { return yield this.client.delete(this.baseURL, opts); } const resources = yield this.list(opts); yield Promise.all(resources.map(r => this.delete(r))); }); } } exports.ResourceClient = ResourceClient; class NamespacedResourceClient extends ResourceClient { constructor(client, apiBaseURL, resourceBaseURL, registry, ns) { super(client, apiBaseURL, resourceBaseURL, registry); this.registry = registry; apiBaseURL = apiBaseURL.replace(/\/$/, ""); resourceBaseURL = resourceBaseURL.replace(/^\//, "").replace(/\/$/, ""); this.ns = ns; if (ns) { this.baseURL = apiBaseURL + "/namespaces/" + ns + "/" + resourceBaseURL; } else { this.baseURL = apiBaseURL + "/" + resourceBaseURL; } } urlForResource(r) { const namespace = r.metadata.namespace || this.ns; if (namespace) { return this.apiBaseURL + "/namespaces/" + namespace + "/" + this.resourceBaseURL + "/" + r.metadata.name; } return this.apiBaseURL + "/" + this.resourceBaseURL + "/" + r.metadata.name; } namespace(ns) { const n = new NamespacedResourceClient(this.client, this.apiBaseURL, this.resourceBaseURL, this.registry, ns); n.supportsCollectionDeletion = this.supportsCollectionDeletion; return n; } allNamespaces() { const n = new NamespacedResourceClient(this.client, this.apiBaseURL, this.resourceBaseURL, this.registry); n.supportsCollectionDeletion = this.supportsCollectionDeletion; return n; } post(resource) { return __awaiter(this, void 0, void 0, function* () { let url = this.baseURL; if (resource.metadata.namespace) { url = this.apiBaseURL + "/namespaces/" + resource.metadata.namespace + "/" + this.resourceBaseURL; } return yield this.client.post(url, resource); }); } } exports.NamespacedResourceClient = NamespacedResourceClient; //# sourceMappingURL=resource.js.map