@mittwald/kubernetes
Version:
Kubernetes client library
216 lines • 9.28 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const request = require("request");
const label_1 = require("./label");
const meta_1 = require("./types/meta");
const debug = require("debug")("kubernetes:client");
exports.patchKindStrategicMergePatch = "application/stategic-merge-patch+json";
exports.patchKindMergePatch = "application/merge-patch+json";
exports.patchKindJSONPatch = "application/json-patch+json";
const joinURL = (left, right) => (left + "/" + right).replace(/([^:])(\/\/)/g, "$1/");
class KubernetesRESTClient {
constructor(config) {
this.config = config;
}
request(url, body, method = "POST", additionalOptions = {}) {
const absoluteURL = joinURL(this.config.apiServerURL, url);
return new Promise((res, rej) => {
let opts = {
method,
url: absoluteURL,
json: true,
};
if (body) {
opts.json = body;
}
opts = this.config.mapRequestOptions(opts);
if (additionalOptions.headers) {
additionalOptions.headers = Object.assign({}, (opts.headers || {}), additionalOptions.headers);
}
opts = Object.assign({}, opts, additionalOptions);
debug(`executing ${method} request on ${opts.url}`);
request(opts, (err, response, responseBody) => {
if (err) {
rej(err);
return;
}
if (meta_1.isStatus(responseBody) && responseBody.status === "Failure") {
rej(new Error(responseBody.message));
return;
}
debug(`${method} request on ${opts.url} succeeded with status ${response.statusCode}: ${responseBody}`);
res(responseBody);
});
});
}
post(url, body) {
return this.request(url, body, "POST");
}
put(url, body) {
return this.request(url, body, "PUT");
}
patch(url, body, patchKind) {
return this.request(url, body, "PATCH", { headers: {
"Content-Type": patchKind,
} });
}
delete(url, deleteOptions, queryParams = {}, body) {
const opts = {};
opts.qs = queryParams;
if (deleteOptions && deleteOptions.labelSelector) {
opts.qs.labelSelector = label_1.selectorToQueryString(deleteOptions.labelSelector);
}
if (deleteOptions && deleteOptions.fieldSelector) {
opts.qs.fieldSelector = label_1.selectorToQueryString(deleteOptions.fieldSelector);
}
return this.request(url, body, "DELETE", opts);
}
watch(url, onUpdate, onError, watchOpts = {}) {
const absoluteURL = joinURL(this.config.apiServerURL, url);
let opts = {
url: absoluteURL,
qs: { watch: "true" },
};
if (watchOpts.labelSelector) {
opts.qs.labelSelector = label_1.selectorToQueryString(watchOpts.labelSelector);
}
if (watchOpts.fieldSelector) {
opts.qs.fieldSelector = label_1.selectorToQueryString(watchOpts.fieldSelector);
}
if (watchOpts.resourceVersion) {
opts.qs.resourceVersion = watchOpts.resourceVersion;
}
opts = this.config.mapRequestOptions(opts);
let lastVersion = watchOpts.resourceVersion || 0;
debug(`executing WATCH request on ${absoluteURL} (starting revision ${lastVersion})`);
return new Promise((res, rej) => {
const req = request(opts, (err, response, bodyString) => {
if (err) {
rej(err);
return;
}
debug(`%o request on %o completed with status %o: %O`, "WATCH", opts.url, response.statusCode, bodyString);
if (response.statusCode && response.statusCode >= 400) {
if (response.statusCode === 410) {
debug(`last known resource has expired -- resync required`);
res({ resourceVersion: lastVersion, resyncRequired: true });
return;
}
rej(new Error("Unexpected status code: " + response.statusCode));
return;
}
if (bodyString.length === 0) {
debug(`WATCH request on ${url} returned empty response`);
res({ resourceVersion: lastVersion });
return;
}
let body;
try {
body = JSON.parse(bodyString);
}
catch (err) {
const bodyLines = bodyString.split("\n");
for (const line of bodyLines) {
if (line === "") {
continue;
}
try {
const parsedLine = JSON.parse(line);
if (parsedLine.type === "ADDED" || parsedLine.type === "MODIFIED" || parsedLine.type === "DELETED") {
const resourceVersion = parseInt(parsedLine.object.metadata.resourceVersion || "0", 10);
if (resourceVersion > lastVersion) {
debug(`watch: emitting missed ${parsedLine.type} event for ${parsedLine.object.metadata.name}`);
lastVersion = resourceVersion;
onUpdate(parsedLine);
}
}
}
catch (err) {
debug(`watch: could not parse JSON line '${line}'`);
rej(err);
return;
}
}
res({ resourceVersion: lastVersion });
return;
}
if (meta_1.isStatus(body) && body.status === "Failure") {
debug(`watch: failed with status %O`, body);
rej(body.message);
return;
}
res({ resourceVersion: lastVersion });
});
let buffer = "";
req.on("data", chunk => {
if (chunk instanceof Buffer) {
chunk = chunk.toString("utf-8");
}
debug("WATCH request on %o received %d bytes of data", opts.url, chunk.length);
buffer += chunk;
try {
const obj = JSON.parse(buffer);
buffer = "";
const resourceVersion = obj.object.metadata.resourceVersion ? parseInt(obj.object.metadata.resourceVersion, 10) : -1;
if (resourceVersion > lastVersion) {
debug(`watch: emitting ${obj.type} event for ${obj.object.metadata.name}`);
lastVersion = resourceVersion;
onUpdate(obj);
}
}
catch (err) {
onError(err);
}
});
});
}
get(url, listOptions = {}) {
const absoluteURL = joinURL(this.config.apiServerURL, url);
const { labelSelector, fieldSelector } = listOptions;
return new Promise((res, rej) => {
let opts = {
url: absoluteURL,
qs: {},
};
if (labelSelector) {
opts.qs.labelSelector = label_1.selectorToQueryString(labelSelector);
}
if (fieldSelector) {
opts.qs.fieldSelector = label_1.selectorToQueryString(fieldSelector);
}
opts = this.config.mapRequestOptions(opts);
debug(`executing GET request on ${opts.url}`);
request(opts, (err, response, body) => {
if (err) {
rej(err);
return;
}
if (response.statusCode === 404) {
debug(`GET request on %o failed with status %o`, opts.url, response.statusCode);
res(undefined);
return;
}
try {
body = JSON.parse(body);
}
catch (err) {
rej(err);
return;
}
if (meta_1.isStatus(body) && body.status === "Failure") {
if (body.code === 404) {
res(undefined);
return;
}
debug(`executing GET request on %o failed. response body: %O`, response.statusCode, body);
rej(new Error(body.message));
return;
}
debug(`GET request on %o succeeded with status %o: %O`, opts.url, response.statusCode, body);
res(body);
});
});
}
}
exports.KubernetesRESTClient = KubernetesRESTClient;
//# sourceMappingURL=client.js.map