Add vendor folder to git

This commit is contained in:
Lucas Käldström 2017-06-26 19:23:05 +03:00
parent 66cf5eaafb
commit 183585f56f
No known key found for this signature in database
GPG key ID: 600FEFBBD0D40D21
6916 changed files with 2629581 additions and 1 deletions

473
vendor/github.com/coreos/etcd/e2e/ctl_v2_test.go generated vendored Normal file
View file

@ -0,0 +1,473 @@
// 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 (
"io/ioutil"
"os"
"strings"
"testing"
"time"
"github.com/coreos/etcd/pkg/fileutil"
"github.com/coreos/etcd/pkg/testutil"
)
func TestCtlV2Set(t *testing.T) { testCtlV2Set(t, &configNoTLS, false) }
func TestCtlV2SetQuorum(t *testing.T) { testCtlV2Set(t, &configNoTLS, true) }
func TestCtlV2SetClientTLS(t *testing.T) { testCtlV2Set(t, &configClientTLS, false) }
func TestCtlV2SetPeerTLS(t *testing.T) { testCtlV2Set(t, &configPeerTLS, false) }
func TestCtlV2SetTLS(t *testing.T) { testCtlV2Set(t, &configTLS, false) }
func testCtlV2Set(t *testing.T, cfg *etcdProcessClusterConfig, quorum bool) {
defer testutil.AfterTest(t)
epc := setupEtcdctlTest(t, cfg, quorum)
defer func() {
if errC := epc.Close(); errC != nil {
t.Fatalf("error closing etcd processes (%v)", errC)
}
}()
key, value := "foo", "bar"
if err := etcdctlSet(epc, key, value); err != nil {
t.Fatalf("failed set (%v)", err)
}
if err := etcdctlGet(epc, key, value, quorum); err != nil {
t.Fatalf("failed get (%v)", err)
}
}
func TestCtlV2Mk(t *testing.T) { testCtlV2Mk(t, &configNoTLS, false) }
func TestCtlV2MkQuorum(t *testing.T) { testCtlV2Mk(t, &configNoTLS, true) }
func TestCtlV2MkTLS(t *testing.T) { testCtlV2Mk(t, &configTLS, false) }
func testCtlV2Mk(t *testing.T, cfg *etcdProcessClusterConfig, quorum bool) {
defer testutil.AfterTest(t)
epc := setupEtcdctlTest(t, cfg, quorum)
defer func() {
if errC := epc.Close(); errC != nil {
t.Fatalf("error closing etcd processes (%v)", errC)
}
}()
key, value := "foo", "bar"
if err := etcdctlMk(epc, key, value, true); err != nil {
t.Fatalf("failed mk (%v)", err)
}
if err := etcdctlMk(epc, key, value, false); err != nil {
t.Fatalf("failed mk (%v)", err)
}
if err := etcdctlGet(epc, key, value, quorum); err != nil {
t.Fatalf("failed get (%v)", err)
}
}
func TestCtlV2Rm(t *testing.T) { testCtlV2Rm(t, &configNoTLS) }
func TestCtlV2RmTLS(t *testing.T) { testCtlV2Rm(t, &configTLS) }
func testCtlV2Rm(t *testing.T, cfg *etcdProcessClusterConfig) {
defer testutil.AfterTest(t)
epc := setupEtcdctlTest(t, cfg, true)
defer func() {
if errC := epc.Close(); errC != nil {
t.Fatalf("error closing etcd processes (%v)", errC)
}
}()
key, value := "foo", "bar"
if err := etcdctlSet(epc, key, value); err != nil {
t.Fatalf("failed set (%v)", err)
}
if err := etcdctlRm(epc, key, value, true); err != nil {
t.Fatalf("failed rm (%v)", err)
}
if err := etcdctlRm(epc, key, value, false); err != nil {
t.Fatalf("failed rm (%v)", err)
}
}
func TestCtlV2Ls(t *testing.T) { testCtlV2Ls(t, &configNoTLS, false) }
func TestCtlV2LsQuorum(t *testing.T) { testCtlV2Ls(t, &configNoTLS, true) }
func TestCtlV2LsTLS(t *testing.T) { testCtlV2Ls(t, &configTLS, false) }
func testCtlV2Ls(t *testing.T, cfg *etcdProcessClusterConfig, quorum bool) {
defer testutil.AfterTest(t)
epc := setupEtcdctlTest(t, cfg, quorum)
defer func() {
if errC := epc.Close(); errC != nil {
t.Fatalf("error closing etcd processes (%v)", errC)
}
}()
key, value := "foo", "bar"
if err := etcdctlSet(epc, key, value); err != nil {
t.Fatalf("failed set (%v)", err)
}
if err := etcdctlLs(epc, key, quorum); err != nil {
t.Fatalf("failed ls (%v)", err)
}
}
func TestCtlV2Watch(t *testing.T) { testCtlV2Watch(t, &configNoTLS, false) }
func TestCtlV2WatchTLS(t *testing.T) { testCtlV2Watch(t, &configTLS, false) }
func TestCtlV2WatchWithProxy(t *testing.T) { testCtlV2Watch(t, &configWithProxy, false) }
func TestCtlV2WatchWithProxyNoSync(t *testing.T) { testCtlV2Watch(t, &configWithProxy, true) }
func testCtlV2Watch(t *testing.T, cfg *etcdProcessClusterConfig, noSync bool) {
defer testutil.AfterTest(t)
epc := setupEtcdctlTest(t, cfg, true)
defer func() {
if errC := epc.Close(); errC != nil {
t.Fatalf("error closing etcd processes (%v)", errC)
}
}()
key, value := "foo", "bar"
errc := etcdctlWatch(epc, key, value, noSync)
if err := etcdctlSet(epc, key, value); err != nil {
t.Fatalf("failed set (%v)", err)
}
select {
case err := <-errc:
if err != nil {
t.Fatalf("failed watch (%v)", err)
}
case <-time.After(5 * time.Second):
t.Fatalf("watch timed out")
}
}
func TestCtlV2GetRoleUser(t *testing.T) { testCtlV2GetRoleUser(t, &configNoTLS) }
func TestCtlV2GetRoleUserWithProxy(t *testing.T) { testCtlV2GetRoleUser(t, &configWithProxy) }
func testCtlV2GetRoleUser(t *testing.T, cfg *etcdProcessClusterConfig) {
defer testutil.AfterTest(t)
epc := setupEtcdctlTest(t, cfg, true)
defer func() {
if err := epc.Close(); err != nil {
t.Fatalf("error closing etcd processes (%v)", err)
}
}()
if err := etcdctlRoleAdd(epc, "foo"); err != nil {
t.Fatalf("failed to add role (%v)", err)
}
if err := etcdctlUserAdd(epc, "username", "password"); err != nil {
t.Fatalf("failed to add user (%v)", err)
}
if err := etcdctlUserGrant(epc, "username", "foo"); err != nil {
t.Fatalf("failed to grant role (%v)", err)
}
if err := etcdctlUserGet(epc, "username"); err != nil {
t.Fatalf("failed to get user (%v)", err)
}
// ensure double grant gives an error; was crashing in 2.3.1
regrantArgs := etcdctlPrefixArgs(epc)
regrantArgs = append(regrantArgs, "user", "grant", "--roles", "foo", "username")
if err := spawnWithExpect(regrantArgs, "duplicate"); err != nil {
t.Fatalf("missing duplicate error on double grant role (%v)", err)
}
}
func TestCtlV2UserListUsername(t *testing.T) { testCtlV2UserList(t, "username") }
func TestCtlV2UserListRoot(t *testing.T) { testCtlV2UserList(t, "root") }
func testCtlV2UserList(t *testing.T, username string) {
defer testutil.AfterTest(t)
epc := setupEtcdctlTest(t, &configWithProxy, false)
defer func() {
if err := epc.Close(); err != nil {
t.Fatalf("error closing etcd processes (%v)", err)
}
}()
if err := etcdctlUserAdd(epc, username, "password"); err != nil {
t.Fatalf("failed to add user (%v)", err)
}
if err := etcdctlUserList(epc, username); err != nil {
t.Fatalf("failed to list users (%v)", err)
}
}
func TestCtlV2RoleList(t *testing.T) {
defer testutil.AfterTest(t)
epc := setupEtcdctlTest(t, &configWithProxy, false)
defer func() {
if err := epc.Close(); err != nil {
t.Fatalf("error closing etcd processes (%v)", err)
}
}()
if err := etcdctlRoleAdd(epc, "foo"); err != nil {
t.Fatalf("failed to add role (%v)", err)
}
if err := etcdctlRoleList(epc, "foo"); err != nil {
t.Fatalf("failed to list roles (%v)", err)
}
}
func TestCtlV2Backup(t *testing.T) { // For https://github.com/coreos/etcd/issues/5360
defer testutil.AfterTest(t)
backupDir, err := ioutil.TempDir("", "testbackup0.etcd")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(backupDir)
epc1 := setupEtcdctlTest(t, &configNoTLS, false)
if err := etcdctlSet(epc1, "foo1", "bar"); err != nil {
t.Fatal(err)
}
if err := etcdctlBackup(epc1, epc1.procs[0].cfg.dataDirPath, backupDir); err != nil {
t.Fatal(err)
}
if err := epc1.Close(); err != nil {
t.Fatalf("error closing etcd processes (%v)", err)
}
// restart from the backup directory
cfg2 := configNoTLS
cfg2.dataDirPath = backupDir
cfg2.keepDataDir = true
cfg2.forceNewCluster = true
epc2 := setupEtcdctlTest(t, &cfg2, false)
// check if backup went through correctly
if err := etcdctlGet(epc2, "foo1", "bar", false); err != nil {
t.Fatal(err)
}
// check if it can serve client requests
if err := etcdctlSet(epc2, "foo2", "bar"); err != nil {
t.Fatal(err)
}
if err := etcdctlGet(epc2, "foo2", "bar", false); err != nil {
t.Fatal(err)
}
if err := epc2.Close(); err != nil {
t.Fatalf("error closing etcd processes (%v)", err)
}
}
func TestCtlV2AuthWithCommonName(t *testing.T) {
defer testutil.AfterTest(t)
copiedCfg := configClientTLS
copiedCfg.clientCertAuthEnabled = true
epc := setupEtcdctlTest(t, &copiedCfg, false)
defer func() {
if err := epc.Close(); err != nil {
t.Fatalf("error closing etcd processes (%v)", err)
}
}()
if err := etcdctlRoleAdd(epc, "testrole"); err != nil {
t.Fatalf("failed to add role (%v)", err)
}
if err := etcdctlRoleGrant(epc, "testrole", "--rw", "--path=/foo"); err != nil {
t.Fatalf("failed to grant role (%v)", err)
}
if err := etcdctlUserAdd(epc, "root", "123"); err != nil {
t.Fatalf("failed to add user (%v)", err)
}
if err := etcdctlUserAdd(epc, "Autogenerated CA", "123"); err != nil {
t.Fatalf("failed to add user (%v)", err)
}
if err := etcdctlUserGrant(epc, "Autogenerated CA", "testrole"); err != nil {
t.Fatalf("failed to grant role (%v)", err)
}
if err := etcdctlAuthEnable(epc); err != nil {
t.Fatalf("failed to enable auth (%v)", err)
}
if err := etcdctlSet(epc, "foo", "bar"); err != nil {
t.Fatalf("failed to write (%v)", err)
}
}
func TestCtlV2ClusterHealth(t *testing.T) {
defer testutil.AfterTest(t)
epc := setupEtcdctlTest(t, &configNoTLS, true)
defer func() {
if err := epc.Close(); err != nil {
t.Fatalf("error closing etcd processes (%v)", err)
}
}()
// has quorum
if err := etcdctlClusterHealth(epc, "cluster is healthy"); err != nil {
t.Fatalf("cluster-health expected to be healthy (%v)", err)
}
// cut quorum
epc.procs[0].Stop()
epc.procs[1].Stop()
if err := etcdctlClusterHealth(epc, "cluster is unhealthy"); err != nil {
t.Fatalf("cluster-health expected to be unhealthy (%v)", err)
}
epc.procs[0], epc.procs[1] = nil, nil
}
func etcdctlPrefixArgs(clus *etcdProcessCluster) []string {
endpoints := ""
if proxies := clus.proxies(); len(proxies) != 0 {
endpoints = proxies[0].cfg.acurl
} else if processes := clus.processes(); len(processes) != 0 {
es := []string{}
for _, b := range processes {
es = append(es, b.cfg.acurl)
}
endpoints = strings.Join(es, ",")
}
cmdArgs := []string{ctlBinPath, "--endpoints", endpoints}
if clus.cfg.clientTLS == clientTLS {
cmdArgs = append(cmdArgs, "--ca-file", caPath, "--cert-file", certPath, "--key-file", privateKeyPath)
}
return cmdArgs
}
func etcdctlClusterHealth(clus *etcdProcessCluster, val string) error {
cmdArgs := append(etcdctlPrefixArgs(clus), "cluster-health")
return spawnWithExpect(cmdArgs, val)
}
func etcdctlSet(clus *etcdProcessCluster, key, value string) error {
cmdArgs := append(etcdctlPrefixArgs(clus), "set", key, value)
return spawnWithExpect(cmdArgs, value)
}
func etcdctlMk(clus *etcdProcessCluster, key, value string, first bool) error {
cmdArgs := append(etcdctlPrefixArgs(clus), "mk", key, value)
if first {
return spawnWithExpect(cmdArgs, value)
}
return spawnWithExpect(cmdArgs, "Error: 105: Key already exists")
}
func etcdctlGet(clus *etcdProcessCluster, key, value string, quorum bool) error {
cmdArgs := append(etcdctlPrefixArgs(clus), "get", key)
if quorum {
cmdArgs = append(cmdArgs, "--quorum")
}
return spawnWithExpect(cmdArgs, value)
}
func etcdctlRm(clus *etcdProcessCluster, key, value string, first bool) error {
cmdArgs := append(etcdctlPrefixArgs(clus), "rm", key)
if first {
return spawnWithExpect(cmdArgs, "PrevNode.Value: "+value)
}
return spawnWithExpect(cmdArgs, "Error: 100: Key not found")
}
func etcdctlLs(clus *etcdProcessCluster, key string, quorum bool) error {
cmdArgs := append(etcdctlPrefixArgs(clus), "ls")
if quorum {
cmdArgs = append(cmdArgs, "--quorum")
}
return spawnWithExpect(cmdArgs, key)
}
func etcdctlWatch(clus *etcdProcessCluster, key, value string, noSync bool) <-chan error {
cmdArgs := append(etcdctlPrefixArgs(clus), "watch", "--after-index=1", key)
if noSync {
cmdArgs = append(cmdArgs, "--no-sync")
}
errc := make(chan error, 1)
go func() {
errc <- spawnWithExpect(cmdArgs, value)
}()
return errc
}
func etcdctlRoleAdd(clus *etcdProcessCluster, role string) error {
cmdArgs := append(etcdctlPrefixArgs(clus), "role", "add", role)
return spawnWithExpect(cmdArgs, role)
}
func etcdctlRoleGrant(clus *etcdProcessCluster, role string, perms ...string) error {
cmdArgs := append(etcdctlPrefixArgs(clus), "role", "grant")
cmdArgs = append(cmdArgs, perms...)
cmdArgs = append(cmdArgs, role)
return spawnWithExpect(cmdArgs, role)
}
func etcdctlRoleList(clus *etcdProcessCluster, expectedRole string) error {
cmdArgs := append(etcdctlPrefixArgs(clus), "role", "list")
return spawnWithExpect(cmdArgs, expectedRole)
}
func etcdctlUserAdd(clus *etcdProcessCluster, user, pass string) error {
cmdArgs := append(etcdctlPrefixArgs(clus), "user", "add", user+":"+pass)
return spawnWithExpect(cmdArgs, "User "+user+" created")
}
func etcdctlUserGrant(clus *etcdProcessCluster, user, role string) error {
cmdArgs := append(etcdctlPrefixArgs(clus), "user", "grant", "--roles", role, user)
return spawnWithExpect(cmdArgs, "User "+user+" updated")
}
func etcdctlUserGet(clus *etcdProcessCluster, user string) error {
cmdArgs := append(etcdctlPrefixArgs(clus), "user", "get", user)
return spawnWithExpect(cmdArgs, "User: "+user)
}
func etcdctlUserList(clus *etcdProcessCluster, expectedUser string) error {
cmdArgs := append(etcdctlPrefixArgs(clus), "user", "list")
return spawnWithExpect(cmdArgs, expectedUser)
}
func etcdctlAuthEnable(clus *etcdProcessCluster) error {
cmdArgs := append(etcdctlPrefixArgs(clus), "auth", "enable")
return spawnWithExpect(cmdArgs, "Authentication Enabled")
}
func etcdctlBackup(clus *etcdProcessCluster, dataDir, backupDir string) error {
cmdArgs := append(etcdctlPrefixArgs(clus), "backup", "--data-dir", dataDir, "--backup-dir", backupDir)
return spawnWithExpects(cmdArgs)
}
func mustEtcdctl(t *testing.T) {
if !fileutil.Exist(binDir + "/etcdctl") {
t.Fatalf("could not find etcdctl binary")
}
}
func setupEtcdctlTest(t *testing.T, cfg *etcdProcessClusterConfig, quorum bool) *etcdProcessCluster {
mustEtcdctl(t)
if !quorum {
cfg = configStandalone(*cfg)
}
epc, err := newEtcdProcessCluster(cfg)
if err != nil {
t.Fatalf("could not start etcd process cluster (%v)", err)
}
return epc
}

