teraslice 2.10.0 → 2.12.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.
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 -93
  118. package/service.js +0 -0
@@ -0,0 +1,226 @@
1
+ import express from 'express';
2
+ import { TSError, parseErrorInfo, logError, toBoolean } from '@terascope/utils';
3
+ import { makeLogger } from '../../workers/helpers/terafoundation.js';
4
+ import { AssetsStorage } from '../../storage/index.js';
5
+ import { getBackendConfig } from '../../storage/assets.js';
6
+ import { makeTable, handleTerasliceRequest, getSearchOptions, sendError, } from '../../utils/api_utils.js';
7
+ export class AssetsService {
8
+ context;
9
+ assetsStorage;
10
+ logger;
11
+ port;
12
+ app;
13
+ running = false;
14
+ constructor(context) {
15
+ this.context = context;
16
+ this.logger = makeLogger(context, 'assets_service');
17
+ this.app = express();
18
+ const { port } = process.env;
19
+ this.port = port;
20
+ this.app.set('json spaces', 4);
21
+ this.app.use((req, _res, next) => {
22
+ // @ts-expect-error
23
+ req.logger = this.logger;
24
+ next();
25
+ });
26
+ }
27
+ async initialize() {
28
+ try {
29
+ this.assetsStorage = new AssetsStorage(this.context);
30
+ await this.assetsStorage.initialize();
31
+ this.app.get('/status', (req, res) => {
32
+ const requestHandler = handleTerasliceRequest(req, res);
33
+ requestHandler(async () => ({ available: this.running }));
34
+ });
35
+ this.app.post('/assets', (req, res) => {
36
+ const blocking = toBoolean(req.query.blocking);
37
+ this.logger.debug('loading an asset', { blocking });
38
+ const results = [];
39
+ req.on('data', (buff) => {
40
+ results.push(buff);
41
+ });
42
+ req.on('end', async () => {
43
+ try {
44
+ const data = Buffer.concat(results);
45
+ const { assetId, created } = await this.assetsStorage.save(data, blocking);
46
+ const code = created ? 201 : 200;
47
+ const assetResponse = {
48
+ asset_id: assetId,
49
+ _id: assetId
50
+ };
51
+ res.status(code).json(assetResponse);
52
+ }
53
+ catch (err) {
54
+ const { statusCode, message } = parseErrorInfo(err);
55
+ logError(this.logger, err, 'failure saving assets via proxy request');
56
+ sendError(res, statusCode, message, this.logger);
57
+ }
58
+ });
59
+ req.on('error', (err) => {
60
+ const { statusCode, message } = parseErrorInfo(err);
61
+ logError(this.logger, err, 'failure writing asset');
62
+ res.status(statusCode).send(message);
63
+ });
64
+ });
65
+ this.app.delete('/assets/:assetId', (req, res) => {
66
+ const { assetId } = req.params;
67
+ // @ts-expect-error
68
+ const requestHandler = handleTerasliceRequest(req, res, `Could not delete asset ${assetId}`);
69
+ if (assetId.length !== 40) {
70
+ res.status(400).json({
71
+ error: `asset ${assetId} is not formatted correctly, please provide the full asset_id`
72
+ });
73
+ }
74
+ else {
75
+ const assetResponse = {
76
+ asset_id: assetId,
77
+ _id: assetId
78
+ };
79
+ requestHandler(async () => {
80
+ await this.assetsStorage.remove(assetId);
81
+ return assetResponse;
82
+ });
83
+ }
84
+ });
85
+ this.app.get('/txt/assets', (req, res) => {
86
+ const query = 'id:*';
87
+ this.createAssetTable(query, req, res);
88
+ });
89
+ this.app.get('/txt/assets/:name', (req, res) => {
90
+ const query = `id:* AND name:"${req.params.name}"`;
91
+ this.createAssetTable(query, req, res);
92
+ });
93
+ this.app.get('/txt/assets/:name/:version', (req, res) => {
94
+ const query = `id:* AND name:"${req.params.name}" AND version:"${req.params.version}"`;
95
+ this.createAssetTable(query, req, res);
96
+ });
97
+ this.app.get('/assets', (req, res) => {
98
+ const query = 'id:*';
99
+ this.assetsSearch(query, req, res);
100
+ });
101
+ this.app.get('/assets/:name', (req, res) => {
102
+ const query = `id:* AND name:"${req.params.name}"`;
103
+ this.assetsSearch(query, req, res);
104
+ });
105
+ this.app.get('/assets/:name/:version', (req, res) => {
106
+ const query = `id:* AND name:"${req.params.name}" AND version:"${req.params.version}"`;
107
+ this.assetsSearch(query, req, res);
108
+ });
109
+ await new Promise((resolve, reject) => {
110
+ // @ts-expect-error
111
+ this.app.listen(this.port, (err) => {
112
+ if (err) {
113
+ reject(err);
114
+ return;
115
+ }
116
+ this.logger.info(`assets_service is listening on port ${this.port}`);
117
+ resolve(true);
118
+ });
119
+ // @ts-expect-error TODO: verify this
120
+ this.app.timeout = this.context.sysconfig.teraslice.api_response_timeout;
121
+ });
122
+ await this.assetsStorage.autoload();
123
+ this.running = true;
124
+ }
125
+ catch (err) {
126
+ this.running = false;
127
+ throw new TSError(err, {
128
+ reason: 'Failure while creating assets_service'
129
+ });
130
+ }
131
+ }
132
+ getS3AssetStatus(s3List, esList) {
133
+ const result = [...esList];
134
+ for (const esRecord of result) {
135
+ esRecord.external_storage = 'missing';
136
+ for (const s3Record of s3List) {
137
+ /// s3AssetId is just the file name without the .zip
138
+ const s3AssetId = s3Record.File.slice(0, -4);
139
+ if (s3AssetId === esRecord.id) {
140
+ esRecord.external_storage = 'available';
141
+ break;
142
+ }
143
+ }
144
+ }
145
+ return result;
146
+ }
147
+ createAssetTable(query, req, res) {
148
+ const { size, from, sort } = getSearchOptions(req, '_created:desc');
149
+ const defaults = [
150
+ 'name',
151
+ 'version',
152
+ 'id',
153
+ '_created',
154
+ 'description',
155
+ 'node_version',
156
+ 'platform',
157
+ 'arch'
158
+ ];
159
+ const s3Defaults = [...defaults, 'external_storage'];
160
+ function mapping(item) {
161
+ return (field) => {
162
+ if (field === 'description') {
163
+ return item[field] ? item[field].slice(0, 30) : item[field];
164
+ }
165
+ return item[field];
166
+ };
167
+ }
168
+ const requestHandler = handleTerasliceRequest(req, res, 'Could not get assets');
169
+ requestHandler(async () => {
170
+ const results = await this.assetsStorage.search(query, from, size, sort, defaults);
171
+ const assets = results.hits.hits.map((asset) => {
172
+ const record = asset._source;
173
+ record.id = asset._id;
174
+ return record;
175
+ });
176
+ const { assetConnectionType } = getBackendConfig(this.context, this.logger);
177
+ if (assetConnectionType === 's3') {
178
+ const s3Assets = await this.assetsStorage.grabS3Info();
179
+ const updatedAssets = this.getS3AssetStatus(s3Assets, assets);
180
+ return makeTable(req, s3Defaults, updatedAssets, mapping);
181
+ }
182
+ return makeTable(req, defaults, assets, mapping);
183
+ });
184
+ }
185
+ assetsSearch(query, req, res) {
186
+ const { size, from, sort } = getSearchOptions(req, '_created:desc');
187
+ const requestHandler = handleTerasliceRequest(req, res, 'Could not get assets');
188
+ requestHandler(async () => {
189
+ const fields = ['_created', 'name', 'version', 'description', 'node_version', 'platform', 'arch'];
190
+ const results = await this.assetsStorage.search(query, from, size, sort, fields);
191
+ const mappedRecords = results.hits.hits.map((asset) => {
192
+ const record = asset._source;
193
+ record.id = asset._id;
194
+ return record;
195
+ });
196
+ const { assetConnectionType } = getBackendConfig(this.context, this.logger);
197
+ if (assetConnectionType === 's3') {
198
+ const s3Assets = await this.assetsStorage.grabS3Info();
199
+ const updatedAssets = this.getS3AssetStatus(s3Assets, mappedRecords);
200
+ return updatedAssets;
201
+ }
202
+ return mappedRecords;
203
+ });
204
+ }
205
+ run() {
206
+ return new Promise((resolve) => {
207
+ if (!this.running) {
208
+ resolve(true);
209
+ return;
210
+ }
211
+ const runningInterval = setInterval(() => {
212
+ if (!this.running) {
213
+ clearInterval(runningInterval);
214
+ resolve(true);
215
+ }
216
+ }, 1000);
217
+ });
218
+ }
219
+ async shutdown() {
220
+ this.running = false;
221
+ if (!this.assetsStorage)
222
+ return;
223
+ await this.assetsStorage.shutdown(true);
224
+ }
225
+ }
226
+ //# sourceMappingURL=assets.js.map
@@ -0,0 +1,192 @@
1
+ import { TSError, logError, get, cloneDeep, pRetry } from '@terascope/utils';
2
+ import { makeLogger } from '../../../../../workers/helpers/terafoundation.js';
3
+ import { K8sResource } from './k8sResource.js';
4
+ import { gen } from './k8sState.js';
5
+ import { K8s } from './k8s.js';
6
+ import { getRetryConfig } from './utils.js';
7
+ /*
8
+ Execution Life Cycle for _status
9
+ pending -> scheduling -> running -> [ paused -> running ] -> [ stopped | completed ]
10
+ Exceptions
11
+ rejected - when a job is rejected prior to scheduling
12
+ failed - when there is an error while the job is running
13
+ aborted - when a job was running at the point when the cluster shutsdown
14
+ */
15
+ export class KubernetesClusterBackend {
16
+ context;
17
+ k8s;
18
+ logger;
19
+ clusterStateInterval;
20
+ clusterState = {};
21
+ clusterNameLabel;
22
+ constructor(context, clusterMasterServer) {
23
+ const kubernetesNamespace = get(context, 'sysconfig.teraslice.kubernetes_namespace', 'default');
24
+ const clusterName = get(context, 'sysconfig.teraslice.name');
25
+ this.context = context;
26
+ this.logger = makeLogger(context, 'kubernetes_cluster_service');
27
+ this.clusterNameLabel = clusterName.replace(/[^a-zA-Z0-9_\-.]/g, '_').substring(0, 63);
28
+ this.clusterState = {};
29
+ this.clusterStateInterval = undefined;
30
+ this.k8s = new K8s(this.logger, null, kubernetesNamespace, context.sysconfig.teraslice.kubernetes_api_poll_delay, context.sysconfig.teraslice.shutdown_timeout);
31
+ clusterMasterServer.onClientOnline((exId) => {
32
+ this.logger.info(`execution ${exId} is connected`);
33
+ });
34
+ }
35
+ /**
36
+ * getClusterState returns a copy of the clusterState object
37
+ * @return {Object} a copy of the clusterState object
38
+ */
39
+ getClusterState() {
40
+ return cloneDeep(this.clusterState);
41
+ }
42
+ /**
43
+ * Creates clusterState by iterating over all k8s pods matching both labels
44
+ * app.kubernetes.io/name=teraslice
45
+ * app.kubernetes.io/instance=${clusterNameLabel}
46
+ * @constructor
47
+ * @return {Promise} [description]
48
+ */
49
+ async _getClusterState() {
50
+ return this.k8s.list(`app.kubernetes.io/name=teraslice,app.kubernetes.io/instance=${this.clusterNameLabel}`, 'pods')
51
+ .then((k8sPods) => gen(k8sPods, this.clusterState))
52
+ .catch((err) => {
53
+ // TODO: We might need to do more here. I think it's OK to just
54
+ // log though. This only gets used to show slicer info through
55
+ // the API. We wouldn't want to disrupt the cluster master
56
+ // for rare failures to reach the k8s API.
57
+ logError(this.logger, err, 'Error listing teraslice pods in k8s');
58
+ });
59
+ }
60
+ /**
61
+ * Return value indicates whether the cluster has enough workers to start
62
+ * an execution. It must be able to allocate a slicer and at least one
63
+ * worker.
64
+ * @return {boolean} Ok to create job?
65
+ */
66
+ readyForAllocation() {
67
+ // return _availableWorkers() >= 2;
68
+ // TODO: This will be addressed in the future, see:
69
+ // https://github.com/terascope/teraslice/issues/744
70
+ return true;
71
+ }
72
+ /**
73
+ * Creates k8s Service and Job for the Teraslice Execution Controller
74
+ * (formerly slicer). This currently works by creating a service with a
75
+ * hostname that contains the exId in it listening on a well known port.
76
+ * The hostname and port are used later by the workers to contact this
77
+ * Execution Controller.
78
+ * @param {Object} execution Object containing execution details
79
+ * @return {Promise} [description]
80
+ */
81
+ async allocateSlicer(ex) {
82
+ const execution = cloneDeep(ex);
83
+ execution.slicer_port = 45680;
84
+ const exJobResource = new K8sResource('jobs', 'execution_controller', this.context.sysconfig.teraslice, execution, this.logger);
85
+ const exJob = exJobResource.resource;
86
+ this.logger.debug(exJob, 'execution allocating slicer');
87
+ const jobResult = await this.k8s.post(exJob, 'job');
88
+ this.logger.debug(jobResult, 'k8s slicer job submitted');
89
+ let controllerLabel;
90
+ if (jobResult.spec.selector.matchLabels['controller-uid']) {
91
+ /// If running on kubernetes < v1.27.0
92
+ controllerLabel = 'controller-uid';
93
+ }
94
+ else {
95
+ /// If running on kubernetes v1.27.0 or later
96
+ controllerLabel = 'batch.kubernetes.io/controller-uid';
97
+ }
98
+ const controllerUid = jobResult.spec.selector.matchLabels[controllerLabel];
99
+ const pod = await this.k8s.waitForSelectedPod(`${controllerLabel}=${controllerUid}`, undefined, this.context.sysconfig.teraslice.slicer_timeout);
100
+ this.logger.debug(`Slicer is using IP: ${pod.status.podIP}`);
101
+ execution.slicer_hostname = `${pod.status.podIP}`;
102
+ return execution;
103
+ }
104
+ /**
105
+ * Creates k8s deployment that executes Teraslice workers for specified
106
+ * Execution.
107
+ * @param {Object} execution Object that contains information of Execution
108
+ * @return {Promise} [description]
109
+ */
110
+ async allocateWorkers(execution) {
111
+ // NOTE: I tried to set these on the execution inside allocateSlicer
112
+ // but these properties were gone by the time this was called, perhaps
113
+ // because they are not on the schema. So I do this k8s API call
114
+ // instead.
115
+ const selector = `app.kubernetes.io/component=execution_controller,teraslice.terascope.io/jobId=${execution.job_id}`;
116
+ const jobs = await pRetry(() => this.k8s.nonEmptyList(selector, 'jobs'), getRetryConfig());
117
+ // @ts-expect-error
118
+ execution.k8sName = jobs.items[0].metadata.name;
119
+ // @ts-expect-error
120
+ execution.k8sUid = jobs.items[0].metadata.uid;
121
+ const kr = new K8sResource('deployments', 'worker', this.context.sysconfig.teraslice, execution, this.logger);
122
+ const workerDeployment = kr.resource;
123
+ this.logger.debug(`workerDeployment:\n\n${JSON.stringify(workerDeployment, null, 2)}`);
124
+ return this.k8s.post(workerDeployment, 'deployment')
125
+ .then((result) => this.logger.debug(`k8s worker deployment submitted: ${JSON.stringify(result)}`))
126
+ .catch((err) => {
127
+ const error = new TSError(err, {
128
+ reason: 'Error submitting k8s worker deployment'
129
+ });
130
+ return Promise.reject(error);
131
+ });
132
+ }
133
+ // FIXME: These functions should probably do something with the response
134
+ // NOTE: I find is strange that the expected return value here is
135
+ // effectively the same as the function inputs
136
+ async addWorkers(executionContext, numWorkers) {
137
+ await this.k8s.scaleExecution(executionContext.ex_id, numWorkers, 'add');
138
+ return { action: 'add', ex_id: executionContext.ex_id, workerNum: numWorkers };
139
+ }
140
+ // NOTE: This is passed exId instead of executionContext like addWorkers and
141
+ // removeWorkers. I don't know why, just dealing with it.
142
+ async removeWorkers(exId, numWorkers) {
143
+ await this.k8s.scaleExecution(exId, numWorkers, 'remove');
144
+ return { action: 'remove', ex_id: exId, workerNum: numWorkers };
145
+ }
146
+ // TODO: fix types here
147
+ async setWorkers(executionContext, numWorkers) {
148
+ await this.k8s.scaleExecution(executionContext.ex_id, numWorkers, 'set');
149
+ return { action: 'set', ex_id: executionContext.ex_id, workerNum: numWorkers };
150
+ }
151
+ /**
152
+ * Stops all workers for exId
153
+ * @param {String} exId The execution ID of the Execution to stop
154
+ * @param {StopExecutionOptions} options force, timeout, and excludeNode
155
+ * force: stop all related pod, deployment, and job resources
156
+ * timeout and excludeNode are not used in k8s clustering.
157
+ * @return {Promise}
158
+ */
159
+ async stopExecution(exId, options) {
160
+ return this.k8s.deleteExecution(exId, options?.force);
161
+ }
162
+ async shutdown() {
163
+ clearInterval(this.clusterStateInterval);
164
+ }
165
+ /**
166
+ * Returns a list of all k8s resources associated with a job ID
167
+ * @param {string} jobId The job ID of the job to list associated resources
168
+ * @returns {Array<any>}
169
+ */
170
+ async listResourcesForJobId(jobId) {
171
+ const resources = [];
172
+ const resourceTypes = ['pods', 'deployments', 'services', 'jobs', 'replicasets'];
173
+ for (const type of resourceTypes) {
174
+ const list = await this.k8s.list(`teraslice.terascope.io/jobId=${jobId}`, type);
175
+ if (list.items.length > 0) {
176
+ resources.push(list.items);
177
+ }
178
+ }
179
+ return resources;
180
+ }
181
+ async initialize() {
182
+ this.logger.info('kubernetes clustering initializing');
183
+ // Periodically update cluster state, update period controlled by:
184
+ // context.sysconfig.teraslice.node_state_interval
185
+ this.clusterStateInterval = setInterval(() => {
186
+ this.logger.trace('cluster_master requesting cluster state update.');
187
+ this._getClusterState();
188
+ }, this.context.sysconfig.teraslice.node_state_interval);
189
+ await this.k8s.init();
190
+ }
191
+ }
192
+ //# sourceMappingURL=index.js.map