| 1 | package console |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "errors" |
| 6 | "log" |
| 7 | "os" |
| 8 | "time" |
| 9 | |
| 10 | "github.com/carlosframework/platform/internal/hostclaim" |
| 11 | "github.com/carlosframework/platform/internal/hostops" |
| 12 | "github.com/carlosframework/platform/internal/provision" |
| 13 | ) |
| 14 | |
| 15 | func (s *Server) retireHostRecord(ctx context.Context, ref provision.RetireRef) error { |
| 16 | if s.cfg.HostClaim { |
| 17 | return s.retireClaimHostRecord(ctx, ref) |
| 18 | } |
| 19 | var claims provision.ClaimRetirer |
| 20 | if s.cfg.HostClaimOps != nil { |
| 21 | claims = s.cfg.HostClaimOps |
| 22 | } |
| 23 | return provision.RetireRecord(ctx, s.cfg.Releases, ref, provision.RetireOptions{HostClaim: s.cfg.HostClaim, Claims: claims}) |
| 24 | } |
| 25 | func (s *Server) adoptHostRecord(ctx context.Context, key string) (hostclaim.Observed, error) { |
| 26 | if !s.cfg.HostClaim || s.cfg.HostClaimOps == nil { |
| 27 | return hostclaim.Observed{}, hostops.ErrContract |
| 28 | } |
| 29 | rec, err := hostops.ReadRecord(ctx, s.cfg.Releases, key) |
| 30 | if err != nil { |
| 31 | return hostclaim.Observed{}, err |
| 32 | } |
| 33 | return s.cfg.HostClaimOps.Adopt(ctx, rec) |
| 34 | } |
| 35 | |
| 36 | const claimRecoveryInterval = 2 * time.Minute |
| 37 | |
| 38 | // Called by serve, never NewServer. Tests can invoke the one-pass method. |
| 39 | func (s *Server) StartClaimRecoveryPass(ctx context.Context) { |
| 40 | if !s.cfg.HostClaim { |
| 41 | return |
| 42 | } |
| 43 | if s.cfg.HostClaimOps == nil { |
| 44 | log.Printf("console: claim recovery: %v", hostops.ErrContract) |
| 45 | return |
| 46 | } |
| 47 | // The eligible-capacity metric (region HA spec 2 §4, regioncapacity.go) |
| 48 | // runs beside claim recovery, on a host-claim console with region HA |
| 49 | // on and a metrics adapter armed. |
| 50 | if s.cfg.RegionHA { |
| 51 | if pub := capacityPublisherFromEnv(ctx, os.Getenv); pub != nil { |
| 52 | go s.runCapacityPass(ctx, pub) |
| 53 | } |
| 54 | // Automatic enrollment of the boxes regions' groups launch |
| 55 | // (region HA spec 2 §5 step 5, enrollpass.go). |
| 56 | s.startEnrollPass(ctx) |
| 57 | } |
| 58 | t := time.NewTicker(claimRecoveryInterval) |
| 59 | defer t.Stop() |
| 60 | for { |
| 61 | if err := s.RunClaimRecoveryPass(ctx); err != nil { |
| 62 | log.Printf("console: claim recovery: %v", err) |
| 63 | } |
| 64 | select { |
| 65 | case <-ctx.Done(): |
| 66 | return |
| 67 | case <-t.C: |
| 68 | } |
| 69 | } |
| 70 | } |
| 71 | func (s *Server) claimRecoveryPass(ctx context.Context) error { |
| 72 | if !s.cfg.HostClaim { |
| 73 | return nil |
| 74 | } |
| 75 | if s.cfg.HostClaimRecovery == nil { |
| 76 | return hostops.ErrContract |
| 77 | } |
| 78 | if a := s.cfg.ClaimAuthority; a != nil && a.Control != nil { |
| 79 | return errors.Join(a.Control.RecoveryPass(ctx, *s.cfg.HostClaimRecovery), a.Control.SweepWakeResets(ctx, hostops.WakeResetTransport{Store: a.Store, Directory: a.Directory, Now: a.Now})) |
| 80 | } |
| 81 | return s.cfg.HostClaimRecovery.Pass(ctx) |
| 82 | } |
| 83 | |