100
vendor/github.com/coreos/etcd/e2e/ctl_v3_alarm_test.go generated vendored Normal file
View file

@ -0,0 +1,100 @@
// 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/clientv3"
"golang.org/x/net/context"
)
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)
}
// 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.grpcEndpoints()
// 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 ctlV3Alarm(cx ctlCtx, cmd string, as ...string) error {
cmdArgs := append(cx.PrefixArgs(), "alarm", cmd)
return spawnWithExpects(cmdArgs, as...)
}

545
vendor/github.com/coreos/etcd/e2e/ctl_v3_auth_test.go generated vendored Normal file
View file

@ -0,0 +1,545 @@
// 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"
"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 TestCtlV3AuthPerfixPerm(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 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 auth fails unconditionally, note that failed RPC is Authenticate(), not Put()
cx.user, cx.pass = "test-user", "pass"
if err := ctlV3PutFailAuthDisabled(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", ""); 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 ctlV3PutFailAuthDisabled(cx ctlCtx, key, val string) error {
return spawnWithExpect(append(cx.PrefixArgs(), "put", key, val), "authentication is not enabled")
}
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)
}
}
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)
// ordinal user cannot add a new member
cx.user, cx.pass = "test-user", "pass"
if err := ctlV3MemberAdd(cx, peerURL); err == nil {
cx.t.Fatalf("ordinal 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)
n1 := cx.cfg.clusterSize
if n1 < 2 {
cx.t.Fatalf("%d-node is too small to test 'member remove'", n1)
}
resp, err := getMemberList(cx)
if err != nil {
cx.t.Fatal(err)
}
if n1 != len(resp.Members) {
cx.t.Fatalf("expected %d, got %d", n1, len(resp.Members))
}
var (
memIDToRemove = fmt.Sprintf("%x", resp.Header.MemberId)
clusterID = fmt.Sprintf("%x", resp.Header.ClusterId)
)
// ordinal user cannot remove a member
cx.user, cx.pass = "test-user", "pass"
if err = ctlV3MemberRemove(cx, memIDToRemove, clusterID); err == nil {
cx.t.Fatalf("ordinal user must not be allowed to remove a member")
}
// root can remove a member
cx.user, cx.pass = "root", "root"
if err = ctlV3MemberRemove(cx, 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)
}
// ordinal 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("ordinal 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)
}
}

