teraslice 3.3.0 → 3.3.2
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/LICENSE +202 -0
- package/package.json +25 -28
- package/dist/src/interfaces.js +0 -12
- package/dist/src/lib/cluster/cluster_master.js +0 -246
- package/dist/src/lib/cluster/node_master.js +0 -355
- package/dist/src/lib/cluster/services/api.js +0 -663
- package/dist/src/lib/cluster/services/assets.js +0 -224
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/index.js +0 -192
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/interfaces.js +0 -2
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8s.js +0 -419
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sDeploymentResource.js +0 -60
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sJobResource.js +0 -55
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sResource.js +0 -357
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sServiceResource.js +0 -37
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sState.js +0 -60
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/utils.js +0 -170
- package/dist/src/lib/cluster/services/cluster/backends/native/dispatch.js +0 -13
- package/dist/src/lib/cluster/services/cluster/backends/native/index.js +0 -526
- package/dist/src/lib/cluster/services/cluster/backends/native/messaging.js +0 -548
- package/dist/src/lib/cluster/services/cluster/backends/state-utils.js +0 -26
- package/dist/src/lib/cluster/services/cluster/index.js +0 -13
- package/dist/src/lib/cluster/services/execution.js +0 -435
- package/dist/src/lib/cluster/services/index.js +0 -6
- package/dist/src/lib/cluster/services/interfaces.js +0 -2
- package/dist/src/lib/cluster/services/jobs.js +0 -458
- package/dist/src/lib/config/default-sysconfig.js +0 -25
- package/dist/src/lib/config/index.js +0 -20
- package/dist/src/lib/config/schemas/system.js +0 -360
- package/dist/src/lib/storage/analytics.js +0 -86
- package/dist/src/lib/storage/assets.js +0 -401
- package/dist/src/lib/storage/backends/elasticsearch_store.js +0 -496
- package/dist/src/lib/storage/backends/mappings/analytics.js +0 -20
- package/dist/src/lib/storage/backends/mappings/asset.js +0 -32
- package/dist/src/lib/storage/backends/mappings/ex.js +0 -53
- package/dist/src/lib/storage/backends/mappings/job.js +0 -42
- package/dist/src/lib/storage/backends/mappings/state.js +0 -16
- package/dist/src/lib/storage/backends/s3_store.js +0 -237
- package/dist/src/lib/storage/execution.js +0 -302
- package/dist/src/lib/storage/index.js +0 -7
- package/dist/src/lib/storage/jobs.js +0 -81
- package/dist/src/lib/storage/state.js +0 -254
- package/dist/src/lib/utils/api_utils.js +0 -128
- package/dist/src/lib/utils/asset_utils.js +0 -94
- package/dist/src/lib/utils/date_utils.js +0 -52
- package/dist/src/lib/utils/encoding_utils.js +0 -27
- package/dist/src/lib/utils/events.js +0 -4
- package/dist/src/lib/utils/file_utils.js +0 -124
- package/dist/src/lib/utils/id_utils.js +0 -15
- package/dist/src/lib/utils/port_utils.js +0 -32
- package/dist/src/lib/workers/assets/index.js +0 -3
- package/dist/src/lib/workers/assets/loader-executable.js +0 -40
- package/dist/src/lib/workers/assets/loader.js +0 -73
- package/dist/src/lib/workers/assets/spawn.js +0 -55
- package/dist/src/lib/workers/context/execution-context.js +0 -12
- package/dist/src/lib/workers/context/terafoundation-context.js +0 -8
- package/dist/src/lib/workers/execution-controller/execution-analytics.js +0 -188
- package/dist/src/lib/workers/execution-controller/index.js +0 -1024
- package/dist/src/lib/workers/execution-controller/recovery.js +0 -151
- package/dist/src/lib/workers/execution-controller/scheduler.js +0 -390
- package/dist/src/lib/workers/execution-controller/slice-analytics.js +0 -96
- package/dist/src/lib/workers/helpers/job.js +0 -80
- package/dist/src/lib/workers/helpers/op-analytics.js +0 -22
- package/dist/src/lib/workers/helpers/terafoundation.js +0 -34
- package/dist/src/lib/workers/helpers/worker-shutdown.js +0 -147
- package/dist/src/lib/workers/metrics/index.js +0 -108
- package/dist/src/lib/workers/worker/index.js +0 -378
- package/dist/src/lib/workers/worker/slice.js +0 -122
- package/dist/test/config/schemas/system_schema-spec.js +0 -26
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8s-v2-spec.js +0 -458
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8sResource-v2-spec.js +0 -818
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8sState-multicluster-v2-spec.js +0 -67
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8sState-v2-spec.js +0 -84
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/utils-v2-spec.js +0 -320
- package/dist/test/lib/cluster/services/cluster/backends/state-utils-spec.js +0 -37
- package/dist/test/node_master-spec.js +0 -194
- package/dist/test/services/api-spec.js +0 -79
- package/dist/test/services/assets-spec.js +0 -158
- package/dist/test/services/messaging-spec.js +0 -440
- package/dist/test/storage/assets_storage-spec.js +0 -95
- package/dist/test/storage/s3_store-spec.js +0 -149
- package/dist/test/test.config.js +0 -23
- package/dist/test/test.setup.js +0 -6
- package/dist/test/utils/api_utils-spec.js +0 -25
- package/dist/test/utils/asset_utils-spec.js +0 -141
- package/dist/test/utils/elastic_utils-spec.js +0 -25
- package/dist/test/workers/execution-controller/execution-controller-spec.js +0 -371
- package/dist/test/workers/execution-controller/execution-special-test-cases-spec.js +0 -519
- package/dist/test/workers/execution-controller/execution-test-cases-spec.js +0 -343
- package/dist/test/workers/execution-controller/recovery-spec.js +0 -160
- package/dist/test/workers/execution-controller/scheduler-spec.js +0 -249
- package/dist/test/workers/execution-controller/slice-analytics-spec.js +0 -121
- package/dist/test/workers/fixtures/ops/example-op/processor.js +0 -20
- package/dist/test/workers/fixtures/ops/example-op/schema.js +0 -19
- package/dist/test/workers/fixtures/ops/example-reader/fetcher.js +0 -20
- package/dist/test/workers/fixtures/ops/example-reader/schema.js +0 -41
- package/dist/test/workers/fixtures/ops/example-reader/slicer.js +0 -37
- package/dist/test/workers/fixtures/ops/new-op/processor.js +0 -29
- package/dist/test/workers/fixtures/ops/new-op/schema.js +0 -18
- package/dist/test/workers/fixtures/ops/new-reader/fetcher.js +0 -19
- package/dist/test/workers/fixtures/ops/new-reader/schema.js +0 -23
- package/dist/test/workers/fixtures/ops/new-reader/slicer.js +0 -13
- package/dist/test/workers/helpers/configs.js +0 -128
- package/dist/test/workers/helpers/execution-controller-helper.js +0 -49
- package/dist/test/workers/helpers/index.js +0 -5
- package/dist/test/workers/helpers/test-context.js +0 -210
- package/dist/test/workers/helpers/zip-directory.js +0 -25
- package/dist/test/workers/worker/slice-spec.js +0 -333
- package/dist/test/workers/worker/worker-spec.js +0 -356
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
import { RecoveryCleanupType } from '@terascope/job-components';
|
|
2
|
-
import { TSError, pRetry, toString, isRetryableError, parseErrorInfo, isTest, times, getFullErrorStack, isKey } from '@terascope/core-utils';
|
|
3
|
-
import { timeseriesIndex } from '../utils/date_utils.js';
|
|
4
|
-
import { makeLogger } from '../workers/helpers/terafoundation.js';
|
|
5
|
-
import { TerasliceElasticsearchStorage } from './backends/elasticsearch_store.js';
|
|
6
|
-
export const SliceState = Object.freeze({
|
|
7
|
-
pending: 'pending',
|
|
8
|
-
start: 'start',
|
|
9
|
-
error: 'error',
|
|
10
|
-
completed: 'completed',
|
|
11
|
-
});
|
|
12
|
-
export class StateStorage {
|
|
13
|
-
backend;
|
|
14
|
-
logger;
|
|
15
|
-
recordType;
|
|
16
|
-
timeseriesFormat;
|
|
17
|
-
baseIndex;
|
|
18
|
-
index;
|
|
19
|
-
constructor(context) {
|
|
20
|
-
const recordType = 'state';
|
|
21
|
-
const logger = makeLogger(context, 'state_storage');
|
|
22
|
-
const config = context.sysconfig.teraslice;
|
|
23
|
-
const _index = `${config.name}__state`;
|
|
24
|
-
// making this to pass down to backend for dynamic index searches
|
|
25
|
-
const indexName = `${_index}*`;
|
|
26
|
-
const timeseriesFormat = config.index_rollover_frequency.state;
|
|
27
|
-
const backendConfig = {
|
|
28
|
-
context,
|
|
29
|
-
indexName,
|
|
30
|
-
recordType,
|
|
31
|
-
idField: 'slice_id',
|
|
32
|
-
fullResponse: false,
|
|
33
|
-
logRecord: true,
|
|
34
|
-
forceRefresh: false,
|
|
35
|
-
storageName: 'state',
|
|
36
|
-
logger
|
|
37
|
-
};
|
|
38
|
-
this.backend = new TerasliceElasticsearchStorage(backendConfig);
|
|
39
|
-
this.logger = logger;
|
|
40
|
-
this.recordType = recordType;
|
|
41
|
-
this.timeseriesFormat = timeseriesFormat;
|
|
42
|
-
this.baseIndex = _index;
|
|
43
|
-
this.index = indexName;
|
|
44
|
-
}
|
|
45
|
-
async initialize() {
|
|
46
|
-
await this.backend.initialize();
|
|
47
|
-
this.logger.info('state storage initialized');
|
|
48
|
-
}
|
|
49
|
-
async createState(exId, slice, state, error) {
|
|
50
|
-
const { record, index } = this._createSliceRecord(exId, slice, state, error);
|
|
51
|
-
return this.backend.indexWithId(slice.slice_id, record, index);
|
|
52
|
-
}
|
|
53
|
-
async createSlices(exId, slices) {
|
|
54
|
-
const bulkRequest = slices.map((slice) => {
|
|
55
|
-
const { record, index } = this._createSliceRecord(exId, slice, SliceState.pending);
|
|
56
|
-
return {
|
|
57
|
-
action: {
|
|
58
|
-
index: {
|
|
59
|
-
_index: index,
|
|
60
|
-
_id: record.slice_id,
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
data: record
|
|
64
|
-
};
|
|
65
|
-
});
|
|
66
|
-
return this.backend.bulkSend(bulkRequest);
|
|
67
|
-
}
|
|
68
|
-
_createSliceRecord(exId, slice, state, error) {
|
|
69
|
-
if (!isKey(SliceState, state)) {
|
|
70
|
-
throw new Error(`Unknown slice state "${state}" on create`);
|
|
71
|
-
}
|
|
72
|
-
const { index } = timeseriesIndex(this.timeseriesFormat, this.baseIndex, slice._created);
|
|
73
|
-
// TODO: type this better
|
|
74
|
-
const record = {
|
|
75
|
-
slice_id: slice.slice_id,
|
|
76
|
-
slicer_id: slice.slicer_id,
|
|
77
|
-
slicer_order: slice.slicer_order,
|
|
78
|
-
request: JSON.stringify(slice.request),
|
|
79
|
-
state,
|
|
80
|
-
ex_id: exId,
|
|
81
|
-
_created: slice._created,
|
|
82
|
-
_updated: slice._created,
|
|
83
|
-
};
|
|
84
|
-
if (error) {
|
|
85
|
-
record.error = toString(error);
|
|
86
|
-
}
|
|
87
|
-
return { record, index };
|
|
88
|
-
}
|
|
89
|
-
// TODO: type this better
|
|
90
|
-
async updateState(slice, state, error) {
|
|
91
|
-
if (!isKey(SliceState, state)) {
|
|
92
|
-
throw new Error(`Unknown slice state "${state}" on update`);
|
|
93
|
-
}
|
|
94
|
-
const indexData = timeseriesIndex(this.timeseriesFormat, this.baseIndex, slice._created);
|
|
95
|
-
// TODO: type this better
|
|
96
|
-
const record = {
|
|
97
|
-
_updated: indexData.timestamp,
|
|
98
|
-
state
|
|
99
|
-
};
|
|
100
|
-
// it will usaully just be error
|
|
101
|
-
if (state === SliceState.error || error) {
|
|
102
|
-
if (error) {
|
|
103
|
-
record.error = getFullErrorStack(error);
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
record.error = new Error('Unknown slice error').stack;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
let notFoundErrCount = 0;
|
|
110
|
-
const updateFn = this.backend.update.bind(this.backend);
|
|
111
|
-
async function update() {
|
|
112
|
-
try {
|
|
113
|
-
return await updateFn(slice.slice_id, record, indexData.index);
|
|
114
|
-
}
|
|
115
|
-
catch (_err) {
|
|
116
|
-
const { statusCode, message } = parseErrorInfo(_err);
|
|
117
|
-
let retryable = isRetryableError(_err);
|
|
118
|
-
if (statusCode === 404) {
|
|
119
|
-
notFoundErrCount++;
|
|
120
|
-
retryable = notFoundErrCount < 3;
|
|
121
|
-
}
|
|
122
|
-
else if (message.includes('Request Timeout')) {
|
|
123
|
-
retryable = true;
|
|
124
|
-
}
|
|
125
|
-
throw new TSError(_err, {
|
|
126
|
-
retryable,
|
|
127
|
-
reason: `Failure to update ${state} state`
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return pRetry(update, {
|
|
132
|
-
retries: 10000,
|
|
133
|
-
delay: isTest ? 100 : 1000,
|
|
134
|
-
backoff: 5,
|
|
135
|
-
endWithFatal: true,
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Get the starting position for the slicer
|
|
140
|
-
*
|
|
141
|
-
* @param {string} exId
|
|
142
|
-
* @param {number} slicerId
|
|
143
|
-
* @returns {Promise<import('@terascope/job-components').SlicerRecoveryData>}
|
|
144
|
-
*/
|
|
145
|
-
async _getSlicerStartingPoint(exId, slicerId) {
|
|
146
|
-
const startQuery = `ex_id:"${exId}" AND slicer_id:"${slicerId}" AND state:${SliceState.completed}`;
|
|
147
|
-
try {
|
|
148
|
-
const [slice] = await this.search(startQuery, 0, 1, 'slicer_order:desc');
|
|
149
|
-
const recoveryData = {
|
|
150
|
-
slicer_id: slicerId,
|
|
151
|
-
lastSlice: undefined
|
|
152
|
-
};
|
|
153
|
-
if (slice) {
|
|
154
|
-
recoveryData.lastSlice = JSON.parse(slice.request);
|
|
155
|
-
this.logger.info(`last slice process for slicer_id ${slicerId}, ex_id: ${exId} is`, slice.lastSlice);
|
|
156
|
-
}
|
|
157
|
-
return recoveryData;
|
|
158
|
-
}
|
|
159
|
-
catch (err) {
|
|
160
|
-
throw new TSError(err, {
|
|
161
|
-
reason: 'Failure getting the newest record'
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Get the starting positions for all of the slicers
|
|
167
|
-
*
|
|
168
|
-
* @param {string} exId
|
|
169
|
-
* @param {number} slicer
|
|
170
|
-
* @returns {Promise<import('@terascope/job-components').SlicerRecoveryData[]>}
|
|
171
|
-
*/
|
|
172
|
-
async getStartingPoints(exId, slicers) {
|
|
173
|
-
const recoveredSlices = times(slicers, (i) => this._getSlicerStartingPoint(exId, i));
|
|
174
|
-
return Promise.all(recoveredSlices);
|
|
175
|
-
}
|
|
176
|
-
// TODO: fix types
|
|
177
|
-
/**
|
|
178
|
-
* @private
|
|
179
|
-
* @param {string} exId
|
|
180
|
-
* @param {number} slicerId
|
|
181
|
-
* @param {import('@terascope/job-components').RecoveryCleanupType} [cleanupType]
|
|
182
|
-
* @returns {string}
|
|
183
|
-
*/
|
|
184
|
-
_getRecoverSlicesQuery(exId, slicerId, cleanupType) {
|
|
185
|
-
let query = `ex_id:"${exId}"`;
|
|
186
|
-
if (slicerId !== -1) {
|
|
187
|
-
query = `${query} AND slicer_id:"${slicerId}"`;
|
|
188
|
-
}
|
|
189
|
-
if (cleanupType && cleanupType === RecoveryCleanupType.errors) {
|
|
190
|
-
query = `${query} AND state:"${SliceState.error}"`;
|
|
191
|
-
}
|
|
192
|
-
else if (cleanupType && cleanupType === RecoveryCleanupType.pending) {
|
|
193
|
-
query = `${query} AND state:"${SliceState.pending}"`;
|
|
194
|
-
}
|
|
195
|
-
else {
|
|
196
|
-
query = `${query} AND NOT state:"${SliceState.completed}"`;
|
|
197
|
-
}
|
|
198
|
-
this.logger.debug('recovery slices query:', query);
|
|
199
|
-
return query;
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* @param {string} exId
|
|
203
|
-
* @param {number} slicerId
|
|
204
|
-
* @param {import('@terascope/job-components').RecoveryCleanupType} [cleanupType]
|
|
205
|
-
* @returns {Promise<import('@terascope/job-components').Slice[]>}
|
|
206
|
-
*/
|
|
207
|
-
async recoverSlices(exId, slicerId, cleanupType) {
|
|
208
|
-
const query = this._getRecoverSlicesQuery(exId, slicerId, cleanupType);
|
|
209
|
-
// Look for all slices that haven't been completed so they can be retried.
|
|
210
|
-
try {
|
|
211
|
-
await this.backend.refresh(this.index);
|
|
212
|
-
const results = await this.search(query, 0, 5000, 'slicer_order:desc');
|
|
213
|
-
return results.map((doc) => ({
|
|
214
|
-
slice_id: doc.slice_id,
|
|
215
|
-
slicer_id: doc.slicer_id,
|
|
216
|
-
request: JSON.parse(doc.request),
|
|
217
|
-
_created: doc._created
|
|
218
|
-
}));
|
|
219
|
-
}
|
|
220
|
-
catch (err) {
|
|
221
|
-
throw new TSError(err, {
|
|
222
|
-
reason: 'Failure to get recovered slices'
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
async search(query, from, size, sort, fields) {
|
|
227
|
-
return this.backend.search(query, from, size, sort || '_updated:desc', fields);
|
|
228
|
-
}
|
|
229
|
-
async count(query, from = 0, sort = '_updated:desc') {
|
|
230
|
-
return this.backend.count(query, from, sort);
|
|
231
|
-
}
|
|
232
|
-
async countByState(exId, state) {
|
|
233
|
-
if (!isKey(SliceState, state)) {
|
|
234
|
-
throw new Error(`Unknown slice state "${state}" on update`);
|
|
235
|
-
}
|
|
236
|
-
const query = `ex_id:"${exId}" AND state:${state}`;
|
|
237
|
-
return this.count(query);
|
|
238
|
-
}
|
|
239
|
-
async shutdown(forceShutdown) {
|
|
240
|
-
this.logger.info('shutting down');
|
|
241
|
-
return this.backend.shutdown(forceShutdown);
|
|
242
|
-
}
|
|
243
|
-
async refresh() {
|
|
244
|
-
const { index } = timeseriesIndex(this.timeseriesFormat, this.baseIndex);
|
|
245
|
-
return this.backend.refresh(index);
|
|
246
|
-
}
|
|
247
|
-
verifyClient() {
|
|
248
|
-
return this.backend.verifyClient();
|
|
249
|
-
}
|
|
250
|
-
async waitForClient() {
|
|
251
|
-
return this.backend.waitForClient();
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
//# sourceMappingURL=state.js.map
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import Table from 'easy-table';
|
|
2
|
-
import { parseErrorInfo, parseList, logError, isString, get, toInteger, TSError } from '@terascope/core-utils';
|
|
3
|
-
export function makeTable(req, defaults, data, mappingFn) {
|
|
4
|
-
const query = fieldsQuery(req.query, defaults);
|
|
5
|
-
let emptyChar = 'N/A';
|
|
6
|
-
// used to create an empty table if there are no jobs
|
|
7
|
-
if (data.length === 0) {
|
|
8
|
-
emptyChar = '';
|
|
9
|
-
data.push({});
|
|
10
|
-
}
|
|
11
|
-
return Table.print(data, (item, cell) => {
|
|
12
|
-
const fn = mappingFn ? mappingFn(item) : (field) => get(item, field, emptyChar);
|
|
13
|
-
query.forEach((field) => {
|
|
14
|
-
cell(field, fn(field));
|
|
15
|
-
});
|
|
16
|
-
}, (table) => {
|
|
17
|
-
if (('headers' in req.query) && req.query.headers === 'false') {
|
|
18
|
-
return table.print();
|
|
19
|
-
}
|
|
20
|
-
return table.toString();
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
function fieldsQuery(query, defaults) {
|
|
24
|
-
if (!query.fields) {
|
|
25
|
-
return defaults || [];
|
|
26
|
-
}
|
|
27
|
-
const results = parseList(query.fields);
|
|
28
|
-
if (results.length === 0) {
|
|
29
|
-
return defaults;
|
|
30
|
-
}
|
|
31
|
-
return results;
|
|
32
|
-
}
|
|
33
|
-
export function handleTerasliceRequest(req, res, defaultErrorMsg = 'Failure to process TerasliceRequest', { errorCode = 500, successCode = 200 } = {}) {
|
|
34
|
-
logTerasliceRequest(req);
|
|
35
|
-
return async (fn) => {
|
|
36
|
-
try {
|
|
37
|
-
const result = await fn();
|
|
38
|
-
if (isString(result)) {
|
|
39
|
-
res.status(successCode).send(result);
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
res.status(successCode).json(result);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
catch (err) {
|
|
46
|
-
const { statusCode, message } = parseErrorInfo(err, {
|
|
47
|
-
defaultErrorMsg,
|
|
48
|
-
defaultStatusCode: errorCode,
|
|
49
|
-
});
|
|
50
|
-
if (statusCode >= 500) {
|
|
51
|
-
logError(req.logger, err);
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
logError(req.logger, message);
|
|
55
|
-
}
|
|
56
|
-
sendError(res, statusCode, message, req.logger);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
export function sendError(res, code, message, logger) {
|
|
61
|
-
if (res.headersSent) {
|
|
62
|
-
const error = new TSError(message);
|
|
63
|
-
error.statusCode = code;
|
|
64
|
-
if (logger) {
|
|
65
|
-
logger.error(error, 'TerasliceRequest send error after headers sent');
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
console.error(error, 'TerasliceRequest send error after headers sent');
|
|
69
|
-
}
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
res.status(code).json({
|
|
73
|
-
error: code,
|
|
74
|
-
message
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
function parseQueryInt(req, key, defaultVal) {
|
|
78
|
-
const val = req.query[key];
|
|
79
|
-
if (val == null || val === '')
|
|
80
|
-
return defaultVal;
|
|
81
|
-
const parsed = toInteger(val);
|
|
82
|
-
// allow the invalid prop to be passed through
|
|
83
|
-
// (because an error should thrown downstream)
|
|
84
|
-
if (parsed === false) {
|
|
85
|
-
return req.query[key];
|
|
86
|
-
}
|
|
87
|
-
return parsed;
|
|
88
|
-
}
|
|
89
|
-
export function getSearchOptions(req, defaultSort = '_updated:desc') {
|
|
90
|
-
const sort = req.query.sort || defaultSort;
|
|
91
|
-
const size = parseQueryInt(req, 'size', 100);
|
|
92
|
-
const from = parseQueryInt(req, 'from', 0);
|
|
93
|
-
const filter = req.query.filter || '';
|
|
94
|
-
return { size, from, sort, filter };
|
|
95
|
-
}
|
|
96
|
-
export function logTerasliceRequest(req) {
|
|
97
|
-
const queryInfo = Object.entries(req.query)
|
|
98
|
-
.map(([key, val]) => `${key}: ${val}`)
|
|
99
|
-
.join(', ');
|
|
100
|
-
const { method, path } = req;
|
|
101
|
-
req.logger.trace(`${method.toUpperCase()} ${path} endpoint has been called, ${queryInfo}`);
|
|
102
|
-
}
|
|
103
|
-
export function createJobActiveQuery(active) {
|
|
104
|
-
if (active === 'true') {
|
|
105
|
-
return 'job_id:* AND !active:false';
|
|
106
|
-
}
|
|
107
|
-
if (active === 'false') {
|
|
108
|
-
return 'job_id:* AND active:false';
|
|
109
|
-
}
|
|
110
|
-
return 'job_id:*';
|
|
111
|
-
}
|
|
112
|
-
export function addDeletedToQuery(deleted, query) {
|
|
113
|
-
if (deleted === 'false') {
|
|
114
|
-
return `${query} AND (_deleted:false OR (* AND -_deleted:*))`;
|
|
115
|
-
}
|
|
116
|
-
return `${query} AND _deleted:true`;
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Combines a base query from an endpoint with an optional filter using AND from lucene.
|
|
120
|
-
* @param query - The base Lucene query string
|
|
121
|
-
* @param filter - Optional filter query to append. getSearchOptions() will return an empty string
|
|
122
|
-
* if no filter is present.
|
|
123
|
-
* @returns The combined query, or original query if filter is empty
|
|
124
|
-
*/
|
|
125
|
-
export function addFilterToQuery(query, filter) {
|
|
126
|
-
return filter ? `(${query}) AND (${filter})` : query;
|
|
127
|
-
}
|
|
128
|
-
//# sourceMappingURL=api_utils.js.map
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { isInteger, trimStart, trim, getFirst, joinList } from '@terascope/core-utils';
|
|
2
|
-
import * as semver from 'semver';
|
|
3
|
-
export function findMatchingAsset(records, name, version) {
|
|
4
|
-
const range = toSemverRange(version);
|
|
5
|
-
const assets = records
|
|
6
|
-
.filter(_isCompatibleAsset(name, range, false))
|
|
7
|
-
.sort((a, b) => semver.rcompare(a.version, b.version));
|
|
8
|
-
return getFirst(assets);
|
|
9
|
-
}
|
|
10
|
-
export function findSimilarAssets(records, name, version) {
|
|
11
|
-
const range = toSemverRange(version);
|
|
12
|
-
const assets = records
|
|
13
|
-
.filter(_isCompatibleAsset(name, range, true))
|
|
14
|
-
.sort((a, b) => semver.rcompare(a.version, b.version));
|
|
15
|
-
return assets;
|
|
16
|
-
}
|
|
17
|
-
export function getInCompatibilityReason(assets, prefix) {
|
|
18
|
-
if (!assets || !assets.length)
|
|
19
|
-
return '';
|
|
20
|
-
const reasons = [];
|
|
21
|
-
assets.slice(0, 3).forEach((asset) => {
|
|
22
|
-
if (!isCompatibleNodeVersion(asset.node_version)) {
|
|
23
|
-
reasons.push('node_version');
|
|
24
|
-
}
|
|
25
|
-
if (asset.platform != null && asset.platform !== process.platform) {
|
|
26
|
-
reasons.push('platform');
|
|
27
|
-
}
|
|
28
|
-
if (asset.arch != null && asset.arch !== process.arch) {
|
|
29
|
-
reasons.push('arch');
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
if (!reasons.length)
|
|
33
|
-
return '';
|
|
34
|
-
return `${prefix ? `${trim(prefix)} ` : ''}${joinList(reasons, ',', 'or')} mismatch`;
|
|
35
|
-
}
|
|
36
|
-
export function getMajorVersion(version) {
|
|
37
|
-
if (version == null)
|
|
38
|
-
return version;
|
|
39
|
-
if (isInteger(version))
|
|
40
|
-
return version;
|
|
41
|
-
return semver.major(version);
|
|
42
|
-
}
|
|
43
|
-
const SYSTEM_NODE_VERSION = getMajorVersion(process.version);
|
|
44
|
-
/**
|
|
45
|
-
* This just compares the major version
|
|
46
|
-
*/
|
|
47
|
-
function isCompatibleNodeVersion(version) {
|
|
48
|
-
if (version == null)
|
|
49
|
-
return true;
|
|
50
|
-
// anything less than or equal to current node version
|
|
51
|
-
return getMajorVersion(version) <= SYSTEM_NODE_VERSION;
|
|
52
|
-
}
|
|
53
|
-
function _isCompatibleAsset(name, range, skipRestrictions = false) {
|
|
54
|
-
return function checkIfCompatibleAsset(record) {
|
|
55
|
-
if (record.name !== name)
|
|
56
|
-
return false;
|
|
57
|
-
if (!semver.satisfies(record.version, range, { includePrerelease: true })) {
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
if (skipRestrictions)
|
|
61
|
-
return true;
|
|
62
|
-
if (!isCompatibleNodeVersion(record.node_version)) {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
if (record.arch != null && record.arch !== process.arch) {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
if (record.platform != null && record.platform !== process.platform) {
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
return true;
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
export function toSemverRange(version) {
|
|
75
|
-
if (!version || version === 'latest')
|
|
76
|
-
return '*';
|
|
77
|
-
if (semver.validRange(version)) {
|
|
78
|
-
return trimStart(trim(version), 'v');
|
|
79
|
-
}
|
|
80
|
-
throw new Error(`Version "${version}" is not a valid semver range`);
|
|
81
|
-
}
|
|
82
|
-
export function toVersionQuery(_version) {
|
|
83
|
-
const version = trimStart(trim(_version));
|
|
84
|
-
if (!version || version === 'latest' || version === '*') {
|
|
85
|
-
return 'version:*';
|
|
86
|
-
}
|
|
87
|
-
// if there a number and * next to each other that is not valid
|
|
88
|
-
// so lets just return the query: 12.34*.55
|
|
89
|
-
if (/\d\*/.test(version))
|
|
90
|
-
return `version:${version}`;
|
|
91
|
-
const range = new semver.Range(version);
|
|
92
|
-
return `version:${range.range.split(' ').join(' AND version:')}`;
|
|
93
|
-
}
|
|
94
|
-
//# sourceMappingURL=asset_utils.js.map
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { isKey, makeISODate } from '@terascope/core-utils';
|
|
2
|
-
const options = {
|
|
3
|
-
year: 'y',
|
|
4
|
-
years: 'y',
|
|
5
|
-
y: 'y',
|
|
6
|
-
months: 'M',
|
|
7
|
-
month: 'M',
|
|
8
|
-
mo: 'M',
|
|
9
|
-
mos: 'M',
|
|
10
|
-
M: 'M',
|
|
11
|
-
weeks: 'w',
|
|
12
|
-
week: 'w',
|
|
13
|
-
wks: 'w',
|
|
14
|
-
wk: 'w',
|
|
15
|
-
w: 'w',
|
|
16
|
-
days: 'd',
|
|
17
|
-
day: 'd',
|
|
18
|
-
d: 'd',
|
|
19
|
-
hours: 'h',
|
|
20
|
-
hour: 'h',
|
|
21
|
-
hr: 'h',
|
|
22
|
-
hrs: 'h',
|
|
23
|
-
h: 'h',
|
|
24
|
-
minutes: 'm',
|
|
25
|
-
minute: 'm',
|
|
26
|
-
min: 'm',
|
|
27
|
-
mins: 'm',
|
|
28
|
-
m: 'm',
|
|
29
|
-
seconds: 's',
|
|
30
|
-
second: 's',
|
|
31
|
-
s: 's',
|
|
32
|
-
milliseconds: 'ms',
|
|
33
|
-
millisecond: 'ms',
|
|
34
|
-
ms: 'ms'
|
|
35
|
-
};
|
|
36
|
-
const formatter = {
|
|
37
|
-
daily: 10,
|
|
38
|
-
monthly: 7,
|
|
39
|
-
yearly: 4
|
|
40
|
-
};
|
|
41
|
-
export function dateOptions(value) {
|
|
42
|
-
if (value && isKey(options, value)) {
|
|
43
|
-
return options[value];
|
|
44
|
-
}
|
|
45
|
-
throw new Error(`the time descriptor of "${value}" for the interval is malformed`);
|
|
46
|
-
}
|
|
47
|
-
export function timeseriesIndex(timeseriesFormat, index, dateStr) {
|
|
48
|
-
const timestamp = makeISODate();
|
|
49
|
-
const dateString = dateStr || timestamp;
|
|
50
|
-
return { index: `${index}-${dateString.slice(0, formatter[timeseriesFormat]).replace(/-/g, '.')}`, timestamp };
|
|
51
|
-
}
|
|
52
|
-
//# sourceMappingURL=date_utils.js.map
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { isString, isObjectEntity, getTypeOf, isArrayLike } from '@terascope/core-utils';
|
|
2
|
-
export function safeEncode(obj) {
|
|
3
|
-
let str;
|
|
4
|
-
if (isString(obj)) {
|
|
5
|
-
str = obj;
|
|
6
|
-
}
|
|
7
|
-
else if (isObjectEntity(obj) || isArrayLike(obj)) {
|
|
8
|
-
str = JSON.stringify(obj);
|
|
9
|
-
}
|
|
10
|
-
else {
|
|
11
|
-
throw new Error(`unprocessable entity to encode: ${getTypeOf(obj)}`);
|
|
12
|
-
}
|
|
13
|
-
return Buffer.from(str).toString('base64');
|
|
14
|
-
}
|
|
15
|
-
// TODO: proper types for this
|
|
16
|
-
export function safeDecode(str) {
|
|
17
|
-
if (!isString(str) && isObjectEntity(str)) {
|
|
18
|
-
return str;
|
|
19
|
-
}
|
|
20
|
-
try {
|
|
21
|
-
return JSON.parse(Buffer.from(str, 'base64').toString('utf-8'));
|
|
22
|
-
}
|
|
23
|
-
catch (err) {
|
|
24
|
-
throw new Error(`Unable to decode ${str}`);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
//# sourceMappingURL=encoding_utils.js.map
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { accessSync, readFileSync } from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import fse from 'fs-extra';
|
|
4
|
-
import semver from 'semver';
|
|
5
|
-
import { Mutex } from 'async-mutex';
|
|
6
|
-
import { TSError } from '@terascope/core-utils';
|
|
7
|
-
import decompress from 'decompress';
|
|
8
|
-
import { getMajorVersion } from './asset_utils.js';
|
|
9
|
-
const mutex = new Mutex();
|
|
10
|
-
const packagePath = path.join(process.cwd(), './package.json');
|
|
11
|
-
let _packageJSON;
|
|
12
|
-
export function getPackageJSON() {
|
|
13
|
-
if (_packageJSON)
|
|
14
|
-
return _packageJSON;
|
|
15
|
-
const file = readFileSync(packagePath, { encoding: 'utf8' });
|
|
16
|
-
const results = JSON.parse(file);
|
|
17
|
-
_packageJSON = results;
|
|
18
|
-
return results;
|
|
19
|
-
}
|
|
20
|
-
export function existsSync(filename) {
|
|
21
|
-
try {
|
|
22
|
-
accessSync(filename);
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
catch (ex) {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
export function deleteDir(dirPath) {
|
|
30
|
-
return fse.remove(dirPath);
|
|
31
|
-
}
|
|
32
|
-
export async function verifyAssetJSON(id, newPath) {
|
|
33
|
-
const hasAssetJSONTopLevel = await fse.pathExists(path.join(newPath, 'asset.json'));
|
|
34
|
-
if (!hasAssetJSONTopLevel) {
|
|
35
|
-
const err = new TSError('asset.json was not found in root directory of asset bundle', { statusCode: 422 });
|
|
36
|
-
throw err;
|
|
37
|
-
}
|
|
38
|
-
let packageData;
|
|
39
|
-
try {
|
|
40
|
-
packageData = await fse.readJson(path.join(newPath, 'asset.json'));
|
|
41
|
-
}
|
|
42
|
-
catch (_err) {
|
|
43
|
-
const err = new TSError(_err, {
|
|
44
|
-
message: 'Failure parsing asset.json, please ensure that\'s it formatted correctly',
|
|
45
|
-
statusCode: 422
|
|
46
|
-
});
|
|
47
|
-
throw err;
|
|
48
|
-
}
|
|
49
|
-
const metadata = Object.assign({ id }, packageData);
|
|
50
|
-
if (!metadata.name) {
|
|
51
|
-
throw new Error('Missing name');
|
|
52
|
-
}
|
|
53
|
-
metadata.version = semver.clean(metadata.version);
|
|
54
|
-
if (!semver.valid(metadata.version)) {
|
|
55
|
-
throw new Error(`Invalid version "${metadata.version}"`);
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* If node_version, platform, or arch is set to a falsey
|
|
59
|
-
* value we should delete it so it is considered a wildcard.
|
|
60
|
-
*
|
|
61
|
-
* This is useful for making an asset bundle that isn't
|
|
62
|
-
* locked down.
|
|
63
|
-
*/
|
|
64
|
-
if (metadata.node_version) {
|
|
65
|
-
metadata.node_version = getMajorVersion(metadata.node_version);
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
delete metadata.node_version;
|
|
69
|
-
}
|
|
70
|
-
if (!metadata.platform) {
|
|
71
|
-
delete metadata.platform;
|
|
72
|
-
}
|
|
73
|
-
if (!metadata.arch) {
|
|
74
|
-
delete metadata.arch;
|
|
75
|
-
}
|
|
76
|
-
if (metadata.minimum_teraslice_version) {
|
|
77
|
-
const terasliceVersion = getPackageJSON().version;
|
|
78
|
-
if (semver.gt(metadata.minimum_teraslice_version, terasliceVersion)) {
|
|
79
|
-
throw new Error(`Asset requires teraslice version ${metadata.minimum_teraslice_version} or greater.`);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return metadata;
|
|
83
|
-
}
|
|
84
|
-
async function _saveAsset(logger, assetsPath, id, binaryData, metaCheck) {
|
|
85
|
-
const newPath = path.join(assetsPath, id);
|
|
86
|
-
try {
|
|
87
|
-
if (await fse.pathExists(newPath)) {
|
|
88
|
-
await fse.emptyDir(newPath);
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
await fse.mkdir(newPath);
|
|
92
|
-
}
|
|
93
|
-
logger.info(`decompressing and saving asset ${id} to ${newPath}`);
|
|
94
|
-
await decompress(binaryData, newPath);
|
|
95
|
-
logger.info(`decompressed asset ${id} to ${newPath}`);
|
|
96
|
-
const metadata = await verifyAssetJSON(id, newPath);
|
|
97
|
-
logger.info(`asset ${id} saved to file ${newPath}`, metadata);
|
|
98
|
-
// storage/assets save fn needs to check the return metadata for uniqueness
|
|
99
|
-
if (metaCheck) {
|
|
100
|
-
return await metaCheck(metadata);
|
|
101
|
-
}
|
|
102
|
-
return metadata;
|
|
103
|
-
}
|
|
104
|
-
catch (err) {
|
|
105
|
-
await deleteDir(newPath);
|
|
106
|
-
throw err;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
export async function saveAsset(logger, assetsPath, id, binaryData, metaCheck) {
|
|
110
|
-
return mutex.runExclusive(() => _saveAsset(logger, assetsPath, id, binaryData, metaCheck));
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Check if a buffer contains a zip file.
|
|
114
|
-
* Note that file extension and mime type are not checked.
|
|
115
|
-
* Any file type that is actually a zip (.xlsx, .jar, .apk,
|
|
116
|
-
* .epub) will return true.
|
|
117
|
-
* @param {Buffer} buffer A buffer containing a file file
|
|
118
|
-
* @returns {boolean}
|
|
119
|
-
*/
|
|
120
|
-
export function isZipFile(buffer) {
|
|
121
|
-
const zipSignature = [0x50, 0x4B, 0x03, 0x04];
|
|
122
|
-
return zipSignature.every((byte, index) => buffer[index] === byte);
|
|
123
|
-
}
|
|
124
|
-
//# sourceMappingURL=file_utils.js.map
|