carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1// The release's own swap demand (zero-downtime deploys spec §4.2): how an
2// awake instance may move onto a build, as the build's shipper said.
3package release
4
5import (
6 "context"
7 "errors"
8 "fmt"
9 "strings"
10)
11
12// DemandHold is the one swap demand a release can make (`carlos deploy
13// --swap hold`, spec §9.1).
14const DemandHold = "hold"
15
16// NormalizeDemand is the demand a box acts on for a manifest's swap field.
17// "" is no demand. Anything else is DemandHold: "hold" itself, and any value
18// a newer shipper wrote that this binary does not know, because missing or
19// unverifiable policy data never permits an overlap (spec §4.2).
20func NormalizeDemand(swap string) string {
21 if swap == "" {
22 return ""
23 }
24 return DemandHold
25}
26
27// ErrStagedDigestMismatch is an adoption attempt's staging error (spec §4.2):
28// the staged executable is not the artifact the canonical manifest names for
29// this platform, so the manifest's demand does not describe the bytes that
30// would run. The attempt is refused, with no failure record against the
31// build.
32var ErrStagedDigestMismatch = errors.New("staged artifact is not the release's canonical artifact")
33
34// CanonicalDemand reads build version's own swap demand from its CANONICAL
35// manifest in the release store, and checks the staged executable at
36// stagedPath against that manifest's digest for platform (spec §4.2).
37//
38// - Match: the manifest's demand (NormalizeDemand), the staged digest, nil.
39// - Mismatch: "", the staged digest, an error wrapping
40// ErrStagedDigestMismatch.
41// - Cannot compare (the manifest is unreadable, it has no artifact for
42// platform, or the staged file cannot be read): DemandHold and a nil
43// error, because missing or unverifiable policy data never permits an
44// overlap. digest is the staged file's when it could be hashed, else "".
45//
46// It is for ADOPTION attempts only (causes release, install, retry). Recovery
47// checks a retained digest with VerifyStaged and never asks the store.
48func CanonicalDemand(ctx context.Context, s Store, acct, app, version, platform, stagedPath string) (demand string, digest string, err error) {
49 digest, herr := FileSHA256(stagedPath)
50 if herr != nil {
51 return DemandHold, "", nil
52 }
53 m, _, merr := GetManifestRaw(ctx, s, acct, app, version)
54 if merr != nil {
55 return DemandHold, digest, nil
56 }
57 art, ok := m.Artifacts[platform]
58 if !ok {
59 return DemandHold, digest, nil
60 }
61 if !strings.EqualFold(art.SHA256, digest) {
62 return "", digest, fmt.Errorf("%w: %s/%s staged at %s hashes to %s; the release names %s",
63 ErrStagedDigestMismatch, app, version, stagedPath, digest, art.SHA256)
64 }
65 return NormalizeDemand(m.Swap), digest, nil
66}
67