Skip to content

Commit

Permalink
Add reservations to admin control subnet allocs
Browse files Browse the repository at this point in the history
This adds ability to add and remove reservations
for subnet allocations. A subnet is reserved with
the host IP (PublicIP). flannel will then use this
subnet for a host requesting a subnet.

Reservations are denoted in etcd as subnets with no
expiration (TTL is zero). When a reserved subnet is
allocated, it's TTL continues to be not set. If a
reservation is removed, the TTL is set to the usual
24 hours.

Also adds corresponding client/server APIs for
reservation management.

Fixes #280
  • Loading branch information
Eugene Yakubovich committed Nov 14, 2015
1 parent 8c97e5b commit ca83d28
Show file tree
Hide file tree
Showing 8 changed files with 465 additions and 51 deletions.
9 changes: 6 additions & 3 deletions network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ func (n *Network) runOnce(extIface *backend.ExternalInterface, inited func(bn ba
defer wg.Wait()

dur := n.bn.Lease().Expiration.Sub(time.Now()) - renewMargin

for {
select {
case <-time.After(dur):
Expand All @@ -164,12 +163,16 @@ func (n *Network) runOnce(extIface *backend.ExternalInterface, inited func(bn ba
dur = n.bn.Lease().Expiration.Sub(time.Now()) - renewMargin

case e := <-evts:
if e.Type == subnet.EventRemoved {
switch e.Type {
case subnet.EventAdded:
n.bn.Lease().Expiration = e.Lease.Expiration
dur = n.bn.Lease().Expiration.Sub(time.Now()) - renewMargin

case subnet.EventRemoved:
log.Warning("Lease has been revoked")
interruptFunc()
return errInterrupted
}
dur = n.bn.Lease().Expiration.Sub(time.Now()) - renewMargin

case <-n.ctx.Done():
return errCanceled
Expand Down
57 changes: 57 additions & 0 deletions remote/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,63 @@ func (m *RemoteManager) WatchNetworks(ctx context.Context, cursor interface{}) (
return wr, nil
}

func (m *RemoteManager) AddReservation(ctx context.Context, network string, r *subnet.Reservation) error {
url := m.mkurl(network, "reservations")

body, err := json.Marshal(r)
if err != nil {
return err
}

resp, err := m.httpVerb(ctx, "POST", url, "application/json", body)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return httpError(resp)
}
return nil
}

func (m *RemoteManager) RemoveReservation(ctx context.Context, network string, sn ip.IP4Net) error {
url := m.mkurl(network, "reservations", subnet.MakeSubnetKey(sn))

resp, err := m.httpVerb(ctx, "DELETE", url, "", nil)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return httpError(resp)
}

return nil
}

func (m *RemoteManager) ListReservations(ctx context.Context, network string) ([]subnet.Reservation, error) {
url := m.mkurl(network, "reservations")

resp, err := m.httpVerb(ctx, "GET", url, "", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, httpError(resp)
}

rs := []subnet.Reservation{}
if err := json.NewDecoder(resp.Body).Decode(&rs); err != nil {
return nil, err
}

return rs, nil
}

func httpError(resp *http.Response) error {
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
Expand Down
72 changes: 72 additions & 0 deletions remote/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,73 @@ func handleNetworks(ctx context.Context, sm subnet.Manager, w http.ResponseWrite
jsonResponse(w, http.StatusOK, wr)
}

// POST /{network}/reservations
func handleAddReservation(ctx context.Context, sm subnet.Manager, w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()

network := mux.Vars(r)["network"]
if network == "_" {
network = ""
}

rsv := &subnet.Reservation{}
if err := json.NewDecoder(r.Body).Decode(rsv); err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, "JSON decoding error: ", err)
return
}

if err := sm.AddReservation(ctx, network, rsv); err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, fmt.Errorf("internal error: %v", err))
return
}

w.WriteHeader(http.StatusOK)
}

// DELETE /{network}/reservations/{subnet}
func handleRemoveReservation(ctx context.Context, sm subnet.Manager, w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()

network := mux.Vars(r)["network"]
if network == "_" {
network = ""
}

sn := subnet.ParseSubnetKey(mux.Vars(r)["subnet"])
if sn == nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, "bad subnet")
return
}

if err := sm.RemoveReservation(ctx, network, *sn); err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, err)
return
}

w.WriteHeader(http.StatusOK)
}

func handleListReservations(ctx context.Context, sm subnet.Manager, w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()

network := mux.Vars(r)["network"]
if network == "_" {
network = ""
}

leases, err := sm.ListReservations(ctx, network)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, err)
}

jsonResponse(w, http.StatusOK, leases)
}

func bindHandler(h handler, ctx context.Context, sm subnet.Manager) http.HandlerFunc {
return func(resp http.ResponseWriter, req *http.Request) {
h(ctx, sm, resp, req)
Expand Down Expand Up @@ -323,13 +390,18 @@ func RunServer(ctx context.Context, sm subnet.Manager, listenAddr, cafile, certf

r := mux.NewRouter()
r.HandleFunc("/v1/{network}/config", bindHandler(handleGetNetworkConfig, ctx, sm)).Methods("GET")

r.HandleFunc("/v1/{network}/leases", bindHandler(handleAcquireLease, ctx, sm)).Methods("POST")
r.HandleFunc("/v1/{network}/leases/{subnet}", bindHandler(handleWatchLease, ctx, sm)).Methods("GET")
r.HandleFunc("/v1/{network}/leases/{subnet}", bindHandler(handleRenewLease, ctx, sm)).Methods("PUT")
r.HandleFunc("/v1/{network}/leases/{subnet}", bindHandler(handleRevokeLease, ctx, sm)).Methods("DELETE")
r.HandleFunc("/v1/{network}/leases", bindHandler(handleWatchLeases, ctx, sm)).Methods("GET")
r.HandleFunc("/v1/", bindHandler(handleNetworks, ctx, sm)).Methods("GET")

r.HandleFunc("/v1/{network}/reservations", bindHandler(handleListReservations, ctx, sm)).Methods("GET")
r.HandleFunc("/v1/{network}/reservations", bindHandler(handleAddReservation, ctx, sm)).Methods("POST")
r.HandleFunc("/v1/{network}/reservations/{subnet}", bindHandler(handleRemoveReservation, ctx, sm)).Methods("DELETE")

l, err := listener(listenAddr, cafile, certfile, keyfile)
if err != nil {
log.Errorf("Error listening on %v: %v", listenAddr, err)
Expand Down
Loading

0 comments on commit ca83d28

Please sign in to comment.