From e2dffb00cfaaa50ce9443a8b7662f536d1995861 Mon Sep 17 00:00:00 2001 From: Chao Chen Date: Wed, 10 Aug 2022 16:43:21 -0700 Subject: [PATCH] common tests framework: cluster client creation fail with invalid auth Signed-off-by: Chao Chen --- tests/common/alarm_test.go | 19 ++++----- tests/common/compact_test.go | 11 ++--- tests/common/defrag_test.go | 7 ++-- tests/common/endpoint_test.go | 9 +++-- tests/common/kv_test.go | 6 +-- tests/common/lease_test.go | 10 ++--- tests/common/member_test.go | 6 ++- tests/common/role_test.go | 10 ++--- tests/common/status_test.go | 3 +- tests/common/txn_test.go | 4 +- tests/common/user_test.go | 10 ++--- tests/framework/config/client.go | 9 +++++ tests/framework/config/cluster.go | 2 + tests/framework/e2e.go | 25 ++++++++++-- tests/framework/e2e/etcdctl.go | 56 ++++++++++++++++++++++++++ tests/framework/integration.go | 53 ++++++++++++++++++++++-- tests/framework/integration/cluster.go | 53 ++++++++++++++---------- tests/framework/interface.go | 10 ++++- 18 files changed, 229 insertions(+), 74 deletions(-) diff --git a/tests/common/alarm_test.go b/tests/common/alarm_test.go index 9f0ace141716..4f48dbfffe6b 100644 --- a/tests/common/alarm_test.go +++ b/tests/common/alarm_test.go @@ -32,17 +32,18 @@ func TestAlarm(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, config.ClusterConfig{ClusterSize: 1, QuotaBackendBytes: int64(13 * os.Getpagesize())}) defer clus.Close() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { // test small put still works smallbuf := strings.Repeat("a", 64) - if err := clus.Client().Put("1st_test", smallbuf, config.PutOptions{}); err != nil { + if err := cc.Put("1st_test", smallbuf, config.PutOptions{}); err != nil { t.Fatalf("alarmTest: put kv error (%v)", err) } // write some chunks to fill up the database buf := strings.Repeat("b", os.Getpagesize()) for { - if err := clus.Client().Put("2nd_test", buf, config.PutOptions{}); err != nil { + if err := cc.Put("2nd_test", buf, config.PutOptions{}); err != nil { if !strings.Contains(err.Error(), "etcdserver: mvcc: database space exceeded") { t.Fatal(err) } @@ -51,20 +52,20 @@ func TestAlarm(t *testing.T) { } // quota alarm should now be on - alarmResp, err := clus.Client().AlarmList() + alarmResp, err := cc.AlarmList() if err != nil { t.Fatalf("alarmTest: Alarm error (%v)", err) } // check that Put is rejected when alarm is on - if err := clus.Client().Put("3rd_test", smallbuf, config.PutOptions{}); err != nil { + if err := cc.Put("3rd_test", smallbuf, config.PutOptions{}); err != nil { if !strings.Contains(err.Error(), "etcdserver: mvcc: database space exceeded") { t.Fatal(err) } } // get latest revision to compact - sresp, err := clus.Client().Status() + sresp, err := cc.Status() if err != nil { t.Fatalf("get endpoint status error: %v", err) } @@ -77,12 +78,12 @@ func TestAlarm(t *testing.T) { } // make some space - _, err = clus.Client().Compact(rvs, config.CompactOption{Physical: true, Timeout: 10 * time.Second}) + _, err = cc.Compact(rvs, config.CompactOption{Physical: true, Timeout: 10 * time.Second}) if err != nil { t.Fatalf("alarmTest: Compact error (%v)", err) } - if err = clus.Client().Defragment(config.DefragOption{Timeout: 10 * time.Second}); err != nil { + if err = cc.Defragment(config.DefragOption{Timeout: 10 * time.Second}); err != nil { t.Fatalf("alarmTest: defrag error (%v)", err) } @@ -92,14 +93,14 @@ func TestAlarm(t *testing.T) { MemberID: alarm.MemberID, Alarm: alarm.Alarm, } - _, err = clus.Client().AlarmDisarm(alarmMember) + _, err = cc.AlarmDisarm(alarmMember) if err != nil { t.Fatalf("alarmTest: Alarm error (%v)", err) } } // put one more key below quota - if err := clus.Client().Put("4th_test", smallbuf, config.PutOptions{}); err != nil { + if err := cc.Put("4th_test", smallbuf, config.PutOptions{}); err != nil { t.Fatal(err) } }) diff --git a/tests/common/compact_test.go b/tests/common/compact_test.go index 2b1031daedab..62e07514d7d5 100644 --- a/tests/common/compact_test.go +++ b/tests/common/compact_test.go @@ -47,14 +47,15 @@ func TestCompact(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, config.ClusterConfig{ClusterSize: 3}) defer clus.Close() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { var kvs = []testutils.KV{{Key: "key", Val: "val1"}, {Key: "key", Val: "val2"}, {Key: "key", Val: "val3"}} for i := range kvs { - if err := clus.Client().Put(kvs[i].Key, kvs[i].Val, config.PutOptions{}); err != nil { + if err := cc.Put(kvs[i].Key, kvs[i].Val, config.PutOptions{}); err != nil { t.Fatalf("compactTest #%d: put kv error (%v)", i, err) } } - get, err := clus.Client().Get("key", config.GetOptions{Revision: 3}) + get, err := cc.Get("key", config.GetOptions{Revision: 3}) if err != nil { t.Fatalf("compactTest: Get kv by revision error (%v)", err) } @@ -62,12 +63,12 @@ func TestCompact(t *testing.T) { getkvs := testutils.KeyValuesFromGetResponse(get) assert.Equal(t, kvs[1:2], getkvs) - _, err = clus.Client().Compact(4, tc.options) + _, err = cc.Compact(4, tc.options) if err != nil { t.Fatalf("compactTest: Compact error (%v)", err) } - get, err = clus.Client().Get("key", config.GetOptions{Revision: 3}) + get, err = cc.Get("key", config.GetOptions{Revision: 3}) if err != nil { if !strings.Contains(err.Error(), "required revision has been compacted") { t.Fatalf("compactTest: Get compact key error (%v)", err) @@ -76,7 +77,7 @@ func TestCompact(t *testing.T) { t.Fatalf("expected '...has been compacted' error, got ") } - _, err = clus.Client().Compact(2, tc.options) + _, err = cc.Compact(2, tc.options) if err != nil { if !strings.Contains(err.Error(), "required revision has been compacted") { t.Fatal(err) diff --git a/tests/common/defrag_test.go b/tests/common/defrag_test.go index 295149312e33..6e4ab9bf4a5b 100644 --- a/tests/common/defrag_test.go +++ b/tests/common/defrag_test.go @@ -29,20 +29,21 @@ func TestDefragOnline(t *testing.T) { defer cancel() options := config.DefragOption{Timeout: 10 * time.Second} clus := testRunner.NewCluster(ctx, t, config.ClusterConfig{ClusterSize: 3}) + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { defer clus.Close() var kvs = []testutils.KV{{Key: "key", Val: "val1"}, {Key: "key", Val: "val2"}, {Key: "key", Val: "val3"}} for i := range kvs { - if err := clus.Client().Put(kvs[i].Key, kvs[i].Val, config.PutOptions{}); err != nil { + if err := cc.Put(kvs[i].Key, kvs[i].Val, config.PutOptions{}); err != nil { t.Fatalf("compactTest #%d: put kv error (%v)", i, err) } } - _, err := clus.Client().Compact(4, config.CompactOption{Physical: true, Timeout: 10 * time.Second}) + _, err := cc.Compact(4, config.CompactOption{Physical: true, Timeout: 10 * time.Second}) if err != nil { t.Fatalf("defrag_test: compact with revision error (%v)", err) } - if err = clus.Client().Defragment(options); err != nil { + if err = cc.Defragment(options); err != nil { t.Fatalf("defrag_test: defrag error (%v)", err) } }) diff --git a/tests/common/endpoint_test.go b/tests/common/endpoint_test.go index d1ba6da18b6d..b4427ef116fc 100644 --- a/tests/common/endpoint_test.go +++ b/tests/common/endpoint_test.go @@ -29,8 +29,9 @@ func TestEndpointStatus(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, config.ClusterConfig{ClusterSize: 3}) defer clus.Close() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { - _, err := clus.Client().Status() + _, err := cc.Status() if err != nil { t.Fatalf("get endpoint status error: %v", err) } @@ -43,8 +44,9 @@ func TestEndpointHashKV(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, config.ClusterConfig{ClusterSize: 3}) defer clus.Close() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { - _, err := clus.Client().HashKV(0) + _, err := cc.HashKV(0) if err != nil { t.Fatalf("get endpoint hashkv error: %v", err) } @@ -57,8 +59,9 @@ func TestEndpointHealth(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, config.ClusterConfig{ClusterSize: 3}) defer clus.Close() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { - if err := clus.Client().Health(); err != nil { + if err := cc.Health(); err != nil { t.Fatalf("get endpoint health error: %v", err) } }) diff --git a/tests/common/kv_test.go b/tests/common/kv_test.go index ef2540ceddbd..be32450be26a 100644 --- a/tests/common/kv_test.go +++ b/tests/common/kv_test.go @@ -33,7 +33,7 @@ func TestKVPut(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, tc.config) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { key, value := "foo", "bar" @@ -67,7 +67,7 @@ func TestKVGet(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, tc.config) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { var ( @@ -127,7 +127,7 @@ func TestKVDelete(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, tc.config) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { kvs := []string{"a", "b", "c", "c/abc", "d"} tests := []struct { diff --git a/tests/common/lease_test.go b/tests/common/lease_test.go index 0281b3cfe385..3a3cb79153f6 100644 --- a/tests/common/lease_test.go +++ b/tests/common/lease_test.go @@ -59,7 +59,7 @@ func TestLeaseGrantTimeToLive(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, tc.config) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { ttl := int64(10) @@ -103,7 +103,7 @@ func TestLeaseGrantAndList(t *testing.T) { t.Logf("Creating cluster...") clus := testRunner.NewCluster(ctx, t, tc.config) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) t.Logf("Created cluster and client") testutils.ExecuteUntil(ctx, t, func() { createdLeases := []clientv3.LeaseID{} @@ -150,7 +150,7 @@ func TestLeaseGrantTimeToLiveExpired(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, tc.config) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { leaseResp, err := cc.Grant(2) @@ -187,7 +187,7 @@ func TestLeaseGrantKeepAliveOnce(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, tc.config) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { leaseResp, err := cc.Grant(2) @@ -216,7 +216,7 @@ func TestLeaseGrantRevoke(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, tc.config) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { leaseResp, err := cc.Grant(20) diff --git a/tests/common/member_test.go b/tests/common/member_test.go index f1b6be3aef03..fd559c861f85 100644 --- a/tests/common/member_test.go +++ b/tests/common/member_test.go @@ -16,9 +16,11 @@ package common import ( "context" - "go.etcd.io/etcd/tests/v3/framework/testutils" "testing" "time" + + "go.etcd.io/etcd/tests/v3/framework/config" + "go.etcd.io/etcd/tests/v3/framework/testutils" ) func TestMemberList(t *testing.T) { @@ -30,7 +32,7 @@ func TestMemberList(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, tc.config) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { resp, err := cc.MemberList() diff --git a/tests/common/role_test.go b/tests/common/role_test.go index 6b1382aa2510..b3ed27a87aa1 100644 --- a/tests/common/role_test.go +++ b/tests/common/role_test.go @@ -34,7 +34,7 @@ func TestRoleAdd_Simple(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, tc.config) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { _, err := cc.RoleAdd("root") @@ -52,7 +52,7 @@ func TestRoleAdd_Error(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, config.ClusterConfig{ClusterSize: 1}) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { _, err := cc.RoleAdd("test-role") if err != nil { @@ -75,7 +75,7 @@ func TestRootRole(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, config.ClusterConfig{ClusterSize: 1}) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { _, err := cc.RoleAdd("root") if err != nil { @@ -105,7 +105,7 @@ func TestRoleGrantRevokePermission(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, config.ClusterConfig{ClusterSize: 1}) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { _, err := cc.RoleAdd("role1") if err != nil { @@ -140,7 +140,7 @@ func TestRoleDelete(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, config.ClusterConfig{ClusterSize: 1}) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { _, err := cc.RoleAdd("role1") if err != nil { diff --git a/tests/common/status_test.go b/tests/common/status_test.go index deb058030f2b..4644bd13c7c3 100644 --- a/tests/common/status_test.go +++ b/tests/common/status_test.go @@ -19,6 +19,7 @@ import ( "testing" "time" + "go.etcd.io/etcd/tests/v3/framework/config" "go.etcd.io/etcd/tests/v3/framework/testutils" ) @@ -32,7 +33,7 @@ func TestStatus(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, tc.config) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { rs, err := cc.Status() diff --git a/tests/common/txn_test.go b/tests/common/txn_test.go index 79497224e335..70f57360c0e5 100644 --- a/tests/common/txn_test.go +++ b/tests/common/txn_test.go @@ -60,7 +60,7 @@ func TestTxnSucc(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, cfg.config) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { if err := cc.Put("key1", "value1", config.PutOptions{}); err != nil { t.Fatalf("could not create key:%s, value:%s", "key1", "value1") @@ -104,7 +104,7 @@ func TestTxnFail(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, cfg.config) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { if err := cc.Put("key1", "value1", config.PutOptions{}); err != nil { t.Fatalf("could not create key:%s, value:%s", "key1", "value1") diff --git a/tests/common/user_test.go b/tests/common/user_test.go index b48a8224ea01..f82ae1d4b0f6 100644 --- a/tests/common/user_test.go +++ b/tests/common/user_test.go @@ -68,7 +68,7 @@ func TestUserAdd_Simple(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, tc.config) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { resp, err := cc.UserAdd(nc.username, nc.password, config.UserAddOptions{NoPassword: nc.noPassword}) @@ -102,7 +102,7 @@ func TestUserAdd_DuplicateUserNotAllowed(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, tc.config) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { user := "barb" @@ -131,7 +131,7 @@ func TestUserList(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, tc.config) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { // No Users Yet @@ -172,7 +172,7 @@ func TestUserDelete(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, tc.config) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { user := "barb" @@ -224,7 +224,7 @@ func TestUserChangePassword(t *testing.T) { defer cancel() clus := testRunner.NewCluster(ctx, t, tc.config) defer clus.Close() - cc := clus.Client() + cc := clus.MustClient(config.ClientOption{}) testutils.ExecuteUntil(ctx, t, func() { user := "barb" diff --git a/tests/framework/config/client.go b/tests/framework/config/client.go index e8ce9ea794a1..2c5b66867e88 100644 --- a/tests/framework/config/client.go +++ b/tests/framework/config/client.go @@ -20,6 +20,15 @@ import ( clientv3 "go.etcd.io/etcd/client/v3" ) +type ClientOption struct { + UserName string + Password string +} + +func (opt ClientOption) Empty() bool { + return opt.UserName == "" && opt.Password == "" +} + type GetOptions struct { Revision int End string diff --git a/tests/framework/config/cluster.go b/tests/framework/config/cluster.go index f18c6e5c4061..46a33dd08252 100644 --- a/tests/framework/config/cluster.go +++ b/tests/framework/config/cluster.go @@ -27,4 +27,6 @@ type ClusterConfig struct { PeerTLS TLSConfig ClientTLS TLSConfig QuotaBackendBytes int64 + AuthToken string + SnapshotCount uint64 } diff --git a/tests/framework/e2e.go b/tests/framework/e2e.go index 4f8ec81daab9..6de746cab4af 100644 --- a/tests/framework/e2e.go +++ b/tests/framework/e2e.go @@ -44,6 +44,8 @@ func (e e2eRunner) NewCluster(ctx context.Context, t testing.TB, cfg config.Clus InitialToken: "new", ClusterSize: cfg.ClusterSize, QuotaBackendBytes: cfg.QuotaBackendBytes, + AuthTokenOpts: cfg.AuthToken, + SnapshotCount: int(cfg.SnapshotCount), } switch cfg.ClientTLS { case config.NoTLS: @@ -74,15 +76,32 @@ func (e e2eRunner) NewCluster(ctx context.Context, t testing.TB, cfg config.Clus if err != nil { t.Fatalf("could not start etcd integrationCluster: %s", err) } - return &e2eCluster{*epc} + return &e2eCluster{t, *epc} } type e2eCluster struct { + t testing.TB e2e.EtcdProcessCluster } -func (c *e2eCluster) Client() Client { - return e2eClient{e2e.NewEtcdctl(c.Cfg, c.EndpointsV3())} +func (c *e2eCluster) Client(cfg config.ClientOption) (Client, error) { + etcdctl := e2e.NewEtcdctl(c.Cfg, c.EndpointsV3()) + if !cfg.Empty() { + var err error + etcdctl, err = etcdctl.WithAuth(cfg.UserName, cfg.Password) + if err != nil { + return nil, err + } + } + return e2eClient{etcdctl}, nil +} + +func (c *e2eCluster) MustClient(cfg config.ClientOption) Client { + cc, err := c.Client(cfg) + if err != nil { + c.t.Fatal(err) + } + return cc } func (c *e2eCluster) Members() (ms []Member) { diff --git a/tests/framework/e2e/etcdctl.go b/tests/framework/e2e/etcdctl.go index f97cc27e2c14..4656f09b3cc2 100644 --- a/tests/framework/e2e/etcdctl.go +++ b/tests/framework/e2e/etcdctl.go @@ -20,16 +20,20 @@ import ( "io" "strconv" "strings" + "time" "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" + "google.golang.org/grpc" ) type EtcdctlV3 struct { cfg *EtcdProcessClusterConfig endpoints []string + userName string + password string } func NewEtcdctl(cfg *EtcdProcessClusterConfig, endpoints []string) *EtcdctlV3 { @@ -39,6 +43,23 @@ func NewEtcdctl(cfg *EtcdProcessClusterConfig, endpoints []string) *EtcdctlV3 { } } +func (ctl *EtcdctlV3) WithAuth(userName, password string) (*EtcdctlV3, error) { + ctl.userName = userName + ctl.password = password + cfg := clientv3.Config{ + Endpoints: ctl.endpoints, + DialTimeout: 5 * time.Second, + DialOptions: []grpc.DialOption{grpc.WithBlock()}, + Username: userName, + Password: password, + } + c, err := clientv3.New(cfg) + if c != nil { + c.Close() + } + return ctl, err +} + func (ctl *EtcdctlV3) DowngradeEnable(version string) error { return SpawnWithExpect(ctl.cmdArgs("downgrade", "enable", version), "Downgrade enable success") } @@ -272,6 +293,9 @@ func (ctl *EtcdctlV3) flags() map[string]string { } } fmap["endpoints"] = strings.Join(ctl.endpoints, ",") + if ctl.userName != "" && ctl.password != "" { + fmap["user"] = ctl.userName + ":" + ctl.password + } return fmap } @@ -435,6 +459,20 @@ func (ctl *EtcdctlV3) AlarmDisarm(_ *clientv3.AlarmMember) (*clientv3.AlarmRespo return &resp, err } +func (ctl *EtcdctlV3) AuthEnable() (*clientv3.AuthEnableResponse, error) { + return &clientv3.AuthEnableResponse{}, SpawnWithExpect(ctl.cmdArgs("auth", "enable"), "Authentication Enabled") +} + +func (ctl *EtcdctlV3) AuthDisable() (*clientv3.AuthDisableResponse, error) { + return &clientv3.AuthDisableResponse{}, SpawnWithExpect(ctl.cmdArgs("auth", "disable"), "Authentication Disabled") +} + +func (ctl *EtcdctlV3) AuthStatus() (*clientv3.AuthStatusResponse, error) { + var resp clientv3.AuthStatusResponse + err := ctl.spawnJsonCmd(&resp, "auth", "status") + return &resp, err +} + func (ctl *EtcdctlV3) UserAdd(name, password string, opts config.UserAddOptions) (*clientv3.AuthUserAddResponse, error) { args := ctl.cmdArgs() args = append(args, "user", "add") @@ -473,6 +511,12 @@ func (ctl *EtcdctlV3) UserAdd(name, password string, opts config.UserAddOptions) return &resp, err } +func (ctl *EtcdctlV3) UserGet(name string) (*clientv3.AuthUserGetResponse, error) { + var resp clientv3.AuthUserGetResponse + err := ctl.spawnJsonCmd(&resp, "user", "get", name) + return &resp, err +} + func (ctl *EtcdctlV3) UserList() (*clientv3.AuthUserListResponse, error) { var resp clientv3.AuthUserListResponse err := ctl.spawnJsonCmd(&resp, "user", "list") @@ -502,6 +546,18 @@ func (ctl *EtcdctlV3) UserChangePass(user, newPass string) error { return err } +func (ctl *EtcdctlV3) UserGrantRole(user string, role string) (*clientv3.AuthUserGrantRoleResponse, error) { + var resp clientv3.AuthUserGrantRoleResponse + err := ctl.spawnJsonCmd(&resp, "user", "grant-role", user, role) + return &resp, err +} + +func (ctl *EtcdctlV3) UserRevokeRole(user string, role string) (*clientv3.AuthUserRevokeRoleResponse, error) { + var resp clientv3.AuthUserRevokeRoleResponse + err := ctl.spawnJsonCmd(&resp, "user", "revoke-role", user, role) + return &resp, err +} + func (ctl *EtcdctlV3) RoleAdd(name string) (*clientv3.AuthRoleAddResponse, error) { var resp clientv3.AuthRoleAddResponse err := ctl.spawnJsonCmd(&resp, "role", "add", name) diff --git a/tests/framework/integration.go b/tests/framework/integration.go index 631b7263e696..12251899fd5a 100644 --- a/tests/framework/integration.go +++ b/tests/framework/integration.go @@ -46,8 +46,11 @@ func (e integrationRunner) NewCluster(ctx context.Context, t testing.TB, cfg con var err error var integrationCfg integration.ClusterConfig integrationCfg.Size = cfg.ClusterSize - integrationCfg.ClientTLS, err = tlsInfo(t, cfg.ClientTLS) integrationCfg.QuotaBackendBytes = cfg.QuotaBackendBytes + integrationCfg.AuthToken = cfg.AuthToken + integrationCfg.SnapshotCount = cfg.SnapshotCount + + integrationCfg.ClientTLS, err = tlsInfo(t, cfg.ClientTLS) if err != nil { t.Fatalf("ClientTLS: %s", err) } @@ -115,12 +118,30 @@ func (c *integrationCluster) Close() error { return nil } -func (c *integrationCluster) Client() Client { - cc, err := c.ClusterClient() +func (c *integrationCluster) Client(cfg config.ClientOption) (Client, error) { + option := func(_ *clientv3.Config) {} + if !cfg.Empty() { + option = func(clientCfg *clientv3.Config) { + clientCfg.Username = cfg.UserName + clientCfg.Password = cfg.Password + } + } + cc, err := c.ClusterClient(option) + if err != nil { + return nil, err + } + c.t.Cleanup(func() { + cc.Close() + }) + return integrationClient{cc, c.ctx}, nil +} + +func (c *integrationCluster) MustClient(cfg config.ClientOption) Client { + cc, err := c.Client(cfg) if err != nil { c.t.Fatal(err) } - return integrationClient{Client: cc, ctx: c.ctx} + return cc } type integrationClient struct { @@ -287,12 +308,28 @@ func (c integrationClient) LeaseRevoke(id clientv3.LeaseID) (*clientv3.LeaseRevo return c.Client.Revoke(c.ctx, id) } +func (c integrationClient) AuthEnable() (*clientv3.AuthEnableResponse, error) { + return c.Client.AuthEnable(context.Background()) +} + +func (c integrationClient) AuthDisable() (*clientv3.AuthDisableResponse, error) { + return c.Client.AuthDisable(context.Background()) +} + +func (c integrationClient) AuthStatus() (*clientv3.AuthStatusResponse, error) { + return c.Client.AuthStatus(context.Background()) +} + func (c integrationClient) UserAdd(name, password string, opts config.UserAddOptions) (*clientv3.AuthUserAddResponse, error) { return c.Client.UserAddWithOptions(c.ctx, name, password, &clientv3.UserAddOptions{ NoPassword: opts.NoPassword, }) } +func (c integrationClient) UserGet(name string) (*clientv3.AuthUserGetResponse, error) { + return c.Client.UserGet(context.Background(), name) +} + func (c integrationClient) UserList() (*clientv3.AuthUserListResponse, error) { return c.Client.UserList(c.ctx) } @@ -306,6 +343,14 @@ func (c integrationClient) UserChangePass(user, newPass string) error { return err } +func (c integrationClient) UserGrantRole(user string, role string) (*clientv3.AuthUserGrantRoleResponse, error) { + return c.Client.UserGrantRole(context.Background(), user, role) +} + +func (c integrationClient) UserRevokeRole(user string, role string) (*clientv3.AuthUserRevokeRoleResponse, error) { + return c.Client.UserRevokeRole(context.Background(), user, role) +} + func (c integrationClient) RoleAdd(name string) (*clientv3.AuthRoleAddResponse, error) { return c.Client.RoleAdd(c.ctx, name) } diff --git a/tests/framework/integration/cluster.go b/tests/framework/integration/cluster.go index a59499701862..98ec91018f28 100644 --- a/tests/framework/integration/cluster.go +++ b/tests/framework/integration/cluster.go @@ -1409,32 +1409,41 @@ func (c *Cluster) Endpoints() []string { return endpoints } -func (c *Cluster) ClusterClient() (client *clientv3.Client, err error) { - if c.clusterClient == nil { - var endpoints []string - for _, m := range c.Members { - endpoints = append(endpoints, m.GrpcURL) - } - cfg := clientv3.Config{ - Endpoints: endpoints, - DialTimeout: 5 * time.Second, - DialOptions: []grpc.DialOption{grpc.WithBlock()}, - MaxCallSendMsgSize: c.Cfg.ClientMaxCallSendMsgSize, - MaxCallRecvMsgSize: c.Cfg.ClientMaxCallRecvMsgSize, - } - if c.Cfg.ClientTLS != nil { - tls, err := c.Cfg.ClientTLS.ClientConfig() - if err != nil { - return nil, err - } - cfg.TLS = tls - } - c.clusterClient, err = newClientV3(cfg) +func (c *Cluster) ClusterClient(opts ...func(*clientv3.Config)) (client *clientv3.Client, err error) { + if c.clusterClient != nil && len(opts) == 0 { + return c.clusterClient, nil + } + + cfg, err := c.newClientCfg() + if err != nil { + return nil, err + } + for _, opt := range opts { + opt(cfg) + } + c.clusterClient, err = newClientV3(*cfg) + if err != nil { + return nil, err + } + return c.clusterClient, err +} + +func (c *Cluster) newClientCfg() (*clientv3.Config, error) { + cfg := &clientv3.Config{ + Endpoints: c.Endpoints(), + DialTimeout: 5 * time.Second, + DialOptions: []grpc.DialOption{grpc.WithBlock()}, + MaxCallSendMsgSize: c.Cfg.ClientMaxCallSendMsgSize, + MaxCallRecvMsgSize: c.Cfg.ClientMaxCallRecvMsgSize, + } + if c.Cfg.ClientTLS != nil { + tls, err := c.Cfg.ClientTLS.ClientConfig() if err != nil { return nil, err } + cfg.TLS = tls } - return c.clusterClient, nil + return cfg, nil } // NewClientV3 creates a new grpc client connection to the member diff --git a/tests/framework/interface.go b/tests/framework/interface.go index a7ded291a884..7a7a9d707187 100644 --- a/tests/framework/interface.go +++ b/tests/framework/interface.go @@ -30,7 +30,8 @@ type testRunner interface { type Cluster interface { Members() []Member - Client() Client + Client(cfg config.ClientOption) (Client, error) + MustClient(cfg config.ClientOption) Client Close() error } @@ -57,11 +58,16 @@ type Client interface { LeaseKeepAliveOnce(id clientv3.LeaseID) (*clientv3.LeaseKeepAliveResponse, error) LeaseRevoke(id clientv3.LeaseID) (*clientv3.LeaseRevokeResponse, error) + AuthEnable() (*clientv3.AuthEnableResponse, error) + AuthDisable() (*clientv3.AuthDisableResponse, error) + AuthStatus() (*clientv3.AuthStatusResponse, error) UserAdd(name, password string, opts config.UserAddOptions) (*clientv3.AuthUserAddResponse, error) + UserGet(name string) (*clientv3.AuthUserGetResponse, error) UserList() (*clientv3.AuthUserListResponse, error) UserDelete(name string) (*clientv3.AuthUserDeleteResponse, error) UserChangePass(user, newPass string) error - + UserGrantRole(user string, role string) (*clientv3.AuthUserGrantRoleResponse, error) + UserRevokeRole(user string, role string) (*clientv3.AuthUserRevokeRoleResponse, 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)