1 | "use strict";
|
2 | var __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 | };
|
11 | var __importDefault = (this && this.__importDefault) || function (mod) {
|
12 | return (mod && mod.__esModule) ? mod : { "default": mod };
|
13 | };
|
14 | Object.defineProperty(exports, "__esModule", { value: true });
|
15 | const request_1 = __importDefault(require("request"));
|
16 | const label_1 = require("./label");
|
17 | const meta_1 = require("./types/meta");
|
18 | const debug = require("debug")("kubernetes:client");
|
19 | exports.patchKindStrategicMergePatch = "application/stategic-merge-patch+json";
|
20 | exports.patchKindMergePatch = "application/merge-patch+json";
|
21 | exports.patchKindJSONPatch = "application/json-patch+json";
|
22 | const joinURL = (left, right) => (left + "/" + right).replace(/([^:])(\/\/)/g, "$1/");
|
23 | class KubernetesRESTClient {
|
24 | constructor(config) {
|
25 | this.config = config;
|
26 | }
|
27 | request(url, body, method = "POST", additionalOptions = {}) {
|
28 | const absoluteURL = joinURL(this.config.apiServerURL, url);
|
29 | return new Promise((res, rej) => {
|
30 | let opts = {
|
31 | method,
|
32 | url: absoluteURL,
|
33 | json: true,
|
34 | };
|
35 | if (body) {
|
36 | opts.json = body;
|
37 | }
|
38 | opts = this.config.mapRequestOptions(opts);
|
39 | if (additionalOptions.headers) {
|
40 | additionalOptions.headers = Object.assign(Object.assign({}, (opts.headers || {})), additionalOptions.headers);
|
41 | }
|
42 | opts = Object.assign(Object.assign({}, opts), additionalOptions);
|
43 | debug(`executing ${method} request on ${opts.url}`);
|
44 | request_1.default(opts, (err, response, responseBody) => {
|
45 | if (err) {
|
46 | rej(err);
|
47 | return;
|
48 | }
|
49 | if (meta_1.isStatus(responseBody) && responseBody.status === "Failure") {
|
50 | rej(new Error(responseBody.message));
|
51 | return;
|
52 | }
|
53 | debug(`${method} request on ${opts.url} succeeded with status ${response.statusCode}: ${responseBody}`);
|
54 | res(responseBody);
|
55 | });
|
56 | });
|
57 | }
|
58 | post(url, body) {
|
59 | return this.request(url, body, "POST");
|
60 | }
|
61 | put(url, body) {
|
62 | return this.request(url, body, "PUT");
|
63 | }
|
64 | patch(url, body, patchKind) {
|
65 | return this.request(url, body, "PATCH", { headers: {
|
66 | "Content-Type": patchKind,
|
67 | } });
|
68 | }
|
69 | delete(url, deleteOptions, queryParams = {}, body) {
|
70 | const opts = {};
|
71 | opts.qs = queryParams;
|
72 | if (deleteOptions && deleteOptions.labelSelector) {
|
73 | opts.qs.labelSelector = label_1.selectorToQueryString(deleteOptions.labelSelector);
|
74 | }
|
75 | if (deleteOptions && deleteOptions.fieldSelector) {
|
76 | opts.qs.fieldSelector = label_1.selectorToQueryString(deleteOptions.fieldSelector);
|
77 | }
|
78 | return this.request(url, body, "DELETE", opts);
|
79 | }
|
80 | watch(url, onUpdate, onError, watchOpts = {}) {
|
81 | const absoluteURL = joinURL(this.config.apiServerURL, url);
|
82 | let opts = {
|
83 | url: absoluteURL,
|
84 | qs: { watch: "true" },
|
85 | };
|
86 | if (watchOpts.labelSelector) {
|
87 | opts.qs.labelSelector = label_1.selectorToQueryString(watchOpts.labelSelector);
|
88 | }
|
89 | if (watchOpts.fieldSelector) {
|
90 | opts.qs.fieldSelector = label_1.selectorToQueryString(watchOpts.fieldSelector);
|
91 | }
|
92 | if (watchOpts.resourceVersion) {
|
93 | opts.qs.resourceVersion = watchOpts.resourceVersion;
|
94 | }
|
95 | opts = this.config.mapRequestOptions(opts);
|
96 | let lastVersion = watchOpts.resourceVersion || 0;
|
97 | debug(`executing WATCH request on ${absoluteURL} (starting revision ${lastVersion})`);
|
98 | return new Promise((res, rej) => {
|
99 | const req = request_1.default(opts, (err, response, bodyString) => __awaiter(this, void 0, void 0, function* () {
|
100 | if (err) {
|
101 | rej(err);
|
102 | return;
|
103 | }
|
104 | debug(`%o request on %o completed with status %o: %O`, "WATCH", opts.url, response.statusCode, bodyString);
|
105 | if (response.statusCode && response.statusCode >= 400) {
|
106 | if (response.statusCode === 410) {
|
107 | debug(`last known resource has expired -- resync required`);
|
108 | res({ resourceVersion: lastVersion, resyncRequired: true });
|
109 | return;
|
110 | }
|
111 | rej(new Error("Unexpected status code: " + response.statusCode));
|
112 | return;
|
113 | }
|
114 | if (bodyString.length === 0) {
|
115 | debug(`WATCH request on ${url} returned empty response`);
|
116 | res({ resourceVersion: lastVersion });
|
117 | return;
|
118 | }
|
119 | let body;
|
120 | try {
|
121 | body = JSON.parse(bodyString);
|
122 | }
|
123 | catch (err) {
|
124 | const bodyLines = bodyString.split("\n");
|
125 | for (const line of bodyLines) {
|
126 | if (line === "") {
|
127 | continue;
|
128 | }
|
129 | try {
|
130 | const parsedLine = JSON.parse(line);
|
131 | if (parsedLine.type === "ADDED" || parsedLine.type === "MODIFIED" || parsedLine.type === "DELETED") {
|
132 | const resourceVersion = parseInt(parsedLine.object.metadata.resourceVersion || "0", 10);
|
133 | if (resourceVersion > lastVersion) {
|
134 | debug(`watch: emitting missed ${parsedLine.type} event for ${parsedLine.object.metadata.name}`);
|
135 | lastVersion = resourceVersion;
|
136 | yield onUpdate(parsedLine);
|
137 | }
|
138 | }
|
139 | }
|
140 | catch (err) {
|
141 | debug(`watch: could not parse JSON line '${line}'`);
|
142 | rej(err);
|
143 | return;
|
144 | }
|
145 | }
|
146 | res({ resourceVersion: lastVersion });
|
147 | return;
|
148 | }
|
149 | if (meta_1.isStatus(body) && body.status === "Failure") {
|
150 | debug(`watch: failed with status %O`, body);
|
151 | rej(body.message);
|
152 | return;
|
153 | }
|
154 | res({ resourceVersion: lastVersion });
|
155 | }));
|
156 | let buffer = "";
|
157 | req.on("data", (chunk) => __awaiter(this, void 0, void 0, function* () {
|
158 | if (chunk instanceof Buffer) {
|
159 | chunk = chunk.toString("utf-8");
|
160 | }
|
161 | debug("WATCH request on %o received %d bytes of data", opts.url, chunk.length);
|
162 | buffer += chunk;
|
163 | try {
|
164 | const obj = JSON.parse(buffer);
|
165 | buffer = "";
|
166 | const resourceVersion = obj.object.metadata.resourceVersion ? parseInt(obj.object.metadata.resourceVersion, 10) : -1;
|
167 | if (resourceVersion > lastVersion) {
|
168 | debug(`watch: emitting ${obj.type} event for ${obj.object.metadata.name}`);
|
169 | lastVersion = resourceVersion;
|
170 | yield onUpdate(obj);
|
171 | }
|
172 | }
|
173 | catch (err) {
|
174 | onError(err);
|
175 | }
|
176 | }));
|
177 | });
|
178 | }
|
179 | get(url, listOptions = {}) {
|
180 | const absoluteURL = joinURL(this.config.apiServerURL, url);
|
181 | const { labelSelector, fieldSelector } = listOptions;
|
182 | return new Promise((res, rej) => {
|
183 | let opts = {
|
184 | url: absoluteURL,
|
185 | qs: {},
|
186 | };
|
187 | if (labelSelector) {
|
188 | opts.qs.labelSelector = label_1.selectorToQueryString(labelSelector);
|
189 | }
|
190 | if (fieldSelector) {
|
191 | opts.qs.fieldSelector = label_1.selectorToQueryString(fieldSelector);
|
192 | }
|
193 | opts = this.config.mapRequestOptions(opts);
|
194 | debug(`executing GET request on ${opts.url}`);
|
195 | request_1.default(opts, (err, response, body) => {
|
196 | if (err) {
|
197 | rej(err);
|
198 | return;
|
199 | }
|
200 | if (response.statusCode === 404) {
|
201 | debug(`GET request on %o failed with status %o`, opts.url, response.statusCode);
|
202 | res(undefined);
|
203 | return;
|
204 | }
|
205 | try {
|
206 | body = JSON.parse(body);
|
207 | }
|
208 | catch (err) {
|
209 | rej(err);
|
210 | return;
|
211 | }
|
212 | if (meta_1.isStatus(body) && body.status === "Failure") {
|
213 | if (body.code === 404) {
|
214 | res(undefined);
|
215 | return;
|
216 | }
|
217 | debug(`executing GET request on %o failed. response body: %O`, response.statusCode, body);
|
218 | rej(new Error(body.message));
|
219 | return;
|
220 | }
|
221 | debug(`GET request on %o succeeded with status %o: %O`, opts.url, response.statusCode, body);
|
222 | res(body);
|
223 | });
|
224 | });
|
225 | }
|
226 | }
|
227 | exports.KubernetesRESTClient = KubernetesRESTClient;
|
228 |
|
\ | No newline at end of file |