From ec18e46641b3b56481a4042e5d44c8b2b875fffb Mon Sep 17 00:00:00 2001 From: Jonathan Boulle Date: Thu, 16 Oct 2014 15:50:06 -0700 Subject: [PATCH] etcdserver/etcdhttp: switch to using fake clock --- etcdserver/etcdhttp/http.go | 9 +++--- etcdserver/etcdhttp/http_test.go | 48 +++++++++++++++++--------------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/etcdserver/etcdhttp/http.go b/etcdserver/etcdhttp/http.go index ac89a6b96..dad127120 100644 --- a/etcdserver/etcdhttp/http.go +++ b/etcdserver/etcdhttp/http.go @@ -13,6 +13,7 @@ import ( "time" "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" "github.com/coreos/etcd/etcdserver" "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) defer cancel() - rr, err := parseRequest(r, etcdserver.GenID()) + rr, err := parseRequest(r, etcdserver.GenID(), clockwork.NewRealClock()) if err != nil { writeError(w, err) 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, // performing validation of supplied fields as appropriate. // 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{} err := r.ParseForm() @@ -354,11 +355,9 @@ func parseRequest(r *http.Request, id uint64) (etcdserverpb.Request, error) { } // 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 { expr := time.Duration(*ttl) * time.Second - rr.Expiration = time.Now().Add(expr).UnixNano() + rr.Expiration = clock.Now().Add(expr).UnixNano() } return rr, nil diff --git a/etcdserver/etcdhttp/http_test.go b/etcdserver/etcdhttp/http_test.go index 87e39aab1..4813003fc 100644 --- a/etcdserver/etcdhttp/http_test.go +++ b/etcdserver/etcdhttp/http_test.go @@ -16,6 +16,7 @@ import ( "time" "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" "github.com/coreos/etcd/etcdserver" "github.com/coreos/etcd/etcdserver/etcdserverpb" @@ -194,7 +195,7 @@ func TestBadParseRequest(t *testing.T) { }, } for i, tt := range tests { - got, err := parseRequest(tt.in, 1234) + got, err := parseRequest(tt.in, 1234, clockwork.NewFakeClock()) if err == nil { t.Errorf("#%d: unexpected nil error!", i) continue @@ -215,6 +216,8 @@ func TestBadParseRequest(t *testing.T) { } func TestGoodParseRequest(t *testing.T) { + fc := clockwork.NewFakeClock() + fc.Tick(1111) tests := []struct { in *http.Request w etcdserverpb.Request @@ -304,6 +307,26 @@ func TestGoodParseRequest(t *testing.T) { 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 mustNewRequest(t, "foo?dir=true"), @@ -405,7 +428,7 @@ func TestGoodParseRequest(t *testing.T) { } for i, tt := range tests { - got, err := parseRequest(tt.in, 1234) + got, err := parseRequest(tt.in, 1234, fc) if 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) } } - - // 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