View file

@ -0,0 +1,75 @@
// 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 <nil>")
}
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 <nil>")
}
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 <nil>")
}
}
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)
}

View file

@ -0,0 +1,73 @@
// 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 TestCtlV3DefragWithAuth(t *testing.T) { testCtl(t, defragTestWithAuth) }
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 defragTestWithAuth(cx ctlCtx) {
maintenanceInitKeys(cx)
if err := authEnable(cx); err != nil {
cx.t.Fatal(err)
}
cx.user, cx.pass = "root", "root"
authSetupTestUser(cx)
// ordinal user cannot defrag
cx.user, cx.pass = "test-user", "pass"
if err := ctlV3Defrag(cx); err == nil {
cx.t.Fatal("ordinal 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 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...)
}

113
vendor/github.com/coreos/etcd/e2e/ctl_v3_elect_test.go generated vendored Normal file
View file

@ -0,0 +1,113 @@
// 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) { 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 = blocked.Close(); 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 = holder.Close(); 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
}

View file

@ -0,0 +1,86 @@
// 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 (
"net/url"
"testing"
)
func TestCtlV3EndpointHealth(t *testing.T) { testCtl(t, endpointHealthTest, withQuorum()) }
func TestCtlV3EndpointStatus(t *testing.T) { testCtl(t, endpointStatusTest, withQuorum()) }
func TestCtlV3EndpointHealthWithAuth(t *testing.T) {
testCtl(t, endpointHealthTestWithAuth, 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.endpoints() {
u, _ := url.Parse(ep)
eps = append(eps, u.Host)
}
return spawnWithExpects(cmdArgs, eps...)
}
func endpointHealthTestWithAuth(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 ordinal 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)
}
}

