implement leave command

This commit is contained in:
Branden J Brown 2025-03-14 23:09:08 -04:00
parent 73068a9c1f
commit bd2085ee87
5 changed files with 52 additions and 2 deletions

View File

@ -18,6 +18,8 @@ type Client interface {
// Bye tells p that its predecessor or successor is leaving
// and had the given successor list.
Bye(ctx context.Context, p, n Peer, succ []Peer) error
// SayBye tells p to leave the network.
SayBye(ctx context.Context, p Peer) error
// Get asks s for a saved value.
Get(ctx context.Context, s Peer, id ID) (string, error)

View File

@ -203,3 +203,28 @@ func (cl *Client) Bye(ctx context.Context, p, n chord.Peer, succ []chord.Peer) e
}
return nil
}
// SayBye tells p to leave the network.
func (cl *Client) SayBye(ctx context.Context, p chord.Peer) error {
_, addr := p.Values()
if !addr.IsValid() {
return errors.New("SayBye with invalid peer")
}
url := url.URL{
Scheme: "http",
Host: addr.String(),
Path: path.Join("/", cl.APIBase),
}
req, err := http.NewRequestWithContext(ctx, "DELETE", url.String(), nil)
if err != nil {
return err
}
resp, err := cl.HTTP.Do(req)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return errors.New(resp.Status)
}
return nil
}

View File

@ -57,6 +57,7 @@ func (n *Node) Router() http.Handler {
m.HandleFunc("POST /pred", n.notify)
m.HandleFunc("GET /neighbors", n.neighbors)
m.HandleFunc("POST /bye", n.bye)
m.HandleFunc("DELETE /", n.saybye)
return m
}
@ -170,6 +171,14 @@ func (n *Node) bye(w http.ResponseWriter, r *http.Request) {
}
n.self.Leave(p, s)
w.WriteHeader(http.StatusNoContent)
// Close the listener so that the server will shut down.
}
func (n *Node) saybye(w http.ResponseWriter, r *http.Request) {
err := chord.Leave(r.Context(), n.client, n.self)
if err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
return
}
w.WriteHeader(http.StatusOK)
n.l.Close()
}

12
main.go
View File

@ -194,7 +194,17 @@ func cliJoin(ctx context.Context, cmd *cli.Command) error {
return srv.Shutdown(ctx)
}
func cliLeave(ctx context.Context, cmd *cli.Command) error { return errors.New("not implemented") }
func cliLeave(ctx context.Context, cmd *cli.Command) error {
// I'm not really clear on why this command takes a startup IP and port.
// All we need is the node to tell to leave.
n, err := netip.ParseAddrPort(cmd.String("n"))
if err != nil {
return err
}
cl := &httpnode.Client{HTTP: http.Client{Timeout: 5 * time.Second}}
return cl.SayBye(ctx, chord.Address(n))
}
func cliLookup(ctx context.Context, cmd *cli.Command) error { return errors.New("not implemented") }
func cliPut(ctx context.Context, cmd *cli.Command) error { return errors.New("not implemented") }
func cliGet(ctx context.Context, cmd *cli.Command) error { return errors.New("not implemented") }

View File

@ -16,4 +16,8 @@ THIRD=$!
sleep 5
# Each node logs its predecessor and successors. At this point, we see the ring.
# Test leaving.
./chord-node leave -n 127.0.0.1:3000
kill $FIRST $SECOND $THIRD