UNPKG

10.9 kBJavaScriptView Raw
1"use strict";
2var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3 if (k2 === undefined) k2 = k;
4 Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5}) : (function(o, m, k, k2) {
6 if (k2 === undefined) k2 = k;
7 o[k2] = m[k];
8}));
9var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10 Object.defineProperty(o, "default", { enumerable: true, value: v });
11}) : function(o, v) {
12 o["default"] = v;
13});
14var __importStar = (this && this.__importStar) || function (mod) {
15 if (mod && mod.__esModule) return mod;
16 var result = {};
17 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18 __setModuleDefault(result, mod);
19 return result;
20};
21var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
22 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
23 return new (P || (P = Promise))(function (resolve, reject) {
24 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
25 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
26 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
27 step((generator = generator.apply(thisArg, _arguments || [])).next());
28 });
29};
30var __importDefault = (this && this.__importDefault) || function (mod) {
31 return (mod && mod.__esModule) ? mod : { "default": mod };
32};
33Object.defineProperty(exports, "__esModule", { value: true });
34exports.KubernetesRESTClient = exports.patchKindJSONPatch = exports.patchKindMergePatch = exports.patchKindStrategicMergePatch = void 0;
35const label_1 = require("./label");
36const meta_1 = require("./types/meta");
37const security_1 = require("./security");
38const axios_1 = __importDefault(require("axios"));
39const http2 = __importStar(require("http2"));
40const qs_1 = __importDefault(require("qs"));
41const debug = require("debug")("kubernetes:client");
42exports.patchKindStrategicMergePatch = "application/stategic-merge-patch+json";
43exports.patchKindMergePatch = "application/merge-patch+json";
44exports.patchKindJSONPatch = "application/json-patch+json";
45const joinURL = (left, right) => (left + "/" + right).replace(/([^:])(\/\/)/g, "$1/");
46class KubernetesRESTClient {
47 constructor(config) {
48 this.config = config;
49 }
50 request(url, body, method = "POST", additionalOptions = {}) {
51 return __awaiter(this, void 0, void 0, function* () {
52 const absoluteURL = joinURL(this.config.apiServerURL, url);
53 let opts = {
54 method,
55 url: absoluteURL,
56 responseType: "json",
57 validateStatus: () => true,
58 };
59 if (body) {
60 opts.data = body;
61 }
62 opts = this.config.mapAxiosOptions(opts);
63 if (additionalOptions.headers) {
64 additionalOptions.headers = Object.assign(Object.assign({}, (opts.headers || {})), additionalOptions.headers);
65 }
66 opts = Object.assign(Object.assign({}, opts), additionalOptions);
67 debug(`executing ${method} request on ${opts.url}`);
68 const response = yield (0, axios_1.default)(opts);
69 const responseBody = response.data;
70 if (typeof responseBody === "object" && (0, meta_1.isStatus)(responseBody) && responseBody.status === "Failure") {
71 throw new Error(responseBody.message);
72 }
73 debug(`${method} request on ${opts.url} succeeded with status ${response.status}: ${(0, security_1.redactResponseBodyForLogging)(responseBody)}`);
74 return responseBody;
75 });
76 }
77 post(url, body) {
78 return this.request(url, body, "POST");
79 }
80 put(url, body) {
81 return this.request(url, body, "PUT");
82 }
83 patch(url, body, patchKind) {
84 return this.request(url, body, "PATCH", {
85 headers: {
86 "Content-Type": patchKind,
87 }
88 });
89 }
90 delete(url, deleteOptions, queryParams = {}, body) {
91 const opts = {};
92 opts.params = queryParams;
93 if (deleteOptions && deleteOptions.labelSelector) {
94 opts.params.labelSelector = (0, label_1.selectorToQueryString)(deleteOptions.labelSelector);
95 }
96 if (deleteOptions && deleteOptions.fieldSelector) {
97 opts.params.fieldSelector = (0, label_1.selectorToQueryString)(deleteOptions.fieldSelector);
98 }
99 return this.request(url, body, "DELETE", opts);
100 }
101 watch(url, onUpdate, onError, watchOpts = {}) {
102 const absoluteURL = joinURL(this.config.apiServerURL, url);
103 const params = { watch: "true" };
104 const { pingIntervalSeconds = 15 } = watchOpts;
105 if (watchOpts.labelSelector) {
106 params.labelSelector = (0, label_1.selectorToQueryString)(watchOpts.labelSelector);
107 }
108 if (watchOpts.fieldSelector) {
109 params.fieldSelector = (0, label_1.selectorToQueryString)(watchOpts.fieldSelector);
110 }
111 if (watchOpts.resourceVersion) {
112 params.resourceVersion = `${watchOpts.resourceVersion}`;
113 }
114 let clientPingInterval;
115 const clientOpts = this.config.mapNativeOptions({});
116 const client = http2.connect(this.config.apiServerURL, clientOpts, (session, socket) => {
117 clientPingInterval = setInterval(() => {
118 session.ping((err, duration) => {
119 if (err) {
120 debug("error on HTTP/2 client ping: %O", err);
121 session.destroy(err);
122 }
123 });
124 }, pingIntervalSeconds * 1000);
125 });
126 let lastVersion = watchOpts.resourceVersion || 0;
127 debug(`executing WATCH request on ${absoluteURL} (starting revision ${lastVersion})`);
128 return new Promise((res, rej) => {
129 const requestHeaders = Object.assign({ [http2.constants.HTTP2_HEADER_METHOD]: "GET", [http2.constants.HTTP2_HEADER_PATH]: url + "?" + qs_1.default.stringify(params), [http2.constants.HTTP2_HEADER_ACCEPT]: "application/json" }, this.config.mapHeaders({}));
130 const request = client.request(requestHeaders);
131 let body = "";
132 let buffer = "";
133 request.on("error", (err) => {
134 debug(`watch: error: %O`, err);
135 rej(err);
136 });
137 request.on("response", (headers, flags) => {
138 const status = headers[":status"];
139 debug(`%o request on %o completed with status %o`, "WATCH", absoluteURL, status);
140 if (status && status >= 400) {
141 if (status === 410) {
142 debug(`last known resource has expired -- resync required`);
143 res({ resourceVersion: lastVersion, resyncRequired: true });
144 return;
145 }
146 rej(new Error("Unexpected status code: " + status));
147 return;
148 }
149 });
150 request.on("end", () => {
151 if (clientPingInterval) {
152 clearInterval(clientPingInterval);
153 }
154 try {
155 const parsedBody = JSON.parse(body);
156 if ((0, meta_1.isStatus)(parsedBody) && parsedBody.status === "Failure") {
157 debug(`watch: failed with status %O`, parsedBody);
158 rej(parsedBody.message);
159 return;
160 }
161 }
162 catch (_) {
163 // this is fine; the request body is not guaranteed to be a single JSON document.
164 }
165 res({ resourceVersion: lastVersion });
166 });
167 request.on("data", (chunk) => __awaiter(this, void 0, void 0, function* () {
168 if (chunk instanceof Buffer) {
169 chunk = chunk.toString("utf-8");
170 }
171 debug("WATCH request on %o received %d bytes of data", absoluteURL, chunk.length);
172 buffer += chunk;
173 body += chunk;
174 // Line is not yet complete; wait for next chunk.
175 if (!buffer.endsWith("\n")) {
176 return;
177 }
178 try {
179 const obj = JSON.parse(buffer);
180 buffer = "";
181 const resourceVersion = obj.object.metadata.resourceVersion ? parseInt(obj.object.metadata.resourceVersion, 10) : -1;
182 if (resourceVersion > lastVersion) {
183 debug(`watch: emitting ${obj.type} event for ${obj.object.metadata.name}`);
184 lastVersion = resourceVersion;
185 yield onUpdate(obj);
186 }
187 }
188 catch (err) {
189 onError(err);
190 }
191 }));
192 });
193 }
194 get(url, listOptions = {}) {
195 return __awaiter(this, void 0, void 0, function* () {
196 const absoluteURL = joinURL(this.config.apiServerURL, url);
197 const { labelSelector, fieldSelector } = listOptions;
198 let opts = {
199 url: absoluteURL,
200 params: {},
201 validateStatus: () => true,
202 };
203 if (labelSelector) {
204 opts.params.labelSelector = (0, label_1.selectorToQueryString)(labelSelector);
205 }
206 if (fieldSelector) {
207 opts.params.fieldSelector = (0, label_1.selectorToQueryString)(fieldSelector);
208 }
209 opts = this.config.mapAxiosOptions(opts);
210 debug(`executing GET request on ${opts.url}`);
211 const response = yield (0, axios_1.default)(opts);
212 if (response.status === 404) {
213 debug(`GET request on %o failed with status %o`, opts.url, response.status);
214 return undefined;
215 }
216 if ((0, meta_1.isStatus)(response.data) && response.data.status === "Failure") {
217 if (response.data.code === 404) {
218 return undefined;
219 }
220 debug(`executing GET request on %o failed. response body: %O`, response.status, response.data);
221 throw (new Error(response.data.message));
222 }
223 debug(`GET request on %o succeeded with status %o: %O`, opts.url, response.status, response.data);
224 return response.data;
225 });
226 }
227}
228exports.KubernetesRESTClient = KubernetesRESTClient;
229//# sourceMappingURL=client.js.map
\No newline at end of file