teraslice 0.87.1 → 0.88.0
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/cluster-service.js +24 -18
- package/dist/src/index.js +42 -0
- package/package.json +11 -15
- package/service.js +4 -6
- package/worker-service.js +6 -6
- package/index.js +0 -21
- package/lib/cluster/cluster_master.js +0 -164
- package/lib/cluster/node_master.js +0 -393
- package/lib/cluster/services/api.js +0 -581
- package/lib/cluster/services/assets.js +0 -211
- package/lib/cluster/services/cluster/backends/kubernetes/deployments/worker.hbs +0 -86
- package/lib/cluster/services/cluster/backends/kubernetes/index.js +0 -225
- package/lib/cluster/services/cluster/backends/kubernetes/jobs/execution_controller.hbs +0 -69
- package/lib/cluster/services/cluster/backends/kubernetes/k8s.js +0 -450
- package/lib/cluster/services/cluster/backends/kubernetes/k8sResource.js +0 -443
- package/lib/cluster/services/cluster/backends/kubernetes/k8sState.js +0 -67
- package/lib/cluster/services/cluster/backends/kubernetes/utils.js +0 -58
- package/lib/cluster/services/cluster/backends/native/index.js +0 -611
- package/lib/cluster/services/cluster/backends/native/messaging.js +0 -563
- package/lib/cluster/services/cluster/backends/state-utils.js +0 -49
- package/lib/cluster/services/cluster/index.js +0 -15
- package/lib/cluster/services/execution.js +0 -459
- package/lib/cluster/services/jobs.js +0 -303
- package/lib/config/default-sysconfig.js +0 -47
- package/lib/config/index.js +0 -32
- package/lib/config/schemas/system.js +0 -333
- package/lib/processors/save_file/index.js +0 -9
- package/lib/processors/save_file/processor.js +0 -17
- package/lib/processors/save_file/schema.js +0 -17
- package/lib/processors/script.js +0 -130
- package/lib/processors/stdout/index.js +0 -9
- package/lib/processors/stdout/processor.js +0 -19
- package/lib/processors/stdout/schema.js +0 -18
- package/lib/storage/analytics.js +0 -106
- package/lib/storage/assets.js +0 -275
- package/lib/storage/backends/elasticsearch_store.js +0 -567
- package/lib/storage/backends/mappings/analytics.json +0 -49
- package/lib/storage/backends/mappings/asset.json +0 -40
- package/lib/storage/backends/mappings/ex.json +0 -55
- package/lib/storage/backends/mappings/job.json +0 -31
- package/lib/storage/backends/mappings/state.json +0 -37
- package/lib/storage/execution.js +0 -331
- package/lib/storage/index.js +0 -16
- package/lib/storage/jobs.js +0 -97
- package/lib/storage/state.js +0 -302
- package/lib/utils/api_utils.js +0 -173
- package/lib/utils/asset_utils.js +0 -117
- package/lib/utils/date_utils.js +0 -58
- package/lib/utils/encoding_utils.js +0 -29
- package/lib/utils/events.js +0 -7
- package/lib/utils/file_utils.js +0 -118
- package/lib/utils/id_utils.js +0 -19
- package/lib/utils/port_utils.js +0 -83
- package/lib/workers/assets/loader.js +0 -109
- package/lib/workers/assets/spawn.js +0 -78
- package/lib/workers/context/execution-context.js +0 -16
- package/lib/workers/context/terafoundation-context.js +0 -10
- package/lib/workers/execution-controller/execution-analytics.js +0 -211
- package/lib/workers/execution-controller/index.js +0 -1033
- package/lib/workers/execution-controller/recovery.js +0 -188
- package/lib/workers/execution-controller/scheduler.js +0 -461
- package/lib/workers/execution-controller/slice-analytics.js +0 -115
- package/lib/workers/helpers/job.js +0 -93
- package/lib/workers/helpers/op-analytics.js +0 -22
- package/lib/workers/helpers/terafoundation.js +0 -43
- package/lib/workers/helpers/worker-shutdown.js +0 -187
- package/lib/workers/metrics/index.js +0 -139
- package/lib/workers/worker/index.js +0 -344
- package/lib/workers/worker/slice.js +0 -143
|
@@ -1,443 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
|
|
6
|
-
const barbe = require('barbe');
|
|
7
|
-
const _ = require('lodash');
|
|
8
|
-
|
|
9
|
-
const { isNumber } = require('@terascope/utils');
|
|
10
|
-
|
|
11
|
-
const { safeEncode } = require('../../../../../utils/encoding_utils');
|
|
12
|
-
const { setMaxOldSpaceViaEnv } = require('./utils');
|
|
13
|
-
|
|
14
|
-
class K8sResource {
|
|
15
|
-
/**
|
|
16
|
-
* K8sResource allows the generation of k8s resources based on templates.
|
|
17
|
-
* After creating the object, the k8s resource is accessible on the objects
|
|
18
|
-
* .resource property.
|
|
19
|
-
*
|
|
20
|
-
* @param {String} resourceType - jobs/services/deployments
|
|
21
|
-
* @param {String} resourceName - worker/execution_controller
|
|
22
|
-
* @param {Object} terasliceConfig - teraslice cluster config from context
|
|
23
|
-
* @param {Object} execution - teraslice execution
|
|
24
|
-
*/
|
|
25
|
-
constructor(resourceType, resourceName, terasliceConfig, execution, logger) {
|
|
26
|
-
this.execution = execution;
|
|
27
|
-
this.jobLabelPrefix = 'job.teraslice.terascope.io';
|
|
28
|
-
this.jobPropertyLabelPrefix = 'job-property.teraslice.terascope.io';
|
|
29
|
-
this.logger = logger;
|
|
30
|
-
this.nodeType = resourceName;
|
|
31
|
-
this.terasliceConfig = terasliceConfig;
|
|
32
|
-
|
|
33
|
-
if (resourceName === 'worker') {
|
|
34
|
-
this.nameInfix = 'wkr';
|
|
35
|
-
} else if (resourceName === 'execution_controller') {
|
|
36
|
-
this.nameInfix = 'exc';
|
|
37
|
-
} else {
|
|
38
|
-
throw new Error(`Unsupported resourceName: ${resourceName}`);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
this.templateGenerator = this._makeTemplate(resourceType, resourceName);
|
|
42
|
-
this.templateConfig = this._makeConfig();
|
|
43
|
-
this.resource = this.templateGenerator(this.templateConfig);
|
|
44
|
-
|
|
45
|
-
this._setJobLabels();
|
|
46
|
-
|
|
47
|
-
// Apply job `targets` setting as k8s nodeAffinity
|
|
48
|
-
// We assume that multiple targets require both to match ...
|
|
49
|
-
// NOTE: If you specify multiple `matchExpressions` associated with
|
|
50
|
-
// `nodeSelectorTerms`, then the pod can be scheduled onto a node
|
|
51
|
-
// only if *all* `matchExpressions` can be satisfied.
|
|
52
|
-
this._setTargets();
|
|
53
|
-
this._setResources();
|
|
54
|
-
this._setVolumes();
|
|
55
|
-
this._setAssetsVolume();
|
|
56
|
-
this._setImagePullSecret();
|
|
57
|
-
this._setEphemeralStorage();
|
|
58
|
-
this._setExternalPorts();
|
|
59
|
-
this._setPriorityClassName();
|
|
60
|
-
|
|
61
|
-
if (resourceName === 'worker') {
|
|
62
|
-
this._setWorkerAntiAffinity();
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Execution controller targets are required nodeAffinities, if
|
|
66
|
-
// required job targets are also supplied, then *all* of the matches
|
|
67
|
-
// will have to be satisfied for the job to be scheduled. This also
|
|
68
|
-
// adds tolerations for any specified targets
|
|
69
|
-
if (resourceName === 'execution_controller') {
|
|
70
|
-
this._setExecutionControllerTargets();
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (this.terasliceConfig.kubernetes_overrides_enabled) {
|
|
74
|
-
this._mergePodSpecOverlay();
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
_makeConfig() {
|
|
79
|
-
const clusterName = _.get(this.terasliceConfig, 'name');
|
|
80
|
-
const clusterNameLabel = clusterName.replace(/[^a-zA-Z0-9_\-.]/g, '_').substring(0, 63);
|
|
81
|
-
const configMapName = _.get(
|
|
82
|
-
this.terasliceConfig,
|
|
83
|
-
'kubernetes_config_map_name',
|
|
84
|
-
`${this.terasliceConfig.name}-worker`
|
|
85
|
-
);
|
|
86
|
-
const dockerImage = this.execution.kubernetes_image
|
|
87
|
-
|| this.terasliceConfig.kubernetes_image;
|
|
88
|
-
// name needs to be a valid DNS name since it is used in the svc name,
|
|
89
|
-
// so we can only permit alphanumeric and - characters. _ is forbidden.
|
|
90
|
-
// -> regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?'
|
|
91
|
-
const jobNameLabel = this.execution.name
|
|
92
|
-
.toLowerCase()
|
|
93
|
-
.replace(/[^a-zA-Z0-9\-.]/g, '-')
|
|
94
|
-
.replace(/^[^a-z]/, 'a')
|
|
95
|
-
.replace(/[^a-z0-9]$/, '0')
|
|
96
|
-
.substring(0, 63);
|
|
97
|
-
const name = `ts-${this.nameInfix}-${jobNameLabel.substring(0, 35)}-${this.execution.job_id.substring(0, 13)}`;
|
|
98
|
-
const shutdownTimeoutMs = _.get(this.terasliceConfig, 'shutdown_timeout', 60000);
|
|
99
|
-
const shutdownTimeoutSeconds = Math.round(shutdownTimeoutMs / 1000);
|
|
100
|
-
|
|
101
|
-
const config = {
|
|
102
|
-
// assetsDirectory: _.get(this.terasliceConfig, 'assets_directory', ''),
|
|
103
|
-
// assetsVolume: _.get(this.terasliceConfig, 'assets_volume', ''),
|
|
104
|
-
clusterName,
|
|
105
|
-
clusterNameLabel,
|
|
106
|
-
configMapName,
|
|
107
|
-
dockerImage,
|
|
108
|
-
execution: safeEncode(this.execution),
|
|
109
|
-
exId: this.execution.ex_id,
|
|
110
|
-
exName: this.execution.k8sName,
|
|
111
|
-
exUid: this.execution.k8sUid,
|
|
112
|
-
jobId: this.execution.job_id,
|
|
113
|
-
jobNameLabel,
|
|
114
|
-
name,
|
|
115
|
-
namespace: _.get(this.terasliceConfig, 'kubernetes_namespace', 'default'),
|
|
116
|
-
nodeType: this.nodeType,
|
|
117
|
-
replicas: this.execution.workers,
|
|
118
|
-
shutdownTimeout: shutdownTimeoutSeconds
|
|
119
|
-
};
|
|
120
|
-
return config;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
_makeTemplate(folder, fileName) {
|
|
124
|
-
const filePath = path.join(__dirname, folder, `${fileName}.hbs`);
|
|
125
|
-
const templateData = fs.readFileSync(filePath, 'utf-8');
|
|
126
|
-
const templateKeys = ['{{', '}}'];
|
|
127
|
-
|
|
128
|
-
return (config) => {
|
|
129
|
-
const templated = barbe(templateData, templateKeys, config);
|
|
130
|
-
return JSON.parse(templated);
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
_setWorkerAntiAffinity() {
|
|
135
|
-
if (this.terasliceConfig.kubernetes_worker_antiaffinity) {
|
|
136
|
-
const targetKey = 'spec.template.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution';
|
|
137
|
-
if (!_.has(this.resource, targetKey)) {
|
|
138
|
-
_.set(this.resource, targetKey, []);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// eslint-disable-next-line max-len
|
|
142
|
-
this.resource.spec.template.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution.push(
|
|
143
|
-
{
|
|
144
|
-
weight: 1,
|
|
145
|
-
podAffinityTerm: {
|
|
146
|
-
labelSelector: {
|
|
147
|
-
matchExpressions: [
|
|
148
|
-
{
|
|
149
|
-
key: 'app.kubernetes.io/name',
|
|
150
|
-
operator: 'In',
|
|
151
|
-
values: [
|
|
152
|
-
'teraslice'
|
|
153
|
-
]
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
key: 'app.kubernetes.io/instance',
|
|
157
|
-
operator: 'In',
|
|
158
|
-
values: [
|
|
159
|
-
this.templateConfig.clusterNameLabel
|
|
160
|
-
]
|
|
161
|
-
}
|
|
162
|
-
]
|
|
163
|
-
},
|
|
164
|
-
topologyKey: 'kubernetes.io/hostname'
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Execution Controllers get tolerations and required affinities
|
|
173
|
-
*
|
|
174
|
-
* NOTE: We considered changing `execution_controller_targets` to be an
|
|
175
|
-
* object but the inconsistency with `targets` made this awkward. See the
|
|
176
|
-
* `teraslice config with execution_controller_targets and job targets set`
|
|
177
|
-
* test for an example. If the syntax for this were to change, we should
|
|
178
|
-
* also consider changing `execution.targets`, which is a change on the job.
|
|
179
|
-
*/
|
|
180
|
-
_setExecutionControllerTargets() {
|
|
181
|
-
if (this.terasliceConfig.execution_controller_targets) {
|
|
182
|
-
_.forEach(this.terasliceConfig.execution_controller_targets, (target) => {
|
|
183
|
-
this._setTargetRequired(target);
|
|
184
|
-
this._setTargetAccepted(target);
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
_setEphemeralStorage() {
|
|
190
|
-
if (this.execution.ephemeral_storage) {
|
|
191
|
-
this.resource.spec.template.spec.containers[0].volumeMounts.push({
|
|
192
|
-
name: 'ephemeral-volume',
|
|
193
|
-
mountPath: '/ephemeral0'
|
|
194
|
-
});
|
|
195
|
-
this.resource.spec.template.spec.volumes.push({
|
|
196
|
-
name: 'ephemeral-volume',
|
|
197
|
-
emptyDir: {}
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
_setExternalPorts() {
|
|
203
|
-
if (this.execution.external_ports) {
|
|
204
|
-
_.forEach(this.execution.external_ports, (portValue) => {
|
|
205
|
-
if (isNumber(portValue)) {
|
|
206
|
-
this.resource.spec.template.spec.containers[0].ports
|
|
207
|
-
.push({ containerPort: portValue });
|
|
208
|
-
} else {
|
|
209
|
-
this.resource.spec.template.spec.containers[0].ports
|
|
210
|
-
.push(
|
|
211
|
-
{
|
|
212
|
-
name: portValue.name,
|
|
213
|
-
containerPort: portValue.port
|
|
214
|
-
}
|
|
215
|
-
);
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
_setImagePullSecret() {
|
|
222
|
-
if (this.terasliceConfig.kubernetes_image_pull_secret) {
|
|
223
|
-
this.resource.spec.template.spec.imagePullSecrets = [
|
|
224
|
-
{ name: this.terasliceConfig.kubernetes_image_pull_secret }
|
|
225
|
-
];
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
_setPriorityClassName() {
|
|
230
|
-
if (this.terasliceConfig.kubernetes_priority_class_name) {
|
|
231
|
-
if (this.nodeType === 'execution_controller') {
|
|
232
|
-
// eslint-disable-next-line max-len
|
|
233
|
-
this.resource.spec.template.spec.priorityClassName = this.terasliceConfig.kubernetes_priority_class_name;
|
|
234
|
-
if (this.execution.stateful) {
|
|
235
|
-
// eslint-disable-next-line max-len
|
|
236
|
-
this.resource.spec.template.metadata.labels[`${this.jobPropertyLabelPrefix}/stateful`] = 'true';
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
if (this.nodeType === 'worker' && this.execution.stateful) {
|
|
240
|
-
// eslint-disable-next-line max-len
|
|
241
|
-
this.resource.spec.template.spec.priorityClassName = this.terasliceConfig.kubernetes_priority_class_name;
|
|
242
|
-
// eslint-disable-next-line max-len
|
|
243
|
-
this.resource.spec.template.metadata.labels[`${this.jobPropertyLabelPrefix}/stateful`] = 'true';
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
_setAssetsVolume() {
|
|
249
|
-
if (this.terasliceConfig.assets_directory && this.terasliceConfig.assets_volume) {
|
|
250
|
-
this.resource.spec.template.spec.volumes.push({
|
|
251
|
-
name: this.terasliceConfig.assets_volume,
|
|
252
|
-
persistentVolumeClaim: { claimName: this.terasliceConfig.assets_volume }
|
|
253
|
-
});
|
|
254
|
-
this.resource.spec.template.spec.containers[0].volumeMounts.push({
|
|
255
|
-
name: this.terasliceConfig.assets_volume,
|
|
256
|
-
mountPath: this.terasliceConfig.assets_directory
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
_setJobLabels() {
|
|
262
|
-
if (this.execution.labels != null) {
|
|
263
|
-
Object.entries(this.execution.labels).forEach(([k, v]) => {
|
|
264
|
-
const key = `${this.jobLabelPrefix}/${_.replace(k, /[^a-zA-Z0-9\-._]/g, '-').substring(0, 63)}`;
|
|
265
|
-
const value = _.replace(v, /[^a-zA-Z0-9\-._]/g, '-').substring(0, 63);
|
|
266
|
-
this.resource.metadata.labels[key] = value;
|
|
267
|
-
|
|
268
|
-
if (this.resource.kind !== 'Service') {
|
|
269
|
-
// Services don't have templates, so if it's a service,
|
|
270
|
-
// don't add this
|
|
271
|
-
this.resource.spec.template.metadata.labels[key] = value;
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
_setVolumes() {
|
|
278
|
-
if (this.execution.volumes != null) {
|
|
279
|
-
_.forEach(this.execution.volumes, (volume) => {
|
|
280
|
-
this.resource.spec.template.spec.volumes.push({
|
|
281
|
-
name: volume.name,
|
|
282
|
-
persistentVolumeClaim: { claimName: volume.name }
|
|
283
|
-
});
|
|
284
|
-
this.resource.spec.template.spec.containers[0].volumeMounts.push({
|
|
285
|
-
name: volume.name,
|
|
286
|
-
mountPath: volume.path
|
|
287
|
-
});
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
_setResources() {
|
|
293
|
-
let cpu;
|
|
294
|
-
let memory;
|
|
295
|
-
let maxMemory;
|
|
296
|
-
|
|
297
|
-
const container = this.resource.spec.template.spec.containers[0];
|
|
298
|
-
|
|
299
|
-
// use teraslice config as defaults and execution config will override it
|
|
300
|
-
const envVars = Object.assign({}, this.terasliceConfig.env_vars, this.execution.env_vars);
|
|
301
|
-
|
|
302
|
-
if (this.nodeType === 'worker') {
|
|
303
|
-
if (this.execution.resources_requests_cpu
|
|
304
|
-
|| this.execution.resources_limits_cpu) {
|
|
305
|
-
if (this.execution.resources_requests_cpu) {
|
|
306
|
-
_.set(container, 'resources.requests.cpu', this.execution.resources_requests_cpu);
|
|
307
|
-
}
|
|
308
|
-
if (this.execution.resources_limits_cpu) {
|
|
309
|
-
_.set(container, 'resources.limits.cpu', this.execution.resources_limits_cpu);
|
|
310
|
-
}
|
|
311
|
-
} else if (this.execution.cpu || this.terasliceConfig.cpu) {
|
|
312
|
-
// The settings on the executions override the cluster configs
|
|
313
|
-
cpu = this.execution.cpu || this.terasliceConfig.cpu || -1;
|
|
314
|
-
_.set(container, 'resources.requests.cpu', cpu);
|
|
315
|
-
_.set(container, 'resources.limits.cpu', cpu);
|
|
316
|
-
}
|
|
317
|
-
if (this.execution.resources_requests_memory
|
|
318
|
-
|| this.execution.resources_limits_memory) {
|
|
319
|
-
_.set(container, 'resources.requests.memory', this.execution.resources_requests_memory);
|
|
320
|
-
_.set(container, 'resources.limits.memory', this.execution.resources_limits_memory);
|
|
321
|
-
maxMemory = this.execution.resources_limits_memory;
|
|
322
|
-
} else if (this.execution.memory || this.terasliceConfig.memory) {
|
|
323
|
-
// The settings on the executions override the cluster configs
|
|
324
|
-
memory = this.execution.memory || this.terasliceConfig.memory || -1;
|
|
325
|
-
_.set(container, 'resources.requests.memory', memory);
|
|
326
|
-
_.set(container, 'resources.limits.memory', memory);
|
|
327
|
-
maxMemory = memory;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
if (this.nodeType === 'execution_controller') {
|
|
332
|
-
// The settings on the executions override the cluster configs
|
|
333
|
-
cpu = this.execution.cpu_execution_controller
|
|
334
|
-
|| this.terasliceConfig.cpu_execution_controller || -1;
|
|
335
|
-
memory = this.execution.memory_execution_controller
|
|
336
|
-
|| this.terasliceConfig.memory_execution_controller || -1;
|
|
337
|
-
_.set(container, 'resources.requests.cpu', cpu);
|
|
338
|
-
_.set(container, 'resources.limits.cpu', cpu);
|
|
339
|
-
_.set(container, 'resources.requests.memory', memory);
|
|
340
|
-
_.set(container, 'resources.limits.memory', memory);
|
|
341
|
-
maxMemory = memory;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// NOTE: This sucks, this manages the memory env var but it ALSO is
|
|
345
|
-
// responsible for doing the config and execution env var merge, which
|
|
346
|
-
// should NOT be in this function
|
|
347
|
-
setMaxOldSpaceViaEnv(container.env, envVars, maxMemory);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
_setTargets() {
|
|
351
|
-
if (_.has(this.execution, 'targets') && (!_.isEmpty(this.execution.targets))) {
|
|
352
|
-
_.forEach(this.execution.targets, (target) => {
|
|
353
|
-
// `required` is the default if no `constraint` is provided for
|
|
354
|
-
// backwards compatibility and as the most likely case
|
|
355
|
-
if (target.constraint === 'required' || !_.has(target, 'constraint')) {
|
|
356
|
-
this._setTargetRequired(target);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
if (target.constraint === 'preferred') {
|
|
360
|
-
this._setTargetPreferred(target);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
if (target.constraint === 'accepted') {
|
|
364
|
-
this._setTargetAccepted(target);
|
|
365
|
-
}
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
_setTargetRequired(target) {
|
|
371
|
-
const targetKey = 'spec.template.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution';
|
|
372
|
-
if (!_.has(this.resource, targetKey)) {
|
|
373
|
-
const nodeSelectorObj = {
|
|
374
|
-
nodeSelectorTerms: [{ matchExpressions: [] }]
|
|
375
|
-
};
|
|
376
|
-
_.set(this.resource, targetKey, nodeSelectorObj);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
this.resource.spec.template.spec.affinity.nodeAffinity
|
|
380
|
-
.requiredDuringSchedulingIgnoredDuringExecution
|
|
381
|
-
.nodeSelectorTerms[0].matchExpressions.push({
|
|
382
|
-
key: target.key,
|
|
383
|
-
operator: 'In',
|
|
384
|
-
values: [target.value]
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
_setTargetPreferred(target) {
|
|
389
|
-
const targetKey = 'spec.template.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution';
|
|
390
|
-
if (!_.has(this.resource, targetKey)) {
|
|
391
|
-
_.set(this.resource, targetKey, []);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
this.resource.spec.template.spec.affinity.nodeAffinity
|
|
395
|
-
.preferredDuringSchedulingIgnoredDuringExecution.push({
|
|
396
|
-
weight: 1,
|
|
397
|
-
preference: {
|
|
398
|
-
matchExpressions: [{
|
|
399
|
-
key: target.key,
|
|
400
|
-
operator: 'In',
|
|
401
|
-
values: [target.value]
|
|
402
|
-
}]
|
|
403
|
-
}
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
_setTargetAccepted(target) {
|
|
408
|
-
const targetKey = 'spec.template.spec.tolerations';
|
|
409
|
-
if (!_.has(this.resource, targetKey)) {
|
|
410
|
-
_.set(this.resource, targetKey, []);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
this.resource.spec.template.spec.tolerations.push({
|
|
414
|
-
key: target.key,
|
|
415
|
-
operator: 'Equal',
|
|
416
|
-
value: target.value,
|
|
417
|
-
effect: 'NoSchedule'
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* _mergePodSpecOverlay - allows the author of the job to override anything
|
|
423
|
-
* in the pod .spec for both the execution controller and the worker pods
|
|
424
|
-
* created in Kubernetes. This can be useful in many ways including these:
|
|
425
|
-
*
|
|
426
|
-
* * add `initContainers` to the pods
|
|
427
|
-
* * add `hostAliases` to the pods
|
|
428
|
-
*
|
|
429
|
-
* Note that this happens at the end of the process, so anything added by
|
|
430
|
-
* this overlay will overwrite any other setting set on the job or by the
|
|
431
|
-
* config.
|
|
432
|
-
*
|
|
433
|
-
* Job setting: `pod_spec_override`
|
|
434
|
-
*/
|
|
435
|
-
_mergePodSpecOverlay() {
|
|
436
|
-
this.resource.spec.template.spec = _.merge(
|
|
437
|
-
this.resource.spec.template.spec,
|
|
438
|
-
this.execution.pod_spec_override
|
|
439
|
-
);
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
module.exports = K8sResource;
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const _ = require('lodash');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Given the k8s Pods API output generates the appropriate Teraslice cluster
|
|
7
|
-
* state. NOTE: This assumes the pods have already been filtered to ensure they
|
|
8
|
-
* are teraslice pods and match the cluster in question.
|
|
9
|
-
* @param {Object} k8sPods k8s pods API object (k8s v1.10+)
|
|
10
|
-
* @param {Object} clusterState Teraslice Cluster State
|
|
11
|
-
* @param {String} clusterNameLabel k8s label containing clusterName
|
|
12
|
-
*/
|
|
13
|
-
function gen(k8sPods, clusterState) {
|
|
14
|
-
// Make sure we clean up the old
|
|
15
|
-
const hostIPs = _.uniq(_.map(k8sPods.items, 'status.hostIP'));
|
|
16
|
-
const oldHostIps = _.difference(_.keys(clusterState), hostIPs);
|
|
17
|
-
_.forEach(oldHostIps, (ip) => {
|
|
18
|
-
delete clusterState[ip];
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
// Loop over the nodes in clusterState and set active = [] so we can append
|
|
22
|
-
// later
|
|
23
|
-
Object.keys(clusterState).forEach((nodeId) => {
|
|
24
|
-
clusterState[nodeId].active = [];
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
// add a worker for each pod
|
|
28
|
-
k8sPods.items.forEach((pod) => {
|
|
29
|
-
if (!_.has(clusterState, pod.status.hostIP)) {
|
|
30
|
-
// If the node isn't in clusterState, add it
|
|
31
|
-
clusterState[pod.status.hostIP] = {
|
|
32
|
-
node_id: pod.status.hostIP,
|
|
33
|
-
hostname: pod.status.hostIP,
|
|
34
|
-
pid: 'N/A',
|
|
35
|
-
node_version: 'N/A',
|
|
36
|
-
teraslice_version: 'N/A',
|
|
37
|
-
total: 'N/A',
|
|
38
|
-
state: 'connected',
|
|
39
|
-
available: 'N/A',
|
|
40
|
-
active: []
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const worker = {
|
|
45
|
-
assets: [],
|
|
46
|
-
assignment: pod.metadata.labels['app.kubernetes.io/component'],
|
|
47
|
-
ex_id: pod.metadata.labels['teraslice.terascope.io/exId'],
|
|
48
|
-
// WARNING: This makes the assumption that the first container
|
|
49
|
-
// in the pod is the teraslice container. Currently it is the
|
|
50
|
-
// only container, so this assumption is safe for now.
|
|
51
|
-
image: pod.spec.containers[0].image,
|
|
52
|
-
job_id: pod.metadata.labels['teraslice.terascope.io/jobId'],
|
|
53
|
-
pod_name: pod.metadata.name,
|
|
54
|
-
pod_ip: pod.status.podIP,
|
|
55
|
-
worker_id: pod.metadata.name,
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
// k8s pods can have status.phase = `Pending`, `Running`, `Succeeded`,
|
|
59
|
-
// `Failed`, `Unknown`. We will only add `Running` pods to the
|
|
60
|
-
// Teraslice cluster state.
|
|
61
|
-
if (pod.status.phase === 'Running') {
|
|
62
|
-
clusterState[pod.status.hostIP].active.push(worker);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
exports.gen = gen;
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const barbe = require('barbe');
|
|
6
|
-
|
|
7
|
-
const { isTest } = require('@terascope/utils');
|
|
8
|
-
|
|
9
|
-
function makeTemplate(folder, fileName) {
|
|
10
|
-
const filePath = path.join(__dirname, folder, `${fileName}.hbs`);
|
|
11
|
-
const templateData = fs.readFileSync(filePath, 'utf-8');
|
|
12
|
-
const templateKeys = ['{{', '}}'];
|
|
13
|
-
|
|
14
|
-
return (config) => {
|
|
15
|
-
const templated = barbe(templateData, templateKeys, config);
|
|
16
|
-
return JSON.parse(templated);
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Convert bytes to MB and reduce by 10%
|
|
21
|
-
function getMaxOldSpace(memory) {
|
|
22
|
-
return Math.round(0.9 * (memory / 1024 / 1024));
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function setMaxOldSpaceViaEnv(envArr, jobEnv, memory) {
|
|
26
|
-
const envObj = {};
|
|
27
|
-
if (memory && memory > -1) {
|
|
28
|
-
// Set NODE_OPTIONS to override max-old-space-size
|
|
29
|
-
const maxOldSpace = getMaxOldSpace(memory);
|
|
30
|
-
envObj.NODE_OPTIONS = `--max-old-space-size=${maxOldSpace}`;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
Object.assign(envObj, jobEnv);
|
|
34
|
-
|
|
35
|
-
Object.entries(envObj).forEach(([name, value]) => {
|
|
36
|
-
envArr.push({
|
|
37
|
-
name,
|
|
38
|
-
value
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const MAX_RETRIES = isTest ? 2 : 3;
|
|
44
|
-
const RETRY_DELAY = isTest ? 50 : 1000; // time in ms
|
|
45
|
-
|
|
46
|
-
function getRetryConfig() {
|
|
47
|
-
return {
|
|
48
|
-
retries: MAX_RETRIES,
|
|
49
|
-
delay: RETRY_DELAY
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
module.exports = {
|
|
54
|
-
getMaxOldSpace,
|
|
55
|
-
getRetryConfig,
|
|
56
|
-
makeTemplate,
|
|
57
|
-
setMaxOldSpaceViaEnv
|
|
58
|
-
};
|