259
vendor/github.com/coreos/etcd/e2e/ctl_v3_kv_test.go generated vendored Normal file
View file

@ -0,0 +1,259 @@
// 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 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 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 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 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) {
var (
kvs = []kv{{"key1", "val1"}}
)
for i := range kvs {
if err := ctlV3Put(cx, kvs[i].key, kvs[i].val, ""); err != nil {
cx.t.Fatalf("getKeysOnlyTest #%d: ctlV3Put error (%v)", i, err)
}
}
cmdArgs := append(cx.PrefixArgs(), "get")
cmdArgs = append(cmdArgs, []string{"--prefix", "--keys-only", "key"}...)
err := spawnWithExpects(cmdArgs, []string{"key1", ""}...)
if err != nil {
cx.t.Fatalf("getKeysOnlyTest : error (%v)", err)
}
}
func delTest(cx ctlCtx) {
tests := []struct {
puts []kv
args []string
deletedNum int
}{
{ // delete all keys
[]kv{{"foo1", "bar"}, {"foo2", "bar"}, {"foo3", "bar"}},
[]string{"", "--prefix"},
3,
},
{ // delete all keys
[]kv{{"foo1", "bar"}, {"foo2", "bar"}, {"foo3", "bar"}},
[]string{"", "--from-key"},
3,
},
{
[]kv{{"this", "value"}},
[]string{"that"},
0,
},
{
[]kv{{"sample", "value"}},
[]string{"sample"},
1,
},
{
[]kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}},
[]string{"key", "--prefix"},
3,
},
{
[]kv{{"zoo1", "bar"}, {"zoo2", "bar2"}, {"zoo3", "bar3"}},
[]string{"zoo1", "--from-key"},
3,
},
}
for i, tt := range tests {
for j := range tt.puts {
if err := ctlV3Put(cx, tt.puts[j].key, tt.puts[j].val, ""); err != nil {
cx.t.Fatalf("delTest #%d-%d: ctlV3Put error (%v)", i, j, err)
}
}
if err := ctlV3Del(cx, tt.args, tt.deletedNum); err != nil {
if cx.dialTimeout > 0 && !isGRPCTimedout(err) {
cx.t.Fatalf("delTest #%d: ctlV3Del error (%v)", i, err)
}
}
}
}
func ctlV3Put(cx ctlCtx, key, value, leaseID string) error {
cmdArgs := append(cx.PrefixArgs(), "put", key, value)
if leaseID != "" {
cmdArgs = append(cmdArgs, "--lease", leaseID)
}
return spawnWithExpect(cmdArgs, "OK")
}
type kv struct {
key, val string
}
func ctlV3Get(cx ctlCtx, args []string, kvs ...kv) error {
cmdArgs := append(cx.PrefixArgs(), "get")
cmdArgs = append(cmdArgs, args...)
if !cx.quorum {
cmdArgs = append(cmdArgs, "--consistency", "s")
}
var lines []string
for _, elem := range kvs {
lines = append(lines, elem.key, elem.val)
}
return spawnWithExpects(cmdArgs, lines...)
}
func ctlV3Del(cx ctlCtx, args []string, num int) error {
cmdArgs := append(cx.PrefixArgs(), "del")
cmdArgs = append(cmdArgs, args...)
return spawnWithExpects(cmdArgs, fmt.Sprintf("%d", num))
}

128
vendor/github.com/coreos/etcd/e2e/ctl_v3_lease_test.go generated vendored Normal file
View file

@ -0,0 +1,128 @@
// 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 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 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))
}

113
vendor/github.com/coreos/etcd/e2e/ctl_v3_lock_test.go generated vendored Normal file
View file

@ -0,0 +1,113 @@
// 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) { 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 = blocked.Close(); 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 = holder.Close(); 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
}

View file

@ -0,0 +1,108 @@
// 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)
}
}

127
vendor/github.com/coreos/etcd/e2e/ctl_v3_member_test.go generated vendored Normal file
View file

@ -0,0 +1,127 @@
// 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"
"strings"
"testing"
"github.com/coreos/etcd/etcdserver/etcdserverpb"
)
func TestCtlV3MemberList(t *testing.T) { testCtl(t, memberListTest) }
func TestCtlV3MemberRemove(t *testing.T) {
testCtl(t, memberRemoveTest, withQuorum(), withNoStrictReconfig())
}
func TestCtlV3MemberAdd(t *testing.T) { testCtl(t, memberAddTest) }
func TestCtlV3MemberUpdate(t *testing.T) { testCtl(t, memberUpdateTest) }
func memberListTest(cx ctlCtx) {
if err := ctlV3MemberList(cx); err != nil {
cx.t.Fatalf("memberListTest ctlV3MemberList error (%v)", err)
}
}
func ctlV3MemberList(cx ctlCtx) error {
cmdArgs := append(cx.PrefixArgs(), "member", "list")
lines := make([]string, cx.cfg.clusterSize)
for i := range lines {
lines[i] = "started"
}
return spawnWithExpects(cmdArgs, lines...)
}
func getMemberList(cx ctlCtx) (etcdserverpb.MemberListResponse, error) {
cmdArgs := append(cx.PrefixArgs(), "--write-out", "json", "member", "list")
proc, err := spawnCmd(cmdArgs)
if err != nil {
return etcdserverpb.MemberListResponse{}, err
}
var txt string
txt, err = proc.Expect("members")
if err != nil {
return etcdserverpb.MemberListResponse{}, err
}
if err = proc.Close(); err != nil {
return etcdserverpb.MemberListResponse{}, err
}
resp := etcdserverpb.MemberListResponse{}
dec := json.NewDecoder(strings.NewReader(txt))
if err := dec.Decode(&resp); err == io.EOF {
return etcdserverpb.MemberListResponse{}, err
}
return resp, nil
}
func memberRemoveTest(cx ctlCtx) {
n1 := cx.cfg.clusterSize
if n1 < 2 {
cx.t.Fatalf("%d-node is too small to test 'member remove'", n1)
}
resp, err := getMemberList(cx)
if err != nil {
cx.t.Fatal(err)
}
if n1 != len(resp.Members) {
cx.t.Fatalf("expected %d, got %d", n1, len(resp.Members))
}
var (
memIDToRemove = fmt.Sprintf("%x", resp.Header.MemberId)
cluserID = fmt.Sprintf("%x", resp.Header.ClusterId)
)
if err = ctlV3MemberRemove(cx, memIDToRemove, cluserID); err != nil {
cx.t.Fatal(err)
}
}
func ctlV3MemberRemove(cx ctlCtx, memberID, clusterID string) error {
cmdArgs := append(cx.PrefixArgs(), "member", "remove", memberID)
return spawnWithExpect(cmdArgs, fmt.Sprintf("%s removed from cluster %s", memberID, clusterID))
}
func memberAddTest(cx ctlCtx) {
if err := ctlV3MemberAdd(cx, fmt.Sprintf("http://localhost:%d", etcdProcessBasePort+11)); err != nil {
cx.t.Fatal(err)
}
}
func ctlV3MemberAdd(cx ctlCtx, peerURL string) error {
cmdArgs := append(cx.PrefixArgs(), "member", "add", "newmember", fmt.Sprintf("--peer-urls=%s", peerURL))
return spawnWithExpect(cmdArgs, " added to cluster ")
}
func memberUpdateTest(cx ctlCtx) {
mr, err := getMemberList(cx)
if err != nil {
cx.t.Fatal(err)
}
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.Fatal(err)
}
}
func ctlV3MemberUpdate(cx ctlCtx, memberID, peerURL string) error {
cmdArgs := append(cx.PrefixArgs(), "member", "update", memberID, fmt.Sprintf("--peer-urls=%s", peerURL))
return spawnWithExpect(cmdArgs, " updated in cluster ")
}

