// Join helper: a newcomer POSTs its JoinInfo to a target node's join endpoint // and adopts the returned ring state (leader preserved, newcomer inserted as // the target's successor). package cluster import ( "bytes" "context" "encoding/json" "fmt" "net/http" "time" ) // JoinRing asks target to add us to its ring and returns the adopted state. func (e *Engine) JoinRing(ctx context.Context, targetAddr string, ji JoinInfo) error { url := fmt.Sprintf("http://%s/api/manager/cluster/join", targetAddr) body, err := json.Marshal(ji) if err != nil { return err } req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") if e.User != "" || e.Pass != "" { req.SetBasicAuth(e.User, e.Pass) } cli := &http.Client{Timeout: 8 * time.Second} resp, err := cli.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("join %s -> HTTP %d", targetAddr, resp.StatusCode) } var out struct { State State `json:"state"` } if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { return err } e.AdoptState(out.State) return nil }