Skip to content

Commit

Permalink
Merge pull request #14020 from chaochn47/role_test
Browse files Browse the repository at this point in the history
migrate e2e & integration role_test to common
  • Loading branch information
serathius authored May 10, 2022
2 parents b7be91f + e004c91 commit 066e510
Show file tree
Hide file tree
Showing 6 changed files with 251 additions and 144 deletions.
168 changes: 168 additions & 0 deletions tests/common/role_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// 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 (
"strings"
"testing"
"time"

"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/tests/v3/framework/config"
"go.etcd.io/etcd/tests/v3/framework/testutils"
)

func TestRoleAdd_Simple(t *testing.T) {
testRunner.BeforeTest(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},
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
clus := testRunner.NewCluster(t, tc.config)
defer clus.Close()
cc := clus.Client()

testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
_, err := cc.RoleAdd("root")
if err != nil {
t.Fatalf("want no error, but got (%v)", err)
}
})
})
}
}

func TestRoleAdd_Error(t *testing.T) {
testRunner.BeforeTest(t)
clus := testRunner.NewCluster(t, config.ClusterConfig{ClusterSize: 1})
defer clus.Close()
cc := clus.Client()
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
_, err := cc.RoleAdd("test-role")
if err != nil {
t.Fatalf("want no error, but got (%v)", err)
}
_, err = cc.RoleAdd("test-role")
if err == nil || !strings.Contains(err.Error(), rpctypes.ErrRoleAlreadyExist.Error()) {
t.Fatalf("want (%v) error, but got (%v)", rpctypes.ErrRoleAlreadyExist, err)
}
_, err = cc.RoleAdd("")
if err == nil || !strings.Contains(err.Error(), rpctypes.ErrRoleEmpty.Error()) {
t.Fatalf("want (%v) error, but got (%v)", rpctypes.ErrRoleEmpty, err)
}
})
}

func TestRootRole(t *testing.T) {
testRunner.BeforeTest(t)
clus := testRunner.NewCluster(t, config.ClusterConfig{ClusterSize: 1})
defer clus.Close()
cc := clus.Client()
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
_, err := cc.RoleAdd("root")
if err != nil {
t.Fatalf("want no error, but got (%v)", err)
}
resp, err := cc.RoleGet("root")
if err != nil {
t.Fatalf("want no error, but got (%v)", err)
}
t.Logf("get role resp %+v", resp)
// granting to root should be refused by server and a no-op
_, err = cc.RoleGrantPermission("root", "foo", "", clientv3.PermissionType(clientv3.PermReadWrite))
if err != nil {
t.Fatalf("want no error, but got (%v)", err)
}
resp2, err := cc.RoleGet("root")
if err != nil {
t.Fatalf("want no error, but got (%v)", err)
}
t.Logf("get role resp %+v", resp2)
})
}

func TestRoleGrantRevokePermission(t *testing.T) {
testRunner.BeforeTest(t)
clus := testRunner.NewCluster(t, config.ClusterConfig{ClusterSize: 1})
defer clus.Close()
cc := clus.Client()
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
_, err := cc.RoleAdd("role1")
if err != nil {
t.Fatalf("want no error, but got (%v)", err)
}
_, err = cc.RoleGrantPermission("role1", "bar", "", clientv3.PermissionType(clientv3.PermRead))
if err != nil {
t.Fatalf("want no error, but got (%v)", err)
}
_, err = cc.RoleGrantPermission("role1", "bar", "", clientv3.PermissionType(clientv3.PermWrite))
if err != nil {
t.Fatalf("want no error, but got (%v)", err)
}
_, err = cc.RoleGrantPermission("role1", "bar", "foo", clientv3.PermissionType(clientv3.PermReadWrite))
if err != nil {
t.Fatalf("want no error, but got (%v)", err)
}
_, err = cc.RoleRevokePermission("role1", "foo", "")
if err == nil || !strings.Contains(err.Error(), rpctypes.ErrPermissionNotGranted.Error()) {
t.Fatalf("want error (%v), but got (%v)", rpctypes.ErrPermissionNotGranted, err)
}
_, err = cc.RoleRevokePermission("role1", "bar", "foo")
if err != nil {
t.Fatalf("want no error, but got (%v)", err)
}
})
}

