carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package hostbox
2
3import (
4 "context"
5 "fmt"
6 "github.com/carlosframework/platform/internal/park"
7 "strings"
8
9 "github.com/carlosframework/platform/internal/genselect"
10 "github.com/carlosframework/platform/internal/hostclaim"
11 "github.com/carlosframework/platform/internal/release"
12)
13
14// Writer supplies transition adapters, not stop, certificate or marker evidence.
15type Writer struct {
16 HomeRegion string
17 Loss *LossAdmission
18 Claims release.Store
19 Data genselect.Reader
20 Bucket, Region, Box string
21 AdmissionEvidence func(context.Context, hostclaim.Observed, hostclaim.Admission, *genselect.Plan) error
22 PublicationEvidence func(context.Context, hostclaim.Observed) error
23 NewID func() string
24}
25
26func (w Writer) Baseline(ctx context.Context, observed hostclaim.Observed) (*genselect.Plan, error) {
27 if w.Data == nil || w.Bucket == "" || w.Region == "" || observed.Absent || observed.Claim == nil || observed.Claim.Holder == nil {
28 return nil, ErrUnsupported
29 }
30 return genselect.Select(ctx, w.Data, genselect.Request{Bucket: w.Bucket, Region: w.Region, Host: observed.Host, Holder: park.HolderRegionView(*observed.Claim.Holder, w.HomeRegion, w.Region)})
31}
32
33func (w Writer) Admit(ctx context.Context, observed hostclaim.Observed, in hostclaim.Admission, baseline *genselect.Plan) (hostclaim.Observed, error) {
34 if in.Mode != "cold" || in.PredecessorCertified {
35 return hostclaim.Observed{}, ErrUnsupported
36 }
37 if w.Claims == nil || w.AdmissionEvidence == nil || w.Box == "" || w.Region == "" || observed.Absent || observed.Claim == nil || observed.Claim.Holder == nil || in.Box != w.Box || in.Region != w.Region {
38 return hostclaim.Observed{}, ErrUnsupported
39 }
40 if record, pending := hostclaim.PendingMoveFenceLoss(observed.Claim); pending {
41 return w.admitFencedLoss(ctx, observed, in, baseline, record)
42 }
43 lossID, err := hostclaim.OperationLossID(observed.Claim)
44 if err != nil {
45 return hostclaim.Observed{}, err
46 }
47 if lossID != "" && !hostclaim.OperationLossConsumed(observed.Claim, lossID) {
48 return w.admitLoss(ctx, observed, in, baseline, lossID)
49 }
50 if in.LossID != "" {
51 if lossID == "" || in.LossID != lossID || !hostclaim.OperationLossConsumed(observed.Claim, lossID) {
52 return hostclaim.Observed{}, hostclaim.ErrLossRecord
53 }
54 in.LossID = ""
55 }
56 if baseline != nil && baseline.MaxTXID != in.RemoteMaxTXID {
57 return hostclaim.Observed{}, ErrChanged
58 }
59 if err := w.AdmissionEvidence(ctx, observed, in, baseline); err != nil {
60 return hostclaim.Observed{}, err
61 }
62 // Exactly one attempt. A conflict requires new evidence, not an internal retry.
63 return hostclaim.Update(ctx, w.Claims, observed, hostclaim.AdmitWriter(in))
64}
65
66func (w Writer) Published(ctx context.Context, observed hostclaim.Observed) (hostclaim.Observed, error) {
67 if w.Claims == nil || w.PublicationEvidence == nil || w.NewID == nil || w.Box == "" || w.Region == "" || observed.Absent || observed.Claim == nil || observed.Claim.Holder == nil || observed.Claim.Op.Exec != w.Box {
68 return hostclaim.Observed{}, ErrUnsupported
69 }
70 if err := w.PublicationEvidence(ctx, observed); err != nil {
71 return hostclaim.Observed{}, err
72 }
73 c := observed.Claim
74 return hostclaim.Update(ctx, w.Claims, observed, hostclaim.PublishGeneration(c.Op.ID, c.Op.Phase, c.Holder.Session, c.Holder.Generation, w.Region, w.NewID))
75}
76
77// GenerationPrefix is a holder's generation prefix. Twins of its rule:
78// genselect's prefix and coordinates, and moves.ReplicaSource (which also
79// covers the root generation); the three change together.
80func GenerationPrefix(holder hostclaim.Holder) (string, error) {
81 generation := holder.Generation
82 if holder.Session == 0 || generation == "" || generation == hostclaim.RootGeneration || generation == "." || generation == ".." || strings.ContainsAny(generation, "/\\\x00") || holder.Location == "" {
83 return "", ErrUnsupported
84 }
85 return fmt.Sprintf("%s/g-%s/", strings.TrimSuffix(holder.Location, "/"), generation), nil
86}
87