mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #14000 from vimalk78/e2e-txn-test
tests: Migrate Txn tests to common framework
This commit is contained in:
commit
5fd69102ce
@ -23,7 +23,7 @@ import (
|
||||
"strings"
|
||||
|
||||
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||
"go.etcd.io/etcd/client/v3"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
"go.etcd.io/etcd/pkg/v3/cobrautl"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@ -85,7 +85,7 @@ func readCompares(r *bufio.Reader) (cmps []clientv3.Cmp) {
|
||||
break
|
||||
}
|
||||
|
||||
cmp, err := parseCompare(line)
|
||||
cmp, err := ParseCompare(line)
|
||||
if err != nil {
|
||||
cobrautl.ExitWithError(cobrautl.ExitInvalidInput, err)
|
||||
}
|
||||
@ -119,7 +119,7 @@ func readOps(r *bufio.Reader) (ops []clientv3.Op) {
|
||||
}
|
||||
|
||||
func parseRequestUnion(line string) (*clientv3.Op, error) {
|
||||
args := argify(line)
|
||||
args := Argify(line)
|
||||
if len(args) < 2 {
|
||||
return nil, fmt.Errorf("invalid txn compare request: %s", line)
|
||||
}
|
||||
@ -153,7 +153,7 @@ func parseRequestUnion(line string) (*clientv3.Op, error) {
|
||||
return &op, nil
|
||||
}
|
||||
|
||||
func parseCompare(line string) (*clientv3.Cmp, error) {
|
||||
func ParseCompare(line string) (*clientv3.Cmp, error) {
|
||||
var (
|
||||
key string
|
||||
op string
|
||||
|
@ -27,7 +27,7 @@ import (
|
||||
"time"
|
||||
|
||||
pb "go.etcd.io/etcd/api/v3/mvccpb"
|
||||
"go.etcd.io/etcd/client/v3"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
"go.etcd.io/etcd/pkg/v3/cobrautl"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@ -56,7 +56,7 @@ func addHexPrefix(s string) string {
|
||||
return string(ns)
|
||||
}
|
||||
|
||||
func argify(s string) []string {
|
||||
func Argify(s string) []string {
|
||||
r := regexp.MustCompile(`"(?:[^"\\]|\\.)*"|'[^']*'|[^'"\s]\S*[^'"\s]?`)
|
||||
args := r.FindAllString(s, -1)
|
||||
for i := range args {
|
||||
|
@ -23,7 +23,7 @@ import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"go.etcd.io/etcd/client/v3"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
"go.etcd.io/etcd/pkg/v3/cobrautl"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@ -103,7 +103,7 @@ func watchInteractiveFunc(cmd *cobra.Command, osArgs []string, envKey, envRange
|
||||
}
|
||||
l = strings.TrimSuffix(l, "\n")
|
||||
|
||||
args := argify(l)
|
||||
args := Argify(l)
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintf(os.Stderr, "Invalid command: %s (watch and progress supported)\n", l)
|
||||
continue
|
||||
|
1
go.mod
1
go.mod
@ -57,7 +57,6 @@ require (
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
|
199
tests/common/txn_test.go
Normal file
199
tests/common/txn_test.go
Normal file
@ -0,0 +1,199 @@
|
||||
// Copyright 2022 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 common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
"go.etcd.io/etcd/tests/v3/framework/config"
|
||||
"go.etcd.io/etcd/tests/v3/framework/testutils"
|
||||
)
|
||||
|
||||
type txnReq struct {
|
||||
compare []string
|
||||
ifSucess []string
|
||||
ifFail []string
|
||||
results []string
|
||||
}
|
||||
|
||||
func TestTxnSucc(t *testing.T) {
|
||||
tcs := []struct {
|
||||
name string
|
||||
config config.ClusterConfig
|
||||
}{
|
||||
{
|
||||
name: "NoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1},
|
||||
},
|
||||
{
|
||||
name: "PeerTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.ManualTLS},
|
||||
},
|
||||
{
|
||||
name: "PeerAutoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.AutoTLS},
|
||||
},
|
||||
{
|
||||
name: "ClientTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.ManualTLS},
|
||||
},
|
||||
{
|
||||
name: "ClientAutoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.AutoTLS},
|
||||
},
|
||||
}
|
||||
reqs := []txnReq{
|
||||
{
|
||||
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", "OK"},
|
||||
},
|
||||
{
|
||||
compare: []string{`version("key \"with\" space") = "1"`},
|
||||
ifSucess: []string{`get "key \"with\" space"`},
|
||||
results: []string{"SUCCESS", `key "with" space`, "value \x23"},
|
||||
},
|
||||
}
|
||||
testRunner.BeforeTest(t)
|
||||
for _, cfg := range tcs {
|
||||
t.Run(cfg.name, func(t *testing.T) {
|
||||
clus := testRunner.NewCluster(t, cfg.config)
|
||||
defer clus.Close()
|
||||
cc := clus.Client()
|
||||
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
|
||||
if err := cc.Put("key1", "value1", config.PutOptions{}); err != nil {
|
||||
t.Fatalf("could not create key:%s, value:%s", "key1", "value1")
|
||||
}
|
||||
if err := cc.Put("key2", "value2", config.PutOptions{}); err != nil {
|
||||
t.Fatalf("could not create key:%s, value:%s", "key2", "value2")
|
||||
}
|
||||
for _, req := range reqs {
|
||||
resp, err := cc.Txn(req.compare, req.ifSucess, req.ifFail, config.TxnOptions{
|
||||
Interactive: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Txn returned error: %s", err)
|
||||
}
|
||||
assert.Equal(t, req.results, getRespValues(resp))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTxnFail(t *testing.T) {
|
||||
tcs := []struct {
|
||||
name string
|
||||
config config.ClusterConfig
|
||||
}{
|
||||
{
|
||||
name: "NoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1},
|
||||
},
|
||||
{
|
||||
name: "PeerTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.ManualTLS},
|
||||
},
|
||||
{
|
||||
name: "PeerAutoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.AutoTLS},
|
||||
},
|
||||
{
|
||||
name: "ClientTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.ManualTLS},
|
||||
},
|
||||
{
|
||||
name: "ClientAutoTLS",
|
||||
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.AutoTLS},
|
||||
},
|
||||
}
|
||||
reqs := []txnReq{
|
||||
{
|
||||
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"},
|
||||
},
|
||||
}
|
||||
testRunner.BeforeTest(t)
|
||||
for _, cfg := range tcs {
|
||||
t.Run(cfg.name, func(t *testing.T) {
|
||||
clus := testRunner.NewCluster(t, cfg.config)
|
||||
defer clus.Close()
|
||||
cc := clus.Client()
|
||||
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
|
||||
if err := cc.Put("key1", "value1", config.PutOptions{}); err != nil {
|
||||
t.Fatalf("could not create key:%s, value:%s", "key1", "value1")
|
||||
}
|
||||
for _, req := range reqs {
|
||||
resp, err := cc.Txn(req.compare, req.ifSucess, req.ifFail, config.TxnOptions{
|
||||
Interactive: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Txn returned error: %s", err)
|
||||
}
|
||||
assert.Equal(t, req.results, getRespValues(resp))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func getRespValues(r *clientv3.TxnResponse) []string {
|
||||
ss := []string{}
|
||||
if r.Succeeded {
|
||||
ss = append(ss, "SUCCESS")
|
||||
} else {
|
||||
ss = append(ss, "FAILURE")
|
||||
}
|
||||
for _, resp := range r.Responses {
|
||||
switch v := resp.Response.(type) {
|
||||
case *pb.ResponseOp_ResponseDeleteRange:
|
||||
r := (clientv3.DeleteResponse)(*v.ResponseDeleteRange)
|
||||
ss = append(ss, fmt.Sprintf("%d", r.Deleted))
|
||||
case *pb.ResponseOp_ResponsePut:
|
||||
r := (clientv3.PutResponse)(*v.ResponsePut)
|
||||
ss = append(ss, "OK")
|
||||
if r.PrevKv != nil {
|
||||
ss = append(ss, string(r.PrevKv.Key), string(r.PrevKv.Value))
|
||||
}
|
||||
case *pb.ResponseOp_ResponseRange:
|
||||
r := (clientv3.GetResponse)(*v.ResponseRange)
|
||||
for _, kv := range r.Kvs {
|
||||
ss = append(ss, string(kv.Key), string(kv.Value))
|
||||
}
|
||||
default:
|
||||
ss = append(ss, fmt.Sprintf("\"Unknown\" : %q\n", fmt.Sprintf("%+v", v)))
|
||||
}
|
||||
}
|
||||
return ss
|
||||
}
|
@ -15,84 +15,9 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"go.etcd.io/etcd/tests/v3/framework/e2e"
|
||||
)
|
||||
|
||||
func TestCtlV3TxnInteractiveSuccess(t *testing.T) {
|
||||
testCtl(t, txnTestSuccess, withInteractive())
|
||||
}
|
||||
func TestCtlV3TxnInteractiveSuccessNoTLS(t *testing.T) {
|
||||
testCtl(t, txnTestSuccess, withInteractive(), withCfg(*e2e.NewConfigNoTLS()))
|
||||
}
|
||||
func TestCtlV3TxnInteractiveSuccessClientTLS(t *testing.T) {
|
||||
testCtl(t, txnTestSuccess, withInteractive(), withCfg(*e2e.NewConfigClientTLS()))
|
||||
}
|
||||
func TestCtlV3TxnInteractiveSuccessPeerTLS(t *testing.T) {
|
||||
testCtl(t, txnTestSuccess, withInteractive(), withCfg(*e2e.NewConfigPeerTLS()))
|
||||
}
|
||||
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
|
||||
|
@ -43,6 +43,10 @@ type DeleteOptions struct {
|
||||
End string
|
||||
}
|
||||
|
||||
type TxnOptions struct {
|
||||
Interactive bool
|
||||
}
|
||||
|
||||
type CompactOption struct {
|
||||
Physical bool
|
||||
Timeout time.Duration
|
||||
|
39
tests/framework/e2e/e2e_test.go
Normal file
39
tests/framework/e2e/e2e_test.go
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2022 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"
|
||||
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
)
|
||||
|
||||
func Test_AddTxnResponse(t *testing.T) {
|
||||
jsonData := `{"header":{"cluster_id":238453183653593855,"member_id":14578408409545168728,"revision":3,"raft_term":2},"succeeded":true,"responses":[{"Response":{"response_range":{"header":{"revision":3},"kvs":[{"key":"a2V5MQ==","create_revision":2,"mod_revision":2,"version":1,"value":"dmFsdWUx"}],"count":1}}},{"Response":{"response_range":{"header":{"revision":3},"kvs":[{"key":"a2V5Mg==","create_revision":3,"mod_revision":3,"version":1,"value":"dmFsdWUy"}],"count":1}}}]}`
|
||||
var resp clientv3.TxnResponse
|
||||
AddTxnResponse(&resp, jsonData)
|
||||
err := json.Unmarshal([]byte(jsonData), &resp)
|
||||
if err != nil {
|
||||
t.Errorf("json Unmarshal failed. err: %s", err)
|
||||
}
|
||||
enc, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
t.Errorf("json Marshal failed. err: %s", err)
|
||||
}
|
||||
if string(enc) != jsonData {
|
||||
t.Error("could not get original message after encoding")
|
||||
}
|
||||
}
|
@ -17,10 +17,12 @@ package e2e
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go.etcd.io/etcd/api/v3/authpb"
|
||||
"go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
"go.etcd.io/etcd/tests/v3/framework/config"
|
||||
)
|
||||
@ -145,6 +147,101 @@ func (ctl *EtcdctlV3) Delete(key string, o config.DeleteOptions) (*clientv3.Dele
|
||||
return &resp, err
|
||||
}
|
||||
|
||||
func (ctl *EtcdctlV3) Txn(compares, ifSucess, ifFail []string, o config.TxnOptions) (*clientv3.TxnResponse, error) {
|
||||
args := ctl.cmdArgs()
|
||||
args = append(args, "txn")
|
||||
if o.Interactive {
|
||||
args = append(args, "--interactive")
|
||||
}
|
||||
args = append(args, "-w", "json", "--hex=true")
|
||||
cmd, err := SpawnCmd(args, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = cmd.Expect("compares:")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, cmp := range compares {
|
||||
if err := cmd.Send(cmp + "\r"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := cmd.Send("\r"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = cmd.Expect("success requests (get, put, del):")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, req := range ifSucess {
|
||||
if err = cmd.Send(req + "\r"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err = cmd.Send("\r"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = cmd.Expect("failure requests (get, put, del):")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, req := range ifFail {
|
||||
if err = cmd.Send(req + "\r"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err = cmd.Send("\r"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var line string
|
||||
line, err = cmd.Expect("header")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var resp clientv3.TxnResponse
|
||||
AddTxnResponse(&resp, line)
|
||||
err = json.Unmarshal([]byte(line), &resp)
|
||||
return &resp, err
|
||||
}
|
||||
|
||||
// AddTxnResponse looks for ResponseOp json tags and adds the objects for json decoding
|
||||
func AddTxnResponse(resp *clientv3.TxnResponse, jsonData string) {
|
||||
if resp == nil {
|
||||
return
|
||||
}
|
||||
if resp.Responses == nil {
|
||||
resp.Responses = []*etcdserverpb.ResponseOp{}
|
||||
}
|
||||
jd := json.NewDecoder(strings.NewReader(jsonData))
|
||||
for {
|
||||
t, e := jd.Token()
|
||||
if e == io.EOF {
|
||||
break
|
||||
}
|
||||
if t == "response_range" {
|
||||
resp.Responses = append(resp.Responses, &etcdserverpb.ResponseOp{
|
||||
Response: &etcdserverpb.ResponseOp_ResponseRange{},
|
||||
})
|
||||
}
|
||||
if t == "response_put" {
|
||||
resp.Responses = append(resp.Responses, &etcdserverpb.ResponseOp{
|
||||
Response: &etcdserverpb.ResponseOp_ResponsePut{},
|
||||
})
|
||||
}
|
||||
if t == "response_delete_range" {
|
||||
resp.Responses = append(resp.Responses, &etcdserverpb.ResponseOp{
|
||||
Response: &etcdserverpb.ResponseOp_ResponseDeleteRange{},
|
||||
})
|
||||
}
|
||||
if t == "response_txn" {
|
||||
resp.Responses = append(resp.Responses, &etcdserverpb.ResponseOp{
|
||||
Response: &etcdserverpb.ResponseOp_ResponseTxn{},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
func (ctl *EtcdctlV3) MemberList() (*clientv3.MemberListResponse, error) {
|
||||
cmd, err := SpawnCmd(ctl.cmdArgs("member", "list", "-w", "json"), nil)
|
||||
if err != nil {
|
||||
|
@ -17,6 +17,7 @@ package framework
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
healthpb "google.golang.org/grpc/health/grpc_health_v1"
|
||||
@ -24,6 +25,7 @@ import (
|
||||
"go.etcd.io/etcd/client/pkg/v3/testutil"
|
||||
"go.etcd.io/etcd/client/pkg/v3/transport"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
etcdctlcmd "go.etcd.io/etcd/etcdctl/v3/ctlv3/command"
|
||||
|
||||
"go.etcd.io/etcd/tests/v3/framework/config"
|
||||
"go.etcd.io/etcd/tests/v3/framework/integration"
|
||||
@ -335,3 +337,46 @@ func (c integrationClient) RoleRevokePermission(role string, key, rangeEnd strin
|
||||
func (c integrationClient) RoleDelete(role string) (*clientv3.AuthRoleDeleteResponse, error) {
|
||||
return c.Client.RoleDelete(context.Background(), role)
|
||||
}
|
||||
|
||||
func (c integrationClient) Txn(compares, ifSucess, ifFail []string, o config.TxnOptions) (*clientv3.TxnResponse, error) {
|
||||
txn := c.Client.Txn(context.Background())
|
||||
cmps := []clientv3.Cmp{}
|
||||
for _, c := range compares {
|
||||
cmp, err := etcdctlcmd.ParseCompare(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmps = append(cmps, *cmp)
|
||||
}
|
||||
succOps, err := getOps(ifSucess)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
failOps, err := getOps(ifFail)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txnrsp, err := txn.
|
||||
If(cmps...).
|
||||
Then(succOps...).
|
||||
Else(failOps...).
|
||||
Commit()
|
||||
return txnrsp, err
|
||||
}
|
||||
|
||||
func getOps(ss []string) ([]clientv3.Op, error) {
|
||||
ops := []clientv3.Op{}
|
||||
for _, s := range ss {
|
||||
s = strings.TrimSpace(s)
|
||||
args := etcdctlcmd.Argify(s)
|
||||
switch args[0] {
|
||||
case "get":
|
||||
ops = append(ops, clientv3.OpGet(args[1]))
|
||||
case "put":
|
||||
ops = append(ops, clientv3.OpPut(args[1], args[2]))
|
||||
case "del":
|
||||
ops = append(ops, clientv3.OpDelete(args[1]))
|
||||
}
|
||||
}
|
||||
return ops, nil
|
||||
}
|
||||
|
@ -60,10 +60,13 @@ type Client interface {
|
||||
UserList() (*clientv3.AuthUserListResponse, error)
|
||||
UserDelete(name string) (*clientv3.AuthUserDeleteResponse, error)
|
||||
UserChangePass(user, newPass string) error
|
||||
|
||||
RoleAdd(name string) (*clientv3.AuthRoleAddResponse, error)
|
||||
RoleGrantPermission(name string, key, rangeEnd string, permType clientv3.PermissionType) (*clientv3.AuthRoleGrantPermissionResponse, error)
|
||||
RoleGet(role string) (*clientv3.AuthRoleGetResponse, error)
|
||||
RoleList() (*clientv3.AuthRoleListResponse, error)
|
||||
RoleRevokePermission(role string, key, rangeEnd string) (*clientv3.AuthRoleRevokePermissionResponse, error)
|
||||
RoleDelete(role string) (*clientv3.AuthRoleDeleteResponse, error)
|
||||
|
||||
Txn(compares, ifSucess, ifFail []string, o config.TxnOptions) (*clientv3.TxnResponse, error)
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ require (
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.6.0-alpha.0
|
||||
go.etcd.io/etcd/client/v2 v2.306.0-alpha.0
|
||||
go.etcd.io/etcd/client/v3 v3.6.0-alpha.0
|
||||
go.etcd.io/etcd/etcdctl/v3 v3.6.0-alpha.0
|
||||
go.etcd.io/etcd/etcdutl/v3 v3.6.0-alpha.0
|
||||
go.etcd.io/etcd/pkg/v3 v3.6.0-alpha.0
|
||||
go.etcd.io/etcd/raft/v3 v3.6.0-alpha.0
|
||||
@ -47,6 +48,7 @@ require (
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/speakeasy v0.1.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
@ -58,7 +60,10 @@ require (
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
@ -81,6 +86,7 @@ require (
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect
|
||||
|
17
tests/go.sum
17
tests/go.sum
@ -46,6 +46,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ=
|
||||
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
@ -92,6 +94,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
|
||||
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
@ -207,6 +211,13 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
@ -217,6 +228,7 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@ -424,6 +436,7 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -449,6 +462,8 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@ -607,6 +622,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
|
Loading…
x
Reference in New Issue
Block a user