func TestRoleDelete(t *testing.T) {
testRunner.BeforeTest(t)
clus := testRunner.NewCluster(t, config.ClusterConfig{ClusterSize: 1})
defer clus.Close()
cc := clus.Client()
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
_, err := cc.RoleAdd("role1")
if err != nil {
t.Fatalf("want no error, but got (%v)", err)
}
_, err = cc.RoleDelete("role1")
if err != nil {
t.Fatalf("want no error, but got (%v)", err)
}
})
}
99 changes: 2 additions & 97 deletions tests/e2e/ctl_v3_role_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,10 @@ import (
"go.etcd.io/etcd/tests/v3/framework/e2e"
)

func TestCtlV3RoleAdd(t *testing.T) { testCtl(t, roleAddTest) }
func TestCtlV3RootRoleGet(t *testing.T) { testCtl(t, rootRoleGetTest) }
func TestCtlV3RoleAddNoTLS(t *testing.T) { testCtl(t, roleAddTest, withCfg(*e2e.NewConfigNoTLS())) }
func TestCtlV3RoleAddClientTLS(t *testing.T) {
testCtl(t, roleAddTest, withCfg(*e2e.NewConfigClientTLS()))
}
func TestCtlV3RoleAddPeerTLS(t *testing.T) { testCtl(t, roleAddTest, withCfg(*e2e.NewConfigPeerTLS())) }
// TestCtlV3RoleAddTimeout tests add role with 0 grpc dial timeout while it tolerates dial timeout error.
// This is unique in e2e test
func TestCtlV3RoleAddTimeout(t *testing.T) { testCtl(t, roleAddTest, withDialTimeout(0)) }

func TestCtlV3RoleGrant(t *testing.T) { testCtl(t, roleGrantTest) }

