diff --git a/clientv3/balancer.go b/clientv3/balancer.go index 75c5cd438230..50746a1c0097 100644 --- a/clientv3/balancer.go +++ b/clientv3/balancer.go @@ -16,6 +16,7 @@ package clientv3 import ( "context" + "fmt" "net/url" "strings" "sync" @@ -291,27 +292,33 @@ func (b *simpleBalancer) up(addr grpc.Address) (func(error), bool) { b.mu.Lock() defer b.mu.Unlock() + fmt.Println("simpleBalancer up:", addr) // gRPC might call Up after it called Close. We add this check // to "fix" it up at application layer. Otherwise, will panic // if b.upc is already closed. if b.closed { + fmt.Println("simpleBalancer up closed:", addr, b.closed) return func(err error) {}, false } // gRPC might call Up on a stale address. // Prevent updating pinAddr with a stale address. if !hasAddr(b.addrs, addr.Addr) { + fmt.Println("simpleBalancer up !hasAddr:", addr, "/", b.addrs) return func(err error) {}, false } if b.pinAddr != "" { + fmt.Println("simpleBalancer up b.pinAddr != '':", addr, "/", b.pinAddr) return func(err error) {}, false } // notify waiting Get()s and pin first connected address close(b.upc) b.downc = make(chan struct{}) b.pinAddr = addr.Addr + fmt.Println("simpleBalancer up pinned:", addr) // notify client that a connection is up b.readyOnce.Do(func() { close(b.readyc) }) return func(err error) { + fmt.Println("simpleBalancer up error:", addr, err) b.mu.Lock() b.upc = make(chan struct{}) close(b.downc) diff --git a/clientv3/health_balancer.go b/clientv3/health_balancer.go index 25c2b1c6b9cf..7b84548a72fa 100644 --- a/clientv3/health_balancer.go +++ b/clientv3/health_balancer.go @@ -16,6 +16,7 @@ package clientv3 import ( "context" + "fmt" "sync" "time" @@ -174,6 +175,7 @@ func (hb *healthBalancer) mayPin(addr grpc.Address) bool { skip := len(hb.addrs) == 1 || len(hb.unhealthy) == 0 _, bad := hb.unhealthy[addr.Addr] hb.mu.RUnlock() + fmt.Println("mayPin:", addr, skip, bad) if skip || !bad { return true } diff --git a/e2e/ctl_v3_alarm_test.go b/e2e/ctl_v3_alarm_test.go deleted file mode 100644 index 0de8019ce5c7..000000000000 --- a/e2e/ctl_v3_alarm_test.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2016 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 ( - "context" - "fmt" - "os" - "path/filepath" - "strings" - "testing" - "time" - - "github.com/coreos/etcd/clientv3" - "github.com/coreos/etcd/mvcc" - "github.com/coreos/etcd/mvcc/backend" -) - -func TestCtlV3Alarm(t *testing.T) { - // The boltdb minimum working set is six pages. - testCtl(t, alarmTest, withQuota(int64(13*os.Getpagesize()))) -} - -func alarmTest(cx ctlCtx) { - // test small put still works - smallbuf := strings.Repeat("a", 64) - if err := ctlV3Put(cx, "1st_test", smallbuf, ""); err != nil { - cx.t.Fatal(err) - } - - // write some chunks to fill up the database - buf := strings.Repeat("b", int(os.Getpagesize())) - for { - if err := ctlV3Put(cx, "2nd_test", buf, ""); err != nil { - if !strings.Contains(err.Error(), "etcdserver: mvcc: database space exceeded") { - cx.t.Fatal(err) - } - break - } - } - - // quota alarm should now be on - if err := ctlV3Alarm(cx, "list", "alarm:NOSPACE"); err != nil { - cx.t.Fatal(err) - } - - // '/health' handler should return 'false' - if err := cURLGet(cx.epc, cURLReq{endpoint: "/health", expected: `{"health":false,"errors":["NOSPACE"]}`}); err != nil { - cx.t.Fatalf("failed get with curl (%v)", err) - } - - // check that Put is rejected when alarm is on - if err := ctlV3Put(cx, "3rd_test", smallbuf, ""); err != nil { - if !strings.Contains(err.Error(), "etcdserver: mvcc: database space exceeded") { - cx.t.Fatal(err) - } - } - - eps := cx.epc.EndpointsV3() - - // get latest revision to compact - cli, err := clientv3.New(clientv3.Config{ - Endpoints: eps, - DialTimeout: 3 * time.Second, - }) - if err != nil { - cx.t.Fatal(err) - } - defer cli.Close() - sresp, err := cli.Status(context.TODO(), eps[0]) - if err != nil { - cx.t.Fatal(err) - } - - // make some space - if err := ctlV3Compact(cx, sresp.Header.Revision, true); err != nil { - cx.t.Fatal(err) - } - if err := ctlV3Defrag(cx); err != nil { - cx.t.Fatal(err) - } - - // turn off alarm - if err := ctlV3Alarm(cx, "disarm", "alarm:NOSPACE"); err != nil { - cx.t.Fatal(err) - } - - // put one more key below quota - if err := ctlV3Put(cx, "4th_test", smallbuf, ""); err != nil { - cx.t.Fatal(err) - } -} - -func TestCtlV3Corrupt(t *testing.T) { - testCtl(t, corruptTest, withCfg(configNoTLS), withQuorum(), withCorruptCheckTime(time.Minute)) -} - -type fakeConsistentIndex struct{ rev uint64 } - -func (f *fakeConsistentIndex) ConsistentIndex() uint64 { return f.rev } - -func corruptTest(cx ctlCtx) { - for i := 0; i < 10; i++ { - if err := ctlV3Put(cx, "k", "v", ""); err != nil { - if cx.dialTimeout > 0 && !isGRPCTimedout(err) { - cx.t.Fatalf("putTest ctlV3Put error (%v)", err) - } - } - } - - eps := cx.epc.EndpointsV3() - cli1, err := clientv3.New(clientv3.Config{Endpoints: []string{eps[1]}, DialTimeout: 3 * time.Second}) - if err != nil { - cx.t.Fatal(err) - } - defer cli1.Close() - - sresp, err := cli1.Status(context.TODO(), eps[0]) - if err != nil { - cx.t.Fatal(err) - } - id0 := sresp.Header.GetMemberId() - - cx.epc.procs[0].Stop() - - // Corrupt member 0 by modifying backend offline. - fp := filepath.Join(cx.epc.procs[0].Config().dataDirPath, "member", "snap", "db") - be := backend.NewDefaultBackend(fp) - s := mvcc.NewStore(be, nil, &fakeConsistentIndex{13}) - s.Put([]byte("abc"), []byte("def"), 0) - s.Put([]byte("xyz"), []byte("123"), 0) - s.Compact(5) - s.Commit() - s.Close() - be.Close() - - ep := cx.epc.procs[0] - proc, err := spawnCmd(append([]string{ep.Config().execPath}, ep.Config().args...)) - if err != nil { - cx.t.Fatal(err) - } - defer proc.Stop() - - // restarting corrupted member should fail - waitReadyExpectProc(proc, []string{fmt.Sprintf("etcdserver: %016x is corrupted", id0)}) -} - -func ctlV3Alarm(cx ctlCtx, cmd string, as ...string) error { - cmdArgs := append(cx.PrefixArgs(), "alarm", cmd) - return spawnWithExpects(cmdArgs, as...) -} diff --git a/e2e/ctl_v3_auth_test.go b/e2e/ctl_v3_auth_test.go deleted file mode 100644 index 3b2b74c5ef03..000000000000 --- a/e2e/ctl_v3_auth_test.go +++ /dev/null @@ -1,962 +0,0 @@ -// Copyright 2016 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. - -// Skip proxy tests for now since auth is broken on grpcproxy. -// +build !cluster_proxy - -package e2e - -import ( - "fmt" - "os" - "testing" - - "github.com/coreos/etcd/clientv3" -) - -func TestCtlV3AuthEnable(t *testing.T) { testCtl(t, authEnableTest) } -func TestCtlV3AuthDisable(t *testing.T) { testCtl(t, authDisableTest) } -func TestCtlV3AuthWriteKey(t *testing.T) { testCtl(t, authCredWriteKeyTest) } -func TestCtlV3AuthRoleUpdate(t *testing.T) { testCtl(t, authRoleUpdateTest) } -func TestCtlV3AuthUserDeleteDuringOps(t *testing.T) { testCtl(t, authUserDeleteDuringOpsTest) } -func TestCtlV3AuthRoleRevokeDuringOps(t *testing.T) { testCtl(t, authRoleRevokeDuringOpsTest) } -func TestCtlV3AuthTxn(t *testing.T) { testCtl(t, authTestTxn) } -func TestCtlV3AuthPrefixPerm(t *testing.T) { testCtl(t, authTestPrefixPerm) } -func TestCtlV3AuthMemberAdd(t *testing.T) { testCtl(t, authTestMemberAdd) } -func TestCtlV3AuthMemberRemove(t *testing.T) { - testCtl(t, authTestMemberRemove, withQuorum(), withNoStrictReconfig()) -} -func TestCtlV3AuthMemberUpdate(t *testing.T) { testCtl(t, authTestMemberUpdate) } -func TestCtlV3AuthCertCN(t *testing.T) { testCtl(t, authTestCertCN, withCfg(configClientTLSCertAuth)) } -func TestCtlV3AuthRevokeWithDelete(t *testing.T) { testCtl(t, authTestRevokeWithDelete) } -func TestCtlV3AuthInvalidMgmt(t *testing.T) { testCtl(t, authTestInvalidMgmt) } -func TestCtlV3AuthFromKeyPerm(t *testing.T) { testCtl(t, authTestFromKeyPerm) } -func TestCtlV3AuthAndWatch(t *testing.T) { testCtl(t, authTestWatch) } - -func TestCtlV3AuthRoleGet(t *testing.T) { testCtl(t, authTestRoleGet) } -func TestCtlV3AuthUserGet(t *testing.T) { testCtl(t, authTestUserGet) } -func TestCtlV3AuthRoleList(t *testing.T) { testCtl(t, authTestRoleList) } - -func TestCtlV3AuthDefrag(t *testing.T) { testCtl(t, authTestDefrag) } -func TestCtlV3AuthEndpointHealth(t *testing.T) { - testCtl(t, authTestEndpointHealth, withQuorum()) -} -func TestCtlV3AuthSnapshot(t *testing.T) { testCtl(t, authTestSnapshot) } - -func authEnableTest(cx ctlCtx) { - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } -} - -func authEnable(cx ctlCtx) error { - // create root user with root role - if err := ctlV3User(cx, []string{"add", "root", "--interactive=false"}, "User root created", []string{"root"}); err != nil { - return fmt.Errorf("failed to create root user %v", err) - } - if err := ctlV3User(cx, []string{"grant-role", "root", "root"}, "Role root is granted to user root", nil); err != nil { - return fmt.Errorf("failed to grant root user root role %v", err) - } - if err := ctlV3AuthEnable(cx); err != nil { - return fmt.Errorf("authEnableTest ctlV3AuthEnable error (%v)", err) - } - return nil -} - -func ctlV3AuthEnable(cx ctlCtx) error { - cmdArgs := append(cx.PrefixArgs(), "auth", "enable") - return spawnWithExpect(cmdArgs, "Authentication Enabled") -} - -func authDisableTest(cx ctlCtx) { - // a key that isn't granted to test-user - if err := ctlV3Put(cx, "hoo", "a", ""); err != nil { - cx.t.Fatal(err) - } - - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - // test-user doesn't have the permission, it must fail - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3PutFailPerm(cx, "hoo", "bar"); err != nil { - cx.t.Fatal(err) - } - - cx.user, cx.pass = "root", "root" - if err := ctlV3AuthDisable(cx); err != nil { - cx.t.Fatalf("authDisableTest ctlV3AuthDisable error (%v)", err) - } - - // now ErrAuthNotEnabled of Authenticate() is simply ignored - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3Put(cx, "hoo", "bar", ""); err != nil { - cx.t.Fatal(err) - } - - // now the key can be accessed - cx.user, cx.pass = "", "" - if err := ctlV3Put(cx, "hoo", "bar", ""); err != nil { - cx.t.Fatal(err) - } - // confirm put succeeded - if err := ctlV3Get(cx, []string{"hoo"}, []kv{{"hoo", "bar"}}...); err != nil { - cx.t.Fatal(err) - } -} - -func ctlV3AuthDisable(cx ctlCtx) error { - cmdArgs := append(cx.PrefixArgs(), "auth", "disable") - return spawnWithExpect(cmdArgs, "Authentication Disabled") -} - -func authCredWriteKeyTest(cx ctlCtx) { - // baseline key to check for failed puts - if err := ctlV3Put(cx, "foo", "a", ""); err != nil { - cx.t.Fatal(err) - } - - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - // confirm root role can access to all keys - if err := ctlV3Put(cx, "foo", "bar", ""); err != nil { - cx.t.Fatal(err) - } - if err := ctlV3Get(cx, []string{"foo"}, []kv{{"foo", "bar"}}...); err != nil { - cx.t.Fatal(err) - } - - // try invalid user - cx.user, cx.pass = "a", "b" - if err := ctlV3PutFailAuth(cx, "foo", "bar"); err != nil { - cx.t.Fatal(err) - } - // confirm put failed - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3Get(cx, []string{"foo"}, []kv{{"foo", "bar"}}...); err != nil { - cx.t.Fatal(err) - } - - // try good user - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3Put(cx, "foo", "bar2", ""); err != nil { - cx.t.Fatal(err) - } - // confirm put succeeded - if err := ctlV3Get(cx, []string{"foo"}, []kv{{"foo", "bar2"}}...); err != nil { - cx.t.Fatal(err) - } - - // try bad password - cx.user, cx.pass = "test-user", "badpass" - if err := ctlV3PutFailAuth(cx, "foo", "baz"); err != nil { - cx.t.Fatal(err) - } - // confirm put failed - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3Get(cx, []string{"foo"}, []kv{{"foo", "bar2"}}...); err != nil { - cx.t.Fatal(err) - } -} - -func authRoleUpdateTest(cx ctlCtx) { - if err := ctlV3Put(cx, "foo", "bar", ""); err != nil { - cx.t.Fatal(err) - } - - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - // try put to not granted key - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3PutFailPerm(cx, "hoo", "bar"); err != nil { - cx.t.Fatal(err) - } - - // grant a new key - cx.user, cx.pass = "root", "root" - if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, "hoo", "", false}); err != nil { - cx.t.Fatal(err) - } - - // try a newly granted key - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3Put(cx, "hoo", "bar", ""); err != nil { - cx.t.Fatal(err) - } - // confirm put succeeded - if err := ctlV3Get(cx, []string{"hoo"}, []kv{{"hoo", "bar"}}...); err != nil { - cx.t.Fatal(err) - } - - // revoke the newly granted key - cx.user, cx.pass = "root", "root" - if err := ctlV3RoleRevokePermission(cx, "test-role", "hoo", "", false); err != nil { - cx.t.Fatal(err) - } - - // try put to the revoked key - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3PutFailPerm(cx, "hoo", "bar"); err != nil { - cx.t.Fatal(err) - } - - // confirm a key still granted can be accessed - if err := ctlV3Get(cx, []string{"foo"}, []kv{{"foo", "bar"}}...); err != nil { - cx.t.Fatal(err) - } -} - -func authUserDeleteDuringOpsTest(cx ctlCtx) { - if err := ctlV3Put(cx, "foo", "bar", ""); err != nil { - cx.t.Fatal(err) - } - - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - // create a key - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3Put(cx, "foo", "bar", ""); err != nil { - cx.t.Fatal(err) - } - // confirm put succeeded - if err := ctlV3Get(cx, []string{"foo"}, []kv{{"foo", "bar"}}...); err != nil { - cx.t.Fatal(err) - } - - // delete the user - cx.user, cx.pass = "root", "root" - err := ctlV3User(cx, []string{"delete", "test-user"}, "User test-user deleted", []string{}) - if err != nil { - cx.t.Fatal(err) - } - - // check the user is deleted - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3PutFailAuth(cx, "foo", "baz"); err != nil { - cx.t.Fatal(err) - } -} - -func authRoleRevokeDuringOpsTest(cx ctlCtx) { - if err := ctlV3Put(cx, "foo", "bar", ""); err != nil { - cx.t.Fatal(err) - } - - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - // create a key - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3Put(cx, "foo", "bar", ""); err != nil { - cx.t.Fatal(err) - } - // confirm put succeeded - if err := ctlV3Get(cx, []string{"foo"}, []kv{{"foo", "bar"}}...); err != nil { - cx.t.Fatal(err) - } - - // create a new role - cx.user, cx.pass = "root", "root" - if err := ctlV3Role(cx, []string{"add", "test-role2"}, "Role test-role2 created"); err != nil { - cx.t.Fatal(err) - } - // grant a new key to the new role - if err := ctlV3RoleGrantPermission(cx, "test-role2", grantingPerm{true, true, "hoo", "", false}); err != nil { - cx.t.Fatal(err) - } - // grant the new role to the user - if err := ctlV3User(cx, []string{"grant-role", "test-user", "test-role2"}, "Role test-role2 is granted to user test-user", nil); err != nil { - cx.t.Fatal(err) - } - - // try a newly granted key - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3Put(cx, "hoo", "bar", ""); err != nil { - cx.t.Fatal(err) - } - // confirm put succeeded - if err := ctlV3Get(cx, []string{"hoo"}, []kv{{"hoo", "bar"}}...); err != nil { - cx.t.Fatal(err) - } - - // revoke a role from the user - cx.user, cx.pass = "root", "root" - err := ctlV3User(cx, []string{"revoke-role", "test-user", "test-role"}, "Role test-role is revoked from user test-user", []string{}) - if err != nil { - cx.t.Fatal(err) - } - - // check the role is revoked and permission is lost from the user - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3PutFailPerm(cx, "foo", "baz"); err != nil { - cx.t.Fatal(err) - } - - // try a key that can be accessed from the remaining role - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3Put(cx, "hoo", "bar2", ""); err != nil { - cx.t.Fatal(err) - } - // confirm put succeeded - if err := ctlV3Get(cx, []string{"hoo"}, []kv{{"hoo", "bar2"}}...); err != nil { - cx.t.Fatal(err) - } -} - -func ctlV3PutFailAuth(cx ctlCtx, key, val string) error { - return spawnWithExpect(append(cx.PrefixArgs(), "put", key, val), "authentication failed") -} - -func ctlV3PutFailPerm(cx ctlCtx, key, val string) error { - return spawnWithExpect(append(cx.PrefixArgs(), "put", key, val), "permission denied") -} - -func authSetupTestUser(cx ctlCtx) { - if err := ctlV3User(cx, []string{"add", "test-user", "--interactive=false"}, "User test-user created", []string{"pass"}); err != nil { - cx.t.Fatal(err) - } - if err := spawnWithExpect(append(cx.PrefixArgs(), "role", "add", "test-role"), "Role test-role created"); err != nil { - cx.t.Fatal(err) - } - if err := ctlV3User(cx, []string{"grant-role", "test-user", "test-role"}, "Role test-role is granted to user test-user", nil); err != nil { - cx.t.Fatal(err) - } - cmd := append(cx.PrefixArgs(), "role", "grant-permission", "test-role", "readwrite", "foo") - if err := spawnWithExpect(cmd, "Role test-role updated"); err != nil { - cx.t.Fatal(err) - } -} - -func authTestTxn(cx ctlCtx) { - // keys with 1 suffix aren't granted to test-user - // keys with 2 suffix are granted to test-user - - keys := []string{"c1", "s1", "f1"} - grantedKeys := []string{"c2", "s2", "f2"} - for _, key := range keys { - if err := ctlV3Put(cx, key, "v", ""); err != nil { - cx.t.Fatal(err) - } - } - - for _, key := range grantedKeys { - if err := ctlV3Put(cx, key, "v", ""); err != nil { - cx.t.Fatal(err) - } - } - - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - // grant keys to test-user - cx.user, cx.pass = "root", "root" - for _, key := range grantedKeys { - if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, key, "", false}); err != nil { - cx.t.Fatal(err) - } - } - - // now test txn - cx.interactive = true - cx.user, cx.pass = "test-user", "pass" - - rqs := txnRequests{ - compare: []string{`version("c2") = "1"`}, - ifSucess: []string{"get s2"}, - ifFail: []string{"get f2"}, - results: []string{"SUCCESS", "s2", "v"}, - } - if err := ctlV3Txn(cx, rqs); err != nil { - cx.t.Fatal(err) - } - - // a key of compare case isn't granted - rqs = txnRequests{ - compare: []string{`version("c1") = "1"`}, - ifSucess: []string{"get s2"}, - ifFail: []string{"get f2"}, - results: []string{"Error: etcdserver: permission denied"}, - } - if err := ctlV3Txn(cx, rqs); err != nil { - cx.t.Fatal(err) - } - - // a key of success case isn't granted - rqs = txnRequests{ - compare: []string{`version("c2") = "1"`}, - ifSucess: []string{"get s1"}, - ifFail: []string{"get f2"}, - results: []string{"Error: etcdserver: permission denied"}, - } - if err := ctlV3Txn(cx, rqs); err != nil { - cx.t.Fatal(err) - } - - // a key of failure case isn't granted - rqs = txnRequests{ - compare: []string{`version("c2") = "1"`}, - ifSucess: []string{"get s2"}, - ifFail: []string{"get f1"}, - results: []string{"Error: etcdserver: permission denied"}, - } - if err := ctlV3Txn(cx, rqs); err != nil { - cx.t.Fatal(err) - } -} - -func authTestPrefixPerm(cx ctlCtx) { - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - prefix := "/prefix/" // directory like prefix - // grant keys to test-user - cx.user, cx.pass = "root", "root" - if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, prefix, "", true}); err != nil { - cx.t.Fatal(err) - } - - // try a prefix granted permission - cx.user, cx.pass = "test-user", "pass" - for i := 0; i < 10; i++ { - key := fmt.Sprintf("%s%d", prefix, i) - if err := ctlV3Put(cx, key, "val", ""); err != nil { - cx.t.Fatal(err) - } - } - - if err := ctlV3PutFailPerm(cx, clientv3.GetPrefixRangeEnd(prefix), "baz"); err != nil { - cx.t.Fatal(err) - } - - // grant the entire keys to test-user - cx.user, cx.pass = "root", "root" - if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, "", "", true}); err != nil { - cx.t.Fatal(err) - } - - prefix2 := "/prefix2/" - cx.user, cx.pass = "test-user", "pass" - for i := 0; i < 10; i++ { - key := fmt.Sprintf("%s%d", prefix2, i) - if err := ctlV3Put(cx, key, "val", ""); err != nil { - cx.t.Fatal(err) - } - } -} - -func authTestMemberAdd(cx ctlCtx) { - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - peerURL := fmt.Sprintf("http://localhost:%d", etcdProcessBasePort+11) - // ordinary user cannot add a new member - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3MemberAdd(cx, peerURL); err == nil { - cx.t.Fatalf("ordinary user must not be allowed to add a member") - } - - // root can add a new member - cx.user, cx.pass = "root", "root" - if err := ctlV3MemberAdd(cx, peerURL); err != nil { - cx.t.Fatal(err) - } -} - -func authTestMemberRemove(cx ctlCtx) { - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - ep, memIDToRemove, clusterID := cx.memberToRemove() - - // ordinary user cannot remove a member - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3MemberRemove(cx, ep, memIDToRemove, clusterID); err == nil { - cx.t.Fatalf("ordinary user must not be allowed to remove a member") - } - - // root can remove a member - cx.user, cx.pass = "root", "root" - if err := ctlV3MemberRemove(cx, ep, memIDToRemove, clusterID); err != nil { - cx.t.Fatal(err) - } -} - -func authTestMemberUpdate(cx ctlCtx) { - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - mr, err := getMemberList(cx) - if err != nil { - cx.t.Fatal(err) - } - - // ordinary user cannot update a member - cx.user, cx.pass = "test-user", "pass" - peerURL := fmt.Sprintf("http://localhost:%d", etcdProcessBasePort+11) - memberID := fmt.Sprintf("%x", mr.Members[0].ID) - if err = ctlV3MemberUpdate(cx, memberID, peerURL); err == nil { - cx.t.Fatalf("ordinary user must not be allowed to update a member") - } - - // root can update a member - cx.user, cx.pass = "root", "root" - if err = ctlV3MemberUpdate(cx, memberID, peerURL); err != nil { - cx.t.Fatal(err) - } -} - -func authTestCertCN(cx ctlCtx) { - if err := ctlV3User(cx, []string{"add", "etcd", "--interactive=false"}, "User etcd created", []string{""}); err != nil { - cx.t.Fatal(err) - } - if err := spawnWithExpect(append(cx.PrefixArgs(), "role", "add", "test-role"), "Role test-role created"); err != nil { - cx.t.Fatal(err) - } - if err := ctlV3User(cx, []string{"grant-role", "etcd", "test-role"}, "Role test-role is granted to user etcd", nil); err != nil { - cx.t.Fatal(err) - } - cmd := append(cx.PrefixArgs(), "role", "grant-permission", "test-role", "readwrite", "foo") - if err := spawnWithExpect(cmd, "Role test-role updated"); err != nil { - cx.t.Fatal(err) - } - - // grant a new key - if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, "hoo", "", false}); err != nil { - cx.t.Fatal(err) - } - - // try a granted key - cx.user, cx.pass = "", "" - if err := ctlV3Put(cx, "hoo", "bar", ""); err != nil { - cx.t.Fatal(err) - } - - // try a non granted key - cx.user, cx.pass = "", "" - if err := ctlV3PutFailPerm(cx, "baz", "bar"); err == nil { - cx.t.Fatal(err) - } -} - -func authTestRevokeWithDelete(cx ctlCtx) { - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - // create a new role - cx.user, cx.pass = "root", "root" - if err := ctlV3Role(cx, []string{"add", "test-role2"}, "Role test-role2 created"); err != nil { - cx.t.Fatal(err) - } - - // grant the new role to the user - if err := ctlV3User(cx, []string{"grant-role", "test-user", "test-role2"}, "Role test-role2 is granted to user test-user", nil); err != nil { - cx.t.Fatal(err) - } - - // check the result - if err := ctlV3User(cx, []string{"get", "test-user"}, "Roles: test-role test-role2", nil); err != nil { - cx.t.Fatal(err) - } - - // delete the role, test-role2 must be revoked from test-user - if err := ctlV3Role(cx, []string{"delete", "test-role2"}, "Role test-role2 deleted"); err != nil { - cx.t.Fatal(err) - } - - // check the result - if err := ctlV3User(cx, []string{"get", "test-user"}, "Roles: test-role", nil); err != nil { - cx.t.Fatal(err) - } -} - -func authTestInvalidMgmt(cx ctlCtx) { - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - - if err := ctlV3Role(cx, []string{"delete", "root"}, "Error: etcdserver: invalid auth management"); err == nil { - cx.t.Fatal("deleting the role root must not be allowed") - } - - if err := ctlV3User(cx, []string{"revoke-role", "root", "root"}, "Error: etcdserver: invalid auth management", []string{}); err == nil { - cx.t.Fatal("revoking the role root from the user root must not be allowed") - } -} - -func authTestFromKeyPerm(cx ctlCtx) { - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - // grant keys after z to test-user - cx.user, cx.pass = "root", "root" - if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, "z", "\x00", false}); err != nil { - cx.t.Fatal(err) - } - - // try the granted open ended permission - cx.user, cx.pass = "test-user", "pass" - for i := 0; i < 10; i++ { - key := fmt.Sprintf("z%d", i) - if err := ctlV3Put(cx, key, "val", ""); err != nil { - cx.t.Fatal(err) - } - } - largeKey := "" - for i := 0; i < 10; i++ { - largeKey += "\xff" - if err := ctlV3Put(cx, largeKey, "val", ""); err != nil { - cx.t.Fatal(err) - } - } - - // try a non granted key - if err := ctlV3PutFailPerm(cx, "x", "baz"); err != nil { - cx.t.Fatal(err) - } - - // revoke the open ended permission - cx.user, cx.pass = "root", "root" - if err := ctlV3RoleRevokePermission(cx, "test-role", "z", "", true); err != nil { - cx.t.Fatal(err) - } - - // try the revoked open ended permission - cx.user, cx.pass = "test-user", "pass" - for i := 0; i < 10; i++ { - key := fmt.Sprintf("z%d", i) - if err := ctlV3PutFailPerm(cx, key, "val"); err != nil { - cx.t.Fatal(err) - } - } - - // grant the entire keys - cx.user, cx.pass = "root", "root" - if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, "", "\x00", false}); err != nil { - cx.t.Fatal(err) - } - - // try keys, of course it must be allowed because test-role has a permission of the entire keys - cx.user, cx.pass = "test-user", "pass" - for i := 0; i < 10; i++ { - key := fmt.Sprintf("z%d", i) - if err := ctlV3Put(cx, key, "val", ""); err != nil { - cx.t.Fatal(err) - } - } - - // revoke the entire keys - cx.user, cx.pass = "root", "root" - if err := ctlV3RoleRevokePermission(cx, "test-role", "", "", true); err != nil { - cx.t.Fatal(err) - } - - // try the revoked entire key permission - cx.user, cx.pass = "test-user", "pass" - for i := 0; i < 10; i++ { - key := fmt.Sprintf("z%d", i) - if err := ctlV3PutFailPerm(cx, key, "val"); err != nil { - cx.t.Fatal(err) - } - } -} - -func authTestWatch(cx ctlCtx) { - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - // grant a key range - if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, "key", "key4", false}); err != nil { - cx.t.Fatal(err) - } - - tests := []struct { - puts []kv - args []string - - wkv []kv - want bool - }{ - { // watch 1 key, should be successful - []kv{{"key", "value"}}, - []string{"key", "--rev", "1"}, - []kv{{"key", "value"}}, - true, - }, - { // watch 3 keys by range, should be successful - []kv{{"key1", "val1"}, {"key3", "val3"}, {"key2", "val2"}}, - []string{"key", "key3", "--rev", "1"}, - []kv{{"key1", "val1"}, {"key2", "val2"}}, - true, - }, - - { // watch 1 key, should not be successful - []kv{}, - []string{"key5", "--rev", "1"}, - []kv{}, - false, - }, - { // watch 3 keys by range, should not be successful - []kv{}, - []string{"key", "key6", "--rev", "1"}, - []kv{}, - false, - }, - } - - cx.user, cx.pass = "test-user", "pass" - for i, tt := range tests { - donec := make(chan struct{}) - go func(i int, puts []kv) { - defer close(donec) - for j := range puts { - if err := ctlV3Put(cx, puts[j].key, puts[j].val, ""); err != nil { - cx.t.Fatalf("watchTest #%d-%d: ctlV3Put error (%v)", i, j, err) - } - } - }(i, tt.puts) - - var err error - if tt.want { - err = ctlV3Watch(cx, tt.args, tt.wkv...) - } else { - err = ctlV3WatchFailPerm(cx, tt.args) - } - - if err != nil { - if cx.dialTimeout > 0 && !isGRPCTimedout(err) { - cx.t.Errorf("watchTest #%d: ctlV3Watch error (%v)", i, err) - } - } - - <-donec - } - -} - -func authTestRoleGet(cx ctlCtx) { - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - expected := []string{ - "Role test-role", - "KV Read:", "foo", - "KV Write:", "foo", - } - if err := spawnWithExpects(append(cx.PrefixArgs(), "role", "get", "test-role"), expected...); err != nil { - cx.t.Fatal(err) - } - - // test-user can get the information of test-role because it belongs to the role - cx.user, cx.pass = "test-user", "pass" - if err := spawnWithExpects(append(cx.PrefixArgs(), "role", "get", "test-role"), expected...); err != nil { - cx.t.Fatal(err) - } - - // test-user cannot get the information of root because it doesn't belong to the role - expected = []string{ - "Error: etcdserver: permission denied", - } - if err := spawnWithExpects(append(cx.PrefixArgs(), "role", "get", "root"), expected...); err != nil { - cx.t.Fatal(err) - } -} - -func authTestUserGet(cx ctlCtx) { - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - expected := []string{ - "User: test-user", - "Roles: test-role", - } - - if err := spawnWithExpects(append(cx.PrefixArgs(), "user", "get", "test-user"), expected...); err != nil { - cx.t.Fatal(err) - } - - // test-user can get the information of test-user itself - cx.user, cx.pass = "test-user", "pass" - if err := spawnWithExpects(append(cx.PrefixArgs(), "user", "get", "test-user"), expected...); err != nil { - cx.t.Fatal(err) - } - - // test-user cannot get the information of root - expected = []string{ - "Error: etcdserver: permission denied", - } - if err := spawnWithExpects(append(cx.PrefixArgs(), "user", "get", "root"), expected...); err != nil { - cx.t.Fatal(err) - } -} - -func authTestRoleList(cx ctlCtx) { - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - if err := spawnWithExpect(append(cx.PrefixArgs(), "role", "list"), "test-role"); err != nil { - cx.t.Fatal(err) - } -} - -func authTestDefrag(cx ctlCtx) { - maintenanceInitKeys(cx) - - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - // ordinary user cannot defrag - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3Defrag(cx); err == nil { - cx.t.Fatal("ordinary user should not be able to issue a defrag request") - } - - // root can defrag - cx.user, cx.pass = "root", "root" - if err := ctlV3Defrag(cx); err != nil { - cx.t.Fatal(err) - } -} - -func authTestSnapshot(cx ctlCtx) { - maintenanceInitKeys(cx) - - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - fpath := "test.snapshot" - defer os.RemoveAll(fpath) - - // ordinary user cannot save a snapshot - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3SnapshotSave(cx, fpath); err == nil { - cx.t.Fatal("ordinary user should not be able to save a snapshot") - } - - // root can save a snapshot - cx.user, cx.pass = "root", "root" - if err := ctlV3SnapshotSave(cx, fpath); err != nil { - cx.t.Fatalf("snapshotTest ctlV3SnapshotSave error (%v)", err) - } - - st, err := getSnapshotStatus(cx, fpath) - if err != nil { - cx.t.Fatalf("snapshotTest getSnapshotStatus error (%v)", err) - } - if st.Revision != 4 { - cx.t.Fatalf("expected 4, got %d", st.Revision) - } - if st.TotalKey < 3 { - cx.t.Fatalf("expected at least 3, got %d", st.TotalKey) - } -} - -func authTestEndpointHealth(cx ctlCtx) { - if err := authEnable(cx); err != nil { - cx.t.Fatal(err) - } - - cx.user, cx.pass = "root", "root" - authSetupTestUser(cx) - - if err := ctlV3EndpointHealth(cx); err != nil { - cx.t.Fatalf("endpointStatusTest ctlV3EndpointHealth error (%v)", err) - } - - // health checking with an ordinary user "succeeds" since permission denial goes through consensus - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3EndpointHealth(cx); err != nil { - cx.t.Fatalf("endpointStatusTest ctlV3EndpointHealth error (%v)", err) - } - - // succeed if permissions granted for ordinary user - cx.user, cx.pass = "root", "root" - if err := ctlV3RoleGrantPermission(cx, "test-role", grantingPerm{true, true, "health", "", false}); err != nil { - cx.t.Fatal(err) - } - cx.user, cx.pass = "test-user", "pass" - if err := ctlV3EndpointHealth(cx); err != nil { - cx.t.Fatalf("endpointStatusTest ctlV3EndpointHealth error (%v)", err) - } -} diff --git a/e2e/ctl_v3_compact_test.go b/e2e/ctl_v3_compact_test.go deleted file mode 100644 index 5b0c51eb4266..000000000000 --- a/e2e/ctl_v3_compact_test.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2016 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 ( - "strconv" - "strings" - "testing" -) - -func TestCtlV3Compact(t *testing.T) { testCtl(t, compactTest) } -func TestCtlV3CompactPhysical(t *testing.T) { testCtl(t, compactTest, withCompactPhysical()) } - -func compactTest(cx ctlCtx) { - compactPhysical := cx.compactPhysical - if err := ctlV3Compact(cx, 2, compactPhysical); err != nil { - if !strings.Contains(err.Error(), "required revision is a future revision") { - cx.t.Fatal(err) - } - } else { - cx.t.Fatalf("expected '...future revision' error, got ") - } - - var kvs = []kv{{"key", "val1"}, {"key", "val2"}, {"key", "val3"}} - for i := range kvs { - if err := ctlV3Put(cx, kvs[i].key, kvs[i].val, ""); err != nil { - cx.t.Fatalf("compactTest #%d: ctlV3Put error (%v)", i, err) - } - } - - if err := ctlV3Get(cx, []string{"key", "--rev", "3"}, kvs[1:2]...); err != nil { - cx.t.Errorf("compactTest: ctlV3Get error (%v)", err) - } - - if err := ctlV3Compact(cx, 4, compactPhysical); err != nil { - cx.t.Fatal(err) - } - - if err := ctlV3Get(cx, []string{"key", "--rev", "3"}, kvs[1:2]...); err != nil { - if !strings.Contains(err.Error(), "required revision has been compacted") { - cx.t.Errorf("compactTest: ctlV3Get error (%v)", err) - } - } else { - cx.t.Fatalf("expected '...has been compacted' error, got ") - } - - if err := ctlV3Compact(cx, 2, compactPhysical); err != nil { - if !strings.Contains(err.Error(), "required revision has been compacted") { - cx.t.Fatal(err) - } - } else { - cx.t.Fatalf("expected '...has been compacted' error, got ") - } -} - -func ctlV3Compact(cx ctlCtx, rev int64, physical bool) error { - rs := strconv.FormatInt(rev, 10) - cmdArgs := append(cx.PrefixArgs(), "compact", rs) - if physical { - cmdArgs = append(cmdArgs, "--physical") - } - return spawnWithExpect(cmdArgs, "compacted revision "+rs) -} diff --git a/e2e/ctl_v3_defrag_test.go b/e2e/ctl_v3_defrag_test.go deleted file mode 100644 index 64c3bb9f0c38..000000000000 --- a/e2e/ctl_v3_defrag_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2016 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 "testing" - -func TestCtlV3Defrag(t *testing.T) { testCtl(t, defragTest) } - -func maintenanceInitKeys(cx ctlCtx) { - var kvs = []kv{{"key", "val1"}, {"key", "val2"}, {"key", "val3"}} - for i := range kvs { - if err := ctlV3Put(cx, kvs[i].key, kvs[i].val, ""); err != nil { - cx.t.Fatal(err) - } - } -} - -func defragTest(cx ctlCtx) { - maintenanceInitKeys(cx) - - if err := ctlV3Compact(cx, 4, cx.compactPhysical); err != nil { - cx.t.Fatal(err) - } - - if err := ctlV3Defrag(cx); err != nil { - cx.t.Fatalf("defragTest ctlV3Defrag error (%v)", err) - } -} - -func ctlV3Defrag(cx ctlCtx) error { - cmdArgs := append(cx.PrefixArgs(), "defrag") - lines := make([]string, cx.epc.cfg.clusterSize) - for i := range lines { - lines[i] = "Finished defragmenting etcd member" - } - return spawnWithExpects(cmdArgs, lines...) -} diff --git a/e2e/ctl_v3_elect_test.go b/e2e/ctl_v3_elect_test.go deleted file mode 100644 index 02c7090f7f92..000000000000 --- a/e2e/ctl_v3_elect_test.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2016 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 ( - "os" - "strings" - "testing" - "time" - - "github.com/coreos/etcd/pkg/expect" -) - -func TestCtlV3Elect(t *testing.T) { - oldenv := os.Getenv("EXPECT_DEBUG") - defer os.Setenv("EXPECT_DEBUG", oldenv) - os.Setenv("EXPECT_DEBUG", "1") - - testCtl(t, testElect) -} - -func testElect(cx ctlCtx) { - name := "a" - - holder, ch, err := ctlV3Elect(cx, name, "p1") - if err != nil { - cx.t.Fatal(err) - } - - l1 := "" - select { - case <-time.After(2 * time.Second): - cx.t.Fatalf("timed out electing") - case l1 = <-ch: - if !strings.HasPrefix(l1, name) { - cx.t.Errorf("got %q, expected %q prefix", l1, name) - } - } - - // blocked process that won't win the election - blocked, ch, err := ctlV3Elect(cx, name, "p2") - if err != nil { - cx.t.Fatal(err) - } - select { - case <-time.After(100 * time.Millisecond): - case <-ch: - cx.t.Fatalf("should block") - } - - // overlap with a blocker that will win the election - blockAcquire, ch, err := ctlV3Elect(cx, name, "p2") - if err != nil { - cx.t.Fatal(err) - } - defer blockAcquire.Stop() - select { - case <-time.After(100 * time.Millisecond): - case <-ch: - cx.t.Fatalf("should block") - } - - // kill blocked process with clean shutdown - if err = blocked.Signal(os.Interrupt); err != nil { - cx.t.Fatal(err) - } - if err = closeWithTimeout(blocked, time.Second); err != nil { - cx.t.Fatal(err) - } - - // kill the holder with clean shutdown - if err = holder.Signal(os.Interrupt); err != nil { - cx.t.Fatal(err) - } - if err = closeWithTimeout(holder, time.Second); err != nil { - cx.t.Fatal(err) - } - - // blockAcquire should win the election - select { - case <-time.After(time.Second): - cx.t.Fatalf("timed out from waiting to holding") - case l2 := <-ch: - if l1 == l2 || !strings.HasPrefix(l2, name) { - cx.t.Fatalf("expected different elect name, got l1=%q, l2=%q", l1, l2) - } - } -} - -// ctlV3Elect creates a elect process with a channel listening for when it wins the election. -func ctlV3Elect(cx ctlCtx, name, proposal string) (*expect.ExpectProcess, <-chan string, error) { - cmdArgs := append(cx.PrefixArgs(), "elect", name, proposal) - proc, err := spawnCmd(cmdArgs) - outc := make(chan string, 1) - if err != nil { - close(outc) - return proc, outc, err - } - go func() { - s, xerr := proc.ExpectFunc(func(string) bool { return true }) - if xerr != nil { - cx.t.Errorf("expect failed (%v)", xerr) - } - outc <- s - }() - return proc, outc, err -} diff --git a/e2e/ctl_v3_endpoint_test.go b/e2e/ctl_v3_endpoint_test.go deleted file mode 100644 index 821e77bb7bc9..000000000000 --- a/e2e/ctl_v3_endpoint_test.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2016 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 ( - "context" - "fmt" - "net/url" - "testing" - "time" - - "github.com/coreos/etcd/clientv3" -) - -func TestCtlV3EndpointHealth(t *testing.T) { testCtl(t, endpointHealthTest, withQuorum()) } -func TestCtlV3EndpointStatus(t *testing.T) { testCtl(t, endpointStatusTest, withQuorum()) } -func TestCtlV3EndpointHashKV(t *testing.T) { testCtl(t, endpointHashKVTest, withQuorum()) } - -func endpointHealthTest(cx ctlCtx) { - if err := ctlV3EndpointHealth(cx); err != nil { - cx.t.Fatalf("endpointStatusTest ctlV3EndpointHealth error (%v)", err) - } -} - -func ctlV3EndpointHealth(cx ctlCtx) error { - cmdArgs := append(cx.PrefixArgs(), "endpoint", "health") - lines := make([]string, cx.epc.cfg.clusterSize) - for i := range lines { - lines[i] = "is healthy" - } - return spawnWithExpects(cmdArgs, lines...) -} - -func endpointStatusTest(cx ctlCtx) { - if err := ctlV3EndpointStatus(cx); err != nil { - cx.t.Fatalf("endpointStatusTest ctlV3EndpointStatus error (%v)", err) - } -} - -func ctlV3EndpointStatus(cx ctlCtx) error { - cmdArgs := append(cx.PrefixArgs(), "endpoint", "status") - var eps []string - for _, ep := range cx.epc.EndpointsV3() { - u, _ := url.Parse(ep) - eps = append(eps, u.Host) - } - return spawnWithExpects(cmdArgs, eps...) -} - -func endpointHashKVTest(cx ctlCtx) { - if err := ctlV3EndpointHashKV(cx); err != nil { - cx.t.Fatalf("endpointHashKVTest ctlV3EndpointHashKV error (%v)", err) - } -} - -func ctlV3EndpointHashKV(cx ctlCtx) error { - eps := cx.epc.EndpointsV3() - - // get latest hash to compare - cli, err := clientv3.New(clientv3.Config{ - Endpoints: eps, - DialTimeout: 3 * time.Second, - }) - if err != nil { - cx.t.Fatal(err) - } - defer cli.Close() - hresp, err := cli.HashKV(context.TODO(), eps[0], 0) - if err != nil { - cx.t.Fatal(err) - } - - cmdArgs := append(cx.PrefixArgs(), "endpoint", "hashkv") - var ss []string - for _, ep := range cx.epc.EndpointsV3() { - u, _ := url.Parse(ep) - ss = append(ss, fmt.Sprintf("%s, %d", u.Host, hresp.Hash)) - } - return spawnWithExpects(cmdArgs, ss...) -} diff --git a/e2e/ctl_v3_kv_test.go b/e2e/ctl_v3_kv_test.go index 2e14bea6416a..6ff7b39de437 100644 --- a/e2e/ctl_v3_kv_test.go +++ b/e2e/ctl_v3_kv_test.go @@ -16,222 +16,15 @@ package e2e import ( "fmt" - "strings" + "os" "testing" ) -func TestCtlV3Put(t *testing.T) { testCtl(t, putTest) } -func TestCtlV3PutNoTLS(t *testing.T) { testCtl(t, putTest, withCfg(configNoTLS)) } -func TestCtlV3PutClientTLS(t *testing.T) { testCtl(t, putTest, withCfg(configClientTLS)) } -func TestCtlV3PutClientAutoTLS(t *testing.T) { testCtl(t, putTest, withCfg(configClientAutoTLS)) } -func TestCtlV3PutPeerTLS(t *testing.T) { testCtl(t, putTest, withCfg(configPeerTLS)) } -func TestCtlV3PutTimeout(t *testing.T) { testCtl(t, putTest, withDialTimeout(0)) } -func TestCtlV3PutClientTLSFlagByEnv(t *testing.T) { - testCtl(t, putTest, withCfg(configClientTLS), withFlagByEnv()) -} -func TestCtlV3PutIgnoreValue(t *testing.T) { testCtl(t, putTestIgnoreValue) } -func TestCtlV3PutIgnoreLease(t *testing.T) { testCtl(t, putTestIgnoreLease) } - -func TestCtlV3Get(t *testing.T) { testCtl(t, getTest) } -func TestCtlV3GetNoTLS(t *testing.T) { testCtl(t, getTest, withCfg(configNoTLS)) } -func TestCtlV3GetClientTLS(t *testing.T) { testCtl(t, getTest, withCfg(configClientTLS)) } -func TestCtlV3GetClientAutoTLS(t *testing.T) { testCtl(t, getTest, withCfg(configClientAutoTLS)) } -func TestCtlV3GetPeerTLS(t *testing.T) { testCtl(t, getTest, withCfg(configPeerTLS)) } -func TestCtlV3GetTimeout(t *testing.T) { testCtl(t, getTest, withDialTimeout(0)) } -func TestCtlV3GetQuorum(t *testing.T) { testCtl(t, getTest, withQuorum()) } - -func TestCtlV3GetFormat(t *testing.T) { testCtl(t, getFormatTest) } -func TestCtlV3GetRev(t *testing.T) { testCtl(t, getRevTest) } -func TestCtlV3GetKeysOnly(t *testing.T) { testCtl(t, getKeysOnlyTest) } - -func TestCtlV3Del(t *testing.T) { testCtl(t, delTest) } -func TestCtlV3DelNoTLS(t *testing.T) { testCtl(t, delTest, withCfg(configNoTLS)) } -func TestCtlV3DelClientTLS(t *testing.T) { testCtl(t, delTest, withCfg(configClientTLS)) } -func TestCtlV3DelPeerTLS(t *testing.T) { testCtl(t, delTest, withCfg(configPeerTLS)) } -func TestCtlV3DelTimeout(t *testing.T) { testCtl(t, delTest, withDialTimeout(0)) } - -func TestCtlV3GetRevokedCRL(t *testing.T) { - cfg := etcdProcessClusterConfig{ - clusterSize: 1, - initialToken: "new", - clientTLS: clientTLS, - isClientCRL: true, - clientCertAuthEnabled: true, - } - testCtl(t, testGetRevokedCRL, withCfg(cfg)) -} - -func testGetRevokedCRL(cx ctlCtx) { - // test reject - if err := ctlV3Put(cx, "k", "v", ""); err == nil || !strings.Contains(err.Error(), "Error:") { - cx.t.Fatalf("expected reset connection on put, got %v", err) - } - // test accept - cx.epc.cfg.isClientCRL = false - if err := ctlV3Put(cx, "k", "v", ""); err != nil { - cx.t.Fatal(err) - } -} - -func putTest(cx ctlCtx) { - key, value := "foo", "bar" - - if err := ctlV3Put(cx, key, value, ""); err != nil { - if cx.dialTimeout > 0 && !isGRPCTimedout(err) { - cx.t.Fatalf("putTest ctlV3Put error (%v)", err) - } - } - if err := ctlV3Get(cx, []string{key}, kv{key, value}); err != nil { - if cx.dialTimeout > 0 && !isGRPCTimedout(err) { - cx.t.Fatalf("putTest ctlV3Get error (%v)", err) - } - } -} - -func putTestIgnoreValue(cx ctlCtx) { - if err := ctlV3Put(cx, "foo", "bar", ""); err != nil { - cx.t.Fatal(err) - } - if err := ctlV3Get(cx, []string{"foo"}, kv{"foo", "bar"}); err != nil { - cx.t.Fatal(err) - } - if err := ctlV3Put(cx, "foo", "", "", "--ignore-value"); err != nil { - cx.t.Fatal(err) - } - if err := ctlV3Get(cx, []string{"foo"}, kv{"foo", "bar"}); err != nil { - cx.t.Fatal(err) - } -} - -func putTestIgnoreLease(cx ctlCtx) { - leaseID, err := ctlV3LeaseGrant(cx, 10) - if err != nil { - cx.t.Fatalf("putTestIgnoreLease: ctlV3LeaseGrant error (%v)", err) - } - if err := ctlV3Put(cx, "foo", "bar", leaseID); err != nil { - cx.t.Fatalf("putTestIgnoreLease: ctlV3Put error (%v)", err) - } - if err := ctlV3Get(cx, []string{"foo"}, kv{"foo", "bar"}); err != nil { - cx.t.Fatalf("putTestIgnoreLease: ctlV3Get error (%v)", err) - } - if err := ctlV3Put(cx, "foo", "bar1", "", "--ignore-lease"); err != nil { - cx.t.Fatalf("putTestIgnoreLease: ctlV3Put error (%v)", err) - } - if err := ctlV3Get(cx, []string{"foo"}, kv{"foo", "bar1"}); err != nil { - cx.t.Fatalf("putTestIgnoreLease: ctlV3Get error (%v)", err) - } - if err := ctlV3LeaseRevoke(cx, leaseID); err != nil { - cx.t.Fatalf("putTestIgnoreLease: ctlV3LeaseRevok error (%v)", err) - } - if err := ctlV3Get(cx, []string{"key"}); err != nil { // expect no output - cx.t.Fatalf("putTestIgnoreLease: ctlV3Get error (%v)", err) - } -} - -func getTest(cx ctlCtx) { - var ( - kvs = []kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}} - revkvs = []kv{{"key3", "val3"}, {"key2", "val2"}, {"key1", "val1"}} - ) - for i := range kvs { - if err := ctlV3Put(cx, kvs[i].key, kvs[i].val, ""); err != nil { - cx.t.Fatalf("getTest #%d: ctlV3Put error (%v)", i, err) - } - } - - tests := []struct { - args []string - - wkv []kv - }{ - {[]string{"key1"}, []kv{{"key1", "val1"}}}, - {[]string{"", "--prefix"}, kvs}, - {[]string{"", "--from-key"}, kvs}, - {[]string{"key", "--prefix"}, kvs}, - {[]string{"key", "--prefix", "--limit=2"}, kvs[:2]}, - {[]string{"key", "--prefix", "--order=ASCEND", "--sort-by=MODIFY"}, kvs}, - {[]string{"key", "--prefix", "--order=ASCEND", "--sort-by=VERSION"}, kvs}, - {[]string{"key", "--prefix", "--sort-by=CREATE"}, kvs}, // ASCEND by default - {[]string{"key", "--prefix", "--order=DESCEND", "--sort-by=CREATE"}, revkvs}, - {[]string{"key", "--prefix", "--order=DESCEND", "--sort-by=KEY"}, revkvs}, - } - for i, tt := range tests { - if err := ctlV3Get(cx, tt.args, tt.wkv...); err != nil { - if cx.dialTimeout > 0 && !isGRPCTimedout(err) { - cx.t.Errorf("getTest #%d: ctlV3Get error (%v)", i, err) - } - } - } -} - -func getFormatTest(cx ctlCtx) { - if err := ctlV3Put(cx, "abc", "123", ""); err != nil { - cx.t.Fatal(err) - } - - tests := []struct { - format string - valueOnly bool - - wstr string - }{ - {"simple", false, "abc"}, - {"simple", true, "123"}, - {"json", false, `"kvs":[{"key":"YWJj"`}, - {"protobuf", false, "\x17\b\x93\xe7\xf6\x93\xd4ņ\xe14\x10\xed"}, - } - - for i, tt := range tests { - cmdArgs := append(cx.PrefixArgs(), "get") - cmdArgs = append(cmdArgs, "--write-out="+tt.format) - if tt.valueOnly { - cmdArgs = append(cmdArgs, "--print-value-only") - } - cmdArgs = append(cmdArgs, "abc") - if err := spawnWithExpect(cmdArgs, tt.wstr); err != nil { - cx.t.Errorf("#%d: error (%v), wanted %v", i, err, tt.wstr) - } - } -} - -func getRevTest(cx ctlCtx) { - var ( - kvs = []kv{{"key", "val1"}, {"key", "val2"}, {"key", "val3"}} - ) - for i := range kvs { - if err := ctlV3Put(cx, kvs[i].key, kvs[i].val, ""); err != nil { - cx.t.Fatalf("getRevTest #%d: ctlV3Put error (%v)", i, err) - } - } - - tests := []struct { - args []string - - wkv []kv - }{ - {[]string{"key", "--rev", "2"}, kvs[:1]}, - {[]string{"key", "--rev", "3"}, kvs[1:2]}, - {[]string{"key", "--rev", "4"}, kvs[2:]}, - } - - for i, tt := range tests { - if err := ctlV3Get(cx, tt.args, tt.wkv...); err != nil { - cx.t.Errorf("getTest #%d: ctlV3Get error (%v)", i, err) - } - } -} - -func getKeysOnlyTest(cx ctlCtx) { - if err := ctlV3Put(cx, "key", "val", ""); err != nil { - cx.t.Fatal(err) - } - cmdArgs := append(cx.PrefixArgs(), []string{"get", "--keys-only", "key"}...) - if err := spawnWithExpect(cmdArgs, "key"); err != nil { - cx.t.Fatal(err) - } - if err := spawnWithExpects(cmdArgs, "val"); err == nil { - cx.t.Fatalf("got value but passed --keys-only") - } +func TestCtlV3DelTimeout(t *testing.T) { + oldenv := os.Getenv("EXPECT_DEBUG") + defer os.Setenv("EXPECT_DEBUG", oldenv) + os.Setenv("EXPECT_DEBUG", "1") + testCtl(t, delTest, withDialTimeout(0)) } func delTest(cx ctlCtx) { diff --git a/e2e/ctl_v3_lease_test.go b/e2e/ctl_v3_lease_test.go deleted file mode 100644 index 34571ac9f176..000000000000 --- a/e2e/ctl_v3_lease_test.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2016 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" - "strconv" - "strings" - "testing" -) - -func TestCtlV3LeaseGrantTimeToLive(t *testing.T) { testCtl(t, leaseTestGrantTimeToLive) } -func TestCtlV3LeaseGrantLeases(t *testing.T) { testCtl(t, leaseTestGrantLeasesList) } -func TestCtlV3LeaseKeepAlive(t *testing.T) { testCtl(t, leaseTestKeepAlive) } -func TestCtlV3LeaseRevoke(t *testing.T) { testCtl(t, leaseTestRevoke) } - -func leaseTestGrantTimeToLive(cx ctlCtx) { - id, err := ctlV3LeaseGrant(cx, 10) - if err != nil { - cx.t.Fatal(err) - } - - cmdArgs := append(cx.PrefixArgs(), "lease", "timetolive", id, "--keys") - proc, err := spawnCmd(cmdArgs) - if err != nil { - cx.t.Fatal(err) - } - line, err := proc.Expect(" granted with TTL(") - if err != nil { - cx.t.Fatal(err) - } - if err = proc.Close(); err != nil { - cx.t.Fatal(err) - } - if !strings.Contains(line, ", attached keys") { - cx.t.Fatalf("expected 'attached keys', got %q", line) - } - if !strings.Contains(line, id) { - cx.t.Fatalf("expected leaseID %q, got %q", id, line) - } -} - -func leaseTestGrantLeasesList(cx ctlCtx) { - id, err := ctlV3LeaseGrant(cx, 10) - if err != nil { - cx.t.Fatal(err) - } - - cmdArgs := append(cx.PrefixArgs(), "lease", "list") - proc, err := spawnCmd(cmdArgs) - if err != nil { - cx.t.Fatal(err) - } - _, err = proc.Expect(id) - if err != nil { - cx.t.Fatal(err) - } - if err = proc.Close(); err != nil { - cx.t.Fatal(err) - } -} - -func leaseTestKeepAlive(cx ctlCtx) { - // put with TTL 10 seconds and keep-alive - leaseID, err := ctlV3LeaseGrant(cx, 10) - if err != nil { - cx.t.Fatalf("leaseTestKeepAlive: ctlV3LeaseGrant error (%v)", err) - } - if err := ctlV3Put(cx, "key", "val", leaseID); err != nil { - cx.t.Fatalf("leaseTestKeepAlive: ctlV3Put error (%v)", err) - } - if err := ctlV3LeaseKeepAlive(cx, leaseID); err != nil { - cx.t.Fatalf("leaseTestKeepAlive: ctlV3LeaseKeepAlive error (%v)", err) - } - if err := ctlV3Get(cx, []string{"key"}, kv{"key", "val"}); err != nil { - cx.t.Fatalf("leaseTestKeepAlive: ctlV3Get error (%v)", err) - } -} - -func leaseTestRevoke(cx ctlCtx) { - // put with TTL 10 seconds and revoke - leaseID, err := ctlV3LeaseGrant(cx, 10) - if err != nil { - cx.t.Fatalf("leaseTestRevoke: ctlV3LeaseGrant error (%v)", err) - } - if err := ctlV3Put(cx, "key", "val", leaseID); err != nil { - cx.t.Fatalf("leaseTestRevoke: ctlV3Put error (%v)", err) - } - if err := ctlV3LeaseRevoke(cx, leaseID); err != nil { - cx.t.Fatalf("leaseTestRevoke: ctlV3LeaseRevok error (%v)", err) - } - if err := ctlV3Get(cx, []string{"key"}); err != nil { // expect no output - cx.t.Fatalf("leaseTestRevoke: ctlV3Get error (%v)", err) - } -} - -func ctlV3LeaseGrant(cx ctlCtx, ttl int) (string, error) { - cmdArgs := append(cx.PrefixArgs(), "lease", "grant", strconv.Itoa(ttl)) - proc, err := spawnCmd(cmdArgs) - if err != nil { - return "", err - } - - line, err := proc.Expect(" granted with TTL(") - if err != nil { - return "", err - } - if err = proc.Close(); err != nil { - return "", err - } - - // parse 'line LEASE_ID granted with TTL(5s)' to get lease ID - hs := strings.Split(line, " ") - if len(hs) < 2 { - return "", fmt.Errorf("lease grant failed with %q", line) - } - return hs[1], nil -} - -func ctlV3LeaseKeepAlive(cx ctlCtx, leaseID string) error { - cmdArgs := append(cx.PrefixArgs(), "lease", "keep-alive", leaseID) - - proc, err := spawnCmd(cmdArgs) - if err != nil { - return err - } - - if _, err = proc.Expect(fmt.Sprintf("lease %s keepalived with TTL(", leaseID)); err != nil { - return err - } - return proc.Stop() -} - -func ctlV3LeaseRevoke(cx ctlCtx, leaseID string) error { - cmdArgs := append(cx.PrefixArgs(), "lease", "revoke", leaseID) - return spawnWithExpect(cmdArgs, fmt.Sprintf("lease %s revoked", leaseID)) -} diff --git a/e2e/ctl_v3_lock_test.go b/e2e/ctl_v3_lock_test.go deleted file mode 100644 index 6bcbe4de2c57..000000000000 --- a/e2e/ctl_v3_lock_test.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2016 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 ( - "os" - "strings" - "testing" - "time" - - "github.com/coreos/etcd/pkg/expect" -) - -func TestCtlV3Lock(t *testing.T) { - oldenv := os.Getenv("EXPECT_DEBUG") - defer os.Setenv("EXPECT_DEBUG", oldenv) - os.Setenv("EXPECT_DEBUG", "1") - - testCtl(t, testLock) -} - -func testLock(cx ctlCtx) { - name := "a" - - holder, ch, err := ctlV3Lock(cx, name) - if err != nil { - cx.t.Fatal(err) - } - - l1 := "" - select { - case <-time.After(2 * time.Second): - cx.t.Fatalf("timed out locking") - case l1 = <-ch: - if !strings.HasPrefix(l1, name) { - cx.t.Errorf("got %q, expected %q prefix", l1, name) - } - } - - // blocked process that won't acquire the lock - blocked, ch, err := ctlV3Lock(cx, name) - if err != nil { - cx.t.Fatal(err) - } - select { - case <-time.After(100 * time.Millisecond): - case <-ch: - cx.t.Fatalf("should block") - } - - // overlap with a blocker that will acquire the lock - blockAcquire, ch, err := ctlV3Lock(cx, name) - if err != nil { - cx.t.Fatal(err) - } - defer blockAcquire.Stop() - select { - case <-time.After(100 * time.Millisecond): - case <-ch: - cx.t.Fatalf("should block") - } - - // kill blocked process with clean shutdown - if err = blocked.Signal(os.Interrupt); err != nil { - cx.t.Fatal(err) - } - if err = closeWithTimeout(blocked, time.Second); err != nil { - cx.t.Fatal(err) - } - - // kill the holder with clean shutdown - if err = holder.Signal(os.Interrupt); err != nil { - cx.t.Fatal(err) - } - if err = closeWithTimeout(holder, time.Second); err != nil { - cx.t.Fatal(err) - } - - // blockAcquire should acquire the lock - select { - case <-time.After(time.Second): - cx.t.Fatalf("timed out from waiting to holding") - case l2 := <-ch: - if l1 == l2 || !strings.HasPrefix(l2, name) { - cx.t.Fatalf("expected different lock name, got l1=%q, l2=%q", l1, l2) - } - } -} - -// ctlV3Lock creates a lock process with a channel listening for when it acquires the lock. -func ctlV3Lock(cx ctlCtx, name string) (*expect.ExpectProcess, <-chan string, error) { - cmdArgs := append(cx.PrefixArgs(), "lock", name) - proc, err := spawnCmd(cmdArgs) - outc := make(chan string, 1) - if err != nil { - close(outc) - return proc, outc, err - } - go func() { - s, xerr := proc.ExpectFunc(func(string) bool { return true }) - if xerr != nil { - cx.t.Errorf("expect failed (%v)", xerr) - } - outc <- s - }() - return proc, outc, err -} diff --git a/e2e/ctl_v3_make_mirror_test.go b/e2e/ctl_v3_make_mirror_test.go deleted file mode 100644 index 8bb92e1d9990..000000000000 --- a/e2e/ctl_v3_make_mirror_test.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2016 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" - "time" -) - -func TestCtlV3MakeMirror(t *testing.T) { testCtl(t, makeMirrorTest) } -func TestCtlV3MakeMirrorModifyDestPrefix(t *testing.T) { testCtl(t, makeMirrorModifyDestPrefixTest) } -func TestCtlV3MakeMirrorNoDestPrefix(t *testing.T) { testCtl(t, makeMirrorNoDestPrefixTest) } - -func makeMirrorTest(cx ctlCtx) { - var ( - flags = []string{} - kvs = []kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}} - prefix = "key" - ) - testMirrorCommand(cx, flags, kvs, kvs, prefix, prefix) -} - -func makeMirrorModifyDestPrefixTest(cx ctlCtx) { - var ( - flags = []string{"--prefix", "o_", "--dest-prefix", "d_"} - kvs = []kv{{"o_key1", "val1"}, {"o_key2", "val2"}, {"o_key3", "val3"}} - kvs2 = []kv{{"d_key1", "val1"}, {"d_key2", "val2"}, {"d_key3", "val3"}} - srcprefix = "o_" - destprefix = "d_" - ) - testMirrorCommand(cx, flags, kvs, kvs2, srcprefix, destprefix) -} - -func makeMirrorNoDestPrefixTest(cx ctlCtx) { - var ( - flags = []string{"--prefix", "o_", "--no-dest-prefix"} - kvs = []kv{{"o_key1", "val1"}, {"o_key2", "val2"}, {"o_key3", "val3"}} - kvs2 = []kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}} - srcprefix = "o_" - destprefix = "key" - ) - - testMirrorCommand(cx, flags, kvs, kvs2, srcprefix, destprefix) -} - -func testMirrorCommand(cx ctlCtx, flags []string, sourcekvs, destkvs []kv, srcprefix, destprefix string) { - // set up another cluster to mirror with - mirrorcfg := configAutoTLS - mirrorcfg.clusterSize = 1 - mirrorcfg.basePort = 10000 - mirrorctx := ctlCtx{ - t: cx.t, - cfg: mirrorcfg, - dialTimeout: 7 * time.Second, - } - - mirrorepc, err := newEtcdProcessCluster(&mirrorctx.cfg) - if err != nil { - cx.t.Fatalf("could not start etcd process cluster (%v)", err) - } - mirrorctx.epc = mirrorepc - - defer func() { - if err = mirrorctx.epc.Close(); err != nil { - cx.t.Fatalf("error closing etcd processes (%v)", err) - } - }() - - cmdArgs := append(cx.PrefixArgs(), "make-mirror") - cmdArgs = append(cmdArgs, flags...) - cmdArgs = append(cmdArgs, fmt.Sprintf("localhost:%d", mirrorcfg.basePort)) - proc, err := spawnCmd(cmdArgs) - if err != nil { - cx.t.Fatal(err) - } - defer func() { - err = proc.Stop() - if err != nil { - cx.t.Fatal(err) - } - }() - - for i := range sourcekvs { - if err = ctlV3Put(cx, sourcekvs[i].key, sourcekvs[i].val, ""); err != nil { - cx.t.Fatal(err) - } - } - if err = ctlV3Get(cx, []string{srcprefix, "--prefix"}, sourcekvs...); err != nil { - cx.t.Fatal(err) - } - - if err = ctlV3Watch(mirrorctx, []string{destprefix, "--rev", "1", "--prefix"}, destkvs...); err != nil { - cx.t.Fatal(err) - } -} diff --git a/e2e/ctl_v3_migrate_test.go b/e2e/ctl_v3_migrate_test.go deleted file mode 100644 index 1bad8c439205..000000000000 --- a/e2e/ctl_v3_migrate_test.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2016 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 ( - "context" - "fmt" - "os" - "testing" - "time" - - "github.com/coreos/etcd/clientv3" - "github.com/coreos/etcd/pkg/testutil" -) - -func TestCtlV3Migrate(t *testing.T) { - defer testutil.AfterTest(t) - - epc := setupEtcdctlTest(t, &configNoTLS, false) - defer func() { - if errC := epc.Close(); errC != nil { - t.Fatalf("error closing etcd processes (%v)", errC) - } - }() - - keys := make([]string, 3) - vals := make([]string, 3) - for i := range keys { - keys[i] = fmt.Sprintf("foo_%d", i) - vals[i] = fmt.Sprintf("bar_%d", i) - } - for i := range keys { - if err := etcdctlSet(epc, keys[i], vals[i]); err != nil { - t.Fatal(err) - } - } - - dataDir := epc.procs[0].Config().dataDirPath - if err := epc.Stop(); err != nil { - t.Fatalf("error closing etcd processes (%v)", err) - } - - os.Setenv("ETCDCTL_API", "3") - defer os.Unsetenv("ETCDCTL_API") - cx := ctlCtx{ - t: t, - cfg: configNoTLS, - dialTimeout: 7 * time.Second, - epc: epc, - } - if err := ctlV3Migrate(cx, dataDir, ""); err != nil { - t.Fatal(err) - } - - epc.procs[0].Config().keepDataDir = true - if err := epc.Restart(); err != nil { - t.Fatal(err) - } - - // to ensure revision increment is continuous from migrated v2 data - if err := ctlV3Put(cx, "test", "value", ""); err != nil { - t.Fatal(err) - } - cli, err := clientv3.New(clientv3.Config{ - Endpoints: epc.EndpointsV3(), - DialTimeout: 3 * time.Second, - }) - if err != nil { - t.Fatal(err) - } - defer cli.Close() - resp, err := cli.Get(context.TODO(), "test") - if err != nil { - t.Fatal(err) - } - if len(resp.Kvs) != 1 { - t.Fatalf("len(resp.Kvs) expected 1, got %+v", resp.Kvs) - } - if resp.Kvs[0].CreateRevision != 7 { - t.Fatalf("resp.Kvs[0].CreateRevision expected 7, got %d", resp.Kvs[0].CreateRevision) - } -} - -func ctlV3Migrate(cx ctlCtx, dataDir, walDir string) error { - cmdArgs := append(cx.PrefixArgs(), "migrate", "--data-dir", dataDir, "--wal-dir", walDir) - return spawnWithExpects(cmdArgs, "finished transforming keys") -} diff --git a/e2e/ctl_v3_move_leader_test.go b/e2e/ctl_v3_move_leader_test.go deleted file mode 100644 index eb2afda5f0c6..000000000000 --- a/e2e/ctl_v3_move_leader_test.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2017 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 ( - "context" - "fmt" - "os" - "testing" - "time" - - "github.com/coreos/etcd/clientv3" - "github.com/coreos/etcd/pkg/testutil" - "github.com/coreos/etcd/pkg/types" -) - -func TestCtlV3MoveLeader(t *testing.T) { - defer testutil.AfterTest(t) - - epc := setupEtcdctlTest(t, &configNoTLS, true) - defer func() { - if errC := epc.Close(); errC != nil { - t.Fatalf("error closing etcd processes (%v)", errC) - } - }() - - var leadIdx int - var leaderID uint64 - var transferee uint64 - for i, ep := range epc.EndpointsV3() { - cli, err := clientv3.New(clientv3.Config{ - Endpoints: []string{ep}, - DialTimeout: 3 * time.Second, - }) - if err != nil { - t.Fatal(err) - } - resp, err := cli.Status(context.Background(), ep) - if err != nil { - t.Fatal(err) - } - cli.Close() - - if resp.Header.GetMemberId() == resp.Leader { - leadIdx = i - leaderID = resp.Leader - } else { - transferee = resp.Header.GetMemberId() - } - } - - os.Setenv("ETCDCTL_API", "3") - defer os.Unsetenv("ETCDCTL_API") - cx := ctlCtx{ - t: t, - cfg: configNoTLS, - dialTimeout: 7 * time.Second, - epc: epc, - } - - tests := []struct { - prefixes []string - expect string - }{ - { // request to non-leader - cx.prefixArgs([]string{cx.epc.EndpointsV3()[(leadIdx+1)%3]}), - "no leader endpoint given at ", - }, - { // request to leader - cx.prefixArgs([]string{cx.epc.EndpointsV3()[leadIdx]}), - fmt.Sprintf("Leadership transferred from %s to %s", types.ID(leaderID), types.ID(transferee)), - }, - } - for i, tc := range tests { - cmdArgs := append(tc.prefixes, "move-leader", types.ID(transferee).String()) - if err := spawnWithExpect(cmdArgs, tc.expect); err != nil { - t.Fatalf("#%d: %v", i, err) - } - } -} diff --git a/e2e/ctl_v3_role_test.go b/e2e/ctl_v3_role_test.go deleted file mode 100644 index 340446a10a96..000000000000 --- a/e2e/ctl_v3_role_test.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2016 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" -) - -func TestCtlV3RoleAdd(t *testing.T) { testCtl(t, roleAddTest) } -func TestCtlV3RoleAddNoTLS(t *testing.T) { testCtl(t, roleAddTest, withCfg(configNoTLS)) } -func TestCtlV3RoleAddClientTLS(t *testing.T) { testCtl(t, roleAddTest, withCfg(configClientTLS)) } -func TestCtlV3RoleAddPeerTLS(t *testing.T) { testCtl(t, roleAddTest, withCfg(configPeerTLS)) } -func TestCtlV3RoleAddTimeout(t *testing.T) { testCtl(t, roleAddTest, withDialTimeout(0)) } - -func TestCtlV3RoleGrant(t *testing.T) { testCtl(t, roleGrantTest) } - -func roleAddTest(cx ctlCtx) { - cmdSet := []struct { - args []string - expectedStr string - }{ - // Add a role. - { - args: []string{"add", "root"}, - expectedStr: "Role root created", - }, - // Try adding the same role. - { - args: []string{"add", "root"}, - expectedStr: "role name already exists", - }, - } - - for i, cmd := range cmdSet { - if err := ctlV3Role(cx, cmd.args, cmd.expectedStr); err != nil { - if cx.dialTimeout > 0 && !isGRPCTimedout(err) { - cx.t.Fatalf("roleAddTest #%d: ctlV3Role error (%v)", i, err) - } - } - } -} - -func roleGrantTest(cx ctlCtx) { - cmdSet := []struct { - args []string - expectedStr string - }{ - // Add a role. - { - args: []string{"add", "root"}, - expectedStr: "Role root created", - }, - // Grant read permission to the role. - { - args: []string{"grant", "root", "read", "foo"}, - expectedStr: "Role root updated", - }, - // Grant write permission to the role. - { - args: []string{"grant", "root", "write", "foo"}, - expectedStr: "Role root updated", - }, - // Grant rw permission to the role. - { - args: []string{"grant", "root", "readwrite", "foo"}, - expectedStr: "Role root updated", - }, - // Try granting invalid permission to the role. - { - args: []string{"grant", "root", "123", "foo"}, - expectedStr: "invalid permission type", - }, - } - - for i, cmd := range cmdSet { - if err := ctlV3Role(cx, cmd.args, cmd.expectedStr); err != nil { - cx.t.Fatalf("roleGrantTest #%d: ctlV3Role error (%v)", i, err) - } - } -} - -func ctlV3Role(cx ctlCtx, args []string, expStr string) error { - cmdArgs := append(cx.PrefixArgs(), "role") - cmdArgs = append(cmdArgs, args...) - - return spawnWithExpect(cmdArgs, expStr) -} - -func ctlV3RoleGrantPermission(cx ctlCtx, rolename string, perm grantingPerm) error { - cmdArgs := append(cx.PrefixArgs(), "role", "grant-permission") - if perm.prefix { - cmdArgs = append(cmdArgs, "--prefix") - } else if len(perm.rangeEnd) == 1 && perm.rangeEnd[0] == '\x00' { - cmdArgs = append(cmdArgs, "--from-key") - } - - cmdArgs = append(cmdArgs, rolename) - cmdArgs = append(cmdArgs, grantingPermToArgs(perm)...) - - proc, err := spawnCmd(cmdArgs) - if err != nil { - return err - } - - expStr := fmt.Sprintf("Role %s updated", rolename) - _, err = proc.Expect(expStr) - return err -} - -func ctlV3RoleRevokePermission(cx ctlCtx, rolename string, key, rangeEnd string, fromKey bool) error { - cmdArgs := append(cx.PrefixArgs(), "role", "revoke-permission") - cmdArgs = append(cmdArgs, rolename) - cmdArgs = append(cmdArgs, key) - expStr := "" - if len(rangeEnd) != 0 { - cmdArgs = append(cmdArgs, rangeEnd) - expStr = fmt.Sprintf("Permission of range [%s, %s) is revoked from role %s", key, rangeEnd, rolename) - } else if fromKey { - cmdArgs = append(cmdArgs, "--from-key") - expStr = fmt.Sprintf("Permission of range [%s, is revoked from role %s", key, rolename) - } else { - expStr = fmt.Sprintf("Permission of key %s is revoked from role %s", key, rolename) - } - - proc, err := spawnCmd(cmdArgs) - if err != nil { - return err - } - - _, err = proc.Expect(expStr) - return err -} - -type grantingPerm struct { - read bool - write bool - key string - rangeEnd string - prefix bool -} - -func grantingPermToArgs(perm grantingPerm) []string { - permstr := "" - - if perm.read { - permstr += "read" - } - - if perm.write { - permstr += "write" - } - - if len(permstr) == 0 { - panic("invalid granting permission") - } - - if len(perm.rangeEnd) == 0 { - return []string{permstr, perm.key} - } - - if len(perm.rangeEnd) == 1 && perm.rangeEnd[0] == '\x00' { - return []string{permstr, perm.key} - } - - return []string{permstr, perm.key, perm.rangeEnd} -} diff --git a/e2e/ctl_v3_snapshot_test.go b/e2e/ctl_v3_snapshot_test.go deleted file mode 100644 index 234d5b037de7..000000000000 --- a/e2e/ctl_v3_snapshot_test.go +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright 2016 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 ( - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - "testing" - "time" - - "github.com/coreos/etcd/pkg/expect" - "github.com/coreos/etcd/pkg/testutil" -) - -func TestCtlV3Snapshot(t *testing.T) { testCtl(t, snapshotTest) } - -func snapshotTest(cx ctlCtx) { - maintenanceInitKeys(cx) - - leaseID, err := ctlV3LeaseGrant(cx, 100) - if err != nil { - cx.t.Fatalf("snapshot: ctlV3LeaseGrant error (%v)", err) - } - if err = ctlV3Put(cx, "withlease", "withlease", leaseID); err != nil { - cx.t.Fatalf("snapshot: ctlV3Put error (%v)", err) - } - - fpath := "test.snapshot" - defer os.RemoveAll(fpath) - - if err = ctlV3SnapshotSave(cx, fpath); err != nil { - cx.t.Fatalf("snapshotTest ctlV3SnapshotSave error (%v)", err) - } - - st, err := getSnapshotStatus(cx, fpath) - if err != nil { - cx.t.Fatalf("snapshotTest getSnapshotStatus error (%v)", err) - } - if st.Revision != 5 { - cx.t.Fatalf("expected 4, got %d", st.Revision) - } - if st.TotalKey < 4 { - cx.t.Fatalf("expected at least 4, got %d", st.TotalKey) - } -} - -func TestCtlV3SnapshotCorrupt(t *testing.T) { testCtl(t, snapshotCorruptTest) } - -func snapshotCorruptTest(cx ctlCtx) { - fpath := "test.snapshot" - defer os.RemoveAll(fpath) - - if err := ctlV3SnapshotSave(cx, fpath); err != nil { - cx.t.Fatalf("snapshotTest ctlV3SnapshotSave error (%v)", err) - } - - // corrupt file - f, oerr := os.OpenFile(fpath, os.O_WRONLY, 0) - if oerr != nil { - cx.t.Fatal(oerr) - } - if _, err := f.Write(make([]byte, 512)); err != nil { - cx.t.Fatal(err) - } - f.Close() - - defer os.RemoveAll("snap.etcd") - serr := spawnWithExpect( - append(cx.PrefixArgs(), "snapshot", "restore", - "--data-dir", "snap.etcd", - fpath), - "expected sha256") - - if serr != nil { - cx.t.Fatal(serr) - } -} - -func ctlV3SnapshotSave(cx ctlCtx, fpath string) error { - cmdArgs := append(cx.PrefixArgs(), "snapshot", "save", fpath) - return spawnWithExpect(cmdArgs, fmt.Sprintf("Snapshot saved at %s", fpath)) -} - -type snapshotStatus struct { - Hash uint32 `json:"hash"` - Revision int64 `json:"revision"` - TotalKey int `json:"totalKey"` - TotalSize int64 `json:"totalSize"` -} - -func getSnapshotStatus(cx ctlCtx, fpath string) (snapshotStatus, error) { - cmdArgs := append(cx.PrefixArgs(), "--write-out", "json", "snapshot", "status", fpath) - - proc, err := spawnCmd(cmdArgs) - if err != nil { - return snapshotStatus{}, err - } - var txt string - txt, err = proc.Expect("totalKey") - if err != nil { - return snapshotStatus{}, err - } - if err = proc.Close(); err != nil { - return snapshotStatus{}, err - } - - resp := snapshotStatus{} - dec := json.NewDecoder(strings.NewReader(txt)) - if err := dec.Decode(&resp); err == io.EOF { - return snapshotStatus{}, err - } - return resp, nil -} - -// TestIssue6361 ensures new member that starts with snapshot correctly -// syncs up with other members and serve correct data. -func TestIssue6361(t *testing.T) { - defer testutil.AfterTest(t) - mustEtcdctl(t) - os.Setenv("ETCDCTL_API", "3") - defer os.Unsetenv("ETCDCTL_API") - - epc, err := newEtcdProcessCluster(&etcdProcessClusterConfig{ - clusterSize: 1, - initialToken: "new", - keepDataDir: true, - }) - if err != nil { - t.Fatalf("could not start etcd process cluster (%v)", err) - } - defer func() { - if errC := epc.Close(); errC != nil { - t.Fatalf("error closing etcd processes (%v)", errC) - } - }() - - dialTimeout := 7 * time.Second - prefixArgs := []string{ctlBinPath, "--endpoints", strings.Join(epc.EndpointsV3(), ","), "--dial-timeout", dialTimeout.String()} - - // write some keys - kvs := []kv{{"foo1", "val1"}, {"foo2", "val2"}, {"foo3", "val3"}} - for i := range kvs { - if err = spawnWithExpect(append(prefixArgs, "put", kvs[i].key, kvs[i].val), "OK"); err != nil { - t.Fatal(err) - } - } - - fpath := filepath.Join(os.TempDir(), "test.snapshot") - defer os.RemoveAll(fpath) - - // etcdctl save snapshot - if err = spawnWithExpect(append(prefixArgs, "snapshot", "save", fpath), fmt.Sprintf("Snapshot saved at %s", fpath)); err != nil { - t.Fatal(err) - } - - if err = epc.procs[0].Stop(); err != nil { - t.Fatal(err) - } - - newDataDir := filepath.Join(os.TempDir(), "test.data") - defer os.RemoveAll(newDataDir) - - // etcdctl restore the snapshot - err = spawnWithExpect([]string{ctlBinPath, "snapshot", "restore", fpath, "--name", epc.procs[0].Config().name, "--initial-cluster", epc.procs[0].Config().initialCluster, "--initial-cluster-token", epc.procs[0].Config().initialToken, "--initial-advertise-peer-urls", epc.procs[0].Config().purl.String(), "--data-dir", newDataDir}, "membership: added member") - if err != nil { - t.Fatal(err) - } - - // start the etcd member using the restored snapshot - epc.procs[0].Config().dataDirPath = newDataDir - for i := range epc.procs[0].Config().args { - if epc.procs[0].Config().args[i] == "--data-dir" { - epc.procs[0].Config().args[i+1] = newDataDir - } - } - if err = epc.procs[0].Restart(); err != nil { - t.Fatal(err) - } - - // ensure the restored member has the correct data - for i := range kvs { - if err = spawnWithExpect(append(prefixArgs, "get", kvs[i].key), kvs[i].val); err != nil { - t.Fatal(err) - } - } - - // add a new member into the cluster - clientURL := fmt.Sprintf("http://localhost:%d", etcdProcessBasePort+30) - peerURL := fmt.Sprintf("http://localhost:%d", etcdProcessBasePort+31) - err = spawnWithExpect(append(prefixArgs, "member", "add", "newmember", fmt.Sprintf("--peer-urls=%s", peerURL)), " added to cluster ") - if err != nil { - t.Fatal(err) - } - - var newDataDir2 string - newDataDir2, err = ioutil.TempDir("", "newdata2") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(newDataDir2) - - name2 := "infra2" - initialCluster2 := epc.procs[0].Config().initialCluster + fmt.Sprintf(",%s=%s", name2, peerURL) - - // start the new member - var nepc *expect.ExpectProcess - nepc, err = spawnCmd([]string{epc.procs[0].Config().execPath, "--name", name2, - "--listen-client-urls", clientURL, "--advertise-client-urls", clientURL, - "--listen-peer-urls", peerURL, "--initial-advertise-peer-urls", peerURL, - "--initial-cluster", initialCluster2, "--initial-cluster-state", "existing", "--data-dir", newDataDir2}) - if err != nil { - t.Fatal(err) - } - if _, err = nepc.Expect("enabled capabilities for version"); err != nil { - t.Fatal(err) - } - - prefixArgs = []string{ctlBinPath, "--endpoints", clientURL, "--dial-timeout", dialTimeout.String()} - - // ensure added member has data from incoming snapshot - for i := range kvs { - if err = spawnWithExpect(append(prefixArgs, "get", kvs[i].key), kvs[i].val); err != nil { - t.Fatal(err) - } - } - - if err = nepc.Stop(); err != nil { - t.Fatal(err) - } -} diff --git a/e2e/ctl_v3_txn_test.go b/e2e/ctl_v3_txn_test.go deleted file mode 100644 index 960e7c06bd00..000000000000 --- a/e2e/ctl_v3_txn_test.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2016 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 "testing" - -func TestCtlV3TxnInteractiveSuccess(t *testing.T) { - testCtl(t, txnTestSuccess, withInteractive()) -} -func TestCtlV3TxnInteractiveSuccessNoTLS(t *testing.T) { - testCtl(t, txnTestSuccess, withInteractive(), withCfg(configNoTLS)) -} -func TestCtlV3TxnInteractiveSuccessClientTLS(t *testing.T) { - testCtl(t, txnTestSuccess, withInteractive(), withCfg(configClientTLS)) -} -func TestCtlV3TxnInteractiveSuccessPeerTLS(t *testing.T) { - testCtl(t, txnTestSuccess, withInteractive(), withCfg(configPeerTLS)) -} -func TestCtlV3TxnInteractiveFail(t *testing.T) { - testCtl(t, txnTestFail, withInteractive()) -} - -func txnTestSuccess(cx ctlCtx) { - if err := ctlV3Put(cx, "key1", "value1", ""); err != nil { - cx.t.Fatalf("txnTestSuccess ctlV3Put error (%v)", err) - } - if err := ctlV3Put(cx, "key2", "value2", ""); err != nil { - cx.t.Fatalf("txnTestSuccess ctlV3Put error (%v)", err) - } - rqs := []txnRequests{ - { - compare: []string{`value("key1") != "value2"`, `value("key2") != "value1"`}, - ifSucess: []string{"get key1", "get key2"}, - results: []string{"SUCCESS", "key1", "value1", "key2", "value2"}, - }, - { - compare: []string{`version("key1") = "1"`, `version("key2") = "1"`}, - ifSucess: []string{"get key1", "get key2", `put "key \"with\" space" "value \x23"`}, - ifFail: []string{`put key1 "fail"`, `put key2 "fail"`}, - results: []string{"SUCCESS", "key1", "value1", "key2", "value2"}, - }, - { - compare: []string{`version("key \"with\" space") = "1"`}, - ifSucess: []string{`get "key \"with\" space"`}, - results: []string{"SUCCESS", `key "with" space`, "value \x23"}, - }, - } - for _, rq := range rqs { - if err := ctlV3Txn(cx, rq); err != nil { - cx.t.Fatal(err) - } - } -} - -func txnTestFail(cx ctlCtx) { - if err := ctlV3Put(cx, "key1", "value1", ""); err != nil { - cx.t.Fatalf("txnTestSuccess ctlV3Put error (%v)", err) - } - rqs := []txnRequests{ - { - compare: []string{`version("key") < "0"`}, - ifSucess: []string{`put key "success"`}, - ifFail: []string{`put key "fail"`}, - results: []string{"FAILURE", "OK"}, - }, - { - compare: []string{`value("key1") != "value1"`}, - ifSucess: []string{`put key1 "success"`}, - ifFail: []string{`put key1 "fail"`}, - results: []string{"FAILURE", "OK"}, - }, - } - for _, rq := range rqs { - if err := ctlV3Txn(cx, rq); err != nil { - cx.t.Fatal(err) - } - } -} - -type txnRequests struct { - compare []string - ifSucess []string - ifFail []string - results []string -} - -func ctlV3Txn(cx ctlCtx, rqs txnRequests) error { - // TODO: support non-interactive mode - cmdArgs := append(cx.PrefixArgs(), "txn") - if cx.interactive { - cmdArgs = append(cmdArgs, "--interactive") - } - proc, err := spawnCmd(cmdArgs) - if err != nil { - return err - } - _, err = proc.Expect("compares:") - if err != nil { - return err - } - for _, req := range rqs.compare { - if err = proc.Send(req + "\r"); err != nil { - return err - } - } - if err = proc.Send("\r"); err != nil { - return err - } - - _, err = proc.Expect("success requests (get, put, del):") - if err != nil { - return err - } - for _, req := range rqs.ifSucess { - if err = proc.Send(req + "\r"); err != nil { - return err - } - } - if err = proc.Send("\r"); err != nil { - return err - } - - _, err = proc.Expect("failure requests (get, put, del):") - if err != nil { - return err - } - for _, req := range rqs.ifFail { - if err = proc.Send(req + "\r"); err != nil { - return err - } - } - if err = proc.Send("\r"); err != nil { - return err - } - - for _, line := range rqs.results { - _, err = proc.Expect(line) - if err != nil { - return err - } - } - return proc.Close() -} diff --git a/e2e/ctl_v3_user_test.go b/e2e/ctl_v3_user_test.go deleted file mode 100644 index 392ab4e42df0..000000000000 --- a/e2e/ctl_v3_user_test.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2016 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 "testing" - -func TestCtlV3UserAdd(t *testing.T) { testCtl(t, userAddTest) } -func TestCtlV3UserAddNoTLS(t *testing.T) { testCtl(t, userAddTest, withCfg(configNoTLS)) } -func TestCtlV3UserAddClientTLS(t *testing.T) { testCtl(t, userAddTest, withCfg(configClientTLS)) } -func TestCtlV3UserAddPeerTLS(t *testing.T) { testCtl(t, userAddTest, withCfg(configPeerTLS)) } -func TestCtlV3UserAddTimeout(t *testing.T) { testCtl(t, userAddTest, withDialTimeout(0)) } - -func TestCtlV3UserDelete(t *testing.T) { testCtl(t, userDelTest) } -func TestCtlV3UserPasswd(t *testing.T) { testCtl(t, userPasswdTest) } - -type userCmdDesc struct { - args []string - expectedStr string - stdIn []string -} - -func userAddTest(cx ctlCtx) { - cmdSet := []userCmdDesc{ - // Adds a user name. - { - args: []string{"add", "username", "--interactive=false"}, - expectedStr: "User username created", - stdIn: []string{"password"}, - }, - // Adds a user name using the usertest:password syntax. - { - args: []string{"add", "usertest:password"}, - expectedStr: "User usertest created", - stdIn: []string{}, - }, - // Tries to add a user with empty username. - { - args: []string{"add", ":password"}, - expectedStr: "empty user name is not allowed.", - stdIn: []string{}, - }, - // Tries to add a user name that already exists. - { - args: []string{"add", "username", "--interactive=false"}, - expectedStr: "user name already exists", - stdIn: []string{"password"}, - }, - } - - for i, cmd := range cmdSet { - if err := ctlV3User(cx, cmd.args, cmd.expectedStr, cmd.stdIn); err != nil { - if cx.dialTimeout > 0 && !isGRPCTimedout(err) { - cx.t.Fatalf("userAddTest #%d: ctlV3User error (%v)", i, err) - } - } - } -} - -func userDelTest(cx ctlCtx) { - cmdSet := []userCmdDesc{ - // Adds a user name. - { - args: []string{"add", "username", "--interactive=false"}, - expectedStr: "User username created", - stdIn: []string{"password"}, - }, - // Deletes the user name just added. - { - args: []string{"delete", "username"}, - expectedStr: "User username deleted", - }, - // Deletes a user name that is not present. - { - args: []string{"delete", "username"}, - expectedStr: "user name not found", - }, - } - - for i, cmd := range cmdSet { - if err := ctlV3User(cx, cmd.args, cmd.expectedStr, cmd.stdIn); err != nil { - cx.t.Fatalf("userDelTest #%d: ctlV3User error (%v)", i, err) - } - } -} - -func userPasswdTest(cx ctlCtx) { - cmdSet := []userCmdDesc{ - // Adds a user name. - { - args: []string{"add", "username", "--interactive=false"}, - expectedStr: "User username created", - stdIn: []string{"password"}, - }, - // Changes the password. - { - args: []string{"passwd", "username", "--interactive=false"}, - expectedStr: "Password updated", - stdIn: []string{"password1"}, - }, - } - - for i, cmd := range cmdSet { - if err := ctlV3User(cx, cmd.args, cmd.expectedStr, cmd.stdIn); err != nil { - cx.t.Fatalf("userPasswdTest #%d: ctlV3User error (%v)", i, err) - } - } -} - -func ctlV3User(cx ctlCtx, args []string, expStr string, stdIn []string) error { - cmdArgs := append(cx.PrefixArgs(), "user") - cmdArgs = append(cmdArgs, args...) - - proc, err := spawnCmd(cmdArgs) - if err != nil { - return err - } - - // Send 'stdIn' strings as input. - for _, s := range stdIn { - if err = proc.Send(s + "\r"); err != nil { - return err - } - } - - _, err = proc.Expect(expStr) - return err -} diff --git a/e2e/ctl_v3_watch_test.go b/e2e/ctl_v3_watch_test.go deleted file mode 100644 index bc9d64ac9704..000000000000 --- a/e2e/ctl_v3_watch_test.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2016 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 ( - "strings" - "testing" -) - -func TestCtlV3Watch(t *testing.T) { testCtl(t, watchTest) } -func TestCtlV3WatchNoTLS(t *testing.T) { testCtl(t, watchTest, withCfg(configNoTLS)) } -func TestCtlV3WatchClientTLS(t *testing.T) { testCtl(t, watchTest, withCfg(configClientTLS)) } -func TestCtlV3WatchPeerTLS(t *testing.T) { testCtl(t, watchTest, withCfg(configPeerTLS)) } -func TestCtlV3WatchTimeout(t *testing.T) { testCtl(t, watchTest, withDialTimeout(0)) } - -func TestCtlV3WatchInteractive(t *testing.T) { - testCtl(t, watchTest, withInteractive()) -} -func TestCtlV3WatchInteractiveNoTLS(t *testing.T) { - testCtl(t, watchTest, withInteractive(), withCfg(configNoTLS)) -} -func TestCtlV3WatchInteractiveClientTLS(t *testing.T) { - testCtl(t, watchTest, withInteractive(), withCfg(configClientTLS)) -} -func TestCtlV3WatchInteractivePeerTLS(t *testing.T) { - testCtl(t, watchTest, withInteractive(), withCfg(configPeerTLS)) -} - -func watchTest(cx ctlCtx) { - tests := []struct { - puts []kv - args []string - - wkv []kv - }{ - { // watch 1 key - []kv{{"sample", "value"}}, - []string{"sample", "--rev", "1"}, - []kv{{"sample", "value"}}, - }, - { // watch 3 keys by prefix - []kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}}, - []string{"key", "--rev", "1", "--prefix"}, - []kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}}, - }, - { // watch by revision - []kv{{"etcd", "revision_1"}, {"etcd", "revision_2"}, {"etcd", "revision_3"}}, - []string{"etcd", "--rev", "2"}, - []kv{{"etcd", "revision_2"}, {"etcd", "revision_3"}}, - }, - { // watch 3 keys by range - []kv{{"key1", "val1"}, {"key3", "val3"}, {"key2", "val2"}}, - []string{"key", "key3", "--rev", "1"}, - []kv{{"key1", "val1"}, {"key2", "val2"}}, - }, - } - - for i, tt := range tests { - donec := make(chan struct{}) - go func(i int, puts []kv) { - for j := range puts { - if err := ctlV3Put(cx, puts[j].key, puts[j].val, ""); err != nil { - cx.t.Fatalf("watchTest #%d-%d: ctlV3Put error (%v)", i, j, err) - } - } - close(donec) - }(i, tt.puts) - if err := ctlV3Watch(cx, tt.args, tt.wkv...); err != nil { - if cx.dialTimeout > 0 && !isGRPCTimedout(err) { - cx.t.Errorf("watchTest #%d: ctlV3Watch error (%v)", i, err) - } - } - <-donec - } -} - -func setupWatchArgs(cx ctlCtx, args []string) []string { - cmdArgs := append(cx.PrefixArgs(), "watch") - if cx.interactive { - cmdArgs = append(cmdArgs, "--interactive") - } else { - cmdArgs = append(cmdArgs, args...) - } - - return cmdArgs -} - -func ctlV3Watch(cx ctlCtx, args []string, kvs ...kv) error { - cmdArgs := setupWatchArgs(cx, args) - - proc, err := spawnCmd(cmdArgs) - if err != nil { - return err - } - - if cx.interactive { - wl := strings.Join(append([]string{"watch"}, args...), " ") + "\r" - if err = proc.Send(wl); err != nil { - return err - } - } - - for _, elem := range kvs { - if _, err = proc.Expect(elem.key); err != nil { - return err - } - if _, err = proc.Expect(elem.val); err != nil { - return err - } - } - return proc.Stop() -} - -func ctlV3WatchFailPerm(cx ctlCtx, args []string) error { - cmdArgs := setupWatchArgs(cx, args) - - proc, err := spawnCmd(cmdArgs) - if err != nil { - return err - } - - if cx.interactive { - wl := strings.Join(append([]string{"watch"}, args...), " ") + "\r" - if err = proc.Send(wl); err != nil { - return err - } - } - - // TODO(mitake): after printing accurate error message that includes - // "permission denied", the above string argument of proc.Expect() - // should be updated. - _, err = proc.Expect("watch is canceled by the server") - if err != nil { - return err - } - return proc.Close() -} diff --git a/e2e/etcd_release_upgrade_test.go b/e2e/etcd_release_upgrade_test.go deleted file mode 100644 index 6b1d42323e78..000000000000 --- a/e2e/etcd_release_upgrade_test.go +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2016 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" - "os" - "sync" - "testing" - "time" - - "github.com/coreos/etcd/pkg/fileutil" - "github.com/coreos/etcd/pkg/testutil" - "github.com/coreos/etcd/version" -) - -// TestReleaseUpgrade ensures that changes to master branch does not affect -// upgrade from latest etcd releases. -func TestReleaseUpgrade(t *testing.T) { - lastReleaseBinary := binDir + "/etcd-last-release" - if !fileutil.Exist(lastReleaseBinary) { - t.Skipf("%q does not exist", lastReleaseBinary) - } - - defer testutil.AfterTest(t) - - copiedCfg := configNoTLS - copiedCfg.execPath = lastReleaseBinary - copiedCfg.snapCount = 3 - copiedCfg.baseScheme = "unix" // to avoid port conflict - - epc, err := newEtcdProcessCluster(&copiedCfg) - if err != nil { - t.Fatalf("could not start etcd process cluster (%v)", err) - } - defer func() { - if errC := epc.Close(); errC != nil { - t.Fatalf("error closing etcd processes (%v)", errC) - } - }() - // 3.0 boots as 2.3 then negotiates up to 3.0 - // so there's a window at boot time where it doesn't have V3rpcCapability enabled - // poll /version until etcdcluster is >2.3.x before making v3 requests - for i := 0; i < 7; i++ { - if err = cURLGet(epc, cURLReq{endpoint: "/version", expected: `"etcdcluster":"` + version.Cluster(version.Version)}); err != nil { - t.Logf("#%d: v3 is not ready yet (%v)", i, err) - time.Sleep(time.Second) - continue - } - break - } - if err != nil { - t.Fatalf("cannot pull version (%v)", err) - } - - os.Setenv("ETCDCTL_API", "3") - defer os.Unsetenv("ETCDCTL_API") - cx := ctlCtx{ - t: t, - cfg: configNoTLS, - dialTimeout: 7 * time.Second, - quorum: true, - epc: epc, - } - var kvs []kv - for i := 0; i < 5; i++ { - kvs = append(kvs, kv{key: fmt.Sprintf("foo%d", i), val: "bar"}) - } - for i := range kvs { - if err := ctlV3Put(cx, kvs[i].key, kvs[i].val, ""); err != nil { - cx.t.Fatalf("#%d: ctlV3Put error (%v)", i, err) - } - } - - for i := range epc.procs { - if err := epc.procs[i].Stop(); err != nil { - t.Fatalf("#%d: error closing etcd process (%v)", i, err) - } - epc.procs[i].Config().execPath = binDir + "/etcd" - epc.procs[i].Config().keepDataDir = true - - if err := epc.procs[i].Restart(); err != nil { - t.Fatalf("error restarting etcd process (%v)", err) - } - - for j := range kvs { - if err := ctlV3Get(cx, []string{kvs[j].key}, []kv{kvs[j]}...); err != nil { - cx.t.Fatalf("#%d-%d: ctlV3Get error (%v)", i, j, err) - } - } - } -} - -func TestReleaseUpgradeWithRestart(t *testing.T) { - lastReleaseBinary := binDir + "/etcd-last-release" - if !fileutil.Exist(lastReleaseBinary) { - t.Skipf("%q does not exist", lastReleaseBinary) - } - - defer testutil.AfterTest(t) - - copiedCfg := configNoTLS - copiedCfg.execPath = lastReleaseBinary - copiedCfg.snapCount = 10 - copiedCfg.baseScheme = "unix" - - epc, err := newEtcdProcessCluster(&copiedCfg) - if err != nil { - t.Fatalf("could not start etcd process cluster (%v)", err) - } - defer func() { - if errC := epc.Close(); errC != nil { - t.Fatalf("error closing etcd processes (%v)", errC) - } - }() - - os.Setenv("ETCDCTL_API", "3") - defer os.Unsetenv("ETCDCTL_API") - cx := ctlCtx{ - t: t, - cfg: configNoTLS, - dialTimeout: 7 * time.Second, - quorum: true, - epc: epc, - } - var kvs []kv - for i := 0; i < 50; i++ { - kvs = append(kvs, kv{key: fmt.Sprintf("foo%d", i), val: "bar"}) - } - for i := range kvs { - if err := ctlV3Put(cx, kvs[i].key, kvs[i].val, ""); err != nil { - cx.t.Fatalf("#%d: ctlV3Put error (%v)", i, err) - } - } - - for i := range epc.procs { - if err := epc.procs[i].Stop(); err != nil { - t.Fatalf("#%d: error closing etcd process (%v)", i, err) - } - } - - var wg sync.WaitGroup - wg.Add(len(epc.procs)) - for i := range epc.procs { - go func(i int) { - epc.procs[i].Config().execPath = binDir + "/etcd" - epc.procs[i].Config().keepDataDir = true - if err := epc.procs[i].Restart(); err != nil { - t.Fatalf("error restarting etcd process (%v)", err) - } - wg.Done() - }(i) - } - wg.Wait() - - if err := ctlV3Get(cx, []string{kvs[0].key}, []kv{kvs[0]}...); err != nil { - t.Fatal(err) - } -} diff --git a/e2e/metrics_test.go b/e2e/metrics_test.go deleted file mode 100644 index d5f13c6804d0..000000000000 --- a/e2e/metrics_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2017 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 ( - "testing" -) - -func TestV3MetricsSecure(t *testing.T) { - cfg := configTLS - cfg.clusterSize = 1 - cfg.metricsURLScheme = "https" - testCtl(t, metricsTest) -} - -func TestV3MetricsInsecure(t *testing.T) { - cfg := configTLS - cfg.clusterSize = 1 - cfg.metricsURLScheme = "http" - testCtl(t, metricsTest) -} - -func metricsTest(cx ctlCtx) { - if err := ctlV3Put(cx, "k", "v", ""); err != nil { - cx.t.Fatal(err) - } - if err := cURLGet(cx.epc, cURLReq{endpoint: "/metrics", expected: `etcd_debugging_mvcc_keys_total 1`, metricsURLScheme: cx.cfg.metricsURLScheme}); err != nil { - cx.t.Fatalf("failed get with curl (%v)", err) - } - if err := cURLGet(cx.epc, cURLReq{endpoint: "/health", expected: `{"health":true}`, metricsURLScheme: cx.cfg.metricsURLScheme}); err != nil { - cx.t.Fatalf("failed get with curl (%v)", err) - } -} diff --git a/e2e/v2_curl_test.go b/e2e/v2_curl_test.go deleted file mode 100644 index 9f350a3e39b0..000000000000 --- a/e2e/v2_curl_test.go +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2016 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" - "math/rand" - "strings" - "testing" - - "github.com/coreos/etcd/pkg/testutil" -) - -func TestV2CurlNoTLS(t *testing.T) { testCurlPutGet(t, &configNoTLS) } -func TestV2CurlAutoTLS(t *testing.T) { testCurlPutGet(t, &configAutoTLS) } -func TestV2CurlAllTLS(t *testing.T) { testCurlPutGet(t, &configTLS) } -func TestV2CurlPeerTLS(t *testing.T) { testCurlPutGet(t, &configPeerTLS) } -func TestV2CurlClientTLS(t *testing.T) { testCurlPutGet(t, &configClientTLS) } -func TestV2CurlClientBoth(t *testing.T) { testCurlPutGet(t, &configClientBoth) } -func testCurlPutGet(t *testing.T, cfg *etcdProcessClusterConfig) { - defer testutil.AfterTest(t) - - // test doesn't use quorum gets, so ensure there are no followers to avoid - // stale reads that will break the test - cfg = configStandalone(*cfg) - - epc, err := newEtcdProcessCluster(cfg) - if err != nil { - t.Fatalf("could not start etcd process cluster (%v)", err) - } - defer func() { - if err := epc.Close(); err != nil { - t.Fatalf("error closing etcd processes (%v)", err) - } - }() - - var ( - expectPut = `{"action":"set","node":{"key":"/foo","value":"bar","` - expectGet = `{"action":"get","node":{"key":"/foo","value":"bar","` - ) - if err := cURLPut(epc, cURLReq{endpoint: "/v2/keys/foo", value: "bar", expected: expectPut}); err != nil { - t.Fatalf("failed put with curl (%v)", err) - } - if err := cURLGet(epc, cURLReq{endpoint: "/v2/keys/foo", expected: expectGet}); err != nil { - t.Fatalf("failed get with curl (%v)", err) - } - if cfg.clientTLS == clientTLSAndNonTLS { - if err := cURLGet(epc, cURLReq{endpoint: "/v2/keys/foo", expected: expectGet, isTLS: true}); err != nil { - t.Fatalf("failed get with curl (%v)", err) - } - } -} - -func TestV2CurlIssue5182(t *testing.T) { - defer testutil.AfterTest(t) - - epc := setupEtcdctlTest(t, &configNoTLS, false) - defer func() { - if err := epc.Close(); err != nil { - t.Fatalf("error closing etcd processes (%v)", err) - } - }() - - expectPut := `{"action":"set","node":{"key":"/foo","value":"bar","` - if err := cURLPut(epc, cURLReq{endpoint: "/v2/keys/foo", value: "bar", expected: expectPut}); err != nil { - t.Fatal(err) - } - - expectUserAdd := `{"user":"foo","roles":null}` - if err := cURLPut(epc, cURLReq{endpoint: "/v2/auth/users/foo", value: `{"user":"foo", "password":"pass"}`, expected: expectUserAdd}); err != nil { - t.Fatal(err) - } - expectRoleAdd := `{"role":"foo","permissions":{"kv":{"read":["/foo/*"],"write":null}}` - if err := cURLPut(epc, cURLReq{endpoint: "/v2/auth/roles/foo", value: `{"role":"foo", "permissions": {"kv": {"read": ["/foo/*"]}}}`, expected: expectRoleAdd}); err != nil { - t.Fatal(err) - } - expectUserUpdate := `{"user":"foo","roles":["foo"]}` - if err := cURLPut(epc, cURLReq{endpoint: "/v2/auth/users/foo", value: `{"user": "foo", "grant": ["foo"]}`, expected: expectUserUpdate}); err != nil { - t.Fatal(err) - } - - if err := etcdctlUserAdd(epc, "root", "a"); err != nil { - t.Fatal(err) - } - if err := etcdctlAuthEnable(epc); err != nil { - t.Fatal(err) - } - - if err := cURLGet(epc, cURLReq{endpoint: "/v2/keys/foo/", username: "root", password: "a", expected: "bar"}); err != nil { - t.Fatal(err) - } - if err := cURLGet(epc, cURLReq{endpoint: "/v2/keys/foo/", username: "foo", password: "pass", expected: "bar"}); err != nil { - t.Fatal(err) - } - if err := cURLGet(epc, cURLReq{endpoint: "/v2/keys/foo/", username: "foo", password: "", expected: "bar"}); err != nil { - if !strings.Contains(err.Error(), `The request requires user authentication`) { - t.Fatalf("expected 'The request requires user authentication' error, got %v", err) - } - } else { - t.Fatalf("expected 'The request requires user authentication' error") - } -} - -type cURLReq struct { - username string - password string - - isTLS bool - timeout int - - endpoint string - - value string - expected string - header string - - metricsURLScheme string -} - -// cURLPrefixArgs builds the beginning of a curl command for a given key -// addressed to a random URL in the given cluster. -func cURLPrefixArgs(clus *etcdProcessCluster, method string, req cURLReq) []string { - var ( - cmdArgs = []string{"curl"} - acurl = clus.procs[rand.Intn(clus.cfg.clusterSize)].Config().acurl - ) - if req.metricsURLScheme != "https" { - if req.isTLS { - if clus.cfg.clientTLS != clientTLSAndNonTLS { - panic("should not use cURLPrefixArgsUseTLS when serving only TLS or non-TLS") - } - cmdArgs = append(cmdArgs, "--cacert", caPath, "--cert", certPath, "--key", privateKeyPath) - acurl = toTLS(clus.procs[rand.Intn(clus.cfg.clusterSize)].Config().acurl) - } else if clus.cfg.clientTLS == clientTLS { - cmdArgs = append(cmdArgs, "--cacert", caPath, "--cert", certPath, "--key", privateKeyPath) - } - } - if req.metricsURLScheme != "" { - acurl = clus.procs[rand.Intn(clus.cfg.clusterSize)].EndpointsMetrics()[0] - } - ep := acurl + req.endpoint - - if req.username != "" || req.password != "" { - cmdArgs = append(cmdArgs, "-L", "-u", fmt.Sprintf("%s:%s", req.username, req.password), ep) - } else { - cmdArgs = append(cmdArgs, "-L", ep) - } - if req.timeout != 0 { - cmdArgs = append(cmdArgs, "-m", fmt.Sprintf("%d", req.timeout)) - } - - if req.header != "" { - cmdArgs = append(cmdArgs, "-H", req.header) - } - - switch method { - case "POST", "PUT": - dt := req.value - if !strings.HasPrefix(dt, "{") { // for non-JSON value - dt = "value=" + dt - } - cmdArgs = append(cmdArgs, "-X", method, "-d", dt) - } - return cmdArgs -} - -func cURLPost(clus *etcdProcessCluster, req cURLReq) error { - return spawnWithExpect(cURLPrefixArgs(clus, "POST", req), req.expected) -} - -func cURLPut(clus *etcdProcessCluster, req cURLReq) error { - return spawnWithExpect(cURLPrefixArgs(clus, "PUT", req), req.expected) -} - -func cURLGet(clus *etcdProcessCluster, req cURLReq) error { - return spawnWithExpect(cURLPrefixArgs(clus, "GET", req), req.expected) -} diff --git a/e2e/v2v3_test.go b/e2e/v2v3_test.go deleted file mode 100644 index b9c69466a450..000000000000 --- a/e2e/v2v3_test.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2017 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. - -// +build v2v3 - -package e2e - -func addV2Args(args []string) []string { - return append(args, "--experimental-enable-v2v3", "v2/") -} diff --git a/e2e/v3_curl_test.go b/e2e/v3_curl_test.go deleted file mode 100644 index a15fa214213a..000000000000 --- a/e2e/v3_curl_test.go +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright 2016 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 ( - "encoding/json" - "testing" - - pb "github.com/coreos/etcd/etcdserver/etcdserverpb" - "github.com/coreos/etcd/pkg/testutil" - - "github.com/grpc-ecosystem/grpc-gateway/runtime" -) - -func TestV3CurlPutGetNoTLS(t *testing.T) { testCurlPutGetGRPCGateway(t, &configNoTLS) } -func TestV3CurlPutGetAutoTLS(t *testing.T) { testCurlPutGetGRPCGateway(t, &configAutoTLS) } -func TestV3CurlPutGetAllTLS(t *testing.T) { testCurlPutGetGRPCGateway(t, &configTLS) } -func TestV3CurlPutGetPeerTLS(t *testing.T) { testCurlPutGetGRPCGateway(t, &configPeerTLS) } -func TestV3CurlPutGetClientTLS(t *testing.T) { testCurlPutGetGRPCGateway(t, &configClientTLS) } -func testCurlPutGetGRPCGateway(t *testing.T, cfg *etcdProcessClusterConfig) { - defer testutil.AfterTest(t) - - epc, err := newEtcdProcessCluster(cfg) - if err != nil { - t.Fatalf("could not start etcd process cluster (%v)", err) - } - defer func() { - if cerr := epc.Close(); err != nil { - t.Fatalf("error closing etcd processes (%v)", cerr) - } - }() - - var ( - key = []byte("foo") - value = []byte("bar") // this will be automatically base64-encoded by Go - - expectPut = `"revision":"` - expectGet = `"value":"` - ) - putData, err := json.Marshal(&pb.PutRequest{ - Key: key, - Value: value, - }) - if err != nil { - t.Fatal(err) - } - rangeData, err := json.Marshal(&pb.RangeRequest{ - Key: key, - }) - if err != nil { - t.Fatal(err) - } - - if err := cURLPost(epc, cURLReq{endpoint: "/v3alpha/kv/put", value: string(putData), expected: expectPut}); err != nil { - t.Fatalf("failed put with curl (%v)", err) - } - if err := cURLPost(epc, cURLReq{endpoint: "/v3alpha/kv/range", value: string(rangeData), expected: expectGet}); err != nil { - t.Fatalf("failed get with curl (%v)", err) - } - - if cfg.clientTLS == clientTLSAndNonTLS { - if err := cURLPost(epc, cURLReq{endpoint: "/v3alpha/kv/range", value: string(rangeData), expected: expectGet, isTLS: true}); err != nil { - t.Fatalf("failed get with curl (%v)", err) - } - } -} - -func TestV3CurlWatch(t *testing.T) { - defer testutil.AfterTest(t) - - epc, err := newEtcdProcessCluster(&configNoTLS) - if err != nil { - t.Fatalf("could not start etcd process cluster (%v)", err) - } - defer func() { - if cerr := epc.Close(); err != nil { - t.Fatalf("error closing etcd processes (%v)", cerr) - } - }() - - // store "bar" into "foo" - putreq, err := json.Marshal(&pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")}) - if err != nil { - t.Fatal(err) - } - if err = cURLPost(epc, cURLReq{endpoint: "/v3alpha/kv/put", value: string(putreq), expected: "revision"}); err != nil { - t.Fatalf("failed put with curl (%v)", err) - } - // watch for first update to "foo" - wcr := &pb.WatchCreateRequest{Key: []byte("foo"), StartRevision: 1} - wreq, err := json.Marshal(wcr) - if err != nil { - t.Fatal(err) - } - // marshaling the grpc to json gives: - // "{"RequestUnion":{"CreateRequest":{"key":"Zm9v","start_revision":1}}}" - // but the gprc-gateway expects a different format.. - wstr := `{"create_request" : ` + string(wreq) + "}" - // expects "bar", timeout after 2 seconds since stream waits forever - if err = cURLPost(epc, cURLReq{endpoint: "/v3alpha/watch", value: wstr, expected: `"YmFy"`, timeout: 2}); err != nil { - t.Fatal(err) - } -} - -func TestV3CurlTxn(t *testing.T) { - defer testutil.AfterTest(t) - epc, err := newEtcdProcessCluster(&configNoTLS) - if err != nil { - t.Fatalf("could not start etcd process cluster (%v)", err) - } - defer func() { - if cerr := epc.Close(); err != nil { - t.Fatalf("error closing etcd processes (%v)", cerr) - } - }() - - txn := &pb.TxnRequest{ - Compare: []*pb.Compare{ - { - Key: []byte("foo"), - Result: pb.Compare_EQUAL, - Target: pb.Compare_CREATE, - TargetUnion: &pb.Compare_CreateRevision{0}, - }, - }, - Success: []*pb.RequestOp{ - { - Request: &pb.RequestOp_RequestPut{ - RequestPut: &pb.PutRequest{ - Key: []byte("foo"), - Value: []byte("bar"), - }, - }, - }, - }, - } - m := &runtime.JSONPb{} - jsonDat, jerr := m.Marshal(txn) - if jerr != nil { - t.Fatal(jerr) - } - expected := `"succeeded":true,"responses":[{"response_put":{"header":{"revision":"2"}}}]` - if err = cURLPost(epc, cURLReq{endpoint: "/v3alpha/kv/txn", value: string(jsonDat), expected: expected}); err != nil { - t.Fatalf("failed txn with curl (%v)", err) - } - - // was crashing etcd server - malformed := `{"compare":[{"result":0,"target":1,"key":"Zm9v","TargetUnion":null}],"success":[{"Request":{"RequestPut":{"key":"Zm9v","value":"YmFy"}}}]}` - if err = cURLPost(epc, cURLReq{endpoint: "/v3alpha/kv/txn", value: malformed, expected: "error"}); err != nil { - t.Fatalf("failed put with curl (%v)", err) - } -} - -func TestV3CurlAuth(t *testing.T) { - defer testutil.AfterTest(t) - epc, err := newEtcdProcessCluster(&configNoTLS) - if err != nil { - t.Fatalf("could not start etcd process cluster (%v)", err) - } - defer func() { - if cerr := epc.Close(); err != nil { - t.Fatalf("error closing etcd processes (%v)", cerr) - } - }() - - // create root user - userreq, err := json.Marshal(&pb.AuthUserAddRequest{Name: string("root"), Password: string("toor")}) - testutil.AssertNil(t, err) - - if err = cURLPost(epc, cURLReq{endpoint: "/v3alpha/auth/user/add", value: string(userreq), expected: "revision"}); err != nil { - t.Fatalf("failed add user with curl (%v)", err) - } - - // create root role - rolereq, err := json.Marshal(&pb.AuthRoleAddRequest{Name: string("root")}) - testutil.AssertNil(t, err) - - if err = cURLPost(epc, cURLReq{endpoint: "/v3alpha/auth/role/add", value: string(rolereq), expected: "revision"}); err != nil { - t.Fatalf("failed create role with curl (%v)", err) - } - - // grant root role - grantrolereq, err := json.Marshal(&pb.AuthUserGrantRoleRequest{User: string("root"), Role: string("root")}) - testutil.AssertNil(t, err) - - if err = cURLPost(epc, cURLReq{endpoint: "/v3alpha/auth/user/grant", value: string(grantrolereq), expected: "revision"}); err != nil { - t.Fatalf("failed grant role with curl (%v)", err) - } - - // enable auth - if err = cURLPost(epc, cURLReq{endpoint: "/v3alpha/auth/enable", value: string("{}"), expected: "revision"}); err != nil { - t.Fatalf("failed enable auth with curl (%v)", err) - } - - // put "bar" into "foo" - putreq, err := json.Marshal(&pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")}) - testutil.AssertNil(t, err) - - // fail put no auth - if err = cURLPost(epc, cURLReq{endpoint: "/v3alpha/kv/put", value: string(putreq), expected: "error"}); err != nil { - t.Fatalf("failed no auth put with curl (%v)", err) - } - - // auth request - authreq, err := json.Marshal(&pb.AuthenticateRequest{Name: string("root"), Password: string("toor")}) - testutil.AssertNil(t, err) - - var ( - authHeader string - cmdArgs []string - lineFunc = func(txt string) bool { return true } - ) - - cmdArgs = cURLPrefixArgs(epc, "POST", cURLReq{endpoint: "/v3alpha/auth/authenticate", value: string(authreq)}) - proc, err := spawnCmd(cmdArgs) - testutil.AssertNil(t, err) - - cURLRes, err := proc.ExpectFunc(lineFunc) - testutil.AssertNil(t, err) - - authRes := make(map[string]interface{}) - testutil.AssertNil(t, json.Unmarshal([]byte(cURLRes), &authRes)) - - token, ok := authRes["token"].(string) - if !ok { - t.Fatalf("failed invalid token in authenticate response with curl") - } - - authHeader = "Authorization : " + token - - // put with auth - if err = cURLPost(epc, cURLReq{endpoint: "/v3alpha/kv/put", value: string(putreq), header: authHeader, expected: "revision"}); err != nil { - t.Fatalf("failed auth put with curl (%v)", err) - } - -}