-
Notifications
You must be signed in to change notification settings - Fork 9.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #398 from benbjohnson/mod-leader
mod/leader
- Loading branch information
Showing
11 changed files
with
310 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package v2 | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"net/url" | ||
|
||
"github.com/gorilla/mux" | ||
) | ||
|
||
// deleteHandler remove a given leader leader. | ||
func (h *handler) deleteHandler(w http.ResponseWriter, req *http.Request) { | ||
vars := mux.Vars(req) | ||
name := req.FormValue("name") | ||
if name == "" { | ||
http.Error(w, "leader name required", http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
// Proxy the request to the the lock service. | ||
u, err := url.Parse(fmt.Sprintf("%s/mod/v2/lock/%s", h.addr, vars["key"])) | ||
if err != nil { | ||
http.Error(w, err.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
q := u.Query() | ||
q.Set("value", name) | ||
u.RawQuery = q.Encode() | ||
|
||
r, err := http.NewRequest("DELETE", u.String(), nil) | ||
if err != nil { | ||
http.Error(w, err.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
// Read from the leader lock. | ||
resp, err := h.client.Do(r) | ||
if err != nil { | ||
http.Error(w, "delete leader http error: " + err.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
defer resp.Body.Close() | ||
w.WriteHeader(resp.StatusCode) | ||
if resp.StatusCode != http.StatusOK { | ||
w.Write([]byte("delete leader error: ")) | ||
} | ||
io.Copy(w, resp.Body) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package v2 | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"net/http" | ||
|
||
"github.com/gorilla/mux" | ||
) | ||
|
||
// getHandler retrieves the current leader. | ||
func (h *handler) getHandler(w http.ResponseWriter, req *http.Request) { | ||
vars := mux.Vars(req) | ||
|
||
// Proxy the request to the lock service. | ||
url := fmt.Sprintf("%s/mod/v2/lock/%s?field=value", h.addr, vars["key"]) | ||
resp, err := h.client.Get(url) | ||
if err != nil { | ||
http.Error(w, "read leader error: " + err.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
defer resp.Body.Close() | ||
|
||
if resp.StatusCode != http.StatusOK { | ||
w.Write([]byte("get leader error: ")) | ||
} | ||
w.WriteHeader(resp.StatusCode) | ||
io.Copy(w, resp.Body) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package v2 | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/gorilla/mux" | ||
) | ||
|
||
// prefix is appended to the lock's prefix since the leader mod uses the lock mod. | ||
const prefix = "/_mod/leader" | ||
|
||
// handler manages the leader HTTP request. | ||
type handler struct { | ||
*mux.Router | ||
client *http.Client | ||
transport *http.Transport | ||
addr string | ||
} | ||
|
||
// NewHandler creates an HTTP handler that can be registered on a router. | ||
func NewHandler(addr string) (http.Handler) { | ||
transport := &http.Transport{DisableKeepAlives: false} | ||
h := &handler{ | ||
Router: mux.NewRouter(), | ||
client: &http.Client{Transport: transport}, | ||
transport: transport, | ||
addr: addr, | ||
} | ||
h.StrictSlash(false) | ||
h.HandleFunc("/{key:.*}", h.getHandler).Methods("GET") | ||
h.HandleFunc("/{key:.*}", h.setHandler).Methods("PUT") | ||
h.HandleFunc("/{key:.*}", h.deleteHandler).Methods("DELETE") | ||
return h | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package v2 | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"net/url" | ||
|
||
"github.com/gorilla/mux" | ||
) | ||
|
||
// setHandler attempts to set the current leader. | ||
func (h *handler) setHandler(w http.ResponseWriter, req *http.Request) { | ||
vars := mux.Vars(req) | ||
name := req.FormValue("name") | ||
if name == "" { | ||
http.Error(w, "leader name required", http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
// Proxy the request to the the lock service. | ||
u, err := url.Parse(fmt.Sprintf("%s/mod/v2/lock/%s", h.addr, vars["key"])) | ||
if err != nil { | ||
http.Error(w, err.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
q := u.Query() | ||
q.Set("value", name) | ||
q.Set("ttl", req.FormValue("ttl")) | ||
q.Set("timeout", req.FormValue("timeout")) | ||
u.RawQuery = q.Encode() | ||
|
||
r, err := http.NewRequest("POST", u.String(), nil) | ||
if err != nil { | ||
http.Error(w, err.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
// Close request if this connection disconnects. | ||
closeNotifier, _ := w.(http.CloseNotifier) | ||
stopChan := make(chan bool) | ||
defer close(stopChan) | ||
go func() { | ||
select { | ||
case <-closeNotifier.CloseNotify(): | ||
h.transport.CancelRequest(r) | ||
case <-stopChan: | ||
} | ||
}() | ||
|
||
// Read from the leader lock. | ||
resp, err := h.client.Do(r) | ||
if err != nil { | ||
http.Error(w, "set leader http error: " + err.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
defer resp.Body.Close() | ||
w.WriteHeader(resp.StatusCode) | ||
if resp.StatusCode != http.StatusOK { | ||
w.Write([]byte("set leader error: ")) | ||
} | ||
io.Copy(w, resp.Body) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package leader | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
"time" | ||
|
||
"github.com/coreos/etcd/server" | ||
"github.com/coreos/etcd/tests" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
// Ensure that a leader can be set and read. | ||
func TestModLeaderSet(t *testing.T) { | ||
tests.RunServer(func(s *server.Server) { | ||
// Set leader. | ||
body, err := testSetLeader(s, "foo", "xxx", 10) | ||
assert.NoError(t, err) | ||
assert.Equal(t, body, "2") | ||
|
||
// Check that the leader is set. | ||
body, err = testGetLeader(s, "foo") | ||
assert.NoError(t, err) | ||
assert.Equal(t, body, "xxx") | ||
|
||
// Delete leader. | ||
body, err = testDeleteLeader(s, "foo", "xxx") | ||
assert.NoError(t, err) | ||
assert.Equal(t, body, "") | ||
|
||
// Check that the leader is removed. | ||
body, err = testGetLeader(s, "foo") | ||
assert.NoError(t, err) | ||
assert.Equal(t, body, "") | ||
}) | ||
} | ||
|
||
// Ensure that a leader can be renewed. | ||
func TestModLeaderRenew(t *testing.T) { | ||
tests.RunServer(func(s *server.Server) { | ||
// Set leader. | ||
body, err := testSetLeader(s, "foo", "xxx", 2) | ||
assert.NoError(t, err) | ||
assert.Equal(t, body, "2") | ||
|
||
time.Sleep(1 * time.Second) | ||
|
||
// Renew leader. | ||
body, err = testSetLeader(s, "foo", "xxx", 3) | ||
assert.NoError(t, err) | ||
assert.Equal(t, body, "2") | ||
|
||
time.Sleep(2 * time.Second) | ||
|
||
// Check that the leader is set. | ||
body, err = testGetLeader(s, "foo") | ||
assert.NoError(t, err) | ||
assert.Equal(t, body, "xxx") | ||
}) | ||
} | ||
|
||
|
||
|
||
func testSetLeader(s *server.Server, key string, name string, ttl int) (string, error) { | ||
resp, err := tests.PutForm(fmt.Sprintf("%s/mod/v2/leader/%s?name=%s&ttl=%d", s.URL(), key, name, ttl), nil) | ||
ret := tests.ReadBody(resp) | ||
return string(ret), err | ||
} | ||
|
||
func testGetLeader(s *server.Server, key string) (string, error) { | ||
resp, err := tests.Get(fmt.Sprintf("%s/mod/v2/leader/%s", s.URL(), key)) | ||
ret := tests.ReadBody(resp) | ||
return string(ret), err | ||
} | ||
|
||
func testDeleteLeader(s *server.Server, key string, name string) (string, error) { | ||
resp, err := tests.DeleteForm(fmt.Sprintf("%s/mod/v2/leader/%s?name=%s", s.URL(), key, name), nil) | ||
ret := tests.ReadBody(resp) | ||
return string(ret), err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters