package cluster import ( "log" "os" "time" "github.com/rqlite/rqlite/v8/cluster/proto" command "github.com/rqlite/rqlite/v8/command/proto" ) const ( removeRetries = 5 removeDelay = 250 * time.Millisecond ) // Control is an interface for interacting with a cluster. type Control interface { WaitForLeader(time.Duration) (string, error) WaitForRemoval(string, time.Duration) error } // Remover executes a node-removal operation. type Remover struct { timeout time.Duration control Control client *Client creds *proto.Credentials log *log.Logger } // / NewRemover returns an instantiated Remover. func NewRemover(client *Client, timeout time.Duration, control Control) *Remover { return &Remover{ client: client, timeout: timeout, control: control, log: log.New(os.Stderr, "[cluster-remove] ", log.LstdFlags), } } // SetCredentials sets the credentials for the Remover. func (r *Remover) SetCredentials(creds *proto.Credentials) { r.creds = creds } // Do executes the node-removal operation. func (r *Remover) Do(id string, confirm bool) error { rn := &command.RemoveNodeRequest{ Id: id, } nRetries := 0 for { err := func() error { laddr, innerErr := r.control.WaitForLeader(r.timeout) if innerErr != nil { return innerErr } r.log.Printf("removing node %s from cluster via leader at %s", id, laddr) if innerErr = r.client.RemoveNode(rn, laddr, r.creds, r.timeout); innerErr != nil { r.log.Printf("failed to remove node %s from cluster via leader at %s: %s", id, laddr, innerErr) return innerErr } return nil }() if err == nil { break } nRetries++ if nRetries == removeRetries { return err } time.Sleep(removeDelay) } if confirm { if err := r.control.WaitForRemoval(id, r.timeout); err != nil { return err } } return nil }