teraslice 2.11.0 → 2.12.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.
Files changed (118) hide show
  1. package/dist/src/interfaces.js +12 -0
  2. package/dist/src/lib/cluster/cluster_master.js +246 -0
  3. package/dist/src/lib/cluster/node_master.js +355 -0
  4. package/dist/src/lib/cluster/services/api.js +663 -0
  5. package/dist/src/lib/cluster/services/assets.js +226 -0
  6. package/dist/src/lib/cluster/services/cluster/backends/kubernetes/index.js +192 -0
  7. package/dist/src/lib/cluster/services/cluster/backends/kubernetes/k8s.js +481 -0
  8. package/dist/src/lib/cluster/services/cluster/backends/kubernetes/k8sResource.js +414 -0
  9. package/dist/src/lib/cluster/services/cluster/backends/kubernetes/k8sState.js +59 -0
  10. package/dist/src/lib/cluster/services/cluster/backends/kubernetes/utils.js +43 -0
  11. package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/index.js +192 -0
  12. package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/interfaces.js +2 -0
  13. package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8s.js +423 -0
  14. package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sDeploymentResource.js +60 -0
  15. package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sJobResource.js +55 -0
  16. package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sResource.js +359 -0
  17. package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sServiceResource.js +37 -0
  18. package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sState.js +60 -0
  19. package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/utils.js +170 -0
  20. package/dist/src/lib/cluster/services/cluster/backends/native/dispatch.js +13 -0
  21. package/dist/src/lib/cluster/services/cluster/backends/native/index.js +526 -0
  22. package/dist/src/lib/cluster/services/cluster/backends/native/messaging.js +547 -0
  23. package/dist/src/lib/cluster/services/cluster/backends/state-utils.js +26 -0
  24. package/dist/src/lib/cluster/services/cluster/index.js +17 -0
  25. package/dist/src/lib/cluster/services/execution.js +435 -0
  26. package/dist/src/lib/cluster/services/index.js +6 -0
  27. package/dist/src/lib/cluster/services/interfaces.js +2 -0
  28. package/dist/src/lib/cluster/services/jobs.js +454 -0
  29. package/dist/src/lib/config/default-sysconfig.js +26 -0
  30. package/dist/src/lib/config/index.js +22 -0
  31. package/dist/src/lib/config/schemas/system.js +360 -0
  32. package/dist/src/lib/storage/analytics.js +86 -0
  33. package/dist/src/lib/storage/assets.js +401 -0
  34. package/dist/src/lib/storage/backends/elasticsearch_store.js +494 -0
  35. package/dist/src/lib/storage/backends/mappings/analytics.js +50 -0
  36. package/dist/src/lib/storage/backends/mappings/asset.js +41 -0
  37. package/dist/src/lib/storage/backends/mappings/ex.js +62 -0
  38. package/dist/src/lib/storage/backends/mappings/job.js +38 -0
  39. package/dist/src/lib/storage/backends/mappings/state.js +38 -0
  40. package/dist/src/lib/storage/backends/s3_store.js +237 -0
  41. package/dist/src/lib/storage/execution.js +300 -0
  42. package/dist/src/lib/storage/index.js +7 -0
  43. package/dist/src/lib/storage/jobs.js +81 -0
  44. package/dist/src/lib/storage/state.js +255 -0
  45. package/dist/src/lib/utils/api_utils.js +157 -0
  46. package/dist/src/lib/utils/asset_utils.js +94 -0
  47. package/dist/src/lib/utils/date_utils.js +52 -0
  48. package/dist/src/lib/utils/encoding_utils.js +27 -0
  49. package/dist/src/lib/utils/events.js +4 -0
  50. package/dist/src/lib/utils/file_utils.js +124 -0
  51. package/dist/src/lib/utils/id_utils.js +15 -0
  52. package/dist/src/lib/utils/port_utils.js +32 -0
  53. package/dist/src/lib/workers/assets/index.js +3 -0
  54. package/dist/src/lib/workers/assets/loader-executable.js +40 -0
  55. package/dist/src/lib/workers/assets/loader.js +73 -0
  56. package/dist/src/lib/workers/assets/spawn.js +55 -0
  57. package/dist/src/lib/workers/context/execution-context.js +12 -0
  58. package/dist/src/lib/workers/context/terafoundation-context.js +8 -0
  59. package/dist/src/lib/workers/execution-controller/execution-analytics.js +188 -0
  60. package/dist/src/lib/workers/execution-controller/index.js +1024 -0
  61. package/dist/src/lib/workers/execution-controller/recovery.js +151 -0
  62. package/dist/src/lib/workers/execution-controller/scheduler.js +390 -0
  63. package/dist/src/lib/workers/execution-controller/slice-analytics.js +96 -0
  64. package/dist/src/lib/workers/helpers/job.js +80 -0
  65. package/dist/src/lib/workers/helpers/op-analytics.js +22 -0
  66. package/dist/src/lib/workers/helpers/terafoundation.js +34 -0
  67. package/dist/src/lib/workers/helpers/worker-shutdown.js +169 -0
  68. package/dist/src/lib/workers/metrics/index.js +108 -0
  69. package/dist/src/lib/workers/worker/index.js +378 -0
  70. package/dist/src/lib/workers/worker/slice.js +122 -0
  71. package/dist/test/config/schemas/system_schema-spec.js +37 -0
  72. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/k8s-spec.js +316 -0
  73. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/k8sResource-spec.js +795 -0
  74. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/k8sState-multicluster-spec.js +67 -0
  75. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/k8sState-spec.js +84 -0
  76. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/utils-spec.js +132 -0
  77. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8s-v2-spec.js +455 -0
  78. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8sResource-v2-spec.js +818 -0
  79. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8sState-multicluster-v2-spec.js +67 -0
  80. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8sState-v2-spec.js +84 -0
  81. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/utils-v2-spec.js +320 -0
  82. package/dist/test/lib/cluster/services/cluster/backends/state-utils-spec.js +37 -0
  83. package/dist/test/node_master-spec.js +188 -0
  84. package/dist/test/services/api-spec.js +80 -0
  85. package/dist/test/services/assets-spec.js +158 -0
  86. package/dist/test/services/messaging-spec.js +440 -0
  87. package/dist/test/storage/assets_storage-spec.js +95 -0
  88. package/dist/test/storage/s3_store-spec.js +138 -0
  89. package/dist/test/test.config.js +8 -0
  90. package/dist/test/test.setup.js +6 -0
  91. package/dist/test/utils/api_utils-spec.js +86 -0
  92. package/dist/test/utils/asset_utils-spec.js +141 -0
  93. package/dist/test/utils/elastic_utils-spec.js +25 -0
  94. package/dist/test/workers/execution-controller/execution-controller-spec.js +371 -0
  95. package/dist/test/workers/execution-controller/execution-special-test-cases-spec.js +520 -0
  96. package/dist/test/workers/execution-controller/execution-test-cases-spec.js +338 -0
  97. package/dist/test/workers/execution-controller/recovery-spec.js +160 -0
  98. package/dist/test/workers/execution-controller/scheduler-spec.js +249 -0
  99. package/dist/test/workers/execution-controller/slice-analytics-spec.js +121 -0
  100. package/dist/test/workers/fixtures/ops/example-op/processor.js +20 -0
  101. package/dist/test/workers/fixtures/ops/example-op/schema.js +19 -0
  102. package/dist/test/workers/fixtures/ops/example-reader/fetcher.js +20 -0
  103. package/dist/test/workers/fixtures/ops/example-reader/schema.js +41 -0
  104. package/dist/test/workers/fixtures/ops/example-reader/slicer.js +37 -0
  105. package/dist/test/workers/fixtures/ops/new-op/processor.js +29 -0
  106. package/dist/test/workers/fixtures/ops/new-op/schema.js +18 -0
  107. package/dist/test/workers/fixtures/ops/new-reader/fetcher.js +19 -0
  108. package/dist/test/workers/fixtures/ops/new-reader/schema.js +23 -0
  109. package/dist/test/workers/fixtures/ops/new-reader/slicer.js +13 -0
  110. package/dist/test/workers/helpers/configs.js +130 -0
  111. package/dist/test/workers/helpers/execution-controller-helper.js +49 -0
  112. package/dist/test/workers/helpers/index.js +5 -0
  113. package/dist/test/workers/helpers/test-context.js +210 -0
  114. package/dist/test/workers/helpers/zip-directory.js +25 -0
  115. package/dist/test/workers/worker/slice-spec.js +333 -0
  116. package/dist/test/workers/worker/worker-spec.js +356 -0
  117. package/package.json +94 -94
  118. package/service.js +0 -0
