package host import ( "context" "maps" "reflect" "strconv" "strings" "time" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/hostops" "github.com/carlosframework/platform/internal/park" ) func (p productionClaimRegional) ordinaryHome(ctx context.Context, o hostclaim.Observed) (hostbox.Actor, string, error) { a, err := p.Identity.Verify(ctx) if err != nil { return a, "", err } if o.Absent || o.ETag == "" || o.Claim == nil || o.Host != o.Claim.Host || o.Claim.Validate() != nil || p.Local == nil || p.Local.assembly == nil { return a, "", park.ErrUnsafe } home, err := park.VerifiedHome(a.FleetHomeRegion, a.HomeRegion, a.Region, o.Claim.Op.Intent["home_region"]) if err != nil || a.FleetHomeRegion == "" || a.Region != a.FleetHomeRegion { return a, "", park.ErrUnsafe } switch o.Claim.Op.Kind { case hostclaim.KindServing, hostclaim.KindDeleting, hostclaim.KindParking: default: return a, "", park.ErrUnsafe } if h := o.Claim.Holder; h != nil { if h.Location != "instances/"+o.Host+"/"+h.Incarnation || (o.Claim.Op.Exec != "" && o.Claim.Op.Exec != a.Box) { return a, "", park.ErrUnsafe } for _, admission := range h.Admissions { if admission.Box != a.Box || !park.SameRegion(home, admission.Region, a.Region) || (admission.PredecessorRegion != "" && !park.SameRegion(home, admission.PredecessorRegion, a.Region)) { return a, "", park.ErrUnsafe } } } return a, home, nil } func (p productionClaimRegional) ordinaryView(ctx context.Context, o hostclaim.Observed) (park.View, error) { a, home, err := p.ordinaryHome(ctx, o) if err != nil { return park.View{}, err } v := park.View{HomeRegion: home, ClaimETag: o.ETag, Region: park.PlacementRegion(a.Region), AuthoritativeRegion: movesNoLiveRegion, ArchivedIncarnations: claimArchivedLocations(o.Claim), RefusedLocations: map[string]error{"moves/" + o.Host: park.ErrUnsafe}} h := o.Claim.Holder if h == nil { return v, nil } v.GenerationOf = func(r park.Ref) (string, bool) { return landingGenerationOf(h.Location, r.Key) } if o.Claim.Op.Kind == hostclaim.KindParking { return v, nil } v.AuthoritativeRegion = a.Region data := hostbox.RequestStore{Store: p.Local.assembly.Data, Check: claimBoundIdentity(p.Identity.Verify, a)} listing, err := data.ListVersions(ctx, a.Bucket, h.Location+"/") if err != nil { return park.View{}, err } if err = readMarker(ctx, data, a.Bucket, o.Host, h.Location, *h, listing, &v); err != nil { return park.View{}, err } databasePins := maps.Clone(v.Pinned) if err = pinFilesLiveSet(ctx, data, a.Bucket, h.Location, listing, &v, liveGenerations(o.Claim, v)...); err != nil { return park.View{}, err } v.FilePins = map[park.Ref]bool{} for ref := range v.Pinned { if !databasePins[ref] && strings.HasPrefix(ref.Key, h.Location+"/files/") { v.FilePins[ref] = true } } return v, nil } func (p productionClaimRegional) VerifyObligation(ctx context.Context, o hostclaim.Observed, ob hostclaim.Obligation) (hostclaim.ParkScope, error) { if p.Parks != nil { if evidence, ok := p.Parks.Evidence.(hostbox.RegionalObligationEvidence); ok { return evidence.VerifyObligation(ctx, o, ob) } return hostclaim.ParkScope{}, park.ErrUnsafe } a, home, err := p.ordinaryHome(ctx, o) if err != nil { return hostclaim.ParkScope{}, err } c, h := o.Claim, o.Claim.Holder if c.Op.Kind != hostclaim.KindServing || c.Op.ID != "" || h == nil || ob.Kind != "park-generation" || ob.Owner != "region:"+ob.Region || !park.SameRegion(home, ob.Region, a.Region) || ob.Box != "" || ob.ParkTarget != "" || len(ob.EvidenceRefs) != 0 || ob.Generation == "" || ob.Generation == h.Generation { return hostclaim.ParkScope{}, park.ErrUnsafe } session, err := strconv.ParseUint(strings.TrimPrefix(ob.Op, "session:"), 10, 64) if err != nil || ob.Op != "session:"+strconv.FormatUint(session, 10) || session == 0 || session > h.Session { return hostclaim.ParkScope{}, park.ErrUnsafe } found := false for _, live := range c.Obligations { live.Park = ob.Park if reflect.DeepEqual(live, ob) { found = true } } if !found { return hostclaim.ParkScope{}, park.ErrChanged } guard := p.Local.assembly.Guard if guard.Root == nil && guard.FenceView == nil { return hostclaim.ParkScope{}, park.ErrUnsafe } var at time.Time for _, admission := range h.Admissions { if admission.Generation != ob.Generation { continue } if admission.Mode != "cold" || admission.Session >= session { return hostclaim.ParkScope{}, park.ErrUnsafe } receipts, err := guard.CompletedServingStops(ctx, o.Host, hostguard.Captured{Incarnation: h.Incarnation, Stamp: admission.Session}, admission.Session, a.Box) if err != nil { return hostclaim.ParkScope{}, err } for _, receipt := range receipts { if !park.SameRegion(home, receipt.Target.Region, a.Region) || receipt.Target.Enrollment != a.Enrollment || receipt.EvidenceAt.IsZero() || verifyRetirementReceipt(ctx, receipt) != nil { return hostclaim.ParkScope{}, park.ErrUnsafe } if ob.Park != nil { if receipt.EvidenceAt.Equal(ob.Park.EvidenceAt) { at = receipt.EvidenceAt } } else if receipt.EvidenceAt.After(at) { at = receipt.EvidenceAt } } } if at.IsZero() { return hostclaim.ParkScope{}, park.ErrUnsafe } scope := hostclaim.ParkScope{Location: h.Location, Incarnation: h.Incarnation, EvidenceAt: at, RequestLimit: hostbox.RequestLimit} if ob.Park != nil && !reflect.DeepEqual(*ob.Park, scope) { return hostclaim.ParkScope{}, park.ErrChanged } return scope, scope.Validate(o.Host) } func (p productionClaimRegional) authorizeOrdinaryRetirement(ctx context.Context, o hostclaim.Observed, ob hostclaim.Obligation) error { a, _, err := p.ordinaryHome(ctx, o) if err != nil { return err } if o.Claim.Op.Kind != hostclaim.KindParking || o.Claim.Holder == nil || ob.Kind != "park-replica" || p.Local.assembly.RetirementAuthority == nil { return park.ErrUnsafe } if ob.ParkTarget != o.Claim.Holder.Location { if ob.ParkTarget != "moves/"+o.Host { return park.ErrUnsafe } data := hostbox.RequestStore{Store: p.Local.assembly.Data, Check: claimBoundIdentity(p.Identity.Verify, a)} versions, err := data.ListVersions(ctx, a.Bucket, ob.ParkTarget+"/") if err != nil { return err } for _, version := range versions { if version.Key != ob.ParkTarget+"/lock.json" || version.VersionID == "" { return park.ErrUnsafe } } } return (hostops.RetirementEvidence{Authority: *p.Local.assembly.RetirementAuthority}).VerifyBound(ctx, o, ob) }