teraslice 3.2.1 → 3.3.1
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/LICENSE +202 -0
- package/package.json +26 -30
- package/dist/src/interfaces.js +0 -12
- package/dist/src/lib/cluster/cluster_master.js +0 -246
- package/dist/src/lib/cluster/node_master.js +0 -355
- package/dist/src/lib/cluster/services/api.js +0 -663
- package/dist/src/lib/cluster/services/assets.js +0 -224
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/index.js +0 -192
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/interfaces.js +0 -2
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8s.js +0 -419
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sDeploymentResource.js +0 -60
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sJobResource.js +0 -55
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sResource.js +0 -357
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sServiceResource.js +0 -37
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sState.js +0 -60
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/utils.js +0 -170
- package/dist/src/lib/cluster/services/cluster/backends/native/dispatch.js +0 -13
- package/dist/src/lib/cluster/services/cluster/backends/native/index.js +0 -526
- package/dist/src/lib/cluster/services/cluster/backends/native/messaging.js +0 -548
- package/dist/src/lib/cluster/services/cluster/backends/state-utils.js +0 -26
- package/dist/src/lib/cluster/services/cluster/index.js +0 -13
- package/dist/src/lib/cluster/services/execution.js +0 -435
- package/dist/src/lib/cluster/services/index.js +0 -6
- package/dist/src/lib/cluster/services/interfaces.js +0 -2
- package/dist/src/lib/cluster/services/jobs.js +0 -458
- package/dist/src/lib/config/default-sysconfig.js +0 -25
- package/dist/src/lib/config/index.js +0 -20
- package/dist/src/lib/config/schemas/system.js +0 -360
- package/dist/src/lib/storage/analytics.js +0 -86
- package/dist/src/lib/storage/assets.js +0 -401
- package/dist/src/lib/storage/backends/elasticsearch_store.js +0 -496
- package/dist/src/lib/storage/backends/mappings/analytics.js +0 -20
- package/dist/src/lib/storage/backends/mappings/asset.js +0 -32
- package/dist/src/lib/storage/backends/mappings/ex.js +0 -53
- package/dist/src/lib/storage/backends/mappings/job.js +0 -42
- package/dist/src/lib/storage/backends/mappings/state.js +0 -16
- package/dist/src/lib/storage/backends/s3_store.js +0 -237
- package/dist/src/lib/storage/execution.js +0 -302
- package/dist/src/lib/storage/index.js +0 -7
- package/dist/src/lib/storage/jobs.js +0 -81
- package/dist/src/lib/storage/state.js +0 -254
- package/dist/src/lib/utils/api_utils.js +0 -128
- package/dist/src/lib/utils/asset_utils.js +0 -94
- package/dist/src/lib/utils/date_utils.js +0 -52
- package/dist/src/lib/utils/encoding_utils.js +0 -27
- package/dist/src/lib/utils/events.js +0 -4
- package/dist/src/lib/utils/file_utils.js +0 -124
- package/dist/src/lib/utils/id_utils.js +0 -15
- package/dist/src/lib/utils/port_utils.js +0 -32
- package/dist/src/lib/workers/assets/index.js +0 -3
- package/dist/src/lib/workers/assets/loader-executable.js +0 -40
- package/dist/src/lib/workers/assets/loader.js +0 -73
- package/dist/src/lib/workers/assets/spawn.js +0 -55
- package/dist/src/lib/workers/context/execution-context.js +0 -12
- package/dist/src/lib/workers/context/terafoundation-context.js +0 -8
- package/dist/src/lib/workers/execution-controller/execution-analytics.js +0 -188
- package/dist/src/lib/workers/execution-controller/index.js +0 -1024
- package/dist/src/lib/workers/execution-controller/recovery.js +0 -151
- package/dist/src/lib/workers/execution-controller/scheduler.js +0 -390
- package/dist/src/lib/workers/execution-controller/slice-analytics.js +0 -96
- package/dist/src/lib/workers/helpers/job.js +0 -80
- package/dist/src/lib/workers/helpers/op-analytics.js +0 -22
- package/dist/src/lib/workers/helpers/terafoundation.js +0 -34
- package/dist/src/lib/workers/helpers/worker-shutdown.js +0 -147
- package/dist/src/lib/workers/metrics/index.js +0 -108
- package/dist/src/lib/workers/worker/index.js +0 -378
- package/dist/src/lib/workers/worker/slice.js +0 -122
- package/dist/test/config/schemas/system_schema-spec.js +0 -26
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8s-v2-spec.js +0 -458
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8sResource-v2-spec.js +0 -818
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8sState-multicluster-v2-spec.js +0 -67
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8sState-v2-spec.js +0 -84
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/utils-v2-spec.js +0 -320
- package/dist/test/lib/cluster/services/cluster/backends/state-utils-spec.js +0 -37
- package/dist/test/node_master-spec.js +0 -194
- package/dist/test/services/api-spec.js +0 -79
- package/dist/test/services/assets-spec.js +0 -158
- package/dist/test/services/messaging-spec.js +0 -440
- package/dist/test/storage/assets_storage-spec.js +0 -95
- package/dist/test/storage/s3_store-spec.js +0 -149
- package/dist/test/test.config.js +0 -23
- package/dist/test/test.setup.js +0 -6
- package/dist/test/utils/api_utils-spec.js +0 -25
- package/dist/test/utils/asset_utils-spec.js +0 -141
- package/dist/test/utils/elastic_utils-spec.js +0 -25
- package/dist/test/workers/execution-controller/execution-controller-spec.js +0 -371
- package/dist/test/workers/execution-controller/execution-special-test-cases-spec.js +0 -519
- package/dist/test/workers/execution-controller/execution-test-cases-spec.js +0 -343
- package/dist/test/workers/execution-controller/recovery-spec.js +0 -160
- package/dist/test/workers/execution-controller/scheduler-spec.js +0 -249
- package/dist/test/workers/execution-controller/slice-analytics-spec.js +0 -121
- package/dist/test/workers/fixtures/ops/example-op/processor.js +0 -20
- package/dist/test/workers/fixtures/ops/example-op/schema.js +0 -19
- package/dist/test/workers/fixtures/ops/example-reader/fetcher.js +0 -20
- package/dist/test/workers/fixtures/ops/example-reader/schema.js +0 -41
- package/dist/test/workers/fixtures/ops/example-reader/slicer.js +0 -37
- package/dist/test/workers/fixtures/ops/new-op/processor.js +0 -29
- package/dist/test/workers/fixtures/ops/new-op/schema.js +0 -18
- package/dist/test/workers/fixtures/ops/new-reader/fetcher.js +0 -19
- package/dist/test/workers/fixtures/ops/new-reader/schema.js +0 -23
- package/dist/test/workers/fixtures/ops/new-reader/slicer.js +0 -13
- package/dist/test/workers/helpers/configs.js +0 -128
- package/dist/test/workers/helpers/execution-controller-helper.js +0 -49
- package/dist/test/workers/helpers/index.js +0 -5
- package/dist/test/workers/helpers/test-context.js +0 -210
- package/dist/test/workers/helpers/zip-directory.js +0 -25
- package/dist/test/workers/worker/slice-spec.js +0 -333
- package/dist/test/workers/worker/worker-spec.js +0 -356
|
@@ -1,419 +0,0 @@
|
|
|
1
|
-
import { TSError, get, isEmpty, pDelay, pRetry } from '@terascope/core-utils';
|
|
2
|
-
import * as k8s from '@kubernetes/client-node';
|
|
3
|
-
import { convertToTSResource, convertToTSResourceList, getRetryConfig, isDeployment, isJob, isPod, isReplicaSet, isService, isTSPod } from './utils.js';
|
|
4
|
-
export class K8s {
|
|
5
|
-
logger;
|
|
6
|
-
apiPollDelay;
|
|
7
|
-
defaultNamespace;
|
|
8
|
-
shutdownTimeout;
|
|
9
|
-
kc;
|
|
10
|
-
k8sAppsV1Api;
|
|
11
|
-
k8sBatchV1Api;
|
|
12
|
-
k8sCoreV1Api;
|
|
13
|
-
constructor(logger, clientConfig, defaultNamespace, apiPollDelay, shutdownTimeout) {
|
|
14
|
-
this.apiPollDelay = apiPollDelay;
|
|
15
|
-
this.defaultNamespace = defaultNamespace || 'default';
|
|
16
|
-
this.logger = logger;
|
|
17
|
-
this.shutdownTimeout = shutdownTimeout; // this is in milliseconds
|
|
18
|
-
this.kc = new k8s.KubeConfig();
|
|
19
|
-
if (clientConfig) {
|
|
20
|
-
this.kc.loadFromOptions(clientConfig);
|
|
21
|
-
}
|
|
22
|
-
else if (process.env.KUBERNETES_SERVICE_HOST && process.env.KUBERNETES_SERVICE_PORT) {
|
|
23
|
-
this.kc.loadFromCluster();
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
this.kc.loadFromDefault();
|
|
27
|
-
}
|
|
28
|
-
this.k8sAppsV1Api = this.kc.makeApiClient(k8s.AppsV1Api);
|
|
29
|
-
this.k8sBatchV1Api = this.kc.makeApiClient(k8s.BatchV1Api);
|
|
30
|
-
this.k8sCoreV1Api = this.kc.makeApiClient(k8s.CoreV1Api);
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Returns the k8s NamespaceList object
|
|
34
|
-
* @return {Promise} [description]
|
|
35
|
-
*/
|
|
36
|
-
async getNamespaces() {
|
|
37
|
-
let namespaces;
|
|
38
|
-
try {
|
|
39
|
-
namespaces = await pRetry(() => this.k8sCoreV1Api.listNamespace(), getRetryConfig());
|
|
40
|
-
}
|
|
41
|
-
catch (err) {
|
|
42
|
-
const error = new TSError(err, {
|
|
43
|
-
reason: 'Failure getting in namespaces'
|
|
44
|
-
});
|
|
45
|
-
throw error;
|
|
46
|
-
}
|
|
47
|
-
return namespaces;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Returns the first pod matching the provided selector after it has
|
|
51
|
-
* entered the `Running` state.
|
|
52
|
-
*
|
|
53
|
-
* TODO: Make more generic to search for different statuses
|
|
54
|
-
*
|
|
55
|
-
* NOTE: If your selector will return multiple pods, this method probably
|
|
56
|
-
* won't work for you.
|
|
57
|
-
* @param {String} selector kubernetes selector, like 'controller-uid=XXX'
|
|
58
|
-
* @param {String} ns namespace to search, this will override the default
|
|
59
|
-
* @param {Number} timeout time, in ms, to wait for pod to start
|
|
60
|
-
* @return {k8s.V1Pod} pod object
|
|
61
|
-
*
|
|
62
|
-
* TODO: Should this use the cluster state that gets polled periodically,
|
|
63
|
-
* rather than making it's own k8s API calls
|
|
64
|
-
*/
|
|
65
|
-
async waitForSelectedPod(selector, statusType, ns, timeout = 10000) {
|
|
66
|
-
const namespace = ns || this.defaultNamespace;
|
|
67
|
-
let now = Date.now();
|
|
68
|
-
const end = now + timeout;
|
|
69
|
-
while (true) {
|
|
70
|
-
const podListObj = await pRetry(() => this.k8sCoreV1Api
|
|
71
|
-
.listNamespacedPod({ namespace, labelSelector: selector }), getRetryConfig());
|
|
72
|
-
// NOTE: This assumes the first pod returned.
|
|
73
|
-
const pod = get(podListObj, 'items[0]');
|
|
74
|
-
if (pod && isTSPod(pod)) {
|
|
75
|
-
if (statusType === 'readiness-probe') {
|
|
76
|
-
if (pod.status.conditions) {
|
|
77
|
-
for (const condition of pod.status.conditions) {
|
|
78
|
-
if (condition.type === 'ContainersReady'
|
|
79
|
-
&& condition.status === 'True') {
|
|
80
|
-
return pod;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
else if (statusType === 'pod-status') {
|
|
86
|
-
if (get(pod, 'status.phase') === 'Running')
|
|
87
|
-
return pod;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
if (now > end)
|
|
91
|
-
throw new Error(`Timeout waiting for pod matching: ${selector}`);
|
|
92
|
-
this.logger.debug(`waiting for pod matching: ${selector}`);
|
|
93
|
-
await pDelay(this.apiPollDelay);
|
|
94
|
-
now = Date.now();
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Waits for the number of pods to equal number.
|
|
99
|
-
* @param {Number} number Number of pods to wait for, e.g.: 0, 10
|
|
100
|
-
* @param {String} selector kubernetes selector, like 'controller-uid=XXX'
|
|
101
|
-
* @param {String} ns namespace to search, this will override the default
|
|
102
|
-
* @param {Number} timeout time, in ms, to wait for pod to start
|
|
103
|
-
* @return {k8s.V1Pod[]} Array of pod objects
|
|
104
|
-
*
|
|
105
|
-
* TODO: Should this use the cluster state that gets polled periodically,
|
|
106
|
-
* rather than making it's own k8s API calls?
|
|
107
|
-
*/
|
|
108
|
-
async waitForNumPods(number, selector, ns, timeout = 10000) {
|
|
109
|
-
const namespace = ns || this.defaultNamespace;
|
|
110
|
-
let now = Date.now();
|
|
111
|
-
const end = now + timeout;
|
|
112
|
-
while (true) {
|
|
113
|
-
const podListObj = await pRetry(() => this.k8sCoreV1Api
|
|
114
|
-
.listNamespacedPod({ namespace, labelSelector: selector }), getRetryConfig());
|
|
115
|
-
const podList = get(podListObj, 'items');
|
|
116
|
-
if (podList.length === number)
|
|
117
|
-
return podList;
|
|
118
|
-
const msg = `Waiting: pods matching ${selector} is ${podList.length}/${number}`;
|
|
119
|
-
if (now > end)
|
|
120
|
-
throw new Error(`Timeout ${msg}`);
|
|
121
|
-
this.logger.debug(msg);
|
|
122
|
-
await pDelay(this.apiPollDelay);
|
|
123
|
-
now = Date.now();
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
async list(selector, objType, ns) {
|
|
127
|
-
const namespace = ns || this.defaultNamespace;
|
|
128
|
-
let resourceListObj;
|
|
129
|
-
const params = {
|
|
130
|
-
namespace,
|
|
131
|
-
labelSelector: selector
|
|
132
|
-
};
|
|
133
|
-
try {
|
|
134
|
-
if (objType === 'deployments') {
|
|
135
|
-
resourceListObj = await pRetry(() => this.k8sAppsV1Api.listNamespacedDeployment(params), getRetryConfig());
|
|
136
|
-
}
|
|
137
|
-
else if (objType === 'jobs') {
|
|
138
|
-
resourceListObj = await pRetry(() => this.k8sBatchV1Api.listNamespacedJob(params), getRetryConfig());
|
|
139
|
-
}
|
|
140
|
-
else if (objType === 'pods') {
|
|
141
|
-
resourceListObj = await pRetry(() => this.k8sCoreV1Api.listNamespacedPod(params), getRetryConfig());
|
|
142
|
-
}
|
|
143
|
-
else if (objType === 'replicasets') {
|
|
144
|
-
resourceListObj = await pRetry(() => this.k8sAppsV1Api.listNamespacedReplicaSet(params), getRetryConfig());
|
|
145
|
-
}
|
|
146
|
-
else if (objType === 'services') {
|
|
147
|
-
resourceListObj = await pRetry(() => this.k8sCoreV1Api.listNamespacedService(params), getRetryConfig());
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
const error = new Error(`Invalid objType provided to get: ${objType}`);
|
|
151
|
-
this.logger.error(error);
|
|
152
|
-
return Promise.reject(error);
|
|
153
|
-
}
|
|
154
|
-
return convertToTSResourceList(resourceListObj);
|
|
155
|
-
}
|
|
156
|
-
catch (e) {
|
|
157
|
-
const err = new Error(`Request k8s.list of ${objType} with selector ${selector} failed: ${e}`);
|
|
158
|
-
this.logger.error(err);
|
|
159
|
-
return Promise.reject(err);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
async nonEmptyJobList(selector) {
|
|
163
|
-
const jobs = await this.list(selector, 'jobs');
|
|
164
|
-
if (jobs.items.length === 1) {
|
|
165
|
-
return jobs;
|
|
166
|
-
}
|
|
167
|
-
else if (jobs.items.length === 0) {
|
|
168
|
-
const msg = `Teraslice job matching the following selector was not found: ${selector} (retriable)`;
|
|
169
|
-
this.logger.warn(msg);
|
|
170
|
-
throw new TSError(msg, { retryable: true });
|
|
171
|
-
}
|
|
172
|
-
else {
|
|
173
|
-
throw new TSError(`Unexpected number of Teraslice jobs matching the following selector: ${selector}`, {
|
|
174
|
-
retryable: true
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
async post(manifest) {
|
|
179
|
-
let resourceObj;
|
|
180
|
-
const namespace = this.defaultNamespace;
|
|
181
|
-
try {
|
|
182
|
-
if (isDeployment(manifest)) {
|
|
183
|
-
resourceObj = await this.k8sAppsV1Api
|
|
184
|
-
.createNamespacedDeployment({ namespace, body: manifest });
|
|
185
|
-
}
|
|
186
|
-
else if (isJob(manifest)) {
|
|
187
|
-
resourceObj = await this.k8sBatchV1Api
|
|
188
|
-
.createNamespacedJob({ namespace, body: manifest });
|
|
189
|
-
}
|
|
190
|
-
else if (isPod(manifest)) {
|
|
191
|
-
resourceObj = await this.k8sCoreV1Api
|
|
192
|
-
.createNamespacedPod({ namespace, body: manifest });
|
|
193
|
-
}
|
|
194
|
-
else if (isReplicaSet(manifest)) {
|
|
195
|
-
resourceObj = await this.k8sAppsV1Api
|
|
196
|
-
.createNamespacedReplicaSet({ namespace, body: manifest });
|
|
197
|
-
}
|
|
198
|
-
else if (isService(manifest)) {
|
|
199
|
-
resourceObj = await this.k8sCoreV1Api
|
|
200
|
-
.createNamespacedService({ namespace, body: manifest });
|
|
201
|
-
}
|
|
202
|
-
else {
|
|
203
|
-
const error = new Error('Invalid manifest type');
|
|
204
|
-
return Promise.reject(error);
|
|
205
|
-
}
|
|
206
|
-
return convertToTSResource(resourceObj);
|
|
207
|
-
}
|
|
208
|
-
catch (e) {
|
|
209
|
-
const err = new Error(`Request k8s.post of ${manifest.kind} with body ${JSON.stringify(manifest)} failed: ${e}`);
|
|
210
|
-
return Promise.reject(err);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* Patches specified k8s deployment with the provided record
|
|
215
|
-
* @param {String} record record, like 'app=teraslice'
|
|
216
|
-
* @param {String} name Name of the deployment to patch
|
|
217
|
-
* @return {Object} k8s V1Deployment object.
|
|
218
|
-
*/
|
|
219
|
-
// TODO: I renamed this from patchDeployment to just patch because this is
|
|
220
|
-
// the low level k8s api method, I expect to eventually change the interface
|
|
221
|
-
// on this to require `objType` to support patching other things
|
|
222
|
-
async patch(record, name) {
|
|
223
|
-
let responseObj;
|
|
224
|
-
try {
|
|
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
|
-
}
|
|
233
|
-
catch (e) {
|
|
234
|
-
const err = new Error(`Request k8s.patch with name: ${name} failed with: ${e}`);
|
|
235
|
-
this.logger.error(err);
|
|
236
|
-
return Promise.reject(err);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
async delete(name, objType, force) {
|
|
240
|
-
if (name === undefined || name.trim() === '') {
|
|
241
|
-
throw new Error(`Name of resource to delete must be specified. Received: "${name}".`);
|
|
242
|
-
}
|
|
243
|
-
let responseObj;
|
|
244
|
-
// To get a Job to remove the associated pods you have to
|
|
245
|
-
// include a body like the one below with the delete request.
|
|
246
|
-
// To force: setting gracePeriodSeconds to 1 will send a SIGKILL command to the resource
|
|
247
|
-
const deleteOptions = {
|
|
248
|
-
apiVersion: 'v1',
|
|
249
|
-
kind: 'DeleteOptions',
|
|
250
|
-
propagationPolicy: 'Background'
|
|
251
|
-
};
|
|
252
|
-
if (force) {
|
|
253
|
-
deleteOptions.gracePeriodSeconds = 1;
|
|
254
|
-
}
|
|
255
|
-
const params = {
|
|
256
|
-
name,
|
|
257
|
-
namespace: this.defaultNamespace,
|
|
258
|
-
body: deleteOptions
|
|
259
|
-
};
|
|
260
|
-
const deleteWithErrorHandling = async (deleteFn) => {
|
|
261
|
-
try {
|
|
262
|
-
const res = await deleteFn();
|
|
263
|
-
return res;
|
|
264
|
-
}
|
|
265
|
-
catch (e) {
|
|
266
|
-
if (e.body) {
|
|
267
|
-
const bodyObj = JSON.parse(e.body);
|
|
268
|
-
// 404 should be an acceptable response to a delete request, not an error
|
|
269
|
-
if (bodyObj.code === 404) {
|
|
270
|
-
this.logger.info(`No ${objType} with name ${name} found while attempting to delete.`);
|
|
271
|
-
return bodyObj;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
throw e;
|
|
275
|
-
}
|
|
276
|
-
};
|
|
277
|
-
try {
|
|
278
|
-
if (objType === 'services') {
|
|
279
|
-
responseObj = await pRetry(() => deleteWithErrorHandling(() => this.k8sCoreV1Api
|
|
280
|
-
.deleteNamespacedService(params)), getRetryConfig());
|
|
281
|
-
}
|
|
282
|
-
else if (objType === 'deployments') {
|
|
283
|
-
responseObj = await pRetry(() => deleteWithErrorHandling(() => this.k8sAppsV1Api
|
|
284
|
-
.deleteNamespacedDeployment(params)), getRetryConfig());
|
|
285
|
-
}
|
|
286
|
-
else if (objType === 'jobs') {
|
|
287
|
-
responseObj = await pRetry(() => deleteWithErrorHandling(() => this.k8sBatchV1Api
|
|
288
|
-
.deleteNamespacedJob(params)), getRetryConfig());
|
|
289
|
-
}
|
|
290
|
-
else if (objType === 'pods') {
|
|
291
|
-
responseObj = await pRetry(() => deleteWithErrorHandling(() => this.k8sCoreV1Api
|
|
292
|
-
.deleteNamespacedPod(params)), getRetryConfig());
|
|
293
|
-
}
|
|
294
|
-
else if (objType === 'replicasets') {
|
|
295
|
-
responseObj = await pRetry(() => deleteWithErrorHandling(() => this.k8sAppsV1Api
|
|
296
|
-
.deleteNamespacedReplicaSet(params)), getRetryConfig());
|
|
297
|
-
}
|
|
298
|
-
else {
|
|
299
|
-
throw new Error(`Invalid objType: ${objType}`);
|
|
300
|
-
}
|
|
301
|
-
return responseObj;
|
|
302
|
-
}
|
|
303
|
-
catch (e) {
|
|
304
|
-
const err = new Error(`Request k8s.delete with name: ${name} failed with: ${e}`);
|
|
305
|
-
this.logger.error(err);
|
|
306
|
-
return Promise.reject(err);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
/**
|
|
310
|
-
* Delete all of Kubernetes resources related to the specified exId
|
|
311
|
-
* @param {String} exId ID of the execution
|
|
312
|
-
* @param {Boolean} force Forcefully stop all pod, deployment,
|
|
313
|
-
* service, replicaset and job resources
|
|
314
|
-
* @return {Promise}
|
|
315
|
-
*/
|
|
316
|
-
async deleteExecution(exId, force = false) {
|
|
317
|
-
if (!exId) {
|
|
318
|
-
throw new Error('deleteExecution requires an executionId');
|
|
319
|
-
}
|
|
320
|
-
if (force) {
|
|
321
|
-
// Order matters. If we delete a parent resource before its children it
|
|
322
|
-
// will be marked for background deletion and then can't be force deleted.
|
|
323
|
-
await this._deleteObjByExId(exId, 'worker', 'pods', force);
|
|
324
|
-
await this._deleteObjByExId(exId, 'worker', 'replicasets', force);
|
|
325
|
-
await this._deleteObjByExId(exId, 'worker', 'deployments', force);
|
|
326
|
-
await this._deleteObjByExId(exId, 'execution_controller', 'pods', force);
|
|
327
|
-
await this._deleteObjByExId(exId, 'execution_controller', 'services', force);
|
|
328
|
-
}
|
|
329
|
-
await this._deleteObjByExId(exId, 'execution_controller', 'jobs', force);
|
|
330
|
-
}
|
|
331
|
-
async _deleteObjByExId(exId, nodeType, objType, force) {
|
|
332
|
-
let objList;
|
|
333
|
-
const deleteResponses = [];
|
|
334
|
-
try {
|
|
335
|
-
objList = await this.list(`app.kubernetes.io/component=${nodeType},teraslice.terascope.io/exId=${exId}`, objType);
|
|
336
|
-
}
|
|
337
|
-
catch (e) {
|
|
338
|
-
const err = new Error(`Request ${objType} list in _deleteObjByExId with app.kubernetes.io/component: ${nodeType} and exId: ${exId} failed with: ${e}`);
|
|
339
|
-
this.logger.error(err);
|
|
340
|
-
return Promise.reject(err);
|
|
341
|
-
}
|
|
342
|
-
if (isEmpty(objList.items)) {
|
|
343
|
-
this.logger.info(`k8s._deleteObjByExId: ${exId} ${nodeType} ${objType} has already been deleted`);
|
|
344
|
-
return Promise.resolve();
|
|
345
|
-
}
|
|
346
|
-
for (const obj of objList.items) {
|
|
347
|
-
const name = obj.metadata.name;
|
|
348
|
-
const deletionTimestamp = obj.metadata.deletionTimestamp;
|
|
349
|
-
// If deletionTimestamp is present then the resource is already terminating.
|
|
350
|
-
// K8s will not change the grace period in this case, so force deletion is not possible
|
|
351
|
-
if (force && deletionTimestamp) {
|
|
352
|
-
this.logger.warn(`Cannot force delete ${name} for ExId: ${exId}. It will finish deleting gracefully by ${deletionTimestamp}`);
|
|
353
|
-
return Promise.resolve();
|
|
354
|
-
}
|
|
355
|
-
this.logger.info(`k8s._deleteObjByExId: ${exId} ${nodeType} ${objType} ${force ? 'force' : ''} deleting: ${name}`);
|
|
356
|
-
try {
|
|
357
|
-
deleteResponses.push(await this.delete(name, objType, force));
|
|
358
|
-
}
|
|
359
|
-
catch (e) {
|
|
360
|
-
const err = new Error(`Request k8s.delete in _deleteObjByExId with name: ${name} failed with: ${e}`);
|
|
361
|
-
this.logger.error(err);
|
|
362
|
-
return Promise.reject(err);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
return deleteResponses;
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* Scales the k8s deployment for the specified exId to the desired number
|
|
369
|
-
* of workers.
|
|
370
|
-
* @param {String} exId exId of execution to scale
|
|
371
|
-
* @param {number} numWorkers number of workers to scale by
|
|
372
|
-
* @param {ScaleOp} op Scale operation: `set`, `add`, `remove`
|
|
373
|
-
* @return {Promise<k8s.V1Deployment>} Body of patch response.
|
|
374
|
-
*/
|
|
375
|
-
async scaleExecution(exId, numWorkers, op) {
|
|
376
|
-
let newScale;
|
|
377
|
-
const selector = `app.kubernetes.io/component=worker,teraslice.terascope.io/exId=${exId}`;
|
|
378
|
-
this.logger.info(`Scaling exId: ${exId}, op: ${op}, numWorkers: ${numWorkers}`);
|
|
379
|
-
const listResponse = await this.list(selector, 'deployments');
|
|
380
|
-
this.logger.debug(`k8s worker query listResponse: ${JSON.stringify(listResponse)}`);
|
|
381
|
-
// the selector provided to list above should always result in a single
|
|
382
|
-
// deployment in the response.
|
|
383
|
-
if (listResponse.items.length === 0) {
|
|
384
|
-
const msg = `Teraslice deployment matching the following selector was not found: ${selector}`;
|
|
385
|
-
this.logger.warn(msg);
|
|
386
|
-
throw new TSError(msg);
|
|
387
|
-
}
|
|
388
|
-
else if (listResponse.items.length > 1) {
|
|
389
|
-
throw new TSError(`Unexpected number of Teraslice deployments matching the following selector: ${selector}`);
|
|
390
|
-
}
|
|
391
|
-
const workerDeployment = listResponse.items[0];
|
|
392
|
-
this.logger.info(`Current Scale for exId=${exId}: ${workerDeployment.spec.replicas}`);
|
|
393
|
-
if (op === 'set') {
|
|
394
|
-
newScale = numWorkers;
|
|
395
|
-
}
|
|
396
|
-
else if (op === 'add') {
|
|
397
|
-
newScale = workerDeployment.spec.replicas + numWorkers;
|
|
398
|
-
}
|
|
399
|
-
else if (op === 'remove') {
|
|
400
|
-
newScale = workerDeployment.spec.replicas - numWorkers;
|
|
401
|
-
}
|
|
402
|
-
else {
|
|
403
|
-
throw new Error('scaleExecution only accepts the following operations: add, remove, set');
|
|
404
|
-
}
|
|
405
|
-
this.logger.info(`New Scale for exId=${exId}: ${newScale}`);
|
|
406
|
-
const scalePatch = [
|
|
407
|
-
{
|
|
408
|
-
op: 'replace',
|
|
409
|
-
path: '/spec/replicas',
|
|
410
|
-
value: newScale
|
|
411
|
-
}
|
|
412
|
-
];
|
|
413
|
-
const patchResponseBody = await this
|
|
414
|
-
.patch(scalePatch, workerDeployment.metadata.name);
|
|
415
|
-
this.logger.debug(`k8s.scaleExecution patchResponseBody: ${JSON.stringify(patchResponseBody)}`);
|
|
416
|
-
return patchResponseBody;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
//# sourceMappingURL=k8s.js.map
|
package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sDeploymentResource.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { V1Deployment } from '@kubernetes/client-node';
|
|
2
|
-
import { convertToTSResource, makeTemplate } from './utils.js';
|
|
3
|
-
import { K8sResource } from './k8sResource.js';
|
|
4
|
-
export class K8sDeploymentResource extends K8sResource {
|
|
5
|
-
nodeType = 'worker';
|
|
6
|
-
nameInfix = 'wkr';
|
|
7
|
-
templateGenerator;
|
|
8
|
-
templateConfig;
|
|
9
|
-
resource;
|
|
10
|
-
exName;
|
|
11
|
-
exUid;
|
|
12
|
-
/**
|
|
13
|
-
* K8sDeploymentResource allows the generation of a k8s deployment based on a template.
|
|
14
|
-
* After creating the object, the k8s deployment is accessible on the objects
|
|
15
|
-
* .resource property.
|
|
16
|
-
*
|
|
17
|
-
* @param {Object} terasliceConfig - teraslice cluster config from context
|
|
18
|
-
* @param {Object} execution - teraslice execution
|
|
19
|
-
* @param {Logger} logger - teraslice logger
|
|
20
|
-
* @param {String} exName - name from execution resource
|
|
21
|
-
* @param {String} exUid - uid from execution resource
|
|
22
|
-
*/
|
|
23
|
-
constructor(terasliceConfig, execution, logger, exName, exUid) {
|
|
24
|
-
super(terasliceConfig, execution, logger);
|
|
25
|
-
this.execution = execution;
|
|
26
|
-
this.logger = logger;
|
|
27
|
-
this.terasliceConfig = terasliceConfig;
|
|
28
|
-
this.exName = exName;
|
|
29
|
-
this.exUid = exUid;
|
|
30
|
-
this.templateGenerator = makeTemplate('deployments', this.nodeType);
|
|
31
|
-
this.templateConfig = this._makeConfig(this.nameInfix, exName, exUid);
|
|
32
|
-
const k8sDeployment = new V1Deployment();
|
|
33
|
-
Object.assign(k8sDeployment, this.templateGenerator(this.templateConfig));
|
|
34
|
-
this.resource = convertToTSResource(k8sDeployment);
|
|
35
|
-
this._setJobLabels(this.resource);
|
|
36
|
-
// Apply job `targets` setting as k8s nodeAffinity
|
|
37
|
-
// We assume that multiple targets require both to match ...
|
|
38
|
-
// NOTE: If you specify multiple `matchExpressions` associated with
|
|
39
|
-
// `nodeSelectorTerms`, then the pod can be scheduled onto a node
|
|
40
|
-
// only if *all* `matchExpressions` can be satisfied.
|
|
41
|
-
this._setTargets(this.resource);
|
|
42
|
-
this._setResources(this.resource);
|
|
43
|
-
this._setVolumes(this.resource);
|
|
44
|
-
if (process.env.MOUNT_LOCAL_TERASLICE !== undefined) {
|
|
45
|
-
this._mountLocalTeraslice(this.resource);
|
|
46
|
-
}
|
|
47
|
-
this._setEnvVariables();
|
|
48
|
-
this._setAssetsVolume(this.resource);
|
|
49
|
-
this._setImagePullSecret(this.resource);
|
|
50
|
-
this._setEphemeralStorage(this.resource);
|
|
51
|
-
this._setExternalPorts(this.resource);
|
|
52
|
-
this._setPriorityClassName(this.resource);
|
|
53
|
-
this._setWorkerAntiAffinity(this.resource);
|
|
54
|
-
// override must happen last
|
|
55
|
-
if (this.terasliceConfig.kubernetes_overrides_enabled) {
|
|
56
|
-
this._mergePodSpecOverlay(this.resource);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
//# sourceMappingURL=k8sDeploymentResource.js.map
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { V1Job } from '@kubernetes/client-node';
|
|
2
|
-
import { convertToTSResource, makeTemplate } from './utils.js';
|
|
3
|
-
import { K8sResource } from './k8sResource.js';
|
|
4
|
-
export class K8sJobResource extends K8sResource {
|
|
5
|
-
nodeType = 'execution_controller';
|
|
6
|
-
nameInfix = 'exc';
|
|
7
|
-
templateGenerator;
|
|
8
|
-
templateConfig;
|
|
9
|
-
resource;
|
|
10
|
-
/**
|
|
11
|
-
* K8sJobResource allows the generation of a k8s job based on a template.
|
|
12
|
-
* After creating the object, the k8s job is accessible on the objects
|
|
13
|
-
* .resource property.
|
|
14
|
-
*
|
|
15
|
-
* @param {Object} terasliceConfig - teraslice cluster config from context
|
|
16
|
-
* @param {Object} execution - teraslice execution
|
|
17
|
-
* @param {Logger} logger - teraslice logger
|
|
18
|
-
*/
|
|
19
|
-
constructor(terasliceConfig, execution, logger) {
|
|
20
|
-
super(terasliceConfig, execution, logger);
|
|
21
|
-
this.templateGenerator = makeTemplate('jobs', this.nodeType);
|
|
22
|
-
this.templateConfig = this._makeConfig(this.nameInfix);
|
|
23
|
-
const k8sJob = new V1Job();
|
|
24
|
-
Object.assign(k8sJob, this.templateGenerator(this.templateConfig));
|
|
25
|
-
this.resource = convertToTSResource(k8sJob);
|
|
26
|
-
this._setJobLabels(this.resource);
|
|
27
|
-
// Apply job `targets` setting as k8s nodeAffinity
|
|
28
|
-
// We assume that multiple targets require both to match ...
|
|
29
|
-
// NOTE: If you specify multiple `matchExpressions` associated with
|
|
30
|
-
// `nodeSelectorTerms`, then the pod can be scheduled onto a node
|
|
31
|
-
// only if *all* `matchExpressions` can be satisfied.
|
|
32
|
-
this._setTargets(this.resource);
|
|
33
|
-
this._setResources(this.resource);
|
|
34
|
-
this._setVolumes(this.resource);
|
|
35
|
-
if (process.env.MOUNT_LOCAL_TERASLICE !== undefined) {
|
|
36
|
-
this._mountLocalTeraslice(this.resource);
|
|
37
|
-
}
|
|
38
|
-
this._setEnvVariables();
|
|
39
|
-
this._setAssetsVolume(this.resource);
|
|
40
|
-
this._setImagePullSecret(this.resource);
|
|
41
|
-
this._setEphemeralStorage(this.resource);
|
|
42
|
-
this._setExternalPorts(this.resource);
|
|
43
|
-
this._setPriorityClassName(this.resource);
|
|
44
|
-
// Execution controller targets are required nodeAffinities, if
|
|
45
|
-
// required job targets are also supplied, then *all* of the matches
|
|
46
|
-
// will have to be satisfied for the job to be scheduled. This also
|
|
47
|
-
// adds tolerations for any specified targets
|
|
48
|
-
this._setExecutionControllerTargets(this.resource);
|
|
49
|
-
// override must happen last
|
|
50
|
-
if (this.terasliceConfig.kubernetes_overrides_enabled) {
|
|
51
|
-
this._mergePodSpecOverlay(this.resource);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
//# sourceMappingURL=k8sJobResource.js.map
|