package hostguard import ( "context" "strconv" "strings" "github.com/carlosframework/platform/internal/hostclaim" ) // writerStopPrefix marks the obligation component of an operation stop that // names the operation's own admitted writer session, not a chore (claim Plan // 9 D4). A landing target stops its probe before a restarted probe or a cold // wake is admitted; no chore exists for that, and none is invented. const writerStopPrefix = "writer-session-" // WriterStopObligation is the obligation component with which an operation's // executor stops writer session s under that operation. It is deterministic, // so a repeated request for the same session meets the same fence and // receipt, and every later session gets its own. func WriterStopObligation(session uint64) string { return writerStopPrefix + strconv.FormatUint(session, 10) } func writerStop(obligation string) bool { return strings.HasPrefix(obligation, writerStopPrefix) } // LandingWriter returns the admission of the claim's current writer session // when that session is box's own landing writer: the op is a landing at a // phase of the landing's session row (probing, probed, committed; claim Plan // 9 D1), box executes it and is holder.writer_box, and the session was // admitted exactly once, cold, on box, into the held generation. A landing's // first probe, whose predecessor is the source's session on another box, is // never box's writer here. func LandingWriter(c *hostclaim.Claim, box string) (hostclaim.AdmissionRecord, bool) { return operationWriter(c, box, hostclaim.KindLanding) } func operationWriter(c *hostclaim.Claim, box, kind string) (hostclaim.AdmissionRecord, bool) { if c == nil || c.Holder == nil || box == "" || c.Holder.Session == 0 { return hostclaim.AdmissionRecord{}, false } h := c.Holder if c.Op.Kind != kind || c.Op.ID == "" || c.Op.Exec != box || h.WriterBox != box || !hostclaim.SessionPhase(kind, c.Op.Phase) { return hostclaim.AdmissionRecord{}, false } var found hostclaim.AdmissionRecord matched := 0 for _, a := range h.Admissions { if a.Session == h.Session { if a.Box != box || a.Mode != "cold" || a.Generation != h.Generation { return hostclaim.AdmissionRecord{}, false } found = a matched++ } } return found, matched == 1 } // LandingWriterStop is the obligation component with which box may ask root // to stop the claim's current writer session as the landing's own writer: // LandingWriter holds, the session was admitted in region, and no chore of // the claim carries the same id. Any other claim has no writer stop. func LandingWriterStop(c *hostclaim.Claim, box, region string) (string, bool) { return operationWriterStop(c, box, region, hostclaim.KindLanding) } // RelocationWriterStop authorizes only this box's admitted relocation writer. func RelocationWriterStop(c *hostclaim.Claim, box, region string) (string, bool) { return operationWriterStop(c, box, region, hostclaim.KindRelocating) } func operationWriterStop(c *hostclaim.Claim, box, region, kind string) (string, bool) { a, ok := operationWriter(c, box, kind) if !ok || region == "" || a.Region != region { return "", false } id := WriterStopObligation(c.Holder.Session) for _, o := range c.Obligations { if o.ID == id { return "", false } } return id, true } // CompletedWriterStops returns every completed writer-session stop journalled // for exactly the captured installation, under op, on box. Fence receipts are // append-only, so a receipt returned here stays effective. func (g Guard) CompletedWriterStops(ctx context.Context, host string, captured Captured, op, box string) ([]FenceReceipt, error) { if ctx == nil || captured.Incarnation == "" || captured.Stamp == 0 || op == "" || box == "" { return nil, ErrInvalidInput } var out []FenceReceipt err := g.fenceLocked(ctx, host, captured, func(d *recordDir) error { r, err := d.read(host) if err != nil { return err } for _, receipt := range r.FenceJournal { t := receipt.Target if t.Host == host && t.Incarnation == captured.Incarnation && t.Stamp == captured.Stamp && t.Op == op && t.Box == box && t.ObligationID == WriterStopObligation(t.Session) { out = append(out, receipt) } } return nil }) return out, err } // CompletedServingStops selects completed stops for one serving installation. // Operation receipts cannot stand in for a serving stop. func (g Guard) CompletedServingStops(ctx context.Context, host string, captured Captured, session uint64, box string) ([]FenceReceipt, error) { if ctx == nil || host == "" || captured.Incarnation == "" || captured.Stamp == 0 || session == 0 || box == "" { return nil, ErrInvalidInput } var out []FenceReceipt err := g.fenceLocked(ctx, host, captured, func(d *recordDir) error { r, err := d.read(host) if err != nil { return err } for _, receipt := range r.FenceJournal { t := receipt.Target if t.Host != host || t.Incarnation != captured.Incarnation || t.Stamp != captured.Stamp || t.Box != box || t.Op != "" || t.Phase != "" || t.ObligationID != "" { continue } if t.Session != session { return ErrNoFenceReceipt } out = append(out, receipt) } if len(out) == 0 { return ErrNoFenceReceipt } return nil }) if err != nil { return nil, err } return out, nil } func RecoveryWriter(c *hostclaim.Claim, box string) (hostclaim.AdmissionRecord, bool) { if c == nil || (c.Op.Kind != hostclaim.KindRestoring && c.Op.Kind != hostclaim.KindReverting) || c.Op.Intent["source"] != box { return hostclaim.AdmissionRecord{}, false } return operationWriter(c, box, c.Op.Kind) } func RecoveryWriterStop(c *hostclaim.Claim, box, region string) (string, bool) { if c != nil && c.Holder != nil && region != "" && box != "" && (c.Op.Kind == hostclaim.KindRestoring || c.Op.Kind == hostclaim.KindReverting) && c.Op.ID != "" && c.Op.Phase == hostclaim.PhaseProbing && c.Op.Exec == box && c.Op.Intent["source"] == box { h := c.Holder stamp, err := strconv.ParseUint(c.Op.Journal[hostclaim.JournalSourceStamp], 10, 64) if err == nil && stamp > 0 && h.Stamp == stamp && h.Session == 0 && h.Generation == "" && h.WriterBox == "" && len(h.Admissions) == 0 && c.Op.Journal[hostclaim.JournalCutSession] == "0" && c.Op.Journal[hostclaim.JournalSrcGen] == "" { id := WriterStopObligation(0) for _, ob := range c.Obligations { if ob.ID == id { return "", false } } return id, true } } if _, ok := RecoveryWriter(c, box); !ok { return "", false } return operationWriterStop(c, box, region, c.Op.Kind) }