@@ -0,0 +1,454 @@
1
+ import { TSError, uniq, cloneDeep, isEmpty, getTypeOf, isString, makeISODate, defaultsDeep } from '@terascope/utils';
2
+ import { JobValidator, parseName } from '@terascope/job-components';
3
+ import { makeLogger } from '../../workers/helpers/terafoundation.js';
4
+ import { spawnAssetLoader } from '../../workers/assets/spawn.js';
5
+ /**
6
+ * New execution result
7
+ * @typedef NewExecutionResult
8
+ * @property {string} job_id
9
+ * @property {string} ex_id
10
+ */
11
+ export class JobsService {
12
+ context;
13
+ jobValidator;
14
+ logger;
15
+ jobsStorage;
16
+ executionStorage;
17
+ executionService;
18
+ constructor(context) {
19
+ this.context = context;
20
+ this.logger = makeLogger(context, 'jobs_service');
21
+ this.jobValidator = new JobValidator(context);
22
+ }
23
+ async initialize() {
24
+ this.logger.info('job service is initializing...');
25
+ const { executionStorage, jobsStorage } = this.context.stores;
26
+ if (jobsStorage == null || executionStorage == null) {
27
+ throw new Error('Missing required stores');
28
+ }
29
+ const { executionService } = this.context.services;
30
+ if (executionService == null) {
31
+ throw new Error('Missing required services');
32
+ }
33
+ this.jobsStorage = jobsStorage;
34
+ this.executionStorage = executionStorage;
35
+ this.executionService = executionService;
36
+ }
37
+ /**
38
+ * Validate the job spec
39
+ *
40
+ * @returns {Promise<import('@terascope/job-components').ValidatedJobConfig>}
41
+ */
42
+ async _validateJobSpec(jobSpec) {
43
+ const parsedAssetJob = await this._ensureAssets(cloneDeep(jobSpec));
44
+ const validJob = await this.jobValidator.validateConfig(parsedAssetJob);
45
+ return validJob;
46
+ }
47
+ async submitJob(jobSpec, shouldRun) {
48
+ // @ts-expect-error
49
+ if (jobSpec.job_id) {
50
+ throw new TSError('Job cannot include a job_id on submit', {
51
+ statusCode: 422,
52
+ });
53
+ }
54
+ this.addExternalPortsToJobSpec(jobSpec);
55
+ const validJob = await this._validateJobSpec(jobSpec);
56
+ // We don't create with the fully parsed validJob as it changes the asset names
57
+ // to their asset id which we don't want stored as at the job level
58
+ const job = await this.jobsStorage.create(jobSpec);
59
+ if (!shouldRun) {
60
+ return { job_id: job.job_id };
61
+ }
62
+ const jobRecord = Object.assign({}, jobSpec, validJob, {
63
+ job_id: job.job_id
64
+ });
65
+ return this.executionService.createExecutionContext(jobRecord);
66
+ }
67
+ /**
68
+ * Sets the `active` property on the job to `true` or `false`.
69
+ * @param {string} jobId
70
+ * @param {boolean} activeState
71
+ */
72
+ async setActiveState(jobId, activeState) {
73
+ const job = await this.jobsStorage.get(jobId);
74
+ if (activeState === true) {
75
+ job.active = true;
76
+ }
77
+ else {
78
+ job.active = false;
79
+ }
80
+ this.logger.info(`Setting jobId: ${jobId} to active: ${activeState}`);
81
+ return this.updateJob(jobId, job);
82
+ }
83
+ async updateJob(jobId, jobSpec) {
84
+ const originalJob = await this.jobsStorage.get(jobId);
85
+ if (originalJob._deleted === true) {
86
+ throw new TSError(`Job ${jobId} has been deleted and cannot be updated.`, {
87
+ statusCode: 410
88
+ });
89
+ }
90
+ // If job is switching from active to inactive job validation is skipped
91
+ // This allows for old jobs that are missing required resources to be marked inactive
92
+ if (originalJob.active !== false && jobSpec.active === false) {
93
+ this.logger.info(`Skipping job validation to set jobId ${jobId} as _inactive`);
94
+ }
95
+ else {
96
+ this.addExternalPortsToJobSpec(jobSpec);
97
+ await this._validateJobSpec(jobSpec);
98
+ }
99
+ return this.jobsStorage.update(jobId, Object.assign({}, jobSpec, {
100
+ _created: originalJob._created
101
+ }));
102
+ }
103
+ /**
104
+ * Start a Job
105
+ *
106
+ * @param {string} jobId
107
+ * @returns {Promise<NewExecutionResult>}
108
+ */
109
+ async startJob(jobId) {
110
+ const activeExecution = await this._getActiveExecution(jobId, true);
111
+ // searching for an active execution, if there is then we reject
112
+ if (activeExecution) {
113
+ throw new TSError(`Job ${jobId} is currently running, cannot have the same job concurrently running`, {
114
+ statusCode: 409
115
+ });
116
+ }
117
+ let currentResources = await this.executionService.listResourcesForJobId(jobId);
118
+ if (currentResources.length > 0) {
119
+ currentResources = currentResources.flat();
120
+ const exIdsSet = new Set();
121
+ for (const resource of currentResources) {
122
+ exIdsSet.add(resource.metadata.labels['teraslice.terascope.io/exId']);
123
+ }
124
+ const exIdsArr = Array.from(exIdsSet);
125
+ const exIdsString = exIdsArr.join(', ');
126
+ throw new TSError(`There are orphaned resources for job: ${jobId}, exId: ${exIdsString}.
127
+ Please wait for Kubernetes to clean up orphaned resources.`);
128
+ }
129
+ const jobSpec = await this.jobsStorage.get(jobId);
130
+ if (jobSpec._deleted === true) {
131
+ throw new TSError(`Job ${jobId} has been deleted and cannot be started.`, {
132
+ statusCode: 410
133
+ });
134
+ }
135
+ const validJob = await this._validateJobSpec(jobSpec);
136
+ if (validJob.autorecover) {
137
+ return this._recoverValidJob(validJob);
138
+ }
139
+ return this.executionService.createExecutionContext(validJob);
140
+ }
141
+ /**
142
+ * Recover a job using the valid configuration
143
+ *
144
+ * @private
145
+ * @param {import('@terascope/job-components').ValidatedJobConfig} validJob
146
+ * @param {import('@terascope/job-components').RecoveryCleanupType} [cleanupType]
147
+ * @returns {Promise<NewExecutionResult>}
148
+ */
149
+ async _recoverValidJob(validJob, cleanupType) {
150
+ const recoverFrom = await this.getLatestExecution(validJob.job_id, undefined, true);
151
+ // if there isn't an execution and autorecover is true
152
+ // create a new execution else throw
153
+ if (!recoverFrom) {
154
+ if (validJob.autorecover) {
155
+ return this.executionService.createExecutionContext(validJob);
156
+ }
157
+ throw new TSError(`Job ${validJob.job_id} is missing an execution to recover from`, {
158
+ statusCode: 404
159
+ });
160
+ }
161
+ if (validJob.slicers !== recoverFrom.slicers) {
162
+ const changedFrom = `from ${recoverFrom.slicers} to ${validJob.slicers}`;
163
+ this.logger.warn(`recovery for job ${recoverFrom.job_id} changed slicers ${changedFrom}`);
164
+ }
165
+ return this.executionService.recoverExecution(
166
+ // apply the latest job config changes
167
+ defaultsDeep({}, validJob, recoverFrom), cleanupType);
168
+ }
169
+ /**
170
+ * Recover a job, applied the last changes to the prev execution
171
+ *
172
+ * @param {string} jobId
173
+ * @param {import('@terascope/job-components').RecoveryCleanupType} [cleanupType]
174
+ * @returns {Promise<NewExecutionResult>}
175
+ */
176
+ async recoverJob(jobId, cleanupType) {
177
+ // we need to do validations since the job config could change between recovery
178
+ const jobSpec = await this.jobsStorage.get(jobId);
179
+ if (jobSpec._deleted === true) {
180
+ throw new TSError(`Job ${jobId} has been deleted and cannot be recovered.`, {
181
+ statusCode: 410
182
+ });
183
+ }
184
+ const validJob = await this._validateJobSpec(jobSpec);
185
+ return this._recoverValidJob(validJob, cleanupType);
186
+ }
187
+ async pauseJob(jobId) {
188
+ const exId = await this.getLatestExecutionId(jobId);
189
+ return this.executionService.pauseExecution(exId);
190
+ }
191
+ async resumeJob(jobId) {
192
+ const exId = await this.getLatestExecutionId(jobId);
193
+ return this.executionService.resumeExecution(exId);
194
+ }
195
+ async softDeleteJob(jobId) {
196
+ const activeExecution = await this._getActiveExecution(jobId, true);
197
+ // searching for an active execution, if there is then we reject
198
+ if (activeExecution) {
199
+ throw new TSError(`Job ${jobId} is currently running, cannot delete a running job.`, {
200
+ statusCode: 409
201
+ });
202
+ }
203
+ // This will return any orphaned resources in k8s clustering
204
+ // or an empty array in native clustering
205
+ let currentResources = await this.executionService.listResourcesForJobId(jobId);
206
+ if (currentResources.length > 0) {
207
+ currentResources = currentResources.flat();
208
+ const exIdsSet = new Set();
209
+ for (const resource of currentResources) {
210
+ exIdsSet.add(resource.metadata.labels['teraslice.terascope.io/exId']);
211
+ }
212
+ const exIdsArr = Array.from(exIdsSet);
213
+ const exIdsString = exIdsArr.join(', ');
214
+ this.logger.info(`There are orphaned resources for job: ${jobId}, exId: ${exIdsString}.\n`
215
+ + 'Removing resources before job deletion.');
216
+ await Promise.all(exIdsArr
217
+ .map((exId) => this.executionService.stopExecution(exId, { force: true })));
218
+ }
219
+ const jobSpec = await this.jobsStorage.get(jobId);
220
+ if (jobSpec._deleted === true) {
221
+ throw new TSError(`Job ${jobId} has already been deleted.`, {
222
+ statusCode: 410
223
+ });
224
+ }
225
+ jobSpec._deleted = true;
226
+ jobSpec._deleted_on = makeISODate();
227
+ jobSpec.active = false;
228
+ const executions = await this.getAllExecutions(jobId, undefined, true);
229
+ for (const execution of executions) {
230
+ await this.executionService.softDeleteExecutionContext(execution.ex_id);
231
+ }
232
+ return this.jobsStorage.update(jobId, jobSpec);
233
+ }
234
+ /**
235
+ * Get all executions related to a jobId
236
+ *
237
+ * @param {string} jobId
238
+ * @param {string} [query]
239
+ * @param {boolean=false} [allowZeroResults]
240
+ * @returns {Promise<import('@terascope/types').ExecutionConfig[]>}
241
+ */
242
+ async getAllExecutions(jobId, query, allowZeroResults = false) {
243
+ if (!jobId || !isString(jobId)) {
244
+ throw new TSError(`Invalid job id, got ${getTypeOf(jobId)}`);
245
+ }
246
+ const executions = await this.executionStorage.search(query || `job_id: "${jobId}"`, undefined, undefined, '_created:desc');
247
+ if (!allowZeroResults && !executions.length) {
248
+ throw new TSError(`No executions were found for job ${jobId}`, {
249
+ statusCode: 404
250
+ });
251
+ }
252
+ return executions;
253
+ }
254
+ /**
255
+ * Get the latest execution
256
+ *
257
+ * @param {string} jobId
258
+ * @param {string} [query]
259
+ * @param {boolean=false} [allowZeroResults]
260
+ * @returns {Promise<import('@terascope/types').ExecutionConfig>}
261
+ */
262
+ async getLatestExecution(jobId, query, allowZeroResults = false) {
263
+ if (!jobId || !isString(jobId)) {
264
+ throw new TSError(`Invalid job id, got ${getTypeOf(jobId)}`);
265
+ }
266
+ const ex = await this.executionStorage.search(query || `job_id: "${jobId}"`, undefined, 1, '_created:desc');
267
+ if (!allowZeroResults && !ex.length) {
268
+ throw new TSError(`No execution was found for job ${jobId}`, {
269
+ statusCode: 404
270
+ });
271
+ }
272
+ return ex[0];
273
+ }
274
+ /**
275
+ * Get the job with the latest ex status
276
+ *
277
+ * @param {string} jobId
278
+ * @param {string} [fields]
279
+ * @returns {Promise<JobConfig>}
280
+ */
281
+ async getJobWithExInfo(jobId, fields) {
282
+ if (!jobId || !isString(jobId)) {
283
+ throw new TSError(`Invalid job id, got ${getTypeOf(jobId)}`);
284
+ }
285
+ const job = await this.jobsStorage.get(jobId);
286
+ const ex = await this.executionStorage.search(`job_id: "${jobId}"`, undefined, 1, '_created:desc', fields || undefined);
287
+ if (!ex.length || ex[0]._deleted === true) {
288
+ job.ex = {};
289
+ return job;
290
+ }
291
+ job.ex = ex[0];
292
+ return job;
293
+ }
294
+ /**
295
+ * Get a list of jobs with the latest ex status for each one
296
+ *
297
+ * @param {string} query
298
+ * @param {number} from
299
+ * @param {number} size
300
+ * @param {string} sort
301
+ * @param {string} [ex_fields]
302
+ * @returns {Promise<JobConfig>}
303
+ */
304
+ async getJobsWithExInfo(query, from, size, sort, ex_fields) {
305
+ const jobList = await this.jobsStorage.search(query, from, size, sort);
306
+ const finalList = [];
307
+ for (const job of jobList) {
308
+ finalList.push(await this.getJobWithExInfo(job.job_id, ex_fields));
309
+ }
310
+ return finalList;
311
+ }
312
+ /**
313
+ * Get the active execution
314
+ *
315
+ * @param {string} jobId
316
+ * @param {boolean} [allowZeroResults]
317
+ * @returns {Promise<import('@terascope/types').ExecutionConfig>}
318
+ */
319
+ async _getActiveExecution(jobId, allowZeroResults) {
320
+ const statuses = this.executionStorage
321
+ .getTerminalStatuses()
322
+ .map((state) => ` _status:"${state}"`)
323
+ .join(' OR ');
324
+ const query = `job_id:"${jobId}" AND _context:ex NOT (${statuses.trim()})`;
325
+ return this.getLatestExecution(jobId, query, allowZeroResults);
326
+ }
327
+ /**
328
+ * Get the active execution
329
+ *
330
+ * @param {string} jobId
331
+ * @returns {Promise<string>}
332
+ */
333
+ async _getActiveExecutionId(jobId) {
334
+ const { ex_id } = await this._getActiveExecution(jobId);
335
+ return ex_id;
336
+ }
337
+ async getLatestExecutionId(jobId) {
338
+ const { ex_id } = await this.getLatestExecution(jobId);
339
+ return ex_id;
340
+ }
341
+ async shutdown() {
342
+ }
343
+ async addWorkers(jobId, workerCount) {
344
+ const exId = await this._getActiveExecutionId(jobId);
345
+ return this.executionService.addWorkers(exId, workerCount);
346
+ }
347
+ async removeWorkers(jobId, workerCount) {
348
+ const exId = await this._getActiveExecutionId(jobId);
349
+ return this.executionService.removeWorkers(exId, workerCount);
350
+ }
351
+ async setWorkers(jobId, workerCount) {
352
+ const exId = await this._getActiveExecutionId(jobId);
353
+ return this.executionService.setWorkers(exId, workerCount);
354
+ }
355
+ async _ensureAssets(jobConfig) {
356
+ const jobAssets = uniq(jobConfig.assets || []);
357
+ if (isEmpty(jobAssets)) {
358
+ return cloneDeep(jobConfig);
359
+ }
360
+ // convert asset references to their id's
361
+ const assetIds = await spawnAssetLoader(jobAssets);
362
+ if (!assetIds.length) {
363
+ throw new Error(`no asset id's were found for assets: ${JSON.stringify(jobAssets)}`);
364
+ }
365
+ if (jobAssets.length !== assetIds.length) {
366
+ throw new Error(`job specified ${jobAssets.length} assets: ${jobConfig.assets} but only ${assetIds.length} where found, assets: ${assetIds}`);
367
+ }
368
+ // need to normalize asset identifiers to their id form
369
+ // but not mutate original job_spec
370
+ const parsedAssetJob = cloneDeep(jobConfig);
371
+ parsedAssetJob.assets = assetIds;
372
+ const assetMapping = new Map();
373
+ for (let i = 0; i < jobAssets.length; i++) {
374
+ assetMapping.set(jobAssets[i], assetIds[i]);
375
+ }
376
+ this.adjustNamesByAsset(parsedAssetJob, assetMapping);
377
+ return parsedAssetJob;
378
+ }
379
+ adjustNamesByAsset(jobConfig, dict) {
380
+ jobConfig.operations = jobConfig.operations.map((op) => {
381
+ if (op.api_name?.includes('@')) {
382
+ const { name, assetIdentifier, tag } = parseName(op.api_name);
383
+ const hashId = dict.get(assetIdentifier);
384
+ if (!hashId) {
385
+ throw new Error(`Invalid operation api_name for _op: ${name}, could not find the hashID for asset identifier ${assetIdentifier}`);
386
+ }
387
+ let hashedName = `${name}@${hashId}`;
388
+ if (tag) {
389
+ hashedName = `${hashedName}:${tag}`;
390
+ }
391
+ op.api_name = hashedName;
392
+ }
393
+ if (op._op.includes('@')) {
394
+ const { name, assetIdentifier } = parseName(op._op);
395
+ const hashId = dict.get(assetIdentifier);
396
+ if (!hashId) {
397
+ throw new Error(`Invalid operation name for _op: ${name}, could not find the hashID for asset identifier ${assetIdentifier}`);
398
+ }
399
+ op._op = `${name}@${hashId}`;
400
+ return op;
401
+ }
402
+ else {
403
+ return op;
404
+ }
405
+ });
406
+ if (jobConfig.apis) {
407
+ jobConfig.apis = jobConfig.apis.map((api) => {
408
+ if (api._name.includes('@')) {
409
+ const { name, assetIdentifier, tag } = parseName(api._name);
410
+ const hashId = dict.get(assetIdentifier);
411
+ if (!hashId) {
412
+ throw new Error(`Invalid api name for _name: ${name}, could not find the hashID for asset identifier ${assetIdentifier}`);
413
+ }
414
+ let hashedName = `${name}@${hashId}`;
415
+ if (tag) {
416
+ hashedName = `${hashedName}:${tag}`;
417
+ }
418
+ api._name = hashedName;
419
+ return api;
420
+ }
421
+ else {
422
+ return api;
423
+ }
424
+ });
425
+ }
426
+ }
427
+ /**
428
+ * Automatically add external_ports to jobSpec if needed.
429
+ * This ensures that the Prometheus exporter server can be scraped.
430
+ * Check if prom_metrics_enabled is true on jobSpec or teraslice config.
431
+ * If so, add or update external_ports property with correct port.
432
+ * @param {Partial<JobConfig>} jobSpec
433
+ */
434
+ addExternalPortsToJobSpec(jobSpec) {
435
+ const { prom_metrics_enabled: enabledInTF, prom_metrics_port: tfPort } = this.context.sysconfig.terafoundation;
436
+ const { prom_metrics_enabled: enabledInJob, prom_metrics_port: jobPort } = jobSpec;
437
+ const portToUse = jobPort || tfPort;
438
+ if (enabledInJob === true || (enabledInJob === undefined && enabledInTF)) {
439
+ let portPresent = false;
440
+ if (!jobSpec.external_ports) {
441
+ jobSpec.external_ports = [];
442
+ }
443
+ for (const item of jobSpec.external_ports) {
444
+ const currentPort = typeof item === 'number' ? item : item.port;
445
+ if (currentPort === portToUse)
446
+ portPresent = true;
447
+ }
448
+ if (!portPresent) {
449
+ jobSpec.external_ports.push({ name: 'metrics', port: portToUse });
450
+ }
451
+ }
452
+ }
453
+ }
454
+ //# sourceMappingURL=jobs.js.map
@@ -0,0 +1,26 @@
1
+ function getConnectors() {
2
+ const connectors = {
3
+ 'elasticsearch-next': {
4
+ default: {
5
+ node: ['localhost:9200']
6
+ }
7
+ },
8
+ kafka: {
9
+ default: {
10
+ brokers: ['localhost:9092']
11
+ }
12
+ }
13
+ };
14
+ return connectors;
15
+ }
16
+ export default {
17
+ terafoundation: {
18
+ environment: 'development',
19
+ connectors: getConnectors()
20
+ },
21
+ teraslice: {
22
+ master: true,
23
+ name: 'teracluster'
24
+ }
25
+ };
26
+ //# sourceMappingURL=default-sysconfig.js.map
@@ -0,0 +1,22 @@
1
+ import path from 'node:path';
2
+ import { get } from '@terascope/utils';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { formats } from '@terascope/job-components';
5
+ import { configSchema } from './schemas/system.js';
6
+ const filePath = fileURLToPath(new URL('.', import.meta.url));
7
+ export function clusterName(configFile) {
8
+ return get(configFile, 'teraslice.name', null);
9
+ }
10
+ export function getTerasliceConfig(sysconfig) {
11
+ return Object.assign({
12
+ name: 'teraslice',
13
+ default_config_file: path.join(filePath, 'default-sysconfig.js'),
14
+ config_schema: configSchema,
15
+ schema_formats: formats,
16
+ cluster_name: clusterName,
17
+ shutdownMessaging: false,
18
+ start_workers: false,
19
+ }, sysconfig);
20
+ }
21
+ export { formats, configSchema, };
22
+ //# sourceMappingURL=index.js.map