func roleAddTest(cx ctlCtx) {
cmdSet := []struct {
args []string
Expand All @@ -58,94 +51,6 @@ func roleAddTest(cx ctlCtx) {
}
}

func rootRoleGetTest(cx ctlCtx) {
cmdSet := []struct {
args []string
expectedStr interface{}
}{
// Add a role of root .
{
args: []string{"add", "root"},
expectedStr: "Role root created",
},
// get root role should always return [, <open ended>
{
args: []string{"get", "root"},
expectedStr: []string{"Role root\r\n", "KV Read:\r\n", "\t[, <open ended>\r\n", "KV Write:\r\n", "\t[, <open ended>\r\n"},
},
// granting to root should be refused by server
{
args: []string{"grant-permission", "root", "readwrite", "foo"},
expectedStr: "Role root updated",
},
{
args: []string{"get", "root"},
expectedStr: []string{"Role root\r\n", "KV Read:\r\n", "\t[, <open ended>\r\n", "KV Write:\r\n", "\t[, <open ended>\r\n"},
},
}

for i, cmd := range cmdSet {
if _, ok := cmd.expectedStr.(string); ok {
if err := ctlV3Role(cx, cmd.args, cmd.expectedStr.(string)); err != nil {
if cx.dialTimeout > 0 && !isGRPCTimedout(err) {
cx.t.Fatalf("roleAddTest #%d: ctlV3Role error (%v)", i, err)
}
}
} else {
if err := ctlV3RoleMultiExpect(cx, cmd.args, cmd.expectedStr.([]string)...); err != nil {
if cx.dialTimeout > 0 && !isGRPCTimedout(err) {
cx.t.Fatalf("roleAddTest #%d: ctlV3Role error (%v)", i, err)
}
}
}
}
}

func roleGrantTest(cx ctlCtx) {
cmdSet := []struct {
args []string
expectedStr string
}{
// Add a role.
{
args: []string{"add", "root"},
expectedStr: "Role root created",
},
// Grant read permission to the role.
{
args: []string{"grant", "root", "read", "foo"},
expectedStr: "Role root updated",
},
// Grant write permission to the role.
{
args: []string{"grant", "root", "write", "foo"},
expectedStr: "Role root updated",
},
// Grant rw permission to the role.
{
args: []string{"grant", "root", "readwrite", "foo"},
expectedStr: "Role root updated",
},
// Try granting invalid permission to the role.
{
args: []string{"grant", "root", "123", "foo"},
expectedStr: "invalid permission type",
},
}

for i, cmd := range cmdSet {
if err := ctlV3Role(cx, cmd.args, cmd.expectedStr); err != nil {
cx.t.Fatalf("roleGrantTest #%d: ctlV3Role error (%v)", i, err)
}
}
}

func ctlV3RoleMultiExpect(cx ctlCtx, args []string, expStr ...string) error {
cmdArgs := append(cx.PrefixArgs(), "role")
cmdArgs = append(cmdArgs, args...)

return e2e.SpawnWithExpects(cmdArgs, cx.envMap, expStr...)
}
func ctlV3Role(cx ctlCtx, args []string, expStr string) error {
cmdArgs := append(cx.PrefixArgs(), "role")
cmdArgs = append(cmdArgs, args...)
Expand Down
51 changes: 51 additions & 0 deletions tests/framework/e2e/etcdctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"strconv"
"strings"

"go.etcd.io/etcd/api/v3/authpb"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/tests/v3/framework/config"
)
Expand Down Expand Up @@ -507,3 +508,53 @@ func (ctl *EtcdctlV3) UserChangePass(user, newPass string) error {
_, err = cmd.Expect("Password updated")
return err
}

func (ctl *EtcdctlV3) RoleAdd(name string) (*clientv3.AuthRoleAddResponse, error) {
var resp clientv3.AuthRoleAddResponse
err := ctl.spawnJsonCmd(&resp, "role", "add", name)
return &resp, err
}

func (ctl *EtcdctlV3) RoleGrantPermission(name string, key, rangeEnd string, permType clientv3.PermissionType) (*clientv3.AuthRoleGrantPermissionResponse, error) {
permissionType := authpb.Permission_Type_name[int32(permType)]
var resp clientv3.AuthRoleGrantPermissionResponse
err := ctl.spawnJsonCmd(&resp, "role", "grant-permission", name, permissionType, key, rangeEnd)
return &resp, err
}

func (ctl *EtcdctlV3) RoleGet(role string) (*clientv3.AuthRoleGetResponse, error) {
var resp clientv3.AuthRoleGetResponse
err := ctl.spawnJsonCmd(&resp, "role", "get", role)
return &resp, err
}

func (ctl *EtcdctlV3) RoleList() (*clientv3.AuthRoleListResponse, error) {
var resp clientv3.AuthRoleListResponse
err := ctl.spawnJsonCmd(&resp, "role", "list")
return &resp, err
}

func (ctl *EtcdctlV3) RoleRevokePermission(role string, key, rangeEnd string) (*clientv3.AuthRoleRevokePermissionResponse, error) {
var resp clientv3.AuthRoleRevokePermissionResponse
err := ctl.spawnJsonCmd(&resp, "role", "revoke-permission", role, key, rangeEnd)
return &resp, err
}

func (ctl *EtcdctlV3) RoleDelete(role string) (*clientv3.AuthRoleDeleteResponse, error) {
var resp clientv3.AuthRoleDeleteResponse
err := ctl.spawnJsonCmd(&resp, "role", "delete", role)
return &resp, err
}

func (ctl *EtcdctlV3) spawnJsonCmd(output interface{}, args ...string) error {
args = append(args, "-w", "json")
cmd, err := SpawnCmd(append(ctl.cmdArgs(), args...), nil)
if err != nil {
return err
}
line, err := cmd.Expect("header")
if err != nil {
return err
}
return json.Unmarshal([]byte(line), output)
}
24 changes: 24 additions & 0 deletions tests/framework/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,3 +311,27 @@ func (c integrationClient) UserChangePass(user, newPass string) error {
_, err := c.Client.UserChangePassword(context.Background(), user, newPass)
return err
}

func (c integrationClient) RoleAdd(name string) (*clientv3.AuthRoleAddResponse, error) {
return c.Client.RoleAdd(context.Background(), name)
}

func (c integrationClient) RoleGrantPermission(name string, key, rangeEnd string, permType clientv3.PermissionType) (*clientv3.AuthRoleGrantPermissionResponse, error) {
return c.Client.RoleGrantPermission(context.Background(), name, key, rangeEnd, permType)
}

func (c integrationClient) RoleGet(role string) (*clientv3.AuthRoleGetResponse, error) {
return c.Client.RoleGet(context.Background(), role)
}

func (c integrationClient) RoleList() (*clientv3.AuthRoleListResponse, error) {
return c.Client.RoleList(context.Background())
}

func (c integrationClient) RoleRevokePermission(role string, key, rangeEnd string) (*clientv3.AuthRoleRevokePermissionResponse, error) {
return c.Client.RoleRevokePermission(context.Background(), role, key, rangeEnd)
}

func (c integrationClient) RoleDelete(role string) (*clientv3.AuthRoleDeleteResponse, error) {
return c.Client.RoleDelete(context.Background(), role)
}
6 changes: 6 additions & 0 deletions tests/framework/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,10 @@ 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)
}
Loading

0 comments on commit 066e510

Please sign in to comment.