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,86 @@
1
+ import { makePrometheus, isPrometheusTerasliceRequest, createJobActiveQuery, addDeletedToQuery } from '../../src/lib/utils/api_utils.js';
2
+ describe('apiUtils', () => {
3
+ it('should be able make a prometheus text format', () => {
4
+ const stats = {
5
+ controllers: {
6
+ processed: 1000,
7
+ failed: 10,
8
+ queued: 5,
9
+ job_duration: 10,
10
+ workers_joined: 20,
11
+ workers_disconnected: 30,
12
+ workers_reconnected: 40
13
+ }
14
+ };
15
+ const r = `# TYPE teraslice_slices_processed counter
16
+ teraslice_slices_processed ${stats.controllers.processed}
17
+ # TYPE teraslice_slices_failed counter
18
+ teraslice_slices_failed ${stats.controllers.failed}
19
+ # TYPE teraslice_slices_queued counter
20
+ teraslice_slices_queued ${stats.controllers.queued}
21
+ # TYPE teraslice_workers_joined counter
22
+ teraslice_workers_joined ${stats.controllers.workers_joined}
23
+ # TYPE teraslice_workers_disconnected counter
24
+ teraslice_workers_disconnected ${stats.controllers.workers_disconnected}
25
+ # TYPE teraslice_workers_reconnected counter
26
+ teraslice_workers_reconnected ${stats.controllers.workers_reconnected}
27
+ `;
28
+ expect(makePrometheus(stats)).toEqual(r);
29
+ });
30
+ it('should be able make a prometheus text format with labels', () => {
31
+ const stats = {
32
+ controllers: {
33
+ processed: 1000,
34
+ failed: 10,
35
+ queued: 5,
36
+ job_duration: 10,
37
+ workers_joined: 20,
38
+ workers_disconnected: 30,
39
+ workers_reconnected: 40
40
+ }
41
+ };
42
+ const r = `# TYPE teraslice_slices_processed counter
43
+ teraslice_slices_processed{foo="bar"} ${stats.controllers.processed}
44
+ # TYPE teraslice_slices_failed counter
45
+ teraslice_slices_failed{foo="bar"} ${stats.controllers.failed}
46
+ # TYPE teraslice_slices_queued counter
47
+ teraslice_slices_queued{foo="bar"} ${stats.controllers.queued}
48
+ # TYPE teraslice_workers_joined counter
49
+ teraslice_workers_joined{foo="bar"} ${stats.controllers.workers_joined}
50
+ # TYPE teraslice_workers_disconnected counter
51
+ teraslice_workers_disconnected{foo="bar"} ${stats.controllers.workers_disconnected}
52
+ # TYPE teraslice_workers_reconnected counter
53
+ teraslice_workers_reconnected{foo="bar"} ${stats.controllers.workers_reconnected}
54
+ `;
55
+ expect(makePrometheus(stats, { foo: 'bar' })).toEqual(r);
56
+ });
57
+ it('should be able to detect if a request is prometheus', () => {
58
+ expect(isPrometheusTerasliceRequest({
59
+ headers: {
60
+ accept: 'blah application/openmetrics-text; blah blah'
61
+ }
62
+ })).toBeTruthy();
63
+ });
64
+ it('should be able to create the proper job queries', () => {
65
+ let query;
66
+ query = createJobActiveQuery('true');
67
+ query = addDeletedToQuery('true', query);
68
+ expect(query).toBe('job_id:* AND !active:false AND _deleted:true');
69
+ query = createJobActiveQuery('true');
70
+ query = addDeletedToQuery('', query);
71
+ expect(query).toBe('job_id:* AND !active:false AND _deleted:true');
72
+ query = createJobActiveQuery('true');
73
+ query = addDeletedToQuery('false', query);
74
+ expect(query).toBe('job_id:* AND !active:false AND (_deleted:false OR (* AND -_deleted:*))');
75
+ query = createJobActiveQuery('false');
76
+ query = addDeletedToQuery('true', query);
77
+ expect(query).toBe('job_id:* AND active:false AND _deleted:true');
78
+ query = createJobActiveQuery('false');
79
+ query = addDeletedToQuery('', query);
80
+ expect(query).toBe('job_id:* AND active:false AND _deleted:true');
81
+ query = createJobActiveQuery('false');
82
+ query = addDeletedToQuery('false', query);
83
+ expect(query).toBe('job_id:* AND active:false AND (_deleted:false OR (* AND -_deleted:*))');
84
+ });
85
+ });
86
+ //# sourceMappingURL=api_utils-spec.js.map
@@ -0,0 +1,141 @@
1
+ import 'jest-extended';
2
+ import { toSemverRange, findMatchingAsset, getMajorVersion, toVersionQuery, findSimilarAssets, getInCompatibilityReason, } from '../../src/lib/utils/asset_utils.js';
3
+ describe('Asset Utils', () => {
4
+ describe('->toSemverRange', () => {
5
+ test.each([
6
+ ['latest', '*'],
7
+ [null, '*'],
8
+ ['^22.0.0', '^22.0.0'],
9
+ ['~22.0.0', '~22.0.0'],
10
+ ['2.0.0', '2.0.0'],
11
+ ['v2.0.0', '2.0.0'],
12
+ ['*', '*'],
13
+ ['1.*', '1.*'],
14
+ ['2.*', '2.*'],
15
+ ['2222.*', '2222.*'],
16
+ ['12.*.1', '12.*.1'],
17
+ ['12.34*.55', '12.34*.55'],
18
+ ['12.34.*', '12.34.*'],
19
+ ['12.34.5*', '12.34.5*'],
20
+ ])('should convert %p to %p', (input, output) => {
21
+ expect(toSemverRange(input)).toEqual(output);
22
+ });
23
+ });
24
+ describe('->toVersionQuery', () => {
25
+ test.each([
26
+ ['latest', 'version:*'],
27
+ [null, 'version:*'],
28
+ ['^22.0.0', 'version:>=22.0.0 AND version:<23.0.0-0'],
29
+ ['~22.0.0', 'version:>=22.0.0 AND version:<22.1.0-0'],
30
+ ['2.0.0', 'version:2.0.0'],
31
+ ['v1.1.1', 'version:1.1.1'],
32
+ ['*', 'version:*'],
33
+ ['1.*', 'version:>=1.0.0 AND version:<2.0.0-0'],
34
+ ['2.*', 'version:>=2.0.0 AND version:<3.0.0-0'],
35
+ ['2222.*', 'version:>=2222.0.0 AND version:<2223.0.0-0'],
36
+ ['12.*.1', 'version:>=12.0.0 AND version:<13.0.0-0'],
37
+ ['12.34*.55', 'version:12.34*.55'],
38
+ ['12.34.*', 'version:>=12.34.0 AND version:<12.35.0-0'],
39
+ ['12.34.5*', 'version:12.34.5*'],
40
+ ])('should convert %p to %p', (input, output) => {
41
+ expect(toVersionQuery(input)).toEqual(output);
42
+ });
43
+ });
44
+ const wrongPlatform = {
45
+ darwin: 'linux',
46
+ linux: 'darwin',
47
+ };
48
+ const currentNodeVersion = getMajorVersion(process.version);
49
+ const assets = [{
50
+ id: 'foo-1',
51
+ name: 'foo',
52
+ version: '2.0.1',
53
+ platform: process.platform,
54
+ arch: process.arch,
55
+ node_version: currentNodeVersion,
56
+ },
57
+ {
58
+ id: 'foo-2',
59
+ name: 'foo',
60
+ version: '2.0.0',
61
+ platform: process.platform,
62
+ arch: process.arch,
63
+ node_version: currentNodeVersion,
64
+ },
65
+ {
66
+ id: 'foo-3',
67
+ name: 'foo',
68
+ version: '2.0.1',
69
+ platform: wrongPlatform[process.platform],
70
+ arch: process.arch,
71
+ node_version: currentNodeVersion,
72
+ },
73
+ {
74
+ id: 'foo-4',
75
+ name: 'foo',
76
+ version: '2.0.2',
77
+ platform: process.platform,
78
+ arch: process.arch,
79
+ node_version: currentNodeVersion + 1,
80
+ },
81
+ {
82
+ id: 'foo-5',
83
+ name: 'foo',
84
+ version: '1.0.0',
85
+ platform: process.platform,
86
+ arch: process.arch,
87
+ node_version: currentNodeVersion,
88
+ },
89
+ {
90
+ id: 'foo-6',
91
+ name: 'foo',
92
+ version: '1.0.0',
93
+ node_version: currentNodeVersion,
94
+ }];
95
+ describe('->findMatchingAsset', () => {
96
+ test.each([
97
+ ['foo',
98
+ 'latest',
99
+ {
100
+ version: '2.0.1'
101
+ }],
102
+ ['foo',
103
+ '2.0.0',
104
+ {
105
+ version: '2.0.0'
106
+ }],
107
+ ['foo',
108
+ '~2.0.0',
109
+ {
110
+ version: '2.0.1'
111
+ }],
112
+ ['foo',
113
+ '1.*',
114
+ {
115
+ version: '1.0.0'
116
+ }],
117
+ ['foo', '0.1.0', null],
118
+ ['foo', '0.1.*', null],
119
+ ])('should return the correct result for %s:%s', (name, version, result) => {
120
+ if (result == null) {
121
+ expect(findMatchingAsset(assets, name, version)).toBeNil();
122
+ }
123
+ else {
124
+ expect(findMatchingAsset(assets, name, version)).toMatchObject(result);
125
+ }
126
+ });
127
+ });
128
+ describe('->getInCompatibilityReason/->findSimilarAssets', () => {
129
+ test.each([
130
+ ['foo', 'latest', 'node_version or platform mismatch'],
131
+ ['foo', '2.0.1', 'platform mismatch'],
132
+ ['foo', '~2.0.0', 'node_version or platform mismatch'],
133
+ ['foo', '3.*', ''],
134
+ ['foo', '0.1.*', ''],
135
+ ])('should return the correct result for %s:%s', (name, version, result) => {
136
+ const reason = getInCompatibilityReason(findSimilarAssets(assets, name, version));
137
+ expect(reason).toEqual(result);
138
+ });
139
+ });
140
+ });
141
+ //# sourceMappingURL=asset_utils-spec.js.map
@@ -0,0 +1,25 @@
1
+ import { dateOptions } from '../../src/lib/utils/date_utils.js';
2
+ describe('elastic_utils', () => {
3
+ it('has methods dateOptions and processInterval', () => {
4
+ expect(dateOptions).toBeDefined();
5
+ expect(typeof dateOptions).toEqual('function');
6
+ });
7
+ it('dateOptions returns a string used for the moment library', () => {
8
+ expect(() => {
9
+ dateOptions('Day');
10
+ }).toThrowError();
11
+ expect(dateOptions('day')).toEqual('d');
12
+ });
13
+ it('dateOptions will throw a new error if not given correct values', () => {
14
+ expect(() => {
15
+ dateOptions('hourz');
16
+ }).toThrowError();
17
+ expect(() => {
18
+ dateOptions(3);
19
+ }).toThrowError();
20
+ expect(() => {
21
+ dateOptions({ some: 'obj' });
22
+ }).toThrowError();
23
+ });
24
+ });
25
+ //# sourceMappingURL=elastic_utils-spec.js.map
@@ -0,0 +1,371 @@
1
+ import { jest } from '@jest/globals';
2
+ import { pDelay, get, set } from '@terascope/utils';
3
+ import { TestContext } from '../helpers/index.js';
4
+ import { findPort } from '../../../src/lib/utils/port_utils.js';
5
+ import { ExecutionController } from '../../../src/lib/workers/execution-controller/index.js';
6
+ describe('ExecutionController', () => {
7
+ describe('when the execution context is invalid', () => {
8
+ let testContext;
9
+ let exController;
10
+ let executionStorage;
11
+ beforeEach(async () => {
12
+ const port = await findPort();
13
+ testContext = new TestContext({
14
+ assignment: 'execution_controller',
15
+ slicerPort: port,
16
+ });
17
+ // needs to be in this order
18
+ await testContext.initialize(true);
19
+ await testContext.addClusterMaster();
20
+ exController = new ExecutionController(testContext.context, testContext.executionContext);
21
+ set(exController, 'isExecutionFinished', true);
22
+ await testContext.addExStore();
23
+ executionStorage = testContext.stores.executionStorage;
24
+ testContext.attachCleanup(() => exController.shutdown().catch(() => {
25
+ /* ignore-error */
26
+ }));
27
+ });
28
+ afterEach(() => testContext.cleanup());
29
+ describe('when the execution does not exist', () => {
30
+ beforeEach(async () => {
31
+ await executionStorage.remove(testContext.exId);
32
+ });
33
+ it('should throw an error on initialize', async () => {
34
+ expect.hasAssertions();
35
+ try {
36
+ await exController.initialize();
37
+ }
38
+ catch (err) {
39
+ expect(err.message).toStartWith(`Cannot get execution status ${testContext.exId}`);
40
+ }
41
+ });
42
+ });
43
+ describe('when the execution is set to running', () => {
44
+ beforeEach(async () => {
45
+ await executionStorage.setStatus(testContext.exId, 'running');
46
+ });
47
+ it('should throw an error on initialize', async () => {
48
+ expect.hasAssertions();
49
+ await exController.initialize();
50
+ expect(get(exController, 'isInitialized')).toBeFalse();
51
+ expect(get(exController, 'isShutdown')).toBeTrue();
52
+ });
53
+ });
54
+ describe('when the execution is in a terminal state', () => {
55
+ beforeEach(async () => {
56
+ await executionStorage.setStatus(testContext.exId, 'completed');
57
+ });
58
+ it('should throw an error on initialize', async () => {
59
+ expect.hasAssertions();
60
+ await exController.initialize();
61
+ expect(get(exController, 'isInitialized')).toBeFalse();
62
+ expect(get(exController, 'isShutdown')).toBeTrue();
63
+ });
64
+ });
65
+ });
66
+ describe('when the slice failure watch dog is started', () => {
67
+ let testContext;
68
+ let executionStorage;
69
+ let exController;
70
+ const probationWindow = 500;
71
+ beforeEach(async () => {
72
+ const port = await findPort();
73
+ testContext = new TestContext({
74
+ assignment: 'execution_controller',
75
+ slicerPort: port,
76
+ probationWindow
77
+ });
78
+ // needs to be in this order
79
+ await testContext.initialize(true);
80
+ await testContext.addClusterMaster();
81
+ exController = new ExecutionController(testContext.context, testContext.executionContext);
82
+ await exController.initialize();
83
+ await testContext.addExStore();
84
+ executionStorage = testContext.stores.executionStorage;
85
+ testContext.attachCleanup(() => exController.shutdown().catch(() => {
86
+ /* ignore-error */
87
+ }));
88
+ });
89
+ afterEach(() => testContext.cleanup());
90
+ it('should handle the probation period correctly', async () => {
91
+ exController.executionAnalytics.increment('processed');
92
+ exController.executionAnalytics.increment('failed');
93
+ // @ts-expect-error
94
+ expect(exController.sliceFailureInterval).toBeNil();
95
+ // @ts-expect-error
96
+ await exController._startSliceFailureWatchDog();
97
+ // @ts-expect-error
98
+ expect(exController.sliceFailureInterval).not.toBeNil();
99
+ // @ts-expect-error
100
+ // should be able to call slice watch again without resetting the interval
101
+ const { sliceFailureInterval } = exController;
102
+ // @ts-expect-error
103
+ await exController._startSliceFailureWatchDog();
104
+ // @ts-expect-error
105
+ expect(exController.sliceFailureInterval).toBe(sliceFailureInterval);
106
+ await expect(executionStorage.getStatus(testContext.exId)).resolves.toEqual('failing');
107
+ await pDelay(probationWindow + 100);
108
+ // should be able to setr the status back to running if more slices are processed
109
+ exController.executionAnalytics.increment('processed');
110
+ await pDelay(probationWindow + 100);
111
+ await expect(executionStorage.getStatus(testContext.exId)).resolves.toEqual('running');
112
+ // @ts-expect-error
113
+ expect(exController.sliceFailureInterval).toBeNil();
114
+ // @ts-expect-error
115
+ await exController._startSliceFailureWatchDog();
116
+ // @ts-expect-error
117
+ expect(exController.sliceFailureInterval).not.toBeNil();
118
+ });
119
+ });
120
+ describe('when testing setFailingStatus', () => {
121
+ let testContext;
122
+ let exController;
123
+ beforeEach(async () => {
124
+ testContext = new TestContext({
125
+ assignment: 'execution_controller'
126
+ });
127
+ await testContext.initialize();
128
+ exController = new ExecutionController(testContext.context, testContext.executionContext);
129
+ });
130
+ afterEach(() => testContext.cleanup());
131
+ describe('when it fails to set the status', () => {
132
+ it('should log the error twice', async () => {
133
+ const logErr = jest.fn();
134
+ // @ts-expect-error
135
+ const setStatus = jest.fn().mockRejectedValue(new Error('Uh oh'));
136
+ const errMeta = { error: 'metadata' };
137
+ const executionMetaData = jest.fn().mockReturnValue(errMeta);
138
+ // @ts-expect-error
139
+ exController.executionStorage = {
140
+ setStatus,
141
+ executionMetaData
142
+ };
143
+ exController.logger.error = logErr;
144
+ const stats = exController.executionAnalytics.getAnalytics();
145
+ await exController.setFailingStatus('some reason');
146
+ expect(setStatus).toHaveBeenCalledWith(testContext.exId, 'failing', errMeta);
147
+ expect(executionMetaData).toHaveBeenCalledWith(stats, `execution ${testContext.exId} has encountered a processing error, reason: some reason`);
148
+ expect(logErr).toHaveBeenCalledTimes(2);
149
+ });
150
+ });
151
+ });
152
+ describe('when testing _terminalError', () => {
153
+ let testContext;
154
+ let exController;
155
+ beforeEach(async () => {
156
+ testContext = new TestContext({
157
+ assignment: 'execution_controller'
158
+ });
159
+ await testContext.initialize();
160
+ exController = new ExecutionController(testContext.context, testContext.executionContext);
161
+ });
162
+ afterEach(() => testContext.cleanup());
163
+ describe('when it fails to set the status', () => {
164
+ it('should log the error twice', async () => {
165
+ const logErr = jest.fn();
166
+ const logFatal = jest.fn();
167
+ // @ts-expect-error
168
+ const setStatus = jest.fn().mockRejectedValue(new Error('Uh oh'));
169
+ const errMeta = { error: 'metadata' };
170
+ const executionMetaData = jest.fn().mockReturnValue(errMeta);
171
+ // @ts-expect-error
172
+ exController.executionStorage = {
173
+ setStatus,
174
+ executionMetaData
175
+ };
176
+ exController.logger.error = logErr;
177
+ exController.logger.fatal = logFatal;
178
+ const stats = exController.executionAnalytics.getAnalytics();
179
+ // @ts-expect-error
180
+ await exController._terminalError('Uh oh');
181
+ // @ts-expect-error
182
+ expect(exController.slicerFailed).toBeTrue();
183
+ expect(setStatus).toHaveBeenCalledWith(testContext.exId, 'failed', errMeta);
184
+ const errMsg = `TSError: slicer for ex ${testContext.exId} had an error, shutting down execution, caused by Uh oh`;
185
+ expect(executionMetaData.mock.calls[0][0]).toEqual(stats);
186
+ expect(executionMetaData.mock.calls[0][1]).toStartWith(errMsg);
187
+ expect(logErr).toHaveBeenCalledTimes(2);
188
+ expect(logFatal).toHaveBeenCalledWith(`execution ${testContext.exId} is ended because of slice failure`);
189
+ });
190
+ });
191
+ describe('when the execution is already done', () => {
192
+ it('should not do anything', async () => {
193
+ exController.isExecutionDone = true;
194
+ const logErr = jest.fn();
195
+ exController.logger.error = logErr;
196
+ // @ts-expect-error
197
+ await exController._terminalError();
198
+ // @ts-expect-error
199
+ expect(exController.slicerFailed).toBeFalsy();
200
+ expect(logErr).not.toHaveBeenCalled();
201
+ });
202
+ });
203
+ });
204
+ describe('when testing shutdown', () => {
205
+ let testContext;
206
+ let exController;
207
+ beforeEach(async () => {
208
+ testContext = new TestContext({
209
+ assignment: 'execution_controller',
210
+ shutdownTimeout: 100
211
+ });
212
+ await testContext.initialize();
213
+ exController = new ExecutionController(testContext.context, testContext.executionContext);
214
+ testContext.attachCleanup(() => exController.shutdown().catch(() => {
215
+ /* ignore-error */
216
+ }));
217
+ });
218
+ afterEach(() => testContext.cleanup());
219
+ describe('when not initialized', () => {
220
+ it('should resolve', () => expect(exController.shutdown()).resolves.toBeNil());
221
+ });
222
+ describe('when initialized', () => {
223
+ beforeEach(() => {
224
+ // @ts-expect-error
225
+ exController.isInitialized = true;
226
+ });
227
+ describe('when controller is already being shutdown', () => {
228
+ beforeEach(() => {
229
+ // @ts-expect-error
230
+ exController.isShuttingDown = true;
231
+ });
232
+ it('should resolve when shutdown passes', async () => {
233
+ setTimeout(() => {
234
+ exController.events.emit('worker:shutdown:complete');
235
+ });
236
+ await expect(exController.shutdown()).resolves.toBeNil();
237
+ });
238
+ it('should reject when shutdown fails', async () => {
239
+ setTimeout(() => {
240
+ exController.events.emit('worker:shutdown:complete', new Error('Uh oh'));
241
+ });
242
+ await expect(exController.shutdown()).rejects.toThrowError('Uh oh');
243
+ });
244
+ });
245
+ describe('when everything errors', () => {
246
+ beforeEach(() => {
247
+ // @ts-expect-error
248
+ exController.isExecutionFinished = true;
249
+ exController.stateStorage = {
250
+ // @ts-expect-error
251
+ shutdown: () => Promise.resolve(true)
252
+ };
253
+ // @ts-expect-error
254
+ exController.executionStorage = {
255
+ shutdown: () => Promise.reject(new Error('Store Error'))
256
+ };
257
+ // @ts-expect-error
258
+ exController.executionAnalytics = {
259
+ shutdown: () => Promise.reject(new Error('Execution Analytics Error'))
260
+ };
261
+ // @ts-expect-error
262
+ exController.collectAnalytics = true;
263
+ // @ts-expect-error
264
+ exController.slicerAnalytics = {
265
+ shutdown: () => Promise.reject(new Error('Slicer Analytics Error'))
266
+ };
267
+ // @ts-expect-error
268
+ exController.scheduler = {
269
+ stop: () => { },
270
+ shutdown: () => Promise.reject(new Error('Scheduler Error'))
271
+ };
272
+ // @ts-expect-error
273
+ exController.server = {
274
+ shutdown: () => Promise.reject(new Error('Execution Controller Server Error'))
275
+ };
276
+ // @ts-expect-error
277
+ exController.client = {
278
+ shutdown: () => Promise.reject(new Error('Cluster Master Client Error'))
279
+ };
280
+ });
281
+ it('should reject with all of the errors', async () => {
282
+ expect.hasAssertions();
283
+ try {
284
+ await exController.shutdown();
285
+ }
286
+ catch (err) {
287
+ const errMsg = err.toString();
288
+ expect(errMsg).toStartWith('Error: Failed to shutdown correctly');
289
+ expect(errMsg).toInclude('Store Error');
290
+ expect(errMsg).toInclude('Execution Analytics Error');
291
+ expect(errMsg).toInclude('Slicer Analytics Error');
292
+ expect(errMsg).toInclude('Scheduler Error');
293
+ expect(errMsg).toInclude('Execution Controller Server Error');
294
+ expect(errMsg).toInclude('Cluster Master Client Error');
295
+ }
296
+ });
297
+ });
298
+ });
299
+ });
300
+ describe('when testing log_level', () => {
301
+ beforeAll(() => {
302
+ process.env.TESTING_LOG_LEVEL = 'true';
303
+ return;
304
+ });
305
+ describe('when there is no log_level set in either job configuration or terafoundation', () => {
306
+ let testContext;
307
+ let exController;
308
+ beforeEach(async () => {
309
+ testContext = new TestContext({
310
+ assignment: 'execution_controller',
311
+ shutdownTimeout: 100
312
+ });
313
+ await testContext.initialize();
314
+ exController = new ExecutionController(testContext.context, testContext.executionContext);
315
+ testContext.attachCleanup(() => exController.shutdown().catch(() => {
316
+ /* ignore-error */
317
+ }));
318
+ });
319
+ it('should have a logger with log_level info', async () => {
320
+ expect(exController.executionContext.context.logger.level()).toBe(30);
321
+ });
322
+ afterEach(() => testContext.cleanup());
323
+ });
324
+ describe('when no log_level is set in job configuration and terafoundation log level is error', () => {
325
+ let testContext;
326
+ let exController;
327
+ beforeEach(async () => {
328
+ testContext = new TestContext({
329
+ assignment: 'execution_controller',
330
+ shutdownTimeout: 100,
331
+ log_level_terafoundation: 'error'
332
+ });
333
+ await testContext.initialize();
334
+ exController = new ExecutionController(testContext.context, testContext.executionContext);
335
+ testContext.attachCleanup(() => exController.shutdown().catch(() => {
336
+ /* ignore-error */
337
+ }));
338
+ });
339
+ it('should have a logger with log_level error', async () => {
340
+ expect(exController.executionContext.context.logger.level()).toBe(50);
341
+ });
342
+ afterEach(() => testContext.cleanup());
343
+ });
344
+ describe('when log_level set to trace in job configuration and terafoundation is set to error', () => {
345
+ let testContext;
346
+ let exController;
347
+ beforeEach(async () => {
348
+ testContext = new TestContext({
349
+ assignment: 'execution_controller',
350
+ shutdownTimeout: 100,
351
+ log_level: 'trace',
352
+ log_level_terafoundation: 'error'
353
+ });
354
+ await testContext.initialize();
355
+ exController = new ExecutionController(testContext.context, testContext.executionContext);
356
+ testContext.attachCleanup(() => exController.shutdown().catch(() => {
357
+ /* ignore-error */
358
+ }));
359
+ });
360
+ it('should have a logger with log_level trace', async () => {
361
+ expect(exController.executionContext.context.logger.level()).toBe(10);
362
+ });
363
+ afterEach(() => testContext.cleanup());
364
+ });
365
+ afterAll(() => {
366
+ process.env.TESTING_LOG_LEVEL = 'false';
367
+ return;
368
+ });
369
+ });
370
+ });
371
+ //# sourceMappingURL=execution-controller-spec.js.map