Ben Johnson 12 years ago
parent
commit
32861246b9

+ 49 - 0
mod/lock/acquire_handler.go

@@ -0,0 +1,49 @@
+package lock
+
+import (
+	"net/http"
+	"path"
+	"strconv"
+
+	"github.com/gorilla/mux"
+)
+
+// acquireHandler attempts to acquire a lock on the given key.
+func (h *handler) acquireHandler(w http.ResponseWriter, req *http.Request) {
+	vars := mux.Vars(req)
+	keypath := path.Join(prefix, vars["key"])
+	ttl, err := strconv.Atoi(vars["ttl"])
+	if err != nil {
+		http.Error(w, "invalid ttl: " + err.Error(), http.StatusInternalServerError)
+		return
+	}
+
+	// Create an incrementing id for the lock.
+	resp, err := h.client.AddChild(keypath, "X", ttl)
+	if err != nil {
+		http.Error(w, "add lock index error: " + err.Error(), http.StatusInternalServerError)
+		return
+	}
+
+	// Extract the lock index.
+	index, _ := strconv.Atoi(path.Base(resp.Key))
+
+	// Read all indices.
+	resp, err = h.client.GetAll(key)
+	if err != nil {
+		http.Error(w, "lock children lookup error: " + err.Error(), http.StatusInternalServerError)
+		return
+	}
+	indices := extractResponseIndices(resp)
+
+	// TODO: child_keys := parse_and_sort_child_keys
+	// TODO: if index == min(child_keys) then return 200
+	// TODO: else:
+	// TODO: h.client.WatchAll(key)
+	// TODO: if next_lowest_key is deleted
+	// TODO: get_all_keys
+	// TODO: if index == min(child_keys) then return 200
+	// TODO: rinse_and_repeat until we're the lowest.
+
+	// TODO: 
+}

+ 29 - 18
mod/lock/handler.go

@@ -1,19 +1,20 @@
 package lock
 package lock
 
 
 import (
 import (
-	"bytes"
+	"fmt"
 	"net/http"
 	"net/http"
-	"os"
 	"path"
 	"path"
-	"time"
 
 
+	"github.com/gorilla/mux"
 	"github.com/coreos/go-etcd/etcd"
 	"github.com/coreos/go-etcd/etcd"
 )
 )
 
 
+const prefix = "/_etcd/locks"
+
 // handler manages the lock HTTP request.
 // handler manages the lock HTTP request.
 type handler struct {
 type handler struct {
 	*mux.Router
 	*mux.Router
-	client string
+	client *etcd.Client
 }
 }
 
 
 // NewHandler creates an HTTP handler that can be registered on a router.
 // NewHandler creates an HTTP handler that can be registered on a router.
@@ -22,23 +23,33 @@ func NewHandler(addr string) (http.Handler) {
 		Router: mux.NewRouter(),
 		Router: mux.NewRouter(),
 		client: etcd.NewClient([]string{addr}),
 		client: etcd.NewClient([]string{addr}),
 	}
 	}
-	h.HandleFunc("/{key:.+}", h.getLockHandler).Methods("GET")
-	h.HandleFunc("/{key:.+}", h.acquireLockHandler).Methods("PUT")
-	h.HandleFunc("/{key:.+}", h.releaseLockHandler).Methods("DELETE")
+	h.StrictSlash(false)
+	h.HandleFunc("/{key:.*}", h.acquireHandler).Methods("POST")
+	h.HandleFunc("/{key_with_index:.*}", h.renewLockHandler).Methods("PUT")
+	h.HandleFunc("/{key_with_index:.*}", h.releaseLockHandler).Methods("DELETE")
+	return h
 }
 }
 
 
-// getLockHandler retrieves whether a lock has been obtained for a given key.
-func (h *handler) getLockHandler(w http.ResponseWriter, req *http.Request) {
-	// TODO
-}
 
 
-// acquireLockHandler attempts to acquire a lock on the given key.
-// The lock is released when the connection is disconnected.
-func (h *handler) acquireLockHandler(w http.ResponseWriter, req *http.Request) {
-	// TODO
+// extractResponseIndices extracts a sorted list of indicies from a response.
+func extractResponseIndices(resp *etcd.Response) []int {
+	var indices []int
+	for _, kv := range resp.Kvs {
+		if index, _ := strconv.Atoi(path.Base(kv.Key)); index > 0 {
+			indicies = append(indices, index)
+		}
+	}
+	return indices
 }
 }
 
 
