diff --git a/tests/common/e2e_test.go b/tests/common/e2e_test.go index 1284714b15eb..bb4c46be8288 100644 --- a/tests/common/e2e_test.go +++ b/tests/common/e2e_test.go @@ -25,6 +25,7 @@ import ( func init() { testRunner = framework.E2eTestRunner + withAuth = WithAuth } func WithAuth(userName, password string) config.ClientOption { diff --git a/tests/common/integration_test.go b/tests/common/integration_test.go index d875dbc82a83..4fbd1c3580a0 100644 --- a/tests/common/integration_test.go +++ b/tests/common/integration_test.go @@ -25,6 +25,7 @@ import ( func init() { testRunner = framework.IntegrationTestRunner + withAuth = WithAuth } func WithAuth(userName, password string) config.ClientOption { diff --git a/tests/common/main_test.go b/tests/common/main_test.go index d03f120e057b..9686533f8681 100644 --- a/tests/common/main_test.go +++ b/tests/common/main_test.go @@ -22,6 +22,7 @@ import ( ) var testRunner = framework.UnitTestRunner +var withAuth = func(userName, password string) config.ClientOption { return func(any) {} } var clusterTestCases = []testCase{ { name: "NoTLS", diff --git a/tests/e2e/cluster_downgrade_test.go b/tests/e2e/cluster_downgrade_test.go index 001f5d7d887a..9443ce934255 100644 --- a/tests/e2e/cluster_downgrade_test.go +++ b/tests/e2e/cluster_downgrade_test.go @@ -21,6 +21,7 @@ import ( "time" "github.com/coreos/go-semver/semver" + "github.com/stretchr/testify/assert" "go.etcd.io/etcd/api/v3/version" "go.etcd.io/etcd/client/pkg/v3/fileutil" clientv3 "go.etcd.io/etcd/client/v3" @@ -122,7 +123,8 @@ func startEtcd(t *testing.T, ep e2e.EtcdProcess, execPath string) { } func downgradeEnable(t *testing.T, epc *e2e.EtcdProcessCluster, ver semver.Version) { - c := e2e.NewEtcdctl(epc.Cfg, epc.EndpointsV3()) + c, err := e2e.NewEtcdctl(epc.Cfg, epc.EndpointsV3()) + assert.NoError(t, err) testutils.ExecuteWithTimeout(t, 20*time.Second, func() { err := c.DowngradeEnable(context.TODO(), ver.String()) if err != nil { diff --git a/tests/e2e/corrupt_test.go b/tests/e2e/corrupt_test.go index 0ca530ffa72a..a47a85103cfc 100644 --- a/tests/e2e/corrupt_test.go +++ b/tests/e2e/corrupt_test.go @@ -116,7 +116,8 @@ func TestPeriodicCheckDetectsCorruption(t *testing.T) { } }) - cc := e2e.NewEtcdctl(epc.Cfg, epc.EndpointsV3()) + cc, err := e2e.NewEtcdctl(epc.Cfg, epc.EndpointsV3()) + assert.NoError(t, err) for i := 0; i < 10; i++ { err := cc.Put(ctx, testutil.PickKey(int64(i)), fmt.Sprint(i), config.PutOptions{}) @@ -164,7 +165,8 @@ func TestCompactHashCheckDetectCorruption(t *testing.T) { } }) - cc := e2e.NewEtcdctl(epc.Cfg, epc.EndpointsV3()) + cc, err := e2e.NewEtcdctl(epc.Cfg, epc.EndpointsV3()) + assert.NoError(t, err) for i := 0; i < 10; i++ { err := cc.Put(ctx, testutil.PickKey(int64(i)), fmt.Sprint(i), config.PutOptions{}) diff --git a/tests/e2e/ctl_v3_grpc_test.go b/tests/e2e/ctl_v3_grpc_test.go index c4a9b5c5bc98..94b7b3a577b5 100644 --- a/tests/e2e/ctl_v3_grpc_test.go +++ b/tests/e2e/ctl_v3_grpc_test.go @@ -98,7 +98,8 @@ func TestAuthority(t *testing.T) { defer epc.Close() endpoints := templateEndpoints(t, tc.clientURLPattern, epc) - client := e2e.NewEtcdctl(cfg, endpoints) + client, err := e2e.NewEtcdctl(cfg, endpoints) + assert.NoError(t, err) err = client.Put(ctx, "foo", "bar", config.PutOptions{}) if err != nil { t.Fatal(err) diff --git a/tests/e2e/etcd_grpcproxy_test.go b/tests/e2e/etcd_grpcproxy_test.go index 71d01cf617bf..b6b0b0f73bc7 100644 --- a/tests/e2e/etcd_grpcproxy_test.go +++ b/tests/e2e/etcd_grpcproxy_test.go @@ -59,7 +59,8 @@ func TestGrpcProxyAutoSync(t *testing.T) { assert.NoError(t, proxyProc.Stop()) }() - proxyCtl := e2e.NewEtcdctl(&e2e.EtcdProcessClusterConfig{}, []string{proxyClientURL}) + proxyCtl, err := e2e.NewEtcdctl(&e2e.EtcdProcessClusterConfig{}, []string{proxyClientURL}) + require.NoError(t, err) err = proxyCtl.Put(ctx, "k1", "v1", config.PutOptions{}) require.NoError(t, err) diff --git a/tests/e2e/v2store_deprecation_test.go b/tests/e2e/v2store_deprecation_test.go index 61dae5a4443e..13d32993766c 100644 --- a/tests/e2e/v2store_deprecation_test.go +++ b/tests/e2e/v2store_deprecation_test.go @@ -109,10 +109,14 @@ func TestV2DeprecationSnapshotMatches(t *testing.T) { } snapshotCount := 10 epc := runEtcdAndCreateSnapshot(t, lastReleaseBinary, lastReleaseData, snapshotCount) - members1 := addAndRemoveKeysAndMembers(ctx, t, e2e.NewEtcdctl(epc.Cfg, epc.EndpointsV3()), snapshotCount) + cc1, err := e2e.NewEtcdctl(epc.Cfg, epc.EndpointsV3()) + assert.NoError(t, err) + members1 := addAndRemoveKeysAndMembers(ctx, t, cc1, snapshotCount) assert.NoError(t, epc.Close()) epc = runEtcdAndCreateSnapshot(t, currentReleaseBinary, currentReleaseData, snapshotCount) - members2 := addAndRemoveKeysAndMembers(ctx, t, e2e.NewEtcdctl(epc.Cfg, epc.EndpointsV3()), snapshotCount) + cc2, err := e2e.NewEtcdctl(epc.Cfg, epc.EndpointsV3()) + assert.NoError(t, err) + members2 := addAndRemoveKeysAndMembers(ctx, t, cc2, snapshotCount) assert.NoError(t, epc.Close()) assertSnapshotsMatch(t, lastReleaseData, currentReleaseData, func(data []byte) []byte { @@ -144,7 +148,8 @@ func TestV2DeprecationSnapshotRecover(t *testing.T) { } epc := runEtcdAndCreateSnapshot(t, lastReleaseBinary, dataDir, 10) - cc := e2e.NewEtcdctl(epc.Cfg, epc.EndpointsV3()) + cc, err := e2e.NewEtcdctl(epc.Cfg, epc.EndpointsV3()) + assert.NoError(t, err) lastReleaseGetResponse, err := cc.Get(ctx, "", config.GetOptions{Prefix: true}) assert.NoError(t, err) @@ -157,7 +162,8 @@ func TestV2DeprecationSnapshotRecover(t *testing.T) { epc, err = e2e.NewEtcdProcessCluster(context.TODO(), t, cfg) assert.NoError(t, err) - cc = e2e.NewEtcdctl(epc.Cfg, epc.EndpointsV3()) + cc, err = e2e.NewEtcdctl(epc.Cfg, epc.EndpointsV3()) + assert.NoError(t, err) currentReleaseGetResponse, err := cc.Get(ctx, "", config.GetOptions{Prefix: true}) assert.NoError(t, err) diff --git a/tests/framework/e2e.go b/tests/framework/e2e.go index ceb8f020ca3c..82f53167d81a 100644 --- a/tests/framework/e2e.go +++ b/tests/framework/e2e.go @@ -88,8 +88,8 @@ type e2eCluster struct { } func (c *e2eCluster) Client(opts ...config.ClientOption) (Client, error) { - etcdctl := e2e.NewEtcdctl(c.Cfg, c.EndpointsV3(), opts...) - return e2eClient{etcdctl}, nil + etcdctl, err := e2e.NewEtcdctl(c.Cfg, c.EndpointsV3(), opts...) + return e2eClient{etcdctl}, err } func (c *e2eCluster) Endpoints() []string { @@ -179,7 +179,11 @@ type e2eMember struct { } func (m e2eMember) Client() Client { - return e2eClient{e2e.NewEtcdctl(m.Cfg, m.EndpointsV3())} + etcdctl, err := e2e.NewEtcdctl(m.Cfg, m.EndpointsV3()) + if err != nil { + panic(err) + } + return e2eClient{etcdctl} } func (m e2eMember) Start(ctx context.Context) error { diff --git a/tests/framework/e2e/cluster.go b/tests/framework/e2e/cluster.go index 1bf508391aba..996b4cbcdf3b 100644 --- a/tests/framework/e2e/cluster.go +++ b/tests/framework/e2e/cluster.go @@ -613,7 +613,11 @@ func (epc *EtcdProcessCluster) Stop() (err error) { } func (epc *EtcdProcessCluster) Client() *EtcdctlV3 { - return NewEtcdctl(epc.Cfg, epc.EndpointsV3()) + etcdctl, err := NewEtcdctl(epc.Cfg, epc.EndpointsV3()) + if err != nil { + panic(err) + } + return etcdctl } func (epc *EtcdProcessCluster) Close() error { diff --git a/tests/framework/e2e/etcdctl.go b/tests/framework/e2e/etcdctl.go index 7bb93fe8d816..12534d15222a 100644 --- a/tests/framework/e2e/etcdctl.go +++ b/tests/framework/e2e/etcdctl.go @@ -21,21 +21,22 @@ 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 + cfg *EtcdProcessClusterConfig + endpoints []string + authConfig clientv3.AuthConfig } -func NewEtcdctl(cfg *EtcdProcessClusterConfig, endpoints []string, opts ...config.ClientOption) *EtcdctlV3 { +func NewEtcdctl(cfg *EtcdProcessClusterConfig, endpoints []string, opts ...config.ClientOption) (*EtcdctlV3, error) { ctl := &EtcdctlV3{ cfg: cfg, endpoints: endpoints, @@ -45,14 +46,28 @@ func NewEtcdctl(cfg *EtcdProcessClusterConfig, endpoints []string, opts ...confi opt(ctl) } - return ctl + if !ctl.authConfig.Empty() { + client, err := clientv3.New(clientv3.Config{ + Endpoints: ctl.endpoints, + DialTimeout: 5 * time.Second, + DialOptions: []grpc.DialOption{grpc.WithBlock()}, + Username: ctl.authConfig.Username, + Password: ctl.authConfig.Password, + }) + if err != nil { + return nil, err + } + client.Close() + } + + return ctl, nil } func WithAuth(userName, password string) config.ClientOption { return func(c any) { ctl := c.(*EtcdctlV3) - ctl.userName = userName - ctl.password = password + ctl.authConfig.Username = userName + ctl.authConfig.Password = password } } @@ -300,8 +315,8 @@ func (ctl *EtcdctlV3) flags() map[string]string { } } fmap["endpoints"] = strings.Join(ctl.endpoints, ",") - if ctl.userName != "" && ctl.password != "" { - fmap["user"] = ctl.userName + ":" + ctl.password + if !ctl.authConfig.Empty() { + fmap["user"] = ctl.authConfig.Username + ":" + ctl.authConfig.Password } return fmap } @@ -473,34 +488,28 @@ func (ctl *EtcdctlV3) AlarmDisarm(ctx context.Context, _ *clientv3.AlarmMember) return &resp, err } -func (ctl *EtcdctlV3) AuthEnable(ctx context.Context) (*clientv3.AuthEnableResponse, error) { +func (ctl *EtcdctlV3) AuthEnable(ctx context.Context) error { args := []string{"auth", "enable"} cmd, err := SpawnCmd(ctl.cmdArgs(args...), nil) if err != nil { - return nil, err + return err } defer cmd.Close() _, err = cmd.ExpectWithContext(ctx, "Authentication Enabled") - if err != nil { - return nil, err - } - return &clientv3.AuthEnableResponse{}, nil + return err } -func (ctl *EtcdctlV3) AuthDisable(ctx context.Context) (*clientv3.AuthDisableResponse, error) { +func (ctl *EtcdctlV3) AuthDisable(ctx context.Context) error { args := []string{"auth", "disable"} cmd, err := SpawnCmd(ctl.cmdArgs(args...), nil) if err != nil { - return nil, err + return err } defer cmd.Close() _, err = cmd.ExpectWithContext(ctx, "Authentication Disabled") - if err != nil { - return nil, err - } - return &clientv3.AuthDisableResponse{}, nil + return err } func (ctl *EtcdctlV3) AuthStatus(ctx context.Context) (*clientv3.AuthStatusResponse, error) { diff --git a/tests/framework/integration.go b/tests/framework/integration.go index 2639346bb2da..c2d7ee533879 100644 --- a/tests/framework/integration.go +++ b/tests/framework/integration.go @@ -274,12 +274,14 @@ func (c integrationClient) Revoke(ctx context.Context, id clientv3.LeaseID) (*cl return c.Client.Revoke(ctx, id) } -func (c integrationClient) AuthEnable(ctx context.Context) (*clientv3.AuthEnableResponse, error) { - return c.Client.AuthEnable(ctx) +func (c integrationClient) AuthEnable(ctx context.Context) error { + _, err := c.Client.AuthEnable(ctx) + return err } -func (c integrationClient) AuthDisable(ctx context.Context) (*clientv3.AuthDisableResponse, error) { - return c.Client.AuthDisable(ctx) +func (c integrationClient) AuthDisable(ctx context.Context) error { + _, err := c.Client.AuthDisable(ctx) + return err } func (c integrationClient) AuthStatus(ctx context.Context) (*clientv3.AuthStatusResponse, error) { diff --git a/tests/framework/integration/cluster.go b/tests/framework/integration/cluster.go index d9ee243996f1..22eed4da7356 100644 --- a/tests/framework/integration/cluster.go +++ b/tests/framework/integration/cluster.go @@ -1457,9 +1457,9 @@ func (c *Cluster) ClusterClient(t testing.TB, opts ...framecfg.ClientOption) (cl func WithAuth(userName, password string) framecfg.ClientOption { return func(c any) { - client := c.(*clientv3.Client) - client.Username = userName - client.Password = password + cfg := c.(*clientv3.Config) + cfg.Username = userName + cfg.Password = password } } diff --git a/tests/framework/interface.go b/tests/framework/interface.go index a1f9bb174fb8..d40097612fda 100644 --- a/tests/framework/interface.go +++ b/tests/framework/interface.go @@ -59,8 +59,8 @@ type Client interface { KeepAliveOnce(context context.Context, id clientv3.LeaseID) (*clientv3.LeaseKeepAliveResponse, error) Revoke(context context.Context, id clientv3.LeaseID) (*clientv3.LeaseRevokeResponse, error) - AuthEnable(context context.Context) (*clientv3.AuthEnableResponse, error) - AuthDisable(context context.Context) (*clientv3.AuthDisableResponse, error) + AuthEnable(context context.Context) error + AuthDisable(context context.Context) error AuthStatus(context context.Context) (*clientv3.AuthStatusResponse, error) UserAdd(context context.Context, name, password string, opts config.UserAddOptions) (*clientv3.AuthUserAddResponse, error) UserGet(context context.Context, name string) (*clientv3.AuthUserGetResponse, error) @@ -75,6 +75,7 @@ type Client interface { RoleList(context context.Context) (*clientv3.AuthRoleListResponse, error) RoleRevokePermission(context context.Context, role string, key, rangeEnd string) (*clientv3.AuthRoleRevokePermissionResponse, error) RoleDelete(context context.Context, role string) (*clientv3.AuthRoleDeleteResponse, error) + Txn(context context.Context, compares, ifSucess, ifFail []string, o config.TxnOptions) (*clientv3.TxnResponse, error) MemberList(context context.Context) (*clientv3.MemberListResponse, error)