package provision import ( "context" "time" ) // ReplaceStep enumerates the resumable steps of a Replace orchestration. // Persisted in replacements.step so a crashed process can continue without // repeating a boot or a destroy. type ReplaceStep string const ( StepOpenNew ReplaceStep = "open_new" // create the replacement node StepProbing ReplaceStep = "probing" // wait for self-register + probe StepNewUp ReplaceStep = "new_up" // promote new node, bump version StepDraining ReplaceStep = "draining" // drain the old node StepDestroyOld ReplaceStep = "destroy_old" // destroy old node + release IP StepDone ReplaceStep = "done" ) // ReplaceStatus is the terminal/running state of a replacement record. type ReplaceStatus string const ( ReplaceRunning ReplaceStatus = "running" ReplaceDone ReplaceStatus = "done" ReplaceFailed ReplaceStatus = "failed" ) // Replacement mirrors a replacements row (crash-recoverable orchestration). type Replacement struct { UUID string OldNodeID int64 NewNodeID int64 // 0 until the new node is created Pool Pool Status ReplaceStatus Step ReplaceStep CreatedAt time.Time UpdatedAt time.Time } // Store is the persistence boundary for the provision package. Both the MySQL // implementation (mysqlStore) and the in-memory test fake satisfy it, keeping // the service / orchestration logic database-agnostic and unit-testable. type Store interface { // --- nodes --- InsertNode(ctx context.Context, n *Node) (int64, error) GetNode(ctx context.Context, id int64) (*Node, error) GetNodeByUUID(ctx context.Context, uuid string) (*Node, error) UpdateNodeStatus(ctx context.Context, id int64, status Status) error UpdateNodeEndpoint(ctx context.Context, id int64, endpoint string) error // SetNodeInstance records the vendor instance ID and IP-derived endpoint. SetNodeInstance(ctx context.Context, id int64, instanceID, endpoint string) error SetNodeWeight(ctx context.Context, id int64, weight int) error // ListNodesByPool returns nodes whose provider belongs to pool, optionally // filtered to a single status (empty = all statuses). ListNodesByPool(ctx context.Context, pool Pool, status Status) ([]*Node, error) // --- providers --- ListProviders(ctx context.Context, pool Pool) ([]*Provider, error) GetProvider(ctx context.Context, id int64) (*Provider, error) // --- events / audit / directory --- WriteNodeEvent(ctx context.Context, nodeID int64, event Event, detailJSON string) error WriteAuditLog(ctx context.Context, actor, action, target, metaJSON string) error BumpDirectoryVersion(ctx context.Context) (int64, error) // --- idempotency --- // LookupIdempotency returns the node UUID previously bound to key, or // (\"\", false, nil) if unseen. LookupIdempotency(ctx context.Context, key string) (string, bool, error) // SaveIdempotency binds key→nodeUUID. It is a no-op if the key already // exists (first write wins). SaveIdempotency(ctx context.Context, key, nodeUUID string) error // --- replacements (crash recovery) --- CreateReplacement(ctx context.Context, r *Replacement) error GetReplacement(ctx context.Context, uuid string) (*Replacement, error) UpdateReplacement(ctx context.Context, r *Replacement) error }