View file

@ -0,0 +1,100 @@
// 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"
"testing"
"time"
"golang.org/x/net/context"
"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].cfg.dataDirPath
if err := epc.StopAll(); 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].cfg.keepDataDir = true
if err := epc.RestartAll(); 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.grpcEndpoints(),
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")
}

165
vendor/github.com/coreos/etcd/e2e/ctl_v3_role_test.go generated vendored Normal file
View file

@ -0,0 +1,165 @@
// 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")
}
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) error {
cmdArgs := append(cx.PrefixArgs(), "role", "revoke-permission")
cmdArgs = append(cmdArgs, rolename)
cmdArgs = append(cmdArgs, key)
if len(rangeEnd) != 0 {
cmdArgs = append(cmdArgs, rangeEnd)
}
proc, err := spawnCmd(cmdArgs)
if err != nil {
return err
}
expStr := fmt.Sprintf("Permission of key %s is revoked from role %s", key, rolename)
_, 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}
}
return []string{permstr, perm.key, perm.rangeEnd}
}

View file

@ -0,0 +1,286 @@
// 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.grpcEndpoints(), ","), "--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.processes()[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].cfg.name, "--initial-cluster", epc.procs[0].cfg.initialCluster, "--initial-cluster-token", epc.procs[0].cfg.initialToken, "--initial-advertise-peer-urls", epc.procs[0].cfg.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].cfg.dataDirPath = newDataDir
for i := range epc.procs[0].cfg.args {
if epc.procs[0].cfg.args[i] == "--data-dir" {
epc.procs[0].cfg.args[i+1] = newDataDir
}
}
if err = epc.processes()[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].cfg.initialCluster + fmt.Sprintf(",%s=%s", name2, peerURL)
// start the new member
var nepc *expect.ExpectProcess
nepc, err = spawnCmd([]string{epc.procs[0].cfg.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)
}
}
func TestCtlV3SnapshotWithAuth(t *testing.T) { testCtl(t, snapshotTestWithAuth) }
func snapshotTestWithAuth(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)
// ordinal user cannot save a snapshot
cx.user, cx.pass = "test-user", "pass"
if err := ctlV3SnapshotSave(cx, fpath); err == nil {
cx.t.Fatal("ordinal 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)
}
}

216
vendor/github.com/coreos/etcd/e2e/ctl_v3_test.go generated vendored Normal file
View file

@ -0,0 +1,216 @@
// 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"
"strings"
"testing"
"time"
"github.com/coreos/etcd/pkg/flags"
"github.com/coreos/etcd/pkg/testutil"
"github.com/coreos/etcd/version"
)
func TestCtlV3Version(t *testing.T) { testCtl(t, versionTest) }
func versionTest(cx ctlCtx) {
if err := ctlV3Version(cx); err != nil {
cx.t.Fatalf("versionTest ctlV3Version error (%v)", err)
}
}
func ctlV3Version(cx ctlCtx) error {
cmdArgs := append(cx.PrefixArgs(), "version")
return spawnWithExpect(cmdArgs, version.Version)
}
// TestCtlV3DialWithHTTPScheme ensures that client handles endpoints with HTTPS scheme.
func TestCtlV3DialWithHTTPScheme(t *testing.T) {
testCtl(t, dialWithSchemeTest, withCfg(configClientTLS))
}
func dialWithSchemeTest(cx ctlCtx) {
cmdArgs := append(cx.prefixArgs(cx.epc.endpoints()), "put", "foo", "bar")
if err := spawnWithExpect(cmdArgs, "OK"); err != nil {
cx.t.Fatal(err)
}
}
type ctlCtx struct {
t *testing.T
cfg etcdProcessClusterConfig
quotaBackendBytes int64
noStrictReconfig bool
epc *etcdProcessCluster
envMap map[string]struct{}
dialTimeout time.Duration
quorum bool // if true, set up 3-node cluster and linearizable read
interactive bool
user string
pass string
// for compaction
compactPhysical bool
}
type ctlOption func(*ctlCtx)
func (cx *ctlCtx) applyOpts(opts []ctlOption) {
for _, opt := range opts {
opt(cx)
}
}
func withCfg(cfg etcdProcessClusterConfig) ctlOption {
return func(cx *ctlCtx) { cx.cfg = cfg }
}
func withDialTimeout(timeout time.Duration) ctlOption {
return func(cx *ctlCtx) { cx.dialTimeout = timeout }
}
func withQuorum() ctlOption {
return func(cx *ctlCtx) { cx.quorum = true }
}
func withInteractive() ctlOption {
return func(cx *ctlCtx) { cx.interactive = true }
}
func withQuota(b int64) ctlOption {
return func(cx *ctlCtx) { cx.quotaBackendBytes = b }
}
func withCompactPhysical() ctlOption {
return func(cx *ctlCtx) { cx.compactPhysical = true }
}
func withNoStrictReconfig() ctlOption {
return func(cx *ctlCtx) { cx.noStrictReconfig = true }
}
func withFlagByEnv() ctlOption {
return func(cx *ctlCtx) { cx.envMap = make(map[string]struct{}) }
}
func testCtl(t *testing.T, testFunc func(ctlCtx), opts ...ctlOption) {
defer testutil.AfterTest(t)
ret := ctlCtx{
t: t,
cfg: configAutoTLS,
dialTimeout: 7 * time.Second,
}
ret.applyOpts(opts)
os.Setenv("ETCDCTL_API", "3")
mustEtcdctl(t)
if !ret.quorum {
ret.cfg = *configStandalone(ret.cfg)
}
if ret.quotaBackendBytes > 0 {
ret.cfg.quotaBackendBytes = ret.quotaBackendBytes
}
ret.cfg.noStrictReconfig = ret.noStrictReconfig
epc, err := newEtcdProcessCluster(&ret.cfg)
if err != nil {
t.Fatalf("could not start etcd process cluster (%v)", err)
}
ret.epc = epc
defer func() {
os.Unsetenv("ETCDCTL_API")
if ret.envMap != nil {
for k := range ret.envMap {
os.Unsetenv(k)
}
}
if errC := ret.epc.Close(); errC != nil {
t.Fatalf("error closing etcd processes (%v)", errC)
}
}()
donec := make(chan struct{})
go func() {
defer close(donec)
testFunc(ret)
}()
timeout := 2*ret.dialTimeout + time.Second
if ret.dialTimeout == 0 {
timeout = 30 * time.Second
}
select {
case <-time.After(timeout):
testutil.FatalStack(t, fmt.Sprintf("test timed out after %v", timeout))
case <-donec:
}
}
func (cx *ctlCtx) prefixArgs(eps []string) []string {
if len(cx.epc.proxies()) > 0 { // TODO: add proxy check as in v2
panic("v3 proxy not implemented")
}
fmap := make(map[string]string)
fmap["endpoints"] = strings.Join(eps, ",")
fmap["dial-timeout"] = cx.dialTimeout.String()
if cx.epc.cfg.clientTLS == clientTLS {
if cx.epc.cfg.isClientAutoTLS {
fmap["insecure-transport"] = "false"
fmap["insecure-skip-tls-verify"] = "true"
} else {
fmap["cacert"] = caPath
fmap["cert"] = certPath
fmap["key"] = privateKeyPath
}
}
if cx.user != "" {
fmap["user"] = cx.user + ":" + cx.pass
}
useEnv := cx.envMap != nil
cmdArgs := []string{ctlBinPath}
for k, v := range fmap {
if useEnv {
ek := flags.FlagToEnv("ETCDCTL", k)
os.Setenv(ek, v)
cx.envMap[ek] = struct{}{}
} else {
cmdArgs = append(cmdArgs, fmt.Sprintf("--%s=%s", k, v))
}
}
return cmdArgs
}
// PrefixArgs prefixes etcdctl command.
// Make sure to unset environment variables after tests.
func (cx *ctlCtx) PrefixArgs() []string {
return cx.prefixArgs(cx.epc.grpcEndpoints())
}
func isGRPCTimedout(err error) bool {
return strings.Contains(err.Error(), "grpc: timed out trying to connect")
}

