hammerkit 2.0.0-next.2 → 2.0.0-next.4
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ensurePersistentData = exports.packSource = exports.restoreLiveFiles = exports.syncLiveFiles = exports.SYNC_FILES = exports.getStateDirsInUse = exports.REMOVE_OTHER_STATES = exports.execInPod = exports.clearClaim = exports.getPodForPersistence = void 0;
|
|
3
|
+
exports.ensurePersistentData = exports.packSource = exports.clearStateFiles = exports.restoreLiveFiles = exports.syncLiveFiles = exports.SYNC_FILES = exports.getStateDirsInUse = exports.REMOVE_OTHER_STATES = exports.execInPod = exports.clearClaim = exports.getPodForPersistence = void 0;
|
|
4
4
|
const volumes_1 = require("./volumes");
|
|
5
5
|
const apply_1 = require("./apply");
|
|
6
6
|
const path_1 = require("path");
|
|
@@ -282,6 +282,31 @@ exports.restoreLiveFiles = restoreLiveFiles;
|
|
|
282
282
|
// Returns whether the upload is needed.
|
|
283
283
|
async function prepareStateFiles(instance, env, work, pod, dir, inUse) {
|
|
284
284
|
const targetPath = `/dev/hammerkit/${work.id()}`;
|
|
285
|
+
await removeOtherStates(instance, env, pod, targetPath, dir, inUse);
|
|
286
|
+
if (inUse.has(dir) && !(await execInPod(instance, env, pod, ['test', '-e', `${targetPath}/${dir}/${UPLOADED}`]))) {
|
|
287
|
+
work.status.write('info', `the files of ${work.name} are there already`);
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
await clearStateDir(instance, env, pod, `${targetPath}/${dir}`);
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
// what a failed upload, restore or task left: tar does not remove files the commit does not have
|
|
294
|
+
async function clearStateDir(instance, env, pod, path) {
|
|
295
|
+
const cleared = await execInPod(instance, env, pod, ['rm', '-rf', path]);
|
|
296
|
+
if (cleared) {
|
|
297
|
+
throw new Error(`unable to clear ${path}: ${cleared}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
// Before a restore of a service run into the folder `dir` of the claim of `work` (in its upload
|
|
301
|
+
// pod `pod`): the folders of the other states go but those of `inUse`, which pods of the run
|
|
302
|
+
// mount, and `dir` is emptied. A claim that took only restores holds one state, as after a Job.
|
|
303
|
+
async function clearStateFiles(instance, env, work, pod, dir, inUse) {
|
|
304
|
+
const targetPath = `/dev/hammerkit/${work.id()}`;
|
|
305
|
+
await removeOtherStates(instance, env, pod, targetPath, dir, inUse);
|
|
306
|
+
await clearStateDir(instance, env, pod, `${targetPath}/${dir}`);
|
|
307
|
+
}
|
|
308
|
+
exports.clearStateFiles = clearStateFiles;
|
|
309
|
+
async function removeOtherStates(instance, env, pod, targetPath, dir, inUse) {
|
|
285
310
|
const removed = await execInPod(instance, env, pod, [
|
|
286
311
|
'sh',
|
|
287
312
|
'-c',
|
|
@@ -294,16 +319,6 @@ async function prepareStateFiles(instance, env, work, pod, dir, inUse) {
|
|
|
294
319
|
if (removed) {
|
|
295
320
|
throw new Error(`unable to remove the files of other commits in ${targetPath}: ${removed}`);
|
|
296
321
|
}
|
|
297
|
-
if (inUse.has(dir) && !(await execInPod(instance, env, pod, ['test', '-e', `${targetPath}/${dir}/${UPLOADED}`]))) {
|
|
298
|
-
work.status.write('info', `the files of ${work.name} are there already`);
|
|
299
|
-
return false;
|
|
300
|
-
}
|
|
301
|
-
// what a failed upload or task left: tar does not remove files the commit does not have
|
|
302
|
-
const cleared = await execInPod(instance, env, pod, ['rm', '-rf', `${targetPath}/${dir}`]);
|
|
303
|
-
if (cleared) {
|
|
304
|
-
throw new Error(`unable to clear ${targetPath}/${dir}: ${cleared}`);
|
|
305
|
-
}
|
|
306
|
-
return true;
|
|
307
322
|
}
|
|
308
323
|
// The archive that uploads a source into the claim: its file or directory, at its place in
|
|
309
324
|
// the claim (see getClaimSubPath) instead of under its own name
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.evictKeptClaims = exports.sweepKeptClaims = exports.expireKeptClaims = exports.recordClaimState = exports.forgetClaimState = exports.useClaimState = exports.isKeptClaim = exports.getClaimName = exports.EXPIRES_ANNOTATION = exports.STATE_ANNOTATION = exports.KEPT_BY_LABEL = exports.VOLUME_SCOPE_LABEL = void 0;
|
|
3
|
+
exports.evictKeptClaims = exports.sweepKeptClaims = exports.expireKeptClaims = exports.recordClaimState = exports.forgetClaimState = exports.rememberedClaimState = exports.useClaimState = exports.isKeptClaim = exports.getClaimName = exports.EXPIRES_ANNOTATION = exports.STATE_ANNOTATION = exports.KEPT_BY_LABEL = exports.VOLUME_SCOPE_LABEL = void 0;
|
|
4
4
|
const crypto_1 = require("crypto");
|
|
5
5
|
const apply_1 = require("./apply");
|
|
6
6
|
const sleep_1 = require("../utils/sleep");
|
|
@@ -98,6 +98,21 @@ async function annotate(instance, namespace, name, annotations) {
|
|
|
98
98
|
throw e;
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
+
// The state key of the outputs whose folder is whole in the claim of a task of a service run
|
|
102
|
+
// (see getStateFilesDir), null when it names none (also while the claim is being removed),
|
|
103
|
+
// undefined when there is no claim. The run's namespace is its own, and only the run writes
|
|
104
|
+
// to the claim: it forgets the state before it writes, records it after.
|
|
105
|
+
async function rememberedClaimState(instance, kubernetes, name) {
|
|
106
|
+
const claim = await readClaim(instance, kubernetes.namespace, name);
|
|
107
|
+
if (!claim) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
if (claim.metadata?.deletionTimestamp) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
return claim.metadata?.annotations?.[exports.STATE_ANNOTATION] ?? null;
|
|
114
|
+
}
|
|
115
|
+
exports.rememberedClaimState = rememberedClaimState;
|
|
101
116
|
// Before a task writes to its kept claim: the outputs in it stop being those of a state.
|
|
102
117
|
// Whether the claim was there, with what an earlier run left in it.
|
|
103
118
|
function forgetClaimState(instance, kubernetes, name) {
|
|
@@ -16,7 +16,9 @@ stateDir) {
|
|
|
16
16
|
exports.getRestoreTarget = getRestoreTarget;
|
|
17
17
|
async function restoreKubernetesData(work, kubernetes, instance, environment, path,
|
|
18
18
|
// the kept claim of the work holds what an earlier run left, which goes first; on a
|
|
19
|
-
// service run, the outputs go into the folder of `stateKey` (see getStateFilesDir)
|
|
19
|
+
// service run, the outputs go into the folder of `stateKey` (see getStateFilesDir);
|
|
20
|
+
// `otherStates`: the folders of the other states go but these (the ones pods of the run
|
|
21
|
+
// mount), and the folder of `stateKey` is emptied first
|
|
20
22
|
options = {}) {
|
|
21
23
|
const persistence = await (0, volumes_1.getKubernetesPersistence)(work, kubernetes, options.stateKey);
|
|
22
24
|
const stateDir = kubernetes.server?.serviceRun ? (0, volumes_1.getItemFilesDir)(work, options.stateKey) : undefined;
|
|
@@ -24,6 +26,9 @@ options = {}) {
|
|
|
24
26
|
if (options.reset) {
|
|
25
27
|
await (0, ensure_persistent_data_1.clearClaim)(instance, kubernetes, work, name);
|
|
26
28
|
}
|
|
29
|
+
if (stateDir && options.otherStates) {
|
|
30
|
+
await (0, ensure_persistent_data_1.clearStateFiles)(instance, kubernetes, work, name, stateDir, options.otherStates);
|
|
31
|
+
}
|
|
27
32
|
for (const generatedArchive of (0, event_cache_1.getArchivePaths)(work.data, path)) {
|
|
28
33
|
if (!(await environment.file.exists(generatedArchive.filename))) {
|
|
29
34
|
continue;
|
|
@@ -170,6 +170,9 @@ function kubernetesTaskRuntime(task, kubernetes) {
|
|
|
170
170
|
// on a server that keeps them, the task's claim stays after the run with the state of its
|
|
171
171
|
// outputs, and the next run of the same source mounts them as they are (see kept-claims)
|
|
172
172
|
const kept = (0, kept_claims_1.isKeptClaim)(task, kubernetes);
|
|
173
|
+
// on a service run, the claim names the last state whose folder is whole (see
|
|
174
|
+
// rememberedClaimState): a wake or a move at that state mounts it without an upload
|
|
175
|
+
const remembers = kept || !!kubernetes.server?.serviceRun;
|
|
173
176
|
const claim = (0, volumes_1.getVolumeName)(task, kubernetes);
|
|
174
177
|
return {
|
|
175
178
|
initialize() {
|
|
@@ -182,6 +185,21 @@ function kubernetesTaskRuntime(task, kubernetes) {
|
|
|
182
185
|
stateKey &&
|
|
183
186
|
(await (0, ensure_persistent_data_1.getStateDirsInUse)(instance, kubernetes.namespace, claim)).has((0, volumes_1.getStateFilesDir)(stateKey))) {
|
|
184
187
|
task.status.write('info', `${task.name}: services of the run use its outputs of this state, they stay`);
|
|
188
|
+
await (0, kept_claims_1.recordClaimState)(instance, kubernetes, claim, stateKey);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (remembers && !kept) {
|
|
192
|
+
// the folder of the state is written now: until it is whole, the claim names no state
|
|
193
|
+
// (a restore without a state key says none, and the next run uploads again)
|
|
194
|
+
await (0, kept_claims_1.forgetClaimState)(instance, kubernetes, claim);
|
|
195
|
+
await (0, restore_kubernetes_data_1.restoreKubernetesData)(task, kubernetes, instance, environment, path, {
|
|
196
|
+
stateKey,
|
|
197
|
+
// the folders of other states no pod of the run mounts go, as before a Job
|
|
198
|
+
...(stateKey ? { otherStates: await (0, ensure_persistent_data_1.getStateDirsInUse)(instance, kubernetes.namespace, claim) } : {}),
|
|
199
|
+
});
|
|
200
|
+
if (stateKey) {
|
|
201
|
+
await (0, kept_claims_1.recordClaimState)(instance, kubernetes, claim, stateKey);
|
|
202
|
+
}
|
|
185
203
|
return;
|
|
186
204
|
}
|
|
187
205
|
if (!kept) {
|
|
@@ -231,11 +249,14 @@ function kubernetesTaskRuntime(task, kubernetes) {
|
|
|
231
249
|
try {
|
|
232
250
|
if (files && (await (0, ensure_persistent_data_1.getStateDirsInUse)(instance, kubernetes.namespace, claim)).has(files.dir)) {
|
|
233
251
|
task.status.write('info', `${task.name}: services of the run use its outputs of this state, they stay`);
|
|
252
|
+
await (0, kept_claims_1.recordClaimState)(instance, kubernetes, claim, options.stateKey);
|
|
234
253
|
return;
|
|
235
254
|
}
|
|
236
255
|
// the task writes to it now: what it holds stops being the outputs of a state, and
|
|
237
|
-
// goes, as a new claim would hold nothing
|
|
238
|
-
|
|
256
|
+
// goes, as a new claim would hold nothing (on a service run, the folders of other
|
|
257
|
+
// states that no pod mounts go)
|
|
258
|
+
const forgotten = remembers && (await (0, kept_claims_1.forgetClaimState)(instance, kubernetes, claim));
|
|
259
|
+
const reset = kept && forgotten;
|
|
239
260
|
await (0, ensure_persistent_data_1.ensurePersistentData)(instance, kubernetes, environment, task, persistence, { reset, files });
|
|
240
261
|
}
|
|
241
262
|
catch (e) {
|
|
@@ -415,7 +436,7 @@ function kubernetesTaskRuntime(task, kubernetes) {
|
|
|
415
436
|
}
|
|
416
437
|
// the finished job stays for its state, the values it read need not
|
|
417
438
|
await removeTaskSecrets(instance, kubernetes, task);
|
|
418
|
-
if (
|
|
439
|
+
if (remembers) {
|
|
419
440
|
await (0, kept_claims_1.recordClaimState)(instance, kubernetes, claim, options.stateKey);
|
|
420
441
|
}
|
|
421
442
|
}
|
|
@@ -471,6 +492,15 @@ function kubernetesTaskRuntime(task, kubernetes) {
|
|
|
471
492
|
// the server removes the Jobs of a run at its end, the claim says
|
|
472
493
|
return (0, kept_claims_1.useClaimState)(instance, kubernetes, claim);
|
|
473
494
|
}
|
|
495
|
+
if (remembers) {
|
|
496
|
+
// a service run that sleeps loses its Jobs but keeps its claims, the claim says. One
|
|
497
|
+
// that names no state is being written (or was, and that failed): the folder of the
|
|
498
|
+
// state of an earlier Job may be gone. Only a task without a claim goes by its Jobs.
|
|
499
|
+
const remembered = await (0, kept_claims_1.rememberedClaimState)(instance, kubernetes, claim);
|
|
500
|
+
if (remembered !== undefined) {
|
|
501
|
+
return remembered;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
474
504
|
const jobs = await instance.batchApi.listNamespacedJob({
|
|
475
505
|
namespace: kubernetes.namespace,
|
|
476
506
|
labelSelector: (0, resources_1.getItemSelector)(task, kubernetes),
|
package/package.json
CHANGED