Browse Source

etcdserver/etcdhttp: switch to using fake clock

Jonathan Boulle 11 years ago
parent
commit
ec18e46641
2 changed files with 29 additions and 28 deletions
  1. 4 5
      etcdserver/etcdhttp/http.go
  2. 25 23
      etcdserver/etcdhttp/http_test.go

+ 4 - 5
etcdserver/etcdhttp/http.go

@@ -13,6 +13,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/coreos/etcd/Godeps/_workspace/src/code.google.com/p/go.net/context"
 	"github.com/coreos/etcd/Godeps/_workspace/src/code.google.com/p/go.net/context"
+	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork"
 	etcdErr "github.com/coreos/etcd/error"
 	etcdErr "github.com/coreos/etcd/error"
 	"github.com/coreos/etcd/etcdserver"
 	"github.com/coreos/etcd/etcdserver"
 	"github.com/coreos/etcd/etcdserver/etcdserverpb"
 	"github.com/coreos/etcd/etcdserver/etcdserverpb"
@@ -89,7 +90,7 @@ func (h serverHandler) serveKeys(w http.ResponseWriter, r *http.Request) {
 	ctx, cancel := context.WithTimeout(context.Background(), h.timeout)
 	ctx, cancel := context.WithTimeout(context.Background(), h.timeout)
 	defer cancel()
 	defer cancel()
 
 
-	rr, err := parseRequest(r, etcdserver.GenID())
+	rr, err := parseRequest(r, etcdserver.GenID(), clockwork.NewRealClock())
 	if err != nil {
 	if err != nil {
 		writeError(w, err)
 		writeError(w, err)
 		return
 		return
@@ -225,7 +226,7 @@ func (h serverHandler) serveRaft(w http.ResponseWriter, r *http.Request) {
 // parseRequest converts a received http.Request to a server Request,
 // parseRequest converts a received http.Request to a server Request,
 // performing validation of supplied fields as appropriate.
 // performing validation of supplied fields as appropriate.
 // If any validation fails, an empty Request and non-nil error is returned.
 // If any validation fails, an empty Request and non-nil error is returned.
-func parseRequest(r *http.Request, id uint64) (etcdserverpb.Request, error) {
+func parseRequest(r *http.Request, id uint64, clock clockwork.Clock) (etcdserverpb.Request, error) {
 	emptyReq := etcdserverpb.Request{}
 	emptyReq := etcdserverpb.Request{}
 
 
 	err := r.ParseForm()
 	err := r.ParseForm()
@@ -354,11 +355,9 @@ func parseRequest(r *http.Request, id uint64) (etcdserverpb.Request, error) {
 	}
 	}
 
 
 	// Null TTL is equivalent to unset Expiration
 	// Null TTL is equivalent to unset Expiration
-	// TODO(jonboulle): use fake clock instead of time module
-	// https://github.com/coreos/etcd/issues/1021
 	if ttl != nil {
 	if ttl != nil {
 		expr := time.Duration(*ttl) * time.Second
 		expr := time.Duration(*ttl) * time.Second
-		rr.Expiration = time.Now().Add(expr).UnixNano()
+		rr.Expiration = clock.Now().Add(expr).UnixNano()
 	}
 	}
 
 
 	return rr, nil
 	return rr, nil

+ 25 - 23
etcdserver/etcdhttp/http_test.go

@@ -16,6 +16,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/coreos/etcd/Godeps/_workspace/src/code.google.com/p/go.net/context"
 	"github.com/coreos/etcd/Godeps/_workspace/src/code.google.com/p/go.net/context"
+	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork"
 	etcdErr "github.com/coreos/etcd/error"
 	etcdErr "github.com/coreos/etcd/error"
 	"github.com/coreos/etcd/etcdserver"
 	"github.com/coreos/etcd/etcdserver"
 	"github.com/coreos/etcd/etcdserver/etcdserverpb"
 	"github.com/coreos/etcd/etcdserver/etcdserverpb"
@@ -194,7 +195,7 @@ func TestBadParseRequest(t *testing.T) {
 		},
 		},
 	}
 	}
 	for i, tt := range tests {
 	for i, tt := range tests {
-		got, err := parseRequest(tt.in, 1234)
+		got, err := parseRequest(tt.in, 1234, clockwork.NewFakeClock())
 		if err == nil {
 		if err == nil {
 			t.Errorf("#%d: unexpected nil error!", i)
 			t.Errorf("#%d: unexpected nil error!", i)
 			continue
 			continue
@@ -215,6 +216,8 @@ func TestBadParseRequest(t *testing.T) {
 }
 }
 
 
 func TestGoodParseRequest(t *testing.T) {
 func TestGoodParseRequest(t *testing.T) {
+	fc := clockwork.NewFakeClock()
+	fc.Tick(1111)
 	tests := []struct {
 	tests := []struct {
 		in *http.Request
 		in *http.Request
 		w  etcdserverpb.Request
 		w  etcdserverpb.Request
@@ -304,6 +307,26 @@ func TestGoodParseRequest(t *testing.T) {
 				Expiration: 0,
 				Expiration: 0,
 			},
 			},
 		},
 		},
+		{
+			// non-empty TTL specified
+			mustNewRequest(t, "foo?ttl=5678"),
+			etcdserverpb.Request{
+				ID:         1234,
+				Method:     "GET",
+				Path:       "/foo",
+				Expiration: fc.Now().Add(5678 * time.Second).UnixNano(),
+			},
+		},
+		{
+			// zero TTL specified
+			mustNewRequest(t, "foo?ttl=0"),
+			etcdserverpb.Request{
+				ID:         1234,
+				Method:     "GET",
+				Path:       "/foo",
+				Expiration: fc.Now().UnixNano(),
+			},
+		},
 		{
 		{
 			// dir specified
 			// dir specified
 			mustNewRequest(t, "foo?dir=true"),
 			mustNewRequest(t, "foo?dir=true"),
@@ -405,7 +428,7 @@ func TestGoodParseRequest(t *testing.T) {
 	}
 	}
 
 
 	for i, tt := range tests {
 	for i, tt := range tests {
-		got, err := parseRequest(tt.in, 1234)
+		got, err := parseRequest(tt.in, 1234, fc)
 		if err != nil {
 		if err != nil {
 			t.Errorf("#%d: err = %v, want %v", i, err, nil)
 			t.Errorf("#%d: err = %v, want %v", i, err, nil)
 		}
 		}
@@ -413,27 +436,6 @@ func TestGoodParseRequest(t *testing.T) {
 			t.Errorf("#%d: request=%#v, want %#v", i, got, tt.w)
 			t.Errorf("#%d: request=%#v, want %#v", i, got, tt.w)
 		}
 		}
 	}
 	}
-
-	// Test TTL separately until we don't rely on the time module...
-	now := time.Now().UnixNano()
-	req := mustNewForm(t, "foo", url.Values{"ttl": []string{"100"}})
-	got, err := parseRequest(req, 1234)
-	if err != nil {
-		t.Fatalf("err = %v, want nil", err)
-	}
-	if got.Expiration <= now {
-		t.Fatalf("expiration = %v, wanted > %v", got.Expiration, now)
-	}
-
-	// ensure TTL=0 results in an expiration time
-	req = mustNewForm(t, "foo", url.Values{"ttl": []string{"0"}})
-	got, err = parseRequest(req, 1234)
-	if err != nil {
-		t.Fatalf("err = %v, want nil", err)
-	}
-	if got.Expiration <= now {
-		t.Fatalf("expiration = %v, wanted > %v", got.Expiration, now)
-	}
 }
 }
 
 
 // eventingWatcher immediately returns a simple event of the given action on its channel
 // eventingWatcher immediately returns a simple event of the given action on its channel