diff --git a/etcdctl/ctlv3/command/txn_command.go b/etcdctl/ctlv3/command/txn_command.go index e20b6d95a..b05a7ea66 100644 --- a/etcdctl/ctlv3/command/txn_command.go +++ b/etcdctl/ctlv3/command/txn_command.go @@ -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 diff --git a/etcdctl/ctlv3/command/util.go b/etcdctl/ctlv3/command/util.go index 86f83be36..8338ef33d 100644 --- a/etcdctl/ctlv3/command/util.go +++ b/etcdctl/ctlv3/command/util.go @@ -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 { diff --git a/etcdctl/ctlv3/command/watch_command.go b/etcdctl/ctlv3/command/watch_command.go index 288265004..d8592cb4e 100644 --- a/etcdctl/ctlv3/command/watch_command.go +++ b/etcdctl/ctlv3/command/watch_command.go @@ -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 diff --git a/go.mod b/go.mod index 0f0e34064..86920d6c5 100644 --- a/go.mod +++ b/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 diff --git a/tests/common/txn_test.go b/tests/common/txn_test.go new file mode 100644 index 000000000..3e982b89c --- /dev/null +++ b/tests/common/txn_test.go @@ -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 +} diff --git a/tests/e2e/ctl_v3_txn_test.go b/tests/e2e/ctl_v3_txn_test.go index af5011607..3b9cc8216 100644 --- a/tests/e2e/ctl_v3_txn_test.go +++ b/tests/e2e/ctl_v3_txn_test.go @@ -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 diff --git a/tests/framework/config/client.go b/tests/framework/config/client.go index 6b39365a1..e8ce9ea79 100644 --- a/tests/framework/config/client.go +++ b/tests/framework/config/client.go @@ -43,6 +43,10 @@ type DeleteOptions struct { End string } +type TxnOptions struct { + Interactive bool +} + type CompactOption struct { Physical bool Timeout time.Duration diff --git a/tests/framework/e2e/e2e_test.go b/tests/framework/e2e/e2e_test.go new file mode 100644 index 000000000..00059df81 --- /dev/null +++ b/tests/framework/e2e/e2e_test.go @@ -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") + } +} diff --git a/tests/framework/e2e/etcdctl.go b/tests/framework/e2e/etcdctl.go index b4a8b6bf9..802de6d59 100644 --- a/tests/framework/e2e/etcdctl.go +++ b/tests/framework/e2e/etcdctl.go @@ -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 { diff --git a/tests/framework/integration.go b/tests/framework/integration.go index 8140bd5dd..01de255c7 100644 --- a/tests/framework/integration.go +++ b/tests/framework/integration.go @@ -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 +} diff --git a/tests/framework/interface.go b/tests/framework/interface.go index 43945a41c..03390f9dd 100644 --- a/tests/framework/interface.go +++ b/tests/framework/interface.go @@ -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) } diff --git a/tests/go.mod b/tests/go.mod index d1fbadc89..f5ec96b06 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -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 diff --git a/tests/go.sum b/tests/go.sum index 5f30eeedd..da23247da 100644 --- a/tests/go.sum +++ b/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=