teraslice 2.14.2 → 2.14.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/lib/cluster/services/cluster/backends/kubernetes/k8sResource.js +1 -1
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8s.js +52 -56
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sResource.js +1 -1
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8s-v2-spec.js +27 -24
- package/package.json +7 -7
|
@@ -78,7 +78,7 @@ export class K8sResource {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
_mountLocalTeraslice() {
|
|
81
|
-
const devMounts = JSON.parse(process.env.MOUNT_LOCAL_TERASLICE);
|
|
81
|
+
const devMounts = JSON.parse(Buffer.from(process.env.MOUNT_LOCAL_TERASLICE, 'base64').toString('utf-8'));
|
|
82
82
|
this.resource.spec.template.spec.containers[0].volumeMounts.push(...devMounts.volumeMounts);
|
|
83
83
|
this.resource.spec.template.spec.volumes.push(...devMounts.volumes);
|
|
84
84
|
this.resource.spec.template.spec.containers[0].args = [
|
|
@@ -44,7 +44,7 @@ export class K8s {
|
|
|
44
44
|
});
|
|
45
45
|
throw error;
|
|
46
46
|
}
|
|
47
|
-
return namespaces
|
|
47
|
+
return namespaces;
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
50
|
* Returns the first pod matching the provided selector after it has
|
|
@@ -67,10 +67,10 @@ export class K8s {
|
|
|
67
67
|
let now = Date.now();
|
|
68
68
|
const end = now + timeout;
|
|
69
69
|
while (true) {
|
|
70
|
-
const
|
|
71
|
-
.listNamespacedPod(namespace,
|
|
70
|
+
const podListObj = await pRetry(() => this.k8sCoreV1Api
|
|
71
|
+
.listNamespacedPod({ namespace, labelSelector: selector }), getRetryConfig());
|
|
72
72
|
// NOTE: This assumes the first pod returned.
|
|
73
|
-
const pod = get(
|
|
73
|
+
const pod = get(podListObj, 'items[0]');
|
|
74
74
|
if (pod && isTSPod(pod)) {
|
|
75
75
|
if (statusType === 'readiness-probe') {
|
|
76
76
|
if (pod.status.conditions) {
|
|
@@ -110,9 +110,9 @@ export class K8s {
|
|
|
110
110
|
let now = Date.now();
|
|
111
111
|
const end = now + timeout;
|
|
112
112
|
while (true) {
|
|
113
|
-
const
|
|
114
|
-
.listNamespacedPod(namespace,
|
|
115
|
-
const podList = get(
|
|
113
|
+
const podListObj = await pRetry(() => this.k8sCoreV1Api
|
|
114
|
+
.listNamespacedPod({ namespace, labelSelector: selector }), getRetryConfig());
|
|
115
|
+
const podList = get(podListObj, 'items');
|
|
116
116
|
if (podList.length === number)
|
|
117
117
|
return podList;
|
|
118
118
|
const msg = `Waiting: pods matching ${selector} is ${podList.length}/${number}`;
|
|
@@ -125,37 +125,33 @@ export class K8s {
|
|
|
125
125
|
}
|
|
126
126
|
async list(selector, objType, ns) {
|
|
127
127
|
const namespace = ns || this.defaultNamespace;
|
|
128
|
-
let
|
|
129
|
-
const params =
|
|
128
|
+
let resourceListObj;
|
|
129
|
+
const params = {
|
|
130
130
|
namespace,
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
undefined,
|
|
134
|
-
undefined,
|
|
135
|
-
selector
|
|
136
|
-
];
|
|
131
|
+
labelSelector: selector
|
|
132
|
+
};
|
|
137
133
|
try {
|
|
138
134
|
if (objType === 'deployments') {
|
|
139
|
-
|
|
135
|
+
resourceListObj = await pRetry(() => this.k8sAppsV1Api.listNamespacedDeployment(params), getRetryConfig());
|
|
140
136
|
}
|
|
141
137
|
else if (objType === 'jobs') {
|
|
142
|
-
|
|
138
|
+
resourceListObj = await pRetry(() => this.k8sBatchV1Api.listNamespacedJob(params), getRetryConfig());
|
|
143
139
|
}
|
|
144
140
|
else if (objType === 'pods') {
|
|
145
|
-
|
|
141
|
+
resourceListObj = await pRetry(() => this.k8sCoreV1Api.listNamespacedPod(params), getRetryConfig());
|
|
146
142
|
}
|
|
147
143
|
else if (objType === 'replicasets') {
|
|
148
|
-
|
|
144
|
+
resourceListObj = await pRetry(() => this.k8sAppsV1Api.listNamespacedReplicaSet(params), getRetryConfig());
|
|
149
145
|
}
|
|
150
146
|
else if (objType === 'services') {
|
|
151
|
-
|
|
147
|
+
resourceListObj = await pRetry(() => this.k8sCoreV1Api.listNamespacedService(params), getRetryConfig());
|
|
152
148
|
}
|
|
153
149
|
else {
|
|
154
150
|
const error = new Error(`Invalid objType provided to get: ${objType}`);
|
|
155
151
|
this.logger.error(error);
|
|
156
152
|
return Promise.reject(error);
|
|
157
153
|
}
|
|
158
|
-
return convertToTSResourceList(
|
|
154
|
+
return convertToTSResourceList(resourceListObj);
|
|
159
155
|
}
|
|
160
156
|
catch (e) {
|
|
161
157
|
const err = new Error(`Request k8s.list of ${objType} with selector ${selector} failed: ${e}`);
|
|
@@ -180,33 +176,34 @@ export class K8s {
|
|
|
180
176
|
}
|
|
181
177
|
}
|
|
182
178
|
async post(manifest) {
|
|
183
|
-
let
|
|
179
|
+
let resourceObj;
|
|
180
|
+
const namespace = this.defaultNamespace;
|
|
184
181
|
try {
|
|
185
182
|
if (isDeployment(manifest)) {
|
|
186
|
-
|
|
187
|
-
.createNamespacedDeployment(
|
|
183
|
+
resourceObj = await this.k8sAppsV1Api
|
|
184
|
+
.createNamespacedDeployment({ namespace, body: manifest });
|
|
188
185
|
}
|
|
189
186
|
else if (isJob(manifest)) {
|
|
190
|
-
|
|
191
|
-
.createNamespacedJob(
|
|
187
|
+
resourceObj = await this.k8sBatchV1Api
|
|
188
|
+
.createNamespacedJob({ namespace, body: manifest });
|
|
192
189
|
}
|
|
193
190
|
else if (isPod(manifest)) {
|
|
194
|
-
|
|
195
|
-
.createNamespacedPod(
|
|
191
|
+
resourceObj = await this.k8sCoreV1Api
|
|
192
|
+
.createNamespacedPod({ namespace, body: manifest });
|
|
196
193
|
}
|
|
197
194
|
else if (isReplicaSet(manifest)) {
|
|
198
|
-
|
|
199
|
-
.createNamespacedReplicaSet(
|
|
195
|
+
resourceObj = await this.k8sAppsV1Api
|
|
196
|
+
.createNamespacedReplicaSet({ namespace, body: manifest });
|
|
200
197
|
}
|
|
201
198
|
else if (isService(manifest)) {
|
|
202
|
-
|
|
203
|
-
.createNamespacedService(
|
|
199
|
+
resourceObj = await this.k8sCoreV1Api
|
|
200
|
+
.createNamespacedService({ namespace, body: manifest });
|
|
204
201
|
}
|
|
205
202
|
else {
|
|
206
203
|
const error = new Error('Invalid manifest type');
|
|
207
204
|
return Promise.reject(error);
|
|
208
205
|
}
|
|
209
|
-
return convertToTSResource(
|
|
206
|
+
return convertToTSResource(resourceObj);
|
|
210
207
|
}
|
|
211
208
|
catch (e) {
|
|
212
209
|
const err = new Error(`Request k8s.post of ${manifest.kind} with body ${JSON.stringify(manifest)} failed: ${e}`);
|
|
@@ -217,7 +214,7 @@ export class K8s {
|
|
|
217
214
|
* Patches specified k8s deployment with the provided record
|
|
218
215
|
* @param {String} record record, like 'app=teraslice'
|
|
219
216
|
* @param {String} name Name of the deployment to patch
|
|
220
|
-
* @return {Object}
|
|
217
|
+
* @return {Object} k8s V1Deployment object.
|
|
221
218
|
*/
|
|
222
219
|
// TODO: I renamed this from patchDeployment to just patch because this is
|
|
223
220
|
// the low level k8s api method, I expect to eventually change the interface
|
|
@@ -225,10 +222,13 @@ export class K8s {
|
|
|
225
222
|
async patch(record, name) {
|
|
226
223
|
let responseObj;
|
|
227
224
|
try {
|
|
228
|
-
const options =
|
|
229
|
-
responseObj = await pRetry(() => this.k8sAppsV1Api
|
|
230
|
-
|
|
231
|
-
|
|
225
|
+
const options = k8s.setHeaderOptions('Content-Type', k8s.PatchStrategy.JsonPatch);
|
|
226
|
+
responseObj = await pRetry(() => this.k8sAppsV1Api.patchNamespacedDeployment({
|
|
227
|
+
name,
|
|
228
|
+
namespace: this.defaultNamespace,
|
|
229
|
+
body: record
|
|
230
|
+
}, options), getRetryConfig());
|
|
231
|
+
return responseObj;
|
|
232
232
|
}
|
|
233
233
|
catch (e) {
|
|
234
234
|
const err = new Error(`Request k8s.patch with name: ${name} failed with: ${e}`);
|
|
@@ -252,27 +252,23 @@ export class K8s {
|
|
|
252
252
|
if (force) {
|
|
253
253
|
deleteOptions.gracePeriodSeconds = 1;
|
|
254
254
|
}
|
|
255
|
-
const params =
|
|
255
|
+
const params = {
|
|
256
256
|
name,
|
|
257
|
-
this.defaultNamespace,
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
undefined,
|
|
261
|
-
undefined,
|
|
262
|
-
undefined,
|
|
263
|
-
deleteOptions
|
|
264
|
-
];
|
|
257
|
+
namespace: this.defaultNamespace,
|
|
258
|
+
body: deleteOptions
|
|
259
|
+
};
|
|
265
260
|
const deleteWithErrorHandling = async (deleteFn) => {
|
|
266
261
|
try {
|
|
267
262
|
const res = await deleteFn();
|
|
268
263
|
return res;
|
|
269
264
|
}
|
|
270
265
|
catch (e) {
|
|
271
|
-
if (e.
|
|
266
|
+
if (e.body) {
|
|
267
|
+
const bodyObj = JSON.parse(e.body);
|
|
272
268
|
// 404 should be an acceptable response to a delete request, not an error
|
|
273
|
-
if (
|
|
269
|
+
if (bodyObj.code === 404) {
|
|
274
270
|
this.logger.info(`No ${objType} with name ${name} found while attempting to delete.`);
|
|
275
|
-
return
|
|
271
|
+
return bodyObj;
|
|
276
272
|
}
|
|
277
273
|
}
|
|
278
274
|
throw e;
|
|
@@ -281,28 +277,28 @@ export class K8s {
|
|
|
281
277
|
try {
|
|
282
278
|
if (objType === 'services') {
|
|
283
279
|
responseObj = await pRetry(() => deleteWithErrorHandling(() => this.k8sCoreV1Api
|
|
284
|
-
.deleteNamespacedService(
|
|
280
|
+
.deleteNamespacedService(params)), getRetryConfig());
|
|
285
281
|
}
|
|
286
282
|
else if (objType === 'deployments') {
|
|
287
283
|
responseObj = await pRetry(() => deleteWithErrorHandling(() => this.k8sAppsV1Api
|
|
288
|
-
.deleteNamespacedDeployment(
|
|
284
|
+
.deleteNamespacedDeployment(params)), getRetryConfig());
|
|
289
285
|
}
|
|
290
286
|
else if (objType === 'jobs') {
|
|
291
287
|
responseObj = await pRetry(() => deleteWithErrorHandling(() => this.k8sBatchV1Api
|
|
292
|
-
.deleteNamespacedJob(
|
|
288
|
+
.deleteNamespacedJob(params)), getRetryConfig());
|
|
293
289
|
}
|
|
294
290
|
else if (objType === 'pods') {
|
|
295
291
|
responseObj = await pRetry(() => deleteWithErrorHandling(() => this.k8sCoreV1Api
|
|
296
|
-
.deleteNamespacedPod(
|
|
292
|
+
.deleteNamespacedPod(params)), getRetryConfig());
|
|
297
293
|
}
|
|
298
294
|
else if (objType === 'replicasets') {
|
|
299
295
|
responseObj = await pRetry(() => deleteWithErrorHandling(() => this.k8sAppsV1Api
|
|
300
|
-
.deleteNamespacedReplicaSet(
|
|
296
|
+
.deleteNamespacedReplicaSet(params)), getRetryConfig());
|
|
301
297
|
}
|
|
302
298
|
else {
|
|
303
299
|
throw new Error(`Invalid objType: ${objType}`);
|
|
304
300
|
}
|
|
305
|
-
return responseObj
|
|
301
|
+
return responseObj;
|
|
306
302
|
}
|
|
307
303
|
catch (e) {
|
|
308
304
|
const err = new Error(`Request k8s.delete with name: ${name} failed with: ${e}`);
|
|
@@ -26,7 +26,7 @@ export class K8sResource {
|
|
|
26
26
|
_setEnvVariables() {
|
|
27
27
|
}
|
|
28
28
|
_mountLocalTeraslice(resource) {
|
|
29
|
-
const devMounts = JSON.parse(process.env.MOUNT_LOCAL_TERASLICE);
|
|
29
|
+
const devMounts = JSON.parse(Buffer.from(process.env.MOUNT_LOCAL_TERASLICE, 'base64').toString('utf-8'));
|
|
30
30
|
resource.spec.template.spec.containers[0].volumeMounts.push(...devMounts.volumeMounts);
|
|
31
31
|
resource.spec.template.spec.volumes.push(...devMounts.volumes);
|
|
32
32
|
if (resource.spec.template.spec.containers[0]) {
|
|
@@ -110,6 +110,12 @@ describe('k8s', () => {
|
|
|
110
110
|
kind: 'Status',
|
|
111
111
|
status: 'Success'
|
|
112
112
|
};
|
|
113
|
+
const apiException = {
|
|
114
|
+
'HTTP-Code': 400,
|
|
115
|
+
Message: 'Unknown API Status Code!',
|
|
116
|
+
Body: { statusCode: 400 },
|
|
117
|
+
Headers: { 'content-type': 'application/json' }
|
|
118
|
+
};
|
|
113
119
|
beforeEach(async () => {
|
|
114
120
|
nock(_url)
|
|
115
121
|
.get('/api/v1/namespaces')
|
|
@@ -280,16 +286,16 @@ describe('k8s', () => {
|
|
|
280
286
|
const response = await k8s.patch({ name: 'testName' }, 'test1');
|
|
281
287
|
expect(response).toEqual({});
|
|
282
288
|
});
|
|
283
|
-
it('will throw on a
|
|
289
|
+
it('will throw on a response code >= 400', async () => {
|
|
284
290
|
nock(_url)
|
|
285
291
|
.patch('/apis/apps/v1/namespaces/default/deployments/bad-response')
|
|
286
|
-
.
|
|
292
|
+
.reply(400, apiException)
|
|
287
293
|
.patch('/apis/apps/v1/namespaces/default/deployments/bad-response')
|
|
288
|
-
.
|
|
294
|
+
.reply(400, apiException)
|
|
289
295
|
.patch('/apis/apps/v1/namespaces/default/deployments/bad-response')
|
|
290
|
-
.
|
|
296
|
+
.reply(400, apiException);
|
|
291
297
|
await expect(k8s.patch({ name: 'bad-response' }, 'bad-response'))
|
|
292
|
-
.rejects.toThrow('
|
|
298
|
+
.rejects.toThrow('HTTP-Code: 400');
|
|
293
299
|
});
|
|
294
300
|
});
|
|
295
301
|
describe('->delete', () => {
|
|
@@ -336,39 +342,36 @@ describe('k8s', () => {
|
|
|
336
342
|
const response = await k8s.delete('test1', 'replicasets');
|
|
337
343
|
expect(response).toEqual({});
|
|
338
344
|
});
|
|
339
|
-
it('will throw on a
|
|
345
|
+
it('will throw on a response code >= 400, excluding 404', async () => {
|
|
340
346
|
nock(_url)
|
|
341
347
|
.delete('/api/v1/namespaces/default/pods/bad-response')
|
|
342
|
-
.
|
|
348
|
+
.reply(400, apiException)
|
|
343
349
|
.delete('/api/v1/namespaces/default/pods/bad-response')
|
|
344
|
-
.
|
|
350
|
+
.reply(400, apiException)
|
|
345
351
|
.delete('/api/v1/namespaces/default/pods/bad-response')
|
|
346
|
-
.
|
|
352
|
+
.reply(400, apiException);
|
|
347
353
|
await expect(k8s.delete('bad-response', 'pods'))
|
|
348
|
-
.rejects.toThrow('
|
|
354
|
+
.rejects.toThrow('HTTP-Code: 400');
|
|
349
355
|
});
|
|
350
356
|
it('will succeed on a 404 response code', async () => {
|
|
351
357
|
const notFoundResponse = {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
code: 404
|
|
361
|
-
},
|
|
362
|
-
statusCode: 404
|
|
358
|
+
kind: 'Status',
|
|
359
|
+
apiVersion: 'v1',
|
|
360
|
+
metadata: {},
|
|
361
|
+
status: 'Failure',
|
|
362
|
+
message: 'pods "non-existent" not found',
|
|
363
|
+
reason: 'NotFound',
|
|
364
|
+
details: { name: 'non-existent', kind: 'pods' },
|
|
365
|
+
code: 404
|
|
363
366
|
};
|
|
364
367
|
nock(_url)
|
|
365
368
|
.delete('/api/v1/namespaces/default/pods/non-existent')
|
|
366
|
-
.
|
|
369
|
+
.reply(404, notFoundResponse);
|
|
367
370
|
const response = await k8s.delete('non-existent', 'pods');
|
|
368
|
-
expect(response).toEqual(notFoundResponse
|
|
371
|
+
expect(response).toEqual(notFoundResponse);
|
|
369
372
|
});
|
|
370
373
|
});
|
|
371
|
-
describe('->
|
|
374
|
+
describe('->_deleteObjByExId', () => {
|
|
372
375
|
it('can delete a single object', async () => {
|
|
373
376
|
nock(_url)
|
|
374
377
|
.get('/apis/batch/v1/namespaces/default/jobs')
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "teraslice",
|
|
3
3
|
"displayName": "Teraslice",
|
|
4
|
-
"version": "2.14.
|
|
4
|
+
"version": "2.14.4",
|
|
5
5
|
"description": "Distributed computing platform for processing JSON data",
|
|
6
6
|
"homepage": "https://github.com/terascope/teraslice#readme",
|
|
7
7
|
"bugs": {
|
|
@@ -38,12 +38,12 @@
|
|
|
38
38
|
"ms": "~2.1.3"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@kubernetes/client-node": "~
|
|
42
|
-
"@terascope/elasticsearch-api": "~4.
|
|
43
|
-
"@terascope/job-components": "~1.
|
|
44
|
-
"@terascope/teraslice-messaging": "~1.
|
|
41
|
+
"@kubernetes/client-node": "~1.1.1",
|
|
42
|
+
"@terascope/elasticsearch-api": "~4.9.0",
|
|
43
|
+
"@terascope/job-components": "~1.10.0",
|
|
44
|
+
"@terascope/teraslice-messaging": "~1.11.0",
|
|
45
45
|
"@terascope/types": "~1.4.1",
|
|
46
|
-
"@terascope/utils": "~1.
|
|
46
|
+
"@terascope/utils": "~1.8.0",
|
|
47
47
|
"async-mutex": "~0.5.0",
|
|
48
48
|
"barbe": "~3.0.17",
|
|
49
49
|
"body-parser": "~2.2.0",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"semver": "~7.7.1",
|
|
63
63
|
"socket.io": "~1.7.4",
|
|
64
64
|
"socket.io-client": "~1.7.4",
|
|
65
|
-
"terafoundation": "~1.
|
|
65
|
+
"terafoundation": "~1.12.1",
|
|
66
66
|
"uuid": "~11.1.0"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|