carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package console
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "math"
8 "net/http"
9 "os"
10 "slices"
11
12 "github.com/carlosframework/platform/internal/provision"
13 "github.com/carlosframework/platform/internal/release"
14)
15
16// refuseSeedMove answers a move of a seed that is not placed (--from) or
17// not committed (import) with the deck's sentence (spec §5.8). Both move
18// doors call it after their gate and before their transition, so each
19// answers the sentence itself; the transitions refuse it again under the
20// record CAS.
21func (s *Server) refuseSeedMove(w http.ResponseWriter, rec *provision.Record) bool {
22 if b := rec.Seed.MoveBlock(); b != "" {
23 jsonError(w, http.StatusConflict, provision.SeedMoveSentence(b))
24 return true
25 }
26 return false
27}
28
29// placeSeedReadFailed is carlos apps place's answer when the strict read of
30// the app's records fails, before anything has moved. The listing after the
31// placement is written keeps main's sentence (placeInstancesFailed).
32const placeSeedReadFailed = "could not update this app's instances. Try again."
33
34// placeSeedBlock is carlos apps place's seed rule (preflight M8(a)): a
35// place re-homes every record of the app, so it is a move of each. It reads
36// the app's records strictly and returns the first record's MoveBlock, ""
37// when none blocks. A record it cannot read could be anything, so it is an
38// error and the place is refused. A record deleted between the listing and
39// its read (the race the place's own walk skips) is read again once.
40//
41// No cap: the place walks every record of the app, and a cap would refuse
42// a large app's place for good.
43func placeSeedBlock(ctx context.Context, st release.Store, acct, app string) (string, error) {
44 ctx, cancel := context.WithTimeout(ctx, instanceStoreTimeout)
45 defer cancel()
46 recs, unreadable, err := provision.ListRecordsStrict(ctx, st, acct, app, math.MaxInt)
47 if errors.Is(err, os.ErrNotExist) {
48 recs, unreadable, err = provision.ListRecordsStrict(ctx, st, acct, app, math.MaxInt)
49 }
50 if err != nil {
51 return "", err
52 }
53 if len(unreadable) > 0 {
54 hosts := make([]string, 0, len(unreadable))
55 for h := range unreadable {
56 hosts = append(hosts, h)
57 }
58 slices.Sort(hosts)
59 return "", fmt.Errorf("records that do not read: %v", hosts)
60 }
61 for _, rec := range recs {
62 if b := rec.Seed.MoveBlock(); b != "" {
63 return b, nil
64 }
65 }
66 return "", nil
67}
68
69// seedMoveConflict is the sentence of a write the record's seed refused
70// under its CAS (provision.SeedMoveError), "" for any other error.
71func seedMoveConflict(err error) string {
72 var refusal *provision.SeedMoveError
73 if errors.As(err, &refusal) {
74 return provision.SeedMoveSentence(refusal.Block)
75 }
76 return ""
77}
78