hammerkit 2.0.0-next.3 → 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
@@ -99,11 +99,15 @@ async function annotate(instance, namespace, name, annotations) {
99
99
  }
100
100
  }
101
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. The run's namespace is its own, and only
103
- // the run writes to the claim: it forgets the state before it writes, records it after.
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.
104
105
  async function rememberedClaimState(instance, kubernetes, name) {
105
106
  const claim = await readClaim(instance, kubernetes.namespace, name);
106
- if (!claim || claim.metadata?.deletionTimestamp) {
107
+ if (!claim) {
108
+ return undefined;
109
+ }
110
+ if (claim.metadata?.deletionTimestamp) {
107
111
  return null;
108
112
  }
109
113
  return claim.metadata?.annotations?.[exports.STATE_ANNOTATION] ?? null;
@@ -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;
@@ -190,8 +190,13 @@ function kubernetesTaskRuntime(task, kubernetes) {
190
190
  }
191
191
  if (remembers && !kept) {
192
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)
193
194
  await (0, kept_claims_1.forgetClaimState)(instance, kubernetes, claim);
194
- await (0, restore_kubernetes_data_1.restoreKubernetesData)(task, kubernetes, instance, environment, path, { stateKey });
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
+ });
195
200
  if (stateKey) {
196
201
  await (0, kept_claims_1.recordClaimState)(instance, kubernetes, claim, stateKey);
197
202
  }
@@ -488,9 +493,11 @@ function kubernetesTaskRuntime(task, kubernetes) {
488
493
  return (0, kept_claims_1.useClaimState)(instance, kubernetes, claim);
489
494
  }
490
495
  if (remembers) {
491
- // a service run that sleeps loses its Jobs but keeps its claims, the claim says
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.
492
499
  const remembered = await (0, kept_claims_1.rememberedClaimState)(instance, kubernetes, claim);
493
- if (remembered) {
500
+ if (remembered !== undefined) {
494
501
  return remembered;
495
502
  }
496
503
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "hammerkit",
3
3
  "description": "Containerized build tool with incremental caching that runs the same build file locally, in Docker and on Kubernetes.",
4
- "version": "2.0.0-next.3",
4
+ "version": "2.0.0-next.4",
5
5
  "keywords": [
6
6
  "build",
7
7
  "build-tool",