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