mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #5076 from gyuho/more_e2e
*: add, clean up e2e tests
This commit is contained in:
commit
6bbdebb281
32
e2e/ctl_v3_auth_test.go
Normal file
32
e2e/ctl_v3_auth_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2016 CoreOS, Inc.
|
||||
//
|
||||
// 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 TestCtlV3AuthEnable(t *testing.T) { testCtl(t, authEnableTest) }
|
||||
|
||||
// TODO: test auth disable
|
||||
|
||||
func authEnableTest(cx ctlCtx) {
|
||||
if err := ctlV3AuthEnable(cx); err != nil {
|
||||
cx.t.Fatalf("authEnableTest ctlV3AuthEnable error (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func ctlV3AuthEnable(cx ctlCtx) error {
|
||||
cmdArgs := append(cx.PrefixArgs(), "auth", "enable")
|
||||
return spawnWithExpect(cmdArgs, "Authentication Enabled")
|
||||
}
|
45
e2e/ctl_v3_defrag_test.go
Normal file
45
e2e/ctl_v3_defrag_test.go
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright 2016 CoreOS, Inc.
|
||||
//
|
||||
// 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 defragTest(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)
|
||||
}
|
||||
}
|
||||
|
||||
if err := ctlV3Compact(cx, 4); err != nil {
|
||||
cx.t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := ctlV3Defrag(cx); err != nil {
|
||||
cx.t.Fatalf("defragTest ctlV3Defrag error (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func ctlV3Defrag(cx ctlCtx) error {
|
||||
cmdArgs := append(cx.PrefixArgs(), "defrag")
|
||||
lines := make([]string, cx.epc.cfg.clusterSize)
|
||||
for i := range lines {
|
||||
lines[i] = "Finished defragmenting etcd member"
|
||||
}
|
||||
return spawnWithExpects(cmdArgs, lines...)
|
||||
}
|
54
e2e/ctl_v3_endpoint_test.go
Normal file
54
e2e/ctl_v3_endpoint_test.go
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2016 CoreOS, Inc.
|
||||
//
|
||||
// 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 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...)
|
||||
}
|
211
e2e/ctl_v3_kv_test.go
Normal file
211
e2e/ctl_v3_kv_test.go
Normal file
@ -0,0 +1,211 @@
|
||||
// Copyright 2016 CoreOS, Inc.
|
||||
//
|
||||
// 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 TestCtlV3PutPeerTLS(t *testing.T) { testCtl(t, putTest, withCfg(configPeerTLS)) }
|
||||
func TestCtlV3PutTimeout(t *testing.T) { testCtl(t, putTest, withDialTimeout(0)) }
|
||||
|
||||
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 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 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{"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", "--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
|
||||
|
||||
wstr string
|
||||
}{
|
||||
{"simple", "abc"},
|
||||
{"json", "\"key\":\"YWJj\""},
|
||||
{"protobuf", "\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)
|
||||
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 delTest(cx ctlCtx) {
|
||||
tests := []struct {
|
||||
puts []kv
|
||||
args []string
|
||||
|
||||
deletedNum int
|
||||
}{
|
||||
{
|
||||
[]kv{{"this", "value"}},
|
||||
[]string{"that"},
|
||||
0,
|
||||
},
|
||||
{
|
||||
[]kv{{"sample", "value"}},
|
||||
[]string{"sample"},
|
||||
1,
|
||||
},
|
||||
{
|
||||
[]kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}},
|
||||
[]string{"key", "--prefix"},
|
||||
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))
|
||||
}
|
34
e2e/ctl_v3_member_test.go
Normal file
34
e2e/ctl_v3_member_test.go
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2016 CoreOS, Inc.
|
||||
//
|
||||
// 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 TestCtlV3MemberList(t *testing.T) { testCtl(t, memberListTest) }
|
||||
|
||||
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...)
|
||||
}
|
@ -15,7 +15,6 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -25,64 +24,18 @@ import (
|
||||
"github.com/coreos/etcd/version"
|
||||
)
|
||||
|
||||
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 TestCtlV3PutPeerTLS(t *testing.T) { testCtl(t, putTest, withCfg(configPeerTLS)) }
|
||||
func TestCtlV3PutTimeout(t *testing.T) { testCtl(t, putTest, withDialTimeout(0)) }
|
||||
func TestCtlV3Version(t *testing.T) { testCtl(t, versionTest) }
|
||||
|
||||
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 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 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 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 versionTest(cx ctlCtx) {
|
||||
if err := ctlV3Version(cx); err != nil {
|
||||
cx.t.Fatalf("versionTest ctlV3Version error (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCtlV3TxnInteractiveSuccess(t *testing.T) {
|
||||
testCtl(t, txnTestSuccess, withInteractive())
|
||||
func ctlV3Version(cx ctlCtx) error {
|
||||
cmdArgs := append(cx.PrefixArgs(), "version")
|
||||
return spawnWithExpect(cmdArgs, version.Version)
|
||||
}
|
||||
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 TestCtlV3Version(t *testing.T) { testCtl(t, versionTest) }
|
||||
func TestCtlV3EpHealthQuorum(t *testing.T) { testCtl(t, epHealthTest, withQuorum()) }
|
||||
|
||||
type ctlCtx struct {
|
||||
t *testing.T
|
||||
@ -119,18 +72,6 @@ func withInteractive() ctlOption {
|
||||
return func(cx *ctlCtx) { cx.interactive = true }
|
||||
}
|
||||
|
||||
func setupCtlV3Test(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
|
||||
}
|
||||
|
||||
func testCtl(t *testing.T, testFunc func(ctlCtx), opts ...ctlOption) {
|
||||
defer testutil.AfterTest(t)
|
||||
|
||||
@ -142,7 +83,15 @@ func testCtl(t *testing.T, testFunc func(ctlCtx), opts ...ctlOption) {
|
||||
ret.applyOpts(opts)
|
||||
|
||||
os.Setenv("ETCDCTL_API", "3")
|
||||
ret.epc = setupCtlV3Test(ret.t, ret.cfg, ret.quorum)
|
||||
mustEtcdctl(t)
|
||||
if !ret.quorum {
|
||||
ret.cfg = *configStandalone(ret.cfg)
|
||||
}
|
||||
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")
|
||||
@ -166,227 +115,6 @@ func testCtl(t *testing.T, testFunc func(ctlCtx), opts ...ctlOption) {
|
||||
}
|
||||
}
|
||||
|
||||
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{"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", "--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
|
||||
|
||||
wstr string
|
||||
}{
|
||||
{"simple", "abc"},
|
||||
{"json", "\"key\":\"YWJj\""},
|
||||
{"protobuf", "\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)
|
||||
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 delTest(cx ctlCtx) {
|
||||
tests := []struct {
|
||||
puts []kv
|
||||
args []string
|
||||
|
||||
deletedNum int
|
||||
}{
|
||||
{
|
||||
[]kv{{"this", "value"}},
|
||||
[]string{"that"},
|
||||
0,
|
||||
},
|
||||
{
|
||||
[]kv{{"sample", "value"}},
|
||||
[]string{"sample"},
|
||||
1,
|
||||
},
|
||||
{
|
||||
[]kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}},
|
||||
[]string{"key", "--prefix"},
|
||||
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 watchTest(cx ctlCtx) {
|
||||
tests := []struct {
|
||||
puts []kv
|
||||
args []string
|
||||
|
||||
wkv []kv
|
||||
}{
|
||||
{
|
||||
[]kv{{"sample", "value"}},
|
||||
[]string{"sample", "--rev", "1"},
|
||||
[]kv{{"sample", "value"}},
|
||||
},
|
||||
{
|
||||
[]kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}},
|
||||
[]string{"key", "--rev", "1", "--prefix"},
|
||||
[]kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}},
|
||||
},
|
||||
{
|
||||
[]kv{{"etcd", "revision_1"}, {"etcd", "revision_2"}, {"etcd", "revision_3"}},
|
||||
[]string{"etcd", "--rev", "2"},
|
||||
[]kv{{"etcd", "revision_2"}, {"etcd", "revision_3"}},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
go func() {
|
||||
for j := range tt.puts {
|
||||
if err := ctlV3Put(cx, tt.puts[j].key, tt.puts[j].val, ""); err != nil {
|
||||
cx.t.Fatalf("watchTest #%d-%d: ctlV3Put error (%v)", i, j, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func versionTest(cx ctlCtx) {
|
||||
if err := ctlV3Version(cx); err != nil {
|
||||
cx.t.Fatalf("versionTest ctlV3Version error (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func epHealthTest(cx ctlCtx) {
|
||||
if err := ctlV3EpHealth(cx); err != nil {
|
||||
cx.t.Fatalf("epHealthTest ctlV3EpHealth error (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
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{`version("key1") = "1"`, `version("key2") = "1"`},
|
||||
ifSucess: []string{"get key1", "get key2"},
|
||||
ifFail: []string{`put key1 "fail"`, `put key2 "fail"`},
|
||||
results: []string{"SUCCESS", "key1", "value1", "key2", "value2"},
|
||||
}
|
||||
if err := ctlV3Txn(cx, rqs); err != nil {
|
||||
cx.t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func txnTestFail(cx ctlCtx) {
|
||||
rqs := txnRequests{
|
||||
compare: []string{`version("key") < "0"`},
|
||||
ifSucess: []string{`put key "success"`},
|
||||
ifFail: []string{`put key "fail"`},
|
||||
results: []string{"FAILURE", "OK"},
|
||||
}
|
||||
if err := ctlV3Txn(cx, rqs); err != nil {
|
||||
cx.t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (cx *ctlCtx) PrefixArgs() []string {
|
||||
if len(cx.epc.proxies()) > 0 { // TODO: add proxy check as in v2
|
||||
panic("v3 proxy not implemented")
|
||||
@ -407,147 +135,6 @@ func (cx *ctlCtx) PrefixArgs() []string {
|
||||
return cmdArgs
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
func ctlV3Version(cx ctlCtx) error {
|
||||
cmdArgs := append(cx.PrefixArgs(), "version")
|
||||
return spawnWithExpect(cmdArgs, version.Version)
|
||||
}
|
||||
|
||||
func ctlV3EpHealth(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 isGRPCTimedout(err error) bool {
|
||||
return strings.Contains(err.Error(), "grpc: timed out trying to connect")
|
||||
}
|
||||
|
129
e2e/ctl_v3_txn_test.go
Normal file
129
e2e/ctl_v3_txn_test.go
Normal file
@ -0,0 +1,129 @@
|
||||
// Copyright 2016 CoreOS, Inc.
|
||||
//
|
||||
// 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{`version("key1") = "1"`, `version("key2") = "1"`},
|
||||
ifSucess: []string{"get key1", "get key2"},
|
||||
ifFail: []string{`put key1 "fail"`, `put key2 "fail"`},
|
||||
results: []string{"SUCCESS", "key1", "value1", "key2", "value2"},
|
||||
}
|
||||
if err := ctlV3Txn(cx, rqs); err != nil {
|
||||
cx.t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func txnTestFail(cx ctlCtx) {
|
||||
rqs := txnRequests{
|
||||
compare: []string{`version("key") < "0"`},
|
||||
ifSucess: []string{`put key "success"`},
|
||||
ifFail: []string{`put key "fail"`},
|
||||
results: []string{"FAILURE", "OK"},
|
||||
}
|
||||
if err := ctlV3Txn(cx, rqs); 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()
|
||||
}
|
@ -24,7 +24,6 @@ func TestCtlV3UserAddTimeout(t *testing.T) { testCtl(t, userAddTest, withDialT
|
||||
|
||||
func TestCtlV3UserDelete(t *testing.T) { testCtl(t, userDelTest) }
|
||||
func TestCtlV3UserPasswd(t *testing.T) { testCtl(t, userPasswdTest) }
|
||||
func TestCtlV3UserGrant(t *testing.T) { testCtl(t, userGrantTest) }
|
||||
|
||||
type userCmdDesc struct {
|
||||
args []string
|
||||
@ -107,43 +106,6 @@ func userPasswdTest(cx ctlCtx) {
|
||||
}
|
||||
}
|
||||
|
||||
func userGrantTest(cx ctlCtx) {
|
||||
// Add a role.
|
||||
if err := ctlV3Role(cx, []string{"add", "root"}, "Role root created"); err != nil {
|
||||
cx.t.Fatalf("userGrantTest: ctlV3Role error (%v)", err)
|
||||
}
|
||||
|
||||
cmdSet := []userCmdDesc{
|
||||
// Add a user name.
|
||||
{
|
||||
args: []string{"add", "username", "--interactive=false"},
|
||||
expectedStr: "User username created",
|
||||
stdIn: []string{"password"},
|
||||
},
|
||||
// Grant the previously added user a role.
|
||||
{
|
||||
args: []string{"grant", "username", "root"},
|
||||
expectedStr: "Role root is granted to user username",
|
||||
},
|
||||
// Supply a wrong user name.
|
||||
{
|
||||
args: []string{"grant", "username1", "root"},
|
||||
expectedStr: "user name not found",
|
||||
},
|
||||
// Supply a wrong role name.
|
||||
{
|
||||
args: []string{"grant", "username", "root1"},
|
||||
expectedStr: "role name not found",
|
||||
},
|
||||
}
|
||||
|
||||
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...)
|
||||
|
110
e2e/ctl_v3_watch_test.go
Normal file
110
e2e/ctl_v3_watch_test.go
Normal file
@ -0,0 +1,110 @@
|
||||
// Copyright 2016 CoreOS, Inc.
|
||||
//
|
||||
// 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
|
||||
}{
|
||||
{
|
||||
[]kv{{"sample", "value"}},
|
||||
[]string{"sample", "--rev", "1"},
|
||||
[]kv{{"sample", "value"}},
|
||||
},
|
||||
{
|
||||
[]kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}},
|
||||
[]string{"key", "--rev", "1", "--prefix"},
|
||||
[]kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}},
|
||||
},
|
||||
{
|
||||
[]kv{{"etcd", "revision_1"}, {"etcd", "revision_2"}, {"etcd", "revision_3"}},
|
||||
[]string{"etcd", "--rev", "2"},
|
||||
[]kv{{"etcd", "revision_2"}, {"etcd", "revision_3"}},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
go func() {
|
||||
for j := range tt.puts {
|
||||
if err := ctlV3Put(cx, tt.puts[j].key, tt.puts[j].val, ""); err != nil {
|
||||
cx.t.Fatalf("watchTest #%d-%d: ctlV3Put error (%v)", i, j, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
102
e2e/etcd_test.go
102
e2e/etcd_test.go
@ -17,15 +17,12 @@ package e2e
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/etcd/pkg/expect"
|
||||
"github.com/coreos/etcd/pkg/fileutil"
|
||||
"github.com/coreos/etcd/pkg/testutil"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -110,97 +107,6 @@ func configStandalone(cfg etcdProcessClusterConfig) *etcdProcessClusterConfig {
|
||||
return &ret
|
||||
}
|
||||
|
||||
func TestBasicOpsNoTLS(t *testing.T) { testBasicOpsPutGet(t, &configNoTLS) }
|
||||
func TestBasicOpsAutoTLS(t *testing.T) { testBasicOpsPutGet(t, &configAutoTLS) }
|
||||
func TestBasicOpsAllTLS(t *testing.T) { testBasicOpsPutGet(t, &configTLS) }
|
||||
func TestBasicOpsPeerTLS(t *testing.T) { testBasicOpsPutGet(t, &configPeerTLS) }
|
||||
func TestBasicOpsClientTLS(t *testing.T) { testBasicOpsPutGet(t, &configClientTLS) }
|
||||
func TestBasicOpsProxyNoTLS(t *testing.T) { testBasicOpsPutGet(t, &configWithProxy) }
|
||||
func TestBasicOpsProxyTLS(t *testing.T) { testBasicOpsPutGet(t, &configWithProxyTLS) }
|
||||
func TestBasicOpsProxyPeerTLS(t *testing.T) { testBasicOpsPutGet(t, &configWithProxyPeerTLS) }
|
||||
func TestBasicOpsClientBoth(t *testing.T) { testBasicOpsPutGet(t, &configClientBoth) }
|
||||
|
||||
func testBasicOpsPutGet(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)
|
||||
}
|
||||
}()
|
||||
|
||||
expectPut := `{"action":"set","node":{"key":"/testKey","value":"foo","`
|
||||
expectGet := `{"action":"get","node":{"key":"/testKey","value":"foo","`
|
||||
|
||||
if cfg.clientTLS == clientTLSAndNonTLS {
|
||||
if err := cURLPut(epc, "testKey", "foo", expectPut); err != nil {
|
||||
t.Fatalf("failed put with curl (%v)", err)
|
||||
}
|
||||
|
||||
if err := cURLGet(epc, "testKey", expectGet); err != nil {
|
||||
t.Fatalf("failed get with curl (%v)", err)
|
||||
}
|
||||
if err := cURLGetUseTLS(epc, "testKey", expectGet); err != nil {
|
||||
t.Fatalf("failed get with curl (%v)", err)
|
||||
}
|
||||
} else {
|
||||
if err := cURLPut(epc, "testKey", "foo", expectPut); err != nil {
|
||||
t.Fatalf("failed put with curl (%v)", err)
|
||||
}
|
||||
|
||||
if err := cURLGet(epc, "testKey", expectGet); err != nil {
|
||||
t.Fatalf("failed get with curl (%v)", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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, key string) []string {
|
||||
cmdArgs := []string{"curl"}
|
||||
acurl := clus.procs[rand.Intn(clus.cfg.clusterSize)].cfg.acurl
|
||||
|
||||
if clus.cfg.clientTLS == clientTLS {
|
||||
cmdArgs = append(cmdArgs, "--cacert", caPath, "--cert", certPath, "--key", privateKeyPath)
|
||||
}
|
||||
keyURL := acurl + "/v2/keys/testKey"
|
||||
cmdArgs = append(cmdArgs, "-L", keyURL)
|
||||
return cmdArgs
|
||||
}
|
||||
|
||||
func cURLPrefixArgsUseTLS(clus *etcdProcessCluster, key string) []string {
|
||||
cmdArgs := []string{"curl"}
|
||||
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
|
||||
keyURL := acurl + "/v2/keys/testKey"
|
||||
cmdArgs = append(cmdArgs, "-L", keyURL)
|
||||
return cmdArgs
|
||||
}
|
||||
|
||||
func cURLPut(clus *etcdProcessCluster, key, val, expected string) error {
|
||||
args := append(cURLPrefixArgs(clus, key), "-XPUT", "-d", "value="+val)
|
||||
return spawnWithExpect(args, expected)
|
||||
}
|
||||
|
||||
func cURLGet(clus *etcdProcessCluster, key, expected string) error {
|
||||
return spawnWithExpect(cURLPrefixArgs(clus, key), expected)
|
||||
}
|
||||
|
||||
func cURLGetUseTLS(clus *etcdProcessCluster, key, expected string) error {
|
||||
return spawnWithExpect(cURLPrefixArgsUseTLS(clus, key), expected)
|
||||
}
|
||||
|
||||
type etcdProcessCluster struct {
|
||||
cfg *etcdProcessClusterConfig
|
||||
procs []*etcdProcess
|
||||
@ -464,3 +370,11 @@ func (epc *etcdProcessCluster) proxies() []*etcdProcess {
|
||||
func (epc *etcdProcessCluster) backends() []*etcdProcess {
|
||||
return epc.procs[:epc.cfg.clusterSize]
|
||||
}
|
||||
|
||||
func (epc *etcdProcessCluster) endpoints() []string {
|
||||
eps := make([]string, epc.cfg.clusterSize)
|
||||
for i, ep := range epc.backends() {
|
||||
eps[i] = ep.cfg.acurl
|
||||
}
|
||||
return eps
|
||||
}
|
||||
|
113
e2e/v2_curl_test.go
Normal file
113
e2e/v2_curl_test.go
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright 2016 CoreOS, Inc.
|
||||
//
|
||||
// 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 (
|
||||
"math/rand"
|
||||
"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)
|
||||
}
|
||||
}()
|
||||
|
||||
expectPut := `{"action":"set","node":{"key":"/testKey","value":"foo","`
|
||||
expectGet := `{"action":"get","node":{"key":"/testKey","value":"foo","`
|
||||
|
||||
if cfg.clientTLS == clientTLSAndNonTLS {
|
||||
if err := cURLPut(epc, "testKey", "foo", expectPut); err != nil {
|
||||
t.Fatalf("failed put with curl (%v)", err)
|
||||
}
|
||||
|
||||
if err := cURLGet(epc, "testKey", expectGet); err != nil {
|
||||
t.Fatalf("failed get with curl (%v)", err)
|
||||
}
|
||||
if err := cURLGetUseTLS(epc, "testKey", expectGet); err != nil {
|
||||
t.Fatalf("failed get with curl (%v)", err)
|
||||
}
|
||||
} else {
|
||||
if err := cURLPut(epc, "testKey", "foo", expectPut); err != nil {
|
||||
t.Fatalf("failed put with curl (%v)", err)
|
||||
}
|
||||
|
||||
if err := cURLGet(epc, "testKey", expectGet); err != nil {
|
||||
t.Fatalf("failed get with curl (%v)", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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, key string) []string {
|
||||
cmdArgs := []string{"curl"}
|
||||
acurl := clus.procs[rand.Intn(clus.cfg.clusterSize)].cfg.acurl
|
||||
|
||||
if clus.cfg.clientTLS == clientTLS {
|
||||
cmdArgs = append(cmdArgs, "--cacert", caPath, "--cert", certPath, "--key", privateKeyPath)
|
||||
}
|
||||
keyURL := acurl + "/v2/keys/testKey"
|
||||
cmdArgs = append(cmdArgs, "-L", keyURL)
|
||||
return cmdArgs
|
||||
}
|
||||
|
||||
func cURLPrefixArgsUseTLS(clus *etcdProcessCluster, key string) []string {
|
||||
cmdArgs := []string{"curl"}
|
||||
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
|
||||
keyURL := acurl + "/v2/keys/testKey"
|
||||
cmdArgs = append(cmdArgs, "-L", keyURL)
|
||||
return cmdArgs
|
||||
}
|
||||
|
||||
func cURLPut(clus *etcdProcessCluster, key, val, expected string) error {
|
||||
args := append(cURLPrefixArgs(clus, key), "-XPUT", "-d", "value="+val)
|
||||
return spawnWithExpect(args, expected)
|
||||
}
|
||||
|
||||
func cURLGet(clus *etcdProcessCluster, key, expected string) error {
|
||||
return spawnWithExpect(cURLPrefixArgs(clus, key), expected)
|
||||
}
|
||||
|
||||
func cURLGetUseTLS(clus *etcdProcessCluster, key, expected string) error {
|
||||
return spawnWithExpect(cURLPrefixArgsUseTLS(clus, key), expected)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user