@mittwald/kubernetes
Version:
Kubernetes client library
277 lines • 11.7 kB
JavaScript
"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