teraslice 2.11.0 → 2.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 -94
- package/service.js +0 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fse from 'fs-extra';
|
|
3
|
+
import crypto from 'node:crypto';
|
|
4
|
+
import { TSError, uniq, isString, toString, filterObject, pDelay } from '@terascope/utils';
|
|
5
|
+
import { TerasliceElasticsearchStorage } from './backends/elasticsearch_store.js';
|
|
6
|
+
import { S3Store } from './backends/s3_store.js';
|
|
7
|
+
import { makeLogger } from '../workers/helpers/terafoundation.js';
|
|
8
|
+
import { saveAsset, isZipFile, deleteDir } from '../utils/file_utils.js';
|
|
9
|
+
import { findMatchingAsset, findSimilarAssets, toVersionQuery, getInCompatibilityReason } from '../utils/asset_utils.js';
|
|
10
|
+
function _metaIsUnique(backend) {
|
|
11
|
+
return async function checkMeta(meta) {
|
|
12
|
+
const includes = ['name', 'version', 'node_version', 'platform', 'arch'];
|
|
13
|
+
// @ts-expect-error
|
|
14
|
+
const query = Object.entries(filterObject(meta, { includes }))
|
|
15
|
+
.map(([key, val]) => `${key}:"${val}"`)
|
|
16
|
+
.join(' AND ');
|
|
17
|
+
const total = await backend.count(query);
|
|
18
|
+
if (total === 0) {
|
|
19
|
+
return meta;
|
|
20
|
+
}
|
|
21
|
+
const error = new TSError(`Asset ${query} already exists, please increment the version and send again`, {
|
|
22
|
+
statusCode: 409
|
|
23
|
+
});
|
|
24
|
+
error.statusCode = 409;
|
|
25
|
+
// @ts-expect-error a flag to differentiate error scenarios
|
|
26
|
+
error.alreadyExists = true;
|
|
27
|
+
throw error;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function getBackendConfig(context, logger) {
|
|
31
|
+
const { teraslice } = context.sysconfig;
|
|
32
|
+
const connectionType = teraslice.asset_storage_connection_type;
|
|
33
|
+
const storageConnection = teraslice.asset_storage_connection;
|
|
34
|
+
const storageBucket = teraslice.asset_storage_bucket;
|
|
35
|
+
/// Check teraslice first before setting to terafoundation configs
|
|
36
|
+
const s3BackendConfig = {
|
|
37
|
+
context,
|
|
38
|
+
terafoundation: context.sysconfig.terafoundation,
|
|
39
|
+
connection: storageConnection,
|
|
40
|
+
bucket: storageBucket,
|
|
41
|
+
logger
|
|
42
|
+
};
|
|
43
|
+
return { s3BackendConfig, assetConnectionType: connectionType };
|
|
44
|
+
}
|
|
45
|
+
// Module to manager job states in Elasticsearch.
|
|
46
|
+
// All functions in this module return promises that must be resolved to
|
|
47
|
+
// get the final result.
|
|
48
|
+
export class AssetsStorage {
|
|
49
|
+
assetsPath;
|
|
50
|
+
esBackend;
|
|
51
|
+
context;
|
|
52
|
+
logger;
|
|
53
|
+
s3Backend;
|
|
54
|
+
constructor(context) {
|
|
55
|
+
const logger = makeLogger(context, 'assets_storage');
|
|
56
|
+
const config = context.sysconfig.teraslice;
|
|
57
|
+
const indexName = `${config.name}__assets`;
|
|
58
|
+
const esBackendConfig = {
|
|
59
|
+
context,
|
|
60
|
+
indexName,
|
|
61
|
+
recordType: 'asset',
|
|
62
|
+
idField: 'id',
|
|
63
|
+
fullResponse: true,
|
|
64
|
+
logRecord: false,
|
|
65
|
+
storageName: 'assets',
|
|
66
|
+
logger
|
|
67
|
+
};
|
|
68
|
+
this.context = context;
|
|
69
|
+
this.logger = logger;
|
|
70
|
+
// TODO: verify this behavior of string vs string[] and undefined
|
|
71
|
+
this.assetsPath = config.assets_directory;
|
|
72
|
+
this.esBackend = new TerasliceElasticsearchStorage(esBackendConfig);
|
|
73
|
+
const { assetConnectionType, s3BackendConfig } = getBackendConfig(context, logger);
|
|
74
|
+
if (assetConnectionType === 's3' && s3BackendConfig.connection) {
|
|
75
|
+
this.s3Backend = new S3Store(s3BackendConfig);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async initialize() {
|
|
79
|
+
await this.ensureAssetDir();
|
|
80
|
+
if (this.s3Backend) {
|
|
81
|
+
await Promise.all([
|
|
82
|
+
this.s3Backend.initialize(),
|
|
83
|
+
this.esBackend.initialize()
|
|
84
|
+
]);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
await this.esBackend.initialize();
|
|
88
|
+
}
|
|
89
|
+
this.logger.info('assets storage initialized');
|
|
90
|
+
}
|
|
91
|
+
async _assetExistsInFS(id) {
|
|
92
|
+
try {
|
|
93
|
+
if (!this.assetsPath)
|
|
94
|
+
return false;
|
|
95
|
+
const access = fse.constants.F_OK | fse.constants.R_OK | fse.constants.W_OK;
|
|
96
|
+
await fse.access(path.join(this.assetsPath, id), access);
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async _assetExistsInStorage(id) {
|
|
104
|
+
try {
|
|
105
|
+
const inES = await this.esBackend.get(id, undefined, ['id', 'name']);
|
|
106
|
+
if (this.s3Backend) {
|
|
107
|
+
const inS3 = await this.s3Backend.get(id);
|
|
108
|
+
return inES != null && inS3 != null;
|
|
109
|
+
}
|
|
110
|
+
return inES != null;
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
if (err.statusCode === 404) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
throw new TSError(err, {
|
|
117
|
+
reason: `Failure checking asset index, could not get asset with id: ${id}`
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async _assetExists(id) {
|
|
122
|
+
const [readable, exists] = await Promise.all([
|
|
123
|
+
this._assetExistsInFS(id),
|
|
124
|
+
this._assetExistsInStorage(id)
|
|
125
|
+
]);
|
|
126
|
+
return readable && exists;
|
|
127
|
+
}
|
|
128
|
+
async _saveToEs(id, assetRecord, blocking, responseTimeout, startTime) {
|
|
129
|
+
if (blocking) {
|
|
130
|
+
const elapsed = Date.now() - startTime;
|
|
131
|
+
const remaining = responseTimeout - elapsed;
|
|
132
|
+
await this.esBackend.indexWithId(id, assetRecord, undefined, remaining);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
await this.esBackend.indexWithId(id, assetRecord, undefined, responseTimeout);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// data is the buffer form of asset, stored in filesystem
|
|
139
|
+
// esData is the base64 version which is stored in elasticsearch
|
|
140
|
+
async _saveAndUpload({ id, data, esData, blocking }) {
|
|
141
|
+
const responseTimeout = this.context.sysconfig.teraslice.api_response_timeout;
|
|
142
|
+
const startTime = Date.now();
|
|
143
|
+
const metaData = await saveAsset(this.logger, this.assetsPath, id, data, _metaIsUnique(this.esBackend));
|
|
144
|
+
const assetRecord = Object.assign({
|
|
145
|
+
blob: esData,
|
|
146
|
+
_created: new Date().toISOString()
|
|
147
|
+
}, metaData);
|
|
148
|
+
await this._saveToEs(id, assetRecord, blocking, responseTimeout, startTime);
|
|
149
|
+
this.logger.info(`assets: ${metaData.name}, id: ${id} has been saved to assets_directory and elasticsearch`);
|
|
150
|
+
}
|
|
151
|
+
async _saveAndUploadS3({ id, data, blocking }) {
|
|
152
|
+
const responseTimeout = this.context.sysconfig.teraslice.api_response_timeout;
|
|
153
|
+
const startTime = Date.now();
|
|
154
|
+
try {
|
|
155
|
+
if (this.s3Backend) {
|
|
156
|
+
if (blocking) {
|
|
157
|
+
const elapsed = Date.now() - startTime;
|
|
158
|
+
const remaining = responseTimeout - elapsed;
|
|
159
|
+
await this.s3Backend.save(id, data, remaining);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
await this.s3Backend.save(id, data, responseTimeout);
|
|
163
|
+
}
|
|
164
|
+
this.logger.info(`asset id: ${id} has been saved to s3 store`);
|
|
165
|
+
}
|
|
166
|
+
const metaData = await saveAsset(this.logger, this.assetsPath, id, data, _metaIsUnique(this.esBackend));
|
|
167
|
+
const assetRecord = Object.assign({
|
|
168
|
+
_created: new Date().toISOString()
|
|
169
|
+
}, metaData);
|
|
170
|
+
await this._saveToEs(id, assetRecord, blocking, responseTimeout, startTime);
|
|
171
|
+
this.logger.info(`assets: ${metaData.name}, id: ${id} has been saved to assets_directory and elasticsearch`);
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
// clean up s3 object or saved asset if a later step fails
|
|
175
|
+
await this.s3Backend?.remove(id);
|
|
176
|
+
await deleteDir(path.join(this.assetsPath, id));
|
|
177
|
+
throw err;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Save an asset to disk and upload to elasticsearch or s3
|
|
182
|
+
*
|
|
183
|
+
* @param {Buffer} data A buffer of the asset file (zipped)
|
|
184
|
+
* @param {boolean=true} blocking If false, save the asset in the background
|
|
185
|
+
* @returns {Promise<{ assetId: string; created: boolean }>}
|
|
186
|
+
*/
|
|
187
|
+
async save(data, blocking = true) {
|
|
188
|
+
if (!isZipFile(data)) {
|
|
189
|
+
throw new Error('Failed to save asset. File type not recognized as zip.');
|
|
190
|
+
}
|
|
191
|
+
const esData = data.toString('base64');
|
|
192
|
+
const id = crypto.createHash('sha1').update(esData)
|
|
193
|
+
.digest('hex');
|
|
194
|
+
const exists = await this._assetExists(id);
|
|
195
|
+
if (exists) {
|
|
196
|
+
this.logger.info(`asset id: ${id} already exists`);
|
|
197
|
+
}
|
|
198
|
+
else if (blocking && this.s3Backend) {
|
|
199
|
+
await this._saveAndUploadS3({
|
|
200
|
+
id, data, blocking
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
else if (blocking && !this.s3Backend) {
|
|
204
|
+
await this._saveAndUpload({
|
|
205
|
+
id, data, esData, blocking
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
// kick this of in the background since it is not blocking
|
|
210
|
+
try {
|
|
211
|
+
if (this.s3Backend) {
|
|
212
|
+
await this._saveAndUploadS3({
|
|
213
|
+
id, data, blocking
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
await this._saveAndUpload({
|
|
218
|
+
id, data, esData, blocking
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
this.logger.error(err, `Failure saving asset: ${id}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
assetId: id,
|
|
228
|
+
created: !exists,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
async search(query, from, size, sort, fields) {
|
|
232
|
+
return this.esBackend.search(query, from, size, sort, fields);
|
|
233
|
+
}
|
|
234
|
+
/*
|
|
235
|
+
* Get all the asset records out of the S3 bucket
|
|
236
|
+
* Returns an array of Records containing: { File: any, Size: any }
|
|
237
|
+
*/
|
|
238
|
+
async grabS3Info() {
|
|
239
|
+
return await this.s3Backend?.list();
|
|
240
|
+
}
|
|
241
|
+
// this should be a SearchResponse as full_response is set to true in backendConfig
|
|
242
|
+
// however for some reason the api ignores that for get and mget, and fullResponse
|
|
243
|
+
// is an argument to the call itself, which can defy the config, defaults to false
|
|
244
|
+
async get(id) {
|
|
245
|
+
let record;
|
|
246
|
+
if (this.s3Backend) {
|
|
247
|
+
record = await this.esBackend.get(id);
|
|
248
|
+
record.blob = await this.s3Backend.get(id);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
record = this.esBackend.get(id);
|
|
252
|
+
}
|
|
253
|
+
return record;
|
|
254
|
+
}
|
|
255
|
+
async _getAssetId(assetIdentifier) {
|
|
256
|
+
// is full _id
|
|
257
|
+
if (assetIdentifier.length === 40) {
|
|
258
|
+
const count = await this.esBackend.count(`id:"${assetIdentifier}"`);
|
|
259
|
+
if (count === 1)
|
|
260
|
+
return assetIdentifier;
|
|
261
|
+
}
|
|
262
|
+
const [name, version] = assetIdentifier.split(':');
|
|
263
|
+
const sort = '_created:desc';
|
|
264
|
+
const fields = ['id', 'name', 'version', 'platform', 'arch', 'node_version'];
|
|
265
|
+
const response = await this.esBackend.search(`name:"${name}" AND ${toVersionQuery(version)}`, undefined, 10000, sort, fields);
|
|
266
|
+
const assets = response.hits.hits.map((doc) => doc._source);
|
|
267
|
+
if (!assets.length) {
|
|
268
|
+
throw new TSError(`No assets found for "${assetIdentifier}"`, {
|
|
269
|
+
statusCode: 404
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
const found = findMatchingAsset(assets, name, version);
|
|
273
|
+
if (!found) {
|
|
274
|
+
const reason = getInCompatibilityReason(findSimilarAssets(assets, name, version), ', due to a potential');
|
|
275
|
+
throw new TSError(`No asset found for "${assetIdentifier}"${reason}`, {
|
|
276
|
+
statusCode: 404
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
return found.id;
|
|
280
|
+
}
|
|
281
|
+
async parseAssetsArray(assetsArray) {
|
|
282
|
+
return Promise.all(uniq(assetsArray).map(this._getAssetId.bind(this)));
|
|
283
|
+
}
|
|
284
|
+
async shutdown(forceShutdown) {
|
|
285
|
+
this.logger.info('shutting asset store down.');
|
|
286
|
+
if (this.s3Backend) {
|
|
287
|
+
return Promise.all([
|
|
288
|
+
this.esBackend.shutdown(forceShutdown),
|
|
289
|
+
this.s3Backend.shutdown()
|
|
290
|
+
]);
|
|
291
|
+
}
|
|
292
|
+
return Promise.all([
|
|
293
|
+
this.esBackend.shutdown(forceShutdown)
|
|
294
|
+
]);
|
|
295
|
+
}
|
|
296
|
+
async remove(assetId) {
|
|
297
|
+
try {
|
|
298
|
+
await this.esBackend.get(assetId, undefined, ['name']);
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
if (toString(err).indexOf('Not Found')) {
|
|
302
|
+
const error = new TSError(`Unable to find asset ${assetId}`, {
|
|
303
|
+
statusCode: 404
|
|
304
|
+
});
|
|
305
|
+
throw error;
|
|
306
|
+
}
|
|
307
|
+
throw err;
|
|
308
|
+
}
|
|
309
|
+
let inFS = true;
|
|
310
|
+
let inStorage = true;
|
|
311
|
+
let delayMS = 100;
|
|
312
|
+
const responseTimeout = this.context.sysconfig.teraslice.api_response_timeout;
|
|
313
|
+
const timeoutID = setTimeout(() => {
|
|
314
|
+
throw new TSError(`Timeout deleting asset ${assetId}`);
|
|
315
|
+
}, responseTimeout);
|
|
316
|
+
while (inFS || inStorage) {
|
|
317
|
+
try {
|
|
318
|
+
await this.esBackend.remove(assetId);
|
|
319
|
+
if (this.s3Backend) {
|
|
320
|
+
await this.s3Backend.remove(assetId);
|
|
321
|
+
}
|
|
322
|
+
await fse.remove(path.join(this.assetsPath, assetId));
|
|
323
|
+
[inFS, inStorage] = await Promise.all([
|
|
324
|
+
this._assetExistsInFS(assetId),
|
|
325
|
+
this._assetExistsInStorage(assetId)
|
|
326
|
+
]);
|
|
327
|
+
}
|
|
328
|
+
catch (err) {
|
|
329
|
+
this.logger.error(err, `Failure deleting asset ${assetId} from S3: ${err}`);
|
|
330
|
+
await pDelay(delayMS);
|
|
331
|
+
if (delayMS < 300000)
|
|
332
|
+
delayMS *= 2;
|
|
333
|
+
}
|
|
334
|
+
finally {
|
|
335
|
+
clearTimeout(timeoutID);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
async ensureAssetDir() {
|
|
340
|
+
if (!this.assetsPath || !isString(this.assetsPath)) {
|
|
341
|
+
throw new Error('Asset Store requires a valid assetsPath');
|
|
342
|
+
}
|
|
343
|
+
try {
|
|
344
|
+
return await fse.ensureDir(this.assetsPath);
|
|
345
|
+
}
|
|
346
|
+
catch (err) {
|
|
347
|
+
throw new Error(`Failure to the ensure assets directory ${this.assetsPath}, for reason ${err.message}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
async findAssetsToAutoload(autoloadDir) {
|
|
351
|
+
const files = await fse.readdir(autoloadDir);
|
|
352
|
+
return files.filter((fileName) => {
|
|
353
|
+
const ext = path.extname(fileName);
|
|
354
|
+
return ext === '.zip';
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
async autoload() {
|
|
358
|
+
// @ts-expect-error TODO: verify this parameter
|
|
359
|
+
const autoloadDir = this.context.sysconfig.teraslice.autoload_directory;
|
|
360
|
+
if (!autoloadDir || !fse.existsSync(autoloadDir))
|
|
361
|
+
return;
|
|
362
|
+
const assets = await this.findAssetsToAutoload(autoloadDir);
|
|
363
|
+
if (!assets || !assets.length)
|
|
364
|
+
return;
|
|
365
|
+
for (const asset of assets) {
|
|
366
|
+
this.logger.info(`autoloading asset ${asset}...`);
|
|
367
|
+
const assetPath = path.join(autoloadDir, asset);
|
|
368
|
+
try {
|
|
369
|
+
const result = await this.save(await fse.readFile(assetPath), true);
|
|
370
|
+
if (result.created) {
|
|
371
|
+
this.logger.debug(`autoloaded asset ${asset}`);
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
this.logger.debug(`autoloaded asset ${asset} already exists`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
catch (err) {
|
|
378
|
+
if (err.alreadyExists) {
|
|
379
|
+
this.logger.debug(`autoloaded asset ${asset} already exists`);
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
throw err;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
this.logger.info('done autoloading assets');
|
|
387
|
+
}
|
|
388
|
+
verifyClient() {
|
|
389
|
+
if (this.s3Backend) {
|
|
390
|
+
return this.esBackend.verifyClient() && this.s3Backend.verifyClient();
|
|
391
|
+
}
|
|
392
|
+
return this.esBackend.verifyClient();
|
|
393
|
+
}
|
|
394
|
+
async waitForClient() {
|
|
395
|
+
if (this.s3Backend) {
|
|
396
|
+
return Promise.all([this.esBackend.waitForClient(), this.s3Backend.waitForClient()]);
|
|
397
|
+
}
|
|
398
|
+
return this.esBackend.waitForClient();
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
//# sourceMappingURL=assets.js.map
|