diff --git a/e2e/util.go b/e2e/util.go index 6960a3acf9b..acbee8ae3d7 100644 --- a/e2e/util.go +++ b/e2e/util.go @@ -15,7 +15,9 @@ package e2e import ( + "encoding/json" "fmt" + "math/rand" "strings" "time" @@ -77,6 +79,18 @@ func spawnWithExpectLines(args []string, xs ...string) ([]string, error) { return lines, perr } +func randomLeaseID() int64 { + return rand.New(rand.NewSource(time.Now().UnixNano())).Int63() +} + +func dataMarshal(data interface{}) (d string, e error) { + m, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(m), nil +} + func closeWithTimeout(p *expect.ExpectProcess, d time.Duration) error { errc := make(chan error, 1) go func() { errc <- p.Close() }() diff --git a/e2e/v3_curl_lease_test.go b/e2e/v3_curl_lease_test.go new file mode 100644 index 00000000000..241bdbf91f2 --- /dev/null +++ b/e2e/v3_curl_lease_test.go @@ -0,0 +1,188 @@ +// Copyright 2018 The etcd Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package e2e + +import ( + "fmt" + "testing" + + pb "github.com/coreos/etcd/etcdserver/etcdserverpb" +) + +func TestV3CurlLeaseGrantNoTLS(t *testing.T) { + for _, p := range apiPrefix { + testCtl(t, testV3CurlLeaseGrant, withApiPrefix(p), withCfg(configNoTLS)) + } +} +func TestV3CurlLeaseRevokeNoTLS(t *testing.T) { + for _, p := range apiPrefix { + testCtl(t, testV3CurlLeaseRevoke, withApiPrefix(p), withCfg(configNoTLS)) + } +} +func TestV3CurlLeaseLeasesNoTLS(t *testing.T) { + for _, p := range apiPrefix { + testCtl(t, testV3CurlLeaseLeases, withApiPrefix(p), withCfg(configNoTLS)) + } +} +func TestV3CurlLeaseKeepAliveNoTLS(t *testing.T) { + for _, p := range apiPrefix { + testCtl(t, testV3CurlLeaseKeepAlive, withApiPrefix(p), withCfg(configNoTLS)) + } +} + +type v3cURLTest struct { + endpoint string + value string + expected string +} + +func testV3CurlLeaseGrant(cx ctlCtx) { + leaseID := randomLeaseID() + + tests := []v3cURLTest{ + { + endpoint: "/lease/grant", + value: gwLeaseGrant(cx, leaseID, 0), + expected: gwLeaseIDExpected(leaseID), + }, + { + endpoint: "/lease/grant", + value: gwLeaseGrant(cx, 0, 20), + expected: `"TTL":"20"`, + }, + { + endpoint: "/kv/put", + value: gwKVPutLease(cx, "foo", "bar", leaseID), + expected: `"revision":"`, + }, + { + endpoint: "/kv/lease/timetolive", + value: gwLeaseTTLWithKeys(cx, leaseID), + expected: `"grantedTTL"`, + }, + } + if err := cURLWithExpected(cx, tests); err != nil { + cx.t.Fatalf("testV3CurlLeaseGrant: %v", err) + } +} + +func testV3CurlLeaseRevoke(cx ctlCtx) { + leaseID := randomLeaseID() + + tests := []v3cURLTest{ + { + endpoint: "/lease/grant", + value: gwLeaseGrant(cx, leaseID, 0), + expected: gwLeaseIDExpected(leaseID), + }, + { + endpoint: "/kv/lease/revoke", + value: gwLeaseRevoke(cx, leaseID), + expected: `"revision":"`, + }, + } + if err := cURLWithExpected(cx, tests); err != nil { + cx.t.Fatalf("testV3CurlLeaseRevoke: %v", err) + } +} + +func testV3CurlLeaseLeases(cx ctlCtx) { + leaseID := randomLeaseID() + + tests := []v3cURLTest{ + { + endpoint: "/lease/grant", + value: gwLeaseGrant(cx, leaseID, 0), + expected: gwLeaseIDExpected(leaseID), + }, + { + endpoint: "/kv/lease/leases", + value: "{}", + expected: gwLeaseIDExpected(leaseID), + }, + } + if err := cURLWithExpected(cx, tests); err != nil { + cx.t.Fatalf("testV3CurlLeaseGrant: %v", err) + } +} + +func testV3CurlLeaseKeepAlive(cx ctlCtx) { + leaseID := randomLeaseID() + + tests := []v3cURLTest{ + { + endpoint: "/lease/grant", + value: gwLeaseGrant(cx, leaseID, 0), + expected: gwLeaseIDExpected(leaseID), + }, + { + endpoint: "/lease/keepalive", + value: gwLeaseKeepAlive(cx, leaseID), + expected: gwLeaseIDExpected(leaseID), + }, + } + if err := cURLWithExpected(cx, tests); err != nil { + cx.t.Fatalf("testV3CurlLeaseGrant: %v", err) + } +} + +func gwLeaseIDExpected(leaseID int64) string { + return fmt.Sprintf(`"ID":"%d"`, leaseID) +} + +func gwLeaseTTLWithKeys(cx ctlCtx, leaseID int64) string { + d := &pb.LeaseTimeToLiveRequest{ID: leaseID, Keys: true} + s, err := dataMarshal(d) + if err != nil { + cx.t.Fatalf("gwLeaseTTLWithKeys: error (%v)", err) + } + return s +} + +func gwLeaseKeepAlive(cx ctlCtx, leaseID int64) string { + d := &pb.LeaseKeepAliveRequest{ID: leaseID} + s, err := dataMarshal(d) + if err != nil { + cx.t.Fatalf("gwLeaseKeepAlive: Marshal error (%v)", err) + } + return s +} + +func gwLeaseGrant(cx ctlCtx, leaseID int64, ttl int64) string { + d := &pb.LeaseGrantRequest{ID: leaseID, TTL: ttl} + s, err := dataMarshal(d) + if err != nil { + cx.t.Fatalf("gwLeaseGrant: Marshal error (%v)", err) + } + return s +} + +func gwLeaseRevoke(cx ctlCtx, leaseID int64) string { + d := &pb.LeaseRevokeRequest{ID: leaseID} + s, err := dataMarshal(d) + if err != nil { + cx.t.Fatalf("gwLeaseRevoke: Marshal error (%v)", err) + } + return s +} + +func gwKVPutLease(cx ctlCtx, k string, v string, leaseID int64) string { + d := pb.PutRequest{Key: []byte(k), Value: []byte(v), Lease: leaseID} + s, err := dataMarshal(d) + if err != nil { + cx.t.Fatalf("gwKVPutLease: Marshal error (%v)", err) + } + return s +} diff --git a/e2e/v3_curl_test.go b/e2e/v3_curl_test.go index 910288b5230..250ea61aab4 100644 --- a/e2e/v3_curl_test.go +++ b/e2e/v3_curl_test.go @@ -17,6 +17,7 @@ package e2e import ( "encoding/base64" "encoding/json" + "fmt" "path" "strconv" "testing" @@ -358,3 +359,14 @@ type campaignResponse struct { Lease string `json:"lease,omitempty"` } `json:"leader,omitempty"` } + +func cURLWithExpected(cx ctlCtx, tests []v3cURLTest) error { + p := cx.apiPrefix + for _, t := range tests { + value := fmt.Sprintf("%v", t.value) + if err := cURLPost(cx.epc, cURLReq{endpoint: path.Join(p, t.endpoint), value: value, expected: t.expected}); err != nil { + return fmt.Errorf("prefix (%s) endpoint (%s): error (%v), wanted %v", p, t.endpoint, err, t.expected) + } + } + return nil +}