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.
- package/dist/src/interfaces.js +12 -0
- package/dist/src/lib/cluster/cluster_master.js +246 -0
- package/dist/src/lib/cluster/node_master.js +355 -0
- package/dist/src/lib/cluster/services/api.js +663 -0
- package/dist/src/lib/cluster/services/assets.js +226 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetes/index.js +192 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetes/k8s.js +481 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetes/k8sResource.js +414 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetes/k8sState.js +59 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetes/utils.js +43 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/index.js +192 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/interfaces.js +2 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8s.js +423 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sDeploymentResource.js +60 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sJobResource.js +55 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sResource.js +359 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sServiceResource.js +37 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sState.js +60 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/utils.js +170 -0
- package/dist/src/lib/cluster/services/cluster/backends/native/dispatch.js +13 -0
- package/dist/src/lib/cluster/services/cluster/backends/native/index.js +526 -0
- package/dist/src/lib/cluster/services/cluster/backends/native/messaging.js +547 -0
- package/dist/src/lib/cluster/services/cluster/backends/state-utils.js +26 -0
- package/dist/src/lib/cluster/services/cluster/index.js +17 -0
- package/dist/src/lib/cluster/services/execution.js +435 -0
- package/dist/src/lib/cluster/services/index.js +6 -0
- package/dist/src/lib/cluster/services/interfaces.js +2 -0
- package/dist/src/lib/cluster/services/jobs.js +454 -0
- package/dist/src/lib/config/default-sysconfig.js +26 -0
- package/dist/src/lib/config/index.js +22 -0
- package/dist/src/lib/config/schemas/system.js +360 -0
- package/dist/src/lib/storage/analytics.js +86 -0
- package/dist/src/lib/storage/assets.js +401 -0
- package/dist/src/lib/storage/backends/elasticsearch_store.js +494 -0
- package/dist/src/lib/storage/backends/mappings/analytics.js +50 -0
- package/dist/src/lib/storage/backends/mappings/asset.js +41 -0
- package/dist/src/lib/storage/backends/mappings/ex.js +62 -0
- package/dist/src/lib/storage/backends/mappings/job.js +38 -0
- package/dist/src/lib/storage/backends/mappings/state.js +38 -0
- package/dist/src/lib/storage/backends/s3_store.js +237 -0
- package/dist/src/lib/storage/execution.js +300 -0
- package/dist/src/lib/storage/index.js +7 -0
- package/dist/src/lib/storage/jobs.js +81 -0
- package/dist/src/lib/storage/state.js +255 -0
- package/dist/src/lib/utils/api_utils.js +157 -0
- package/dist/src/lib/utils/asset_utils.js +94 -0
- package/dist/src/lib/utils/date_utils.js +52 -0
- package/dist/src/lib/utils/encoding_utils.js +27 -0
- package/dist/src/lib/utils/events.js +4 -0
- package/dist/src/lib/utils/file_utils.js +124 -0
- package/dist/src/lib/utils/id_utils.js +15 -0
- package/dist/src/lib/utils/port_utils.js +32 -0
- package/dist/src/lib/workers/assets/index.js +3 -0
- package/dist/src/lib/workers/assets/loader-executable.js +40 -0
- package/dist/src/lib/workers/assets/loader.js +73 -0
- package/dist/src/lib/workers/assets/spawn.js +55 -0
- package/dist/src/lib/workers/context/execution-context.js +12 -0
- package/dist/src/lib/workers/context/terafoundation-context.js +8 -0
- package/dist/src/lib/workers/execution-controller/execution-analytics.js +188 -0
- package/dist/src/lib/workers/execution-controller/index.js +1024 -0
- package/dist/src/lib/workers/execution-controller/recovery.js +151 -0
- package/dist/src/lib/workers/execution-controller/scheduler.js +390 -0
- package/dist/src/lib/workers/execution-controller/slice-analytics.js +96 -0
- package/dist/src/lib/workers/helpers/job.js +80 -0
- package/dist/src/lib/workers/helpers/op-analytics.js +22 -0
- package/dist/src/lib/workers/helpers/terafoundation.js +34 -0
- package/dist/src/lib/workers/helpers/worker-shutdown.js +169 -0
- package/dist/src/lib/workers/metrics/index.js +108 -0
- package/dist/src/lib/workers/worker/index.js +378 -0
- package/dist/src/lib/workers/worker/slice.js +122 -0
- package/dist/test/config/schemas/system_schema-spec.js +37 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/k8s-spec.js +316 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/k8sResource-spec.js +795 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/k8sState-multicluster-spec.js +67 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/k8sState-spec.js +84 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/utils-spec.js +132 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8s-v2-spec.js +455 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8sResource-v2-spec.js +818 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8sState-multicluster-v2-spec.js +67 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8sState-v2-spec.js +84 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/utils-v2-spec.js +320 -0
- package/dist/test/lib/cluster/services/cluster/backends/state-utils-spec.js +37 -0
- package/dist/test/node_master-spec.js +188 -0
- package/dist/test/services/api-spec.js +80 -0
- package/dist/test/services/assets-spec.js +158 -0
- package/dist/test/services/messaging-spec.js +440 -0
- package/dist/test/storage/assets_storage-spec.js +95 -0
- package/dist/test/storage/s3_store-spec.js +138 -0
- package/dist/test/test.config.js +8 -0
- package/dist/test/test.setup.js +6 -0
- package/dist/test/utils/api_utils-spec.js +86 -0
- package/dist/test/utils/asset_utils-spec.js +141 -0
- package/dist/test/utils/elastic_utils-spec.js +25 -0
- package/dist/test/workers/execution-controller/execution-controller-spec.js +371 -0
- package/dist/test/workers/execution-controller/execution-special-test-cases-spec.js +520 -0
- package/dist/test/workers/execution-controller/execution-test-cases-spec.js +338 -0
- package/dist/test/workers/execution-controller/recovery-spec.js +160 -0
- package/dist/test/workers/execution-controller/scheduler-spec.js +249 -0
- package/dist/test/workers/execution-controller/slice-analytics-spec.js +121 -0
- package/dist/test/workers/fixtures/ops/example-op/processor.js +20 -0
- package/dist/test/workers/fixtures/ops/example-op/schema.js +19 -0
- package/dist/test/workers/fixtures/ops/example-reader/fetcher.js +20 -0
- package/dist/test/workers/fixtures/ops/example-reader/schema.js +41 -0
- package/dist/test/workers/fixtures/ops/example-reader/slicer.js +37 -0
- package/dist/test/workers/fixtures/ops/new-op/processor.js +29 -0
- package/dist/test/workers/fixtures/ops/new-op/schema.js +18 -0
- package/dist/test/workers/fixtures/ops/new-reader/fetcher.js +19 -0
- package/dist/test/workers/fixtures/ops/new-reader/schema.js +23 -0
- package/dist/test/workers/fixtures/ops/new-reader/slicer.js +13 -0
- package/dist/test/workers/helpers/configs.js +130 -0
- package/dist/test/workers/helpers/execution-controller-helper.js +49 -0
- package/dist/test/workers/helpers/index.js +5 -0
- package/dist/test/workers/helpers/test-context.js +210 -0
- package/dist/test/workers/helpers/zip-directory.js +25 -0
- package/dist/test/workers/worker/slice-spec.js +333 -0
- package/dist/test/workers/worker/worker-spec.js +356 -0
- package/package.json +94 -93
- 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
|