fix: select rollback artifact pairs atomically
This commit is contained in:
@@ -292,13 +292,12 @@ export async function verifyRollbackReleaseCoherence(
|
||||
) {
|
||||
const readArtifact = options.readArtifact ?? readJsonArtifact;
|
||||
const paths = options.paths ?? DEFAULT_ROLLBACK_PATHS;
|
||||
const release = await releaseManifest(readArtifact, paths);
|
||||
const runtimeValue = await readPrimaryOrFallback(
|
||||
const artifactPair = await selectRollbackArtifactPair(
|
||||
readArtifact,
|
||||
paths.primaryRuntime,
|
||||
paths.fallbackRuntime,
|
||||
paths,
|
||||
);
|
||||
const runtimeArtifact = parseRuntimeConfigArtifact(runtimeValue);
|
||||
const release = parseReleaseArtifact(artifactPair.release);
|
||||
const runtimeArtifact = parseRuntimeConfigArtifact(artifactPair.runtime);
|
||||
const runtime = {
|
||||
...runtimeArtifact,
|
||||
BUILD_ID: requireIdentity(runtimeArtifact.BUILD_ID, "runtime BUILD_ID"),
|
||||
@@ -377,6 +376,75 @@ function requireIdentity(value: string | undefined, label: string): string {
|
||||
return value;
|
||||
}
|
||||
|
||||
async function selectRollbackArtifactPair(
|
||||
readArtifact: JsonArtifactReader,
|
||||
paths: RollbackArtifactPaths,
|
||||
): Promise<Readonly<{ release: unknown; runtime: unknown }>> {
|
||||
const primary = await readArtifactPair(
|
||||
readArtifact,
|
||||
paths.primaryRelease,
|
||||
paths.primaryRuntime,
|
||||
);
|
||||
if (
|
||||
primary.release.status === "fulfilled" &&
|
||||
primary.runtime.status === "fulfilled"
|
||||
) {
|
||||
return Object.freeze({
|
||||
release: primary.release.value,
|
||||
runtime: primary.runtime.value,
|
||||
});
|
||||
}
|
||||
|
||||
const releaseMissing =
|
||||
primary.release.status === "rejected" &&
|
||||
hasErrorCode(primary.release.reason, "ENOENT");
|
||||
const runtimeMissing =
|
||||
primary.runtime.status === "rejected" &&
|
||||
hasErrorCode(primary.runtime.reason, "ENOENT");
|
||||
if (releaseMissing && runtimeMissing) {
|
||||
const fallback = await readArtifactPair(
|
||||
readArtifact,
|
||||
paths.fallbackRelease,
|
||||
paths.fallbackRuntime,
|
||||
);
|
||||
if (fallback.release.status === "rejected") {
|
||||
throw fallback.release.reason;
|
||||
}
|
||||
if (fallback.runtime.status === "rejected") {
|
||||
throw fallback.runtime.reason;
|
||||
}
|
||||
return Object.freeze({
|
||||
release: fallback.release.value,
|
||||
runtime: fallback.runtime.value,
|
||||
});
|
||||
}
|
||||
|
||||
if (primary.release.status === "rejected" && !releaseMissing) {
|
||||
throw primary.release.reason;
|
||||
}
|
||||
if (primary.runtime.status === "rejected" && !runtimeMissing) {
|
||||
throw primary.runtime.reason;
|
||||
}
|
||||
throw new Error("primary rollback artifact pair is incomplete");
|
||||
}
|
||||
|
||||
async function readArtifactPair(
|
||||
readArtifact: JsonArtifactReader,
|
||||
releasePath: string,
|
||||
runtimePath: string,
|
||||
): Promise<
|
||||
Readonly<{
|
||||
release: PromiseSettledResult<unknown>;
|
||||
runtime: PromiseSettledResult<unknown>;
|
||||
}>
|
||||
> {
|
||||
const [release, runtime] = await Promise.allSettled([
|
||||
Promise.resolve().then(() => readArtifact(releasePath)),
|
||||
Promise.resolve().then(() => readArtifact(runtimePath)),
|
||||
]);
|
||||
return Object.freeze({ release, runtime });
|
||||
}
|
||||
|
||||
async function readPrimaryOrFallback(
|
||||
readArtifact: JsonArtifactReader,
|
||||
primary: string,
|
||||
|
||||
Reference in New Issue
Block a user