155
vendor/github.com/coreos/etcd/e2e/ctl_v3_txn_test.go generated vendored Normal file
View file

@ -0,0 +1,155 @@
// 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, delete):")
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, delete):")
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()
}

139
vendor/github.com/coreos/etcd/e2e/ctl_v3_user_test.go generated vendored Normal file
View file

@ -0,0 +1,139 @@
// 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
}

118
vendor/github.com/coreos/etcd/e2e/ctl_v3_watch_test.go generated vendored Normal file
View file

@ -0,0 +1,118 @@
// 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 ctlV3Watch(cx ctlCtx, args []string, kvs ...kv) error {
cmdArgs := append(cx.PrefixArgs(), "watch")
if cx.interactive {
cmdArgs = append(cmdArgs, "--interactive")
} else {
cmdArgs = append(cmdArgs, 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()
}

24
vendor/github.com/coreos/etcd/e2e/doc.go generated vendored Normal file
View file

@ -0,0 +1,24 @@
// 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 implements tests built upon etcd binaries, and focus on
end-to-end testing.
Features/goals of the end-to-end tests:
1. test command-line parsing and arguments.
2. test user-facing command-line API.
3. launch full processes and check for expected outputs.
*/
package e2e

34
vendor/github.com/coreos/etcd/e2e/etcd_config_test.go generated vendored Normal file
View file

@ -0,0 +1,34 @@
// 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"
)
const exampleConfigFile = "../etcd.conf.yml.sample"
func TestEtcdExampleConfig(t *testing.T) {
proc, err := spawnCmd([]string{binDir + "/etcd", "--config-file", exampleConfigFile})
if err != nil {
t.Fatal(err)
}
if err = waitReadyExpectProc(proc, false); err != nil {
t.Fatal(err)
}
if err = proc.Stop(); err != nil {
t.Fatal(err)
}
}

View file

@ -0,0 +1,170 @@
// 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"
)
// 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":"3.0`}); 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].cfg.execPath = binDir + "/etcd"
epc.procs[i].cfg.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].cfg.execPath = binDir + "/etcd"
epc.procs[i].cfg.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)
}
}

555
vendor/github.com/coreos/etcd/e2e/etcd_test.go generated vendored Normal file
View file