-// releaseLockHandler forces the release of a lock on the given key.
-func (h *handler) releaseLockHandler(w http.ResponseWriter, req *http.Request) {
-	// TODO
+// findPrevIndex retrieves the previous index before the given index.
+func findPrevIndex(indices []int, idx int) int {
+	var prevIndex int
+	for _, index := range indices {
+		if index == idx {
+			break
+		}
+		prevIndex = index
+	}
+	return prevIndex
 }
 }

+ 11 - 0
mod/lock/release_handler.go

@@ -0,0 +1,11 @@
+package lock
+
+import (
+	"net/http"
+)
+
+// releaseLockHandler deletes the lock.
+func (h *handler) releaseLockHandler(w http.ResponseWriter, req *http.Request) {
+	// TODO: h.client.Delete(key_with_index)
+}
+

+ 16 - 0
mod/lock/renew_handler.go

@@ -0,0 +1,16 @@
+package lock
+
+import (
+	"net/http"
+)
+
+// renewLockHandler attempts to update the TTL on an existing lock.
+// Returns a 200 OK if successful. Otherwie 
+func (h *handler) renewLockHandler(w http.ResponseWriter, req *http.Request) {
+	vars := mux.Vars(req)
+	key := path.Join(prefix, vars["key"])
+	ttl := vars["ttl"]
+	w.Write([]byte(fmt.Sprintf("%s-%s", key, ttl)))
+
+	// TODO:
+}

+ 19 - 4
mod/lock/handler_test.go → mod/lock/tests/handler_test.go

@@ -1,17 +1,32 @@
 package lock
 package lock
 
 
 import (
 import (
+	"fmt"
+	"net/url"
 	"testing"
 	"testing"
+	"time"
 
 
+	"github.com/coreos/etcd/server"
+	"github.com/coreos/etcd/tests"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
 )
 )
 
 
 // Ensure that a lock can be acquired and released.
 // Ensure that a lock can be acquired and released.
 func TestModLockAcquire(t *testing.T) {
 func TestModLockAcquire(t *testing.T) {
-	// TODO: Acquire lock.
-	// TODO: Check that it has been acquired.
-	// TODO: Release lock.
-	// TODO: Check that it has been released.
+	v := url.Values{}
+	tests.RunServer(func(s *server.Server) {
+		// Acquire lock.
+		resp, err := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/mod/lock"), v)
+		assert.NoError(t, err)
+		ret := tests.ReadBody(resp)
+		assert.Equal(t, string(ret), "XXX")
+
+		fmt.Println("URL:", fmt.Sprintf("http://%s%s", s.URL(), "/mod/lock"))
+		time.Sleep(60 * time.Second)
+		// TODO: Check that it has been acquired.
+		// TODO: Release lock.
+		// TODO: Check that it has been released.
+	})
 }
 }
 
 
 // Ensure that a lock can be acquired and another process is blocked until released.
 // Ensure that a lock can be acquired and another process is blocked until released.

+ 4 - 4
mod/mod.go

@@ -13,9 +13,9 @@ var ServeMux *http.Handler
 
 
 func HttpHandler() (handler http.Handler) {
 func HttpHandler() (handler http.Handler) {
 	r := mux.NewRouter()
 	r := mux.NewRouter()
-	r.PathPrefix("/dashboard/").
-		Handler(http.StripPrefix("/dashboard/", dashboard.HttpHandler()))
-	r.PathPrefix("/lock/").
-		Handler(http.StripPrefix("/lock", lock.NewHandler()))
+	r.PathPrefix("/dashboard/").Handler(http.StripPrefix("/dashboard/", dashboard.HttpHandler()))
+
+	// TODO: Use correct addr.
+	r.PathPrefix("/lock").Handler(http.StripPrefix("/lock", lock.NewHandler("127.0.0.1:4001")))
 	return r
 	return r
 }
 }