@ -0,0 +1,555 @@
// 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"
"io/ioutil"
"net/url"
"os"
"strings"
"github.com/coreos/etcd/etcdserver"
"github.com/coreos/etcd/pkg/expect"
"github.com/coreos/etcd/pkg/fileutil"
)
const etcdProcessBasePort = 20000
var (
binPath string
ctlBinPath string
certPath string
privateKeyPath string
caPath string
)
type clientConnType int
const (
clientNonTLS clientConnType = iota
clientTLS
clientTLSAndNonTLS
)
var (
configNoTLS = etcdProcessClusterConfig{
clusterSize: 3,
proxySize: 0,
initialToken: "new",
}
configAutoTLS = etcdProcessClusterConfig{
clusterSize: 3,
isPeerTLS: true,
isPeerAutoTLS: true,
initialToken: "new",
}
configTLS = etcdProcessClusterConfig{
clusterSize: 3,
proxySize: 0,
clientTLS: clientTLS,
isPeerTLS: true,
initialToken: "new",
}
configClientTLS = etcdProcessClusterConfig{
clusterSize: 3,
proxySize: 0,
clientTLS: clientTLS,
initialToken: "new",
}
configClientBoth = etcdProcessClusterConfig{
clusterSize: 1,
proxySize: 0,
clientTLS: clientTLSAndNonTLS,
initialToken: "new",
}
configClientAutoTLS = etcdProcessClusterConfig{
clusterSize: 1,
proxySize: 0,
isClientAutoTLS: true,
clientTLS: clientTLS,
initialToken: "new",
}
configPeerTLS = etcdProcessClusterConfig{
clusterSize: 3,
proxySize: 0,
isPeerTLS: true,
initialToken: "new",
}
configWithProxy = etcdProcessClusterConfig{
clusterSize: 3,
proxySize: 1,
initialToken: "new",
}
configWithProxyTLS = etcdProcessClusterConfig{
clusterSize: 3,
proxySize: 1,
clientTLS: clientTLS,
isPeerTLS: true,
initialToken: "new",
}
configWithProxyPeerTLS = etcdProcessClusterConfig{
clusterSize: 3,
proxySize: 1,
isPeerTLS: true,
initialToken: "new",
}
)
func configStandalone(cfg etcdProcessClusterConfig) *etcdProcessClusterConfig {
ret := cfg
ret.clusterSize = 1
return &ret
}
type etcdProcessCluster struct {
cfg *etcdProcessClusterConfig
procs []*etcdProcess
}
type etcdProcess struct {
cfg *etcdProcessConfig
proc *expect.ExpectProcess
donec chan struct{} // closed when Interact() terminates
}
type etcdProcessConfig struct {
execPath string
args []string
dataDirPath string
keepDataDir bool
name string
purl url.URL
acurl string
// additional url for tls connection when the etcd process
// serves both http and https
acurltls string
acurlHost string
initialToken string
initialCluster string
isProxy bool
}
type etcdProcessClusterConfig struct {
execPath string
dataDirPath string
keepDataDir bool
clusterSize int
baseScheme string
basePort int
proxySize int
snapCount int // default is 10000
clientTLS clientConnType
clientCertAuthEnabled bool
isPeerTLS bool
isPeerAutoTLS bool
isClientAutoTLS bool
forceNewCluster bool
initialToken string
quotaBackendBytes int64
noStrictReconfig bool
}
// newEtcdProcessCluster launches a new cluster from etcd processes, returning
// a new etcdProcessCluster once all nodes are ready to accept client requests.
func newEtcdProcessCluster(cfg *etcdProcessClusterConfig) (*etcdProcessCluster, error) {
etcdCfgs := cfg.etcdProcessConfigs()
epc := &etcdProcessCluster{
cfg: cfg,
procs: make([]*etcdProcess, cfg.clusterSize+cfg.proxySize),
}
// launch etcd processes
for i := range etcdCfgs {
proc, err := newEtcdProcess(etcdCfgs[i])
if err != nil {
epc.Close()
return nil, err
}
epc.procs[i] = proc
}
return epc, epc.Start()
}
func newEtcdProcess(cfg *etcdProcessConfig) (*etcdProcess, error) {
if !fileutil.Exist(cfg.execPath) {
return nil, fmt.Errorf("could not find etcd binary")
}
if !cfg.keepDataDir {
if err := os.RemoveAll(cfg.dataDirPath); err != nil {
return nil, err
}
}
child, err := spawnCmd(append([]string{cfg.execPath}, cfg.args...))
if err != nil {
return nil, err
}
return &etcdProcess{cfg: cfg, proc: child, donec: make(chan struct{})}, nil
}
func (cfg *etcdProcessClusterConfig) etcdProcessConfigs() []*etcdProcessConfig {
binPath = binDir + "/etcd"
ctlBinPath = binDir + "/etcdctl"
certPath = certDir + "/server.crt"
privateKeyPath = certDir + "/server.key.insecure"
caPath = certDir + "/ca.crt"
if cfg.basePort == 0 {
cfg.basePort = etcdProcessBasePort
}
if cfg.execPath == "" {
cfg.execPath = binPath
}
if cfg.snapCount == 0 {
cfg.snapCount = etcdserver.DefaultSnapCount
}
clientScheme := "http"
if cfg.clientTLS == clientTLS {
clientScheme = "https"
}
peerScheme := cfg.baseScheme
if peerScheme == "" {
peerScheme = "http"
}
if cfg.isPeerTLS {
peerScheme += "s"
}
etcdCfgs := make([]*etcdProcessConfig, cfg.clusterSize+cfg.proxySize)
initialCluster := make([]string, cfg.clusterSize)
for i := 0; i < cfg.clusterSize; i++ {
var curls []string
var curl, curltls string
port := cfg.basePort + 2*i
curlHost := fmt.Sprintf("localhost:%d", port)
switch cfg.clientTLS {
case clientNonTLS, clientTLS:
curl = (&url.URL{Scheme: clientScheme, Host: curlHost}).String()
curls = []string{curl}
case clientTLSAndNonTLS:
curl = (&url.URL{Scheme: "http", Host: curlHost}).String()
curltls = (&url.URL{Scheme: "https", Host: curlHost}).String()
curls = []string{curl, curltls}
}
purl := url.URL{Scheme: peerScheme, Host: fmt.Sprintf("localhost:%d", port+1)}
name := fmt.Sprintf("testname%d", i)
dataDirPath := cfg.dataDirPath
if cfg.dataDirPath == "" {
var derr error
dataDirPath, derr = ioutil.TempDir("", name+".etcd")
if derr != nil {
panic("could not get tempdir for datadir")
}
}
initialCluster[i] = fmt.Sprintf("%s=%s", name, purl.String())
args := []string{
"--name", name,
"--listen-client-urls", strings.Join(curls, ","),
"--advertise-client-urls", strings.Join(curls, ","),
"--listen-peer-urls", purl.String(),
"--initial-advertise-peer-urls", purl.String(),
"--initial-cluster-token", cfg.initialToken,
"--data-dir", dataDirPath,
"--snapshot-count", fmt.Sprintf("%d", cfg.snapCount),
}
if cfg.forceNewCluster {
args = append(args, "--force-new-cluster")
}
if cfg.quotaBackendBytes > 0 {
args = append(args,
"--quota-backend-bytes", fmt.Sprintf("%d", cfg.quotaBackendBytes),
)
}
if cfg.noStrictReconfig {
args = append(args, "--strict-reconfig-check=false")
}
args = append(args, cfg.tlsArgs()...)
etcdCfgs[i] = &etcdProcessConfig{
execPath: cfg.execPath,
args: args,
dataDirPath: dataDirPath,
keepDataDir: cfg.keepDataDir,
name: name,
purl: purl,
acurl: curl,
acurltls: curltls,
acurlHost: curlHost,
initialToken: cfg.initialToken,
}
}
for i := 0; i < cfg.proxySize; i++ {
port := cfg.basePort + 2*cfg.clusterSize + i + 1
curlHost := fmt.Sprintf("localhost:%d", port)
curl := url.URL{Scheme: clientScheme, Host: curlHost}
name := fmt.Sprintf("testname-proxy%d", i)
dataDirPath, derr := ioutil.TempDir("", name+".etcd")
if derr != nil {
panic("could not get tempdir for datadir")
}
args := []string{
"--name", name,
"--proxy", "on",
"--listen-client-urls", curl.String(),
"--data-dir", dataDirPath,
}
args = append(args, cfg.tlsArgs()...)
etcdCfgs[cfg.clusterSize+i] = &etcdProcessConfig{
execPath: cfg.execPath,
args: args,
dataDirPath: dataDirPath,
keepDataDir: cfg.keepDataDir,
name: name,
acurl: curl.String(),
acurlHost: curlHost,
isProxy: true,
}
}
initialClusterArgs := []string{"--initial-cluster", strings.Join(initialCluster, ",")}
for i := range etcdCfgs {
etcdCfgs[i].initialCluster = strings.Join(initialCluster, ",")
etcdCfgs[i].args = append(etcdCfgs[i].args, initialClusterArgs...)
}
return etcdCfgs
}
func (cfg *etcdProcessClusterConfig) tlsArgs() (args []string) {
if cfg.clientTLS != clientNonTLS {
if cfg.isClientAutoTLS {
args = append(args, "--auto-tls=true")
} else {
tlsClientArgs := []string{
"--cert-file", certPath,
"--key-file", privateKeyPath,
"--ca-file", caPath,
}
args = append(args, tlsClientArgs...)
if cfg.clientCertAuthEnabled {
args = append(args, "--client-cert-auth")
}
}
}
if cfg.isPeerTLS {
if cfg.isPeerAutoTLS {
args = append(args, "--peer-auto-tls=true")
} else {
tlsPeerArgs := []string{
"--peer-cert-file", certPath,
"--peer-key-file", privateKeyPath,
"--peer-ca-file", caPath,
}
args = append(args, tlsPeerArgs...)
}
}
return args
}
func (epc *etcdProcessCluster) Start() (err error) {
readyC := make(chan error, epc.cfg.clusterSize+epc.cfg.proxySize)
for i := range epc.procs {
go func(n int) { readyC <- epc.procs[n].waitReady() }(i)
}
for range epc.procs {
if err := <-readyC; err != nil {
epc.Close()
return err
}
}
return nil
}
func (epc *etcdProcessCluster) RestartAll() error {
for i := range epc.procs {
proc, err := newEtcdProcess(epc.procs[i].cfg)
if err != nil {
epc.Close()
return err
}
epc.procs[i] = proc
}
return epc.Start()
}
func (epc *etcdProcessCluster) StopAll() (err error) {
for _, p := range epc.procs {
if p == nil {
continue
}
if curErr := p.Stop(); curErr != nil {
if err != nil {
err = fmt.Errorf("%v; %v", err, curErr)
} else {
err = curErr
}
}
}
return err
}
func (epc *etcdProcessCluster) Close() error {
err := epc.StopAll()
for _, p := range epc.procs {
// p is nil when newEtcdProcess fails in the middle
// Close still gets called to clean up test data
if p == nil {
continue
}
os.RemoveAll(p.cfg.dataDirPath)
}
return err
}
func (ep *etcdProcess) Restart() error {
newEp, err := newEtcdProcess(ep.cfg)
if err != nil {
ep.Stop()
return err
}
*ep = *newEp
if err = ep.waitReady(); err != nil {
ep.Stop()
return err
}
return nil
}
func (ep *etcdProcess) Stop() error {
if ep == nil {
return nil
}
if err := ep.proc.Stop(); err != nil {
return err
}
<-ep.donec
if ep.cfg.purl.Scheme == "unix" || ep.cfg.purl.Scheme == "unixs" {
os.RemoveAll(ep.cfg.purl.Host)
}
return nil
}
func (ep *etcdProcess) waitReady() error {
defer close(ep.donec)
return waitReadyExpectProc(ep.proc, ep.cfg.isProxy)
}
func waitReadyExpectProc(exproc *expect.ExpectProcess, isProxy bool) error {
readyStrs := []string{"enabled capabilities for version", "published"}
if isProxy {
readyStrs = []string{"httpproxy: endpoints found"}
}
c := 0
matchSet := func(l string) bool {
for _, s := range readyStrs {
if strings.Contains(l, s) {
c++
break
}
}
return c == len(readyStrs)
}
_, err := exproc.ExpectFunc(matchSet)
return err
}
func spawnCmd(args []string) (*expect.ExpectProcess, error) {
return expect.NewExpect(args[0], args[1:]...)
}
func spawnWithExpect(args []string, expected string) error {
return spawnWithExpects(args, []string{expected}...)
}
func spawnWithExpects(args []string, xs ...string) error {
proc, err := spawnCmd(args)
if err != nil {
return err
}
// process until either stdout or stderr contains
// the expected string
var (
lines []string
lineFunc = func(txt string) bool { return true }
)
for _, txt := range xs {
for {
l, err := proc.ExpectFunc(lineFunc)
if err != nil {
proc.Close()
return fmt.Errorf("%v (expected %q, got %q)", err, txt, lines)
}
lines = append(lines, l)
if strings.Contains(l, txt) {
break
}
}
}
perr := proc.Close()
if err != nil {
return err
}
if len(xs) == 0 && proc.LineCount() != 0 { // expect no output
return fmt.Errorf("unexpected output (got lines %q, line count %d)", lines, proc.LineCount())
}
return perr
}
// proxies returns only the proxy etcdProcess.
func (epc *etcdProcessCluster) proxies() []*etcdProcess {
return epc.procs[epc.cfg.clusterSize:]
}
func (epc *etcdProcessCluster) processes() []*etcdProcess {
return epc.procs[:epc.cfg.clusterSize]
}
func (epc *etcdProcessCluster) endpoints() []string {
eps := make([]string, epc.cfg.clusterSize)
for i, ep := range epc.processes() {
eps[i] = ep.cfg.acurl
}
return eps
}
func (epc *etcdProcessCluster) grpcEndpoints() []string {
eps := make([]string, epc.cfg.clusterSize)
for i, ep := range epc.processes() {
eps[i] = ep.cfg.acurlHost
}
return eps
}

60
vendor/github.com/coreos/etcd/e2e/gateway_test.go generated vendored Normal file
View file

@ -0,0 +1,60 @@
// 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"
"github.com/coreos/etcd/pkg/expect"
)
var (
defaultGatewayEndpoint = "127.0.0.1:23790"
)
func TestGateway(t *testing.T) {
ec, err := newEtcdProcessCluster(&configNoTLS)
if err != nil {
t.Fatal(err)
}
defer ec.StopAll()
eps := strings.Join(ec.grpcEndpoints(), ",")
p := startGateway(t, eps)
defer p.Stop()
os.Setenv("ETCDCTL_API", "3")
defer os.Unsetenv("ETCDCTL_API")
err = spawnWithExpect([]string{ctlBinPath, "--endpoints=" + defaultGatewayEndpoint, "put", "foo", "bar"}, "OK\r\n")
if err != nil {
t.Errorf("failed to finish put request through gateway: %v", err)
}
}
func startGateway(t *testing.T, endpoints string) *expect.ExpectProcess {
p, err := expect.NewExpect(binPath, "gateway", "--endpoints="+endpoints, "start")
if err != nil {
t.Fatal(err)
}
_, err = p.Expect("tcpproxy: ready to proxy client requests to")
if err != nil {
t.Fatal(err)
}
return p
}

32
vendor/github.com/coreos/etcd/e2e/main_test.go generated vendored Normal file
View file

@ -0,0 +1,32 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package e2e
import (
"flag"
"os"
"runtime"
"testing"
"github.com/coreos/etcd/pkg/testutil"
)
var binDir string
var certDir string
func TestMain(m *testing.M) {
os.Setenv("ETCD_UNSUPPORTED_ARCH", runtime.GOARCH)
os.Unsetenv("ETCDCTL_API")
flag.StringVar(&binDir, "bin-dir", "../bin", "The directory for store etcd and etcdctl binaries.")
flag.StringVar(&certDir, "cert-dir", "../integration/fixtures", "The directory for store certificate files.")
flag.Parse()
v := m.Run()
if v == 0 && testutil.CheckLeakedGoroutine() {
os.Exit(1)
}
os.Exit(v)
}

180
vendor/github.com/coreos/etcd/e2e/v2_curl_test.go generated vendored Normal file
View file

@ -0,0 +1,180 @@
// 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 TestV2CurlProxyNoTLS(t *testing.T) { testCurlPutGet(t, &configWithProxy) }
func TestV2CurlProxyTLS(t *testing.T) { testCurlPutGet(t, &configWithProxyTLS) }
func TestV2CurlProxyPeerTLS(t *testing.T) { testCurlPutGet(t, &configWithProxyPeerTLS) }
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
}
// 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)].cfg.acurl
)
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 = clus.procs[rand.Intn(clus.cfg.clusterSize)].cfg.acurltls
} else if clus.cfg.clientTLS == clientTLS {
cmdArgs = append(cmdArgs, "--cacert", caPath, "--cert", certPath, "--key", privateKeyPath)
}
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))
}
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)
}

113
vendor/github.com/coreos/etcd/e2e/v3_curl_test.go generated vendored Normal file
View file

@ -0,0 +1,113 @@
// 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"
)
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)
}
}