Skip to content

Commit

Permalink
migrate e2e & integration role_test to common
Browse files Browse the repository at this point in the history
  • Loading branch information
chaochn47 committed May 9, 2022
1 parent 545f04f commit a90c638
Show file tree
Hide file tree
Showing 6 changed files with 277 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
77 changes: 77 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,79 @@ func (ctl *EtcdctlV3) UserChangePass(user, newPass string) error {
_, err = cmd.Expect("Password updated")
return err
}

func (ctl *EtcdctlV3) RoleAdd(name string) (*clientv3.AuthRoleAddResponse, error) {
line, err := SpawnCmdWithJsonPrinter(ctl.cmdArgs("role", "add", name), nil)
if err != nil {
return nil, err
}
var resp clientv3.AuthRoleAddResponse
err = json.Unmarshal([]byte(line), &resp)
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)]
line, err := SpawnCmdWithJsonPrinter(ctl.cmdArgs("role", "grant-permission", name, permissionType, key, rangeEnd), nil)
if err != nil {
return nil, err
}
var resp clientv3.AuthRoleGrantPermissionResponse
err = json.Unmarshal([]byte(line), &resp)
return &resp, err
}

func (ctl *EtcdctlV3) RoleGet(role string) (*clientv3.AuthRoleGetResponse, error) {
line, err := SpawnCmdWithJsonPrinter(ctl.cmdArgs("role", "get", role), nil)
if err != nil {
return nil, err
}
var resp clientv3.AuthRoleGetResponse
err = json.Unmarshal([]byte(line), &resp)
return &resp, err
}

func (ctl *EtcdctlV3) RoleList() (*clientv3.AuthRoleListResponse, error) {
line, err := SpawnCmdWithJsonPrinter(ctl.cmdArgs("role", "list"), nil)
if err != nil {
return nil, err
}
var resp clientv3.AuthRoleListResponse
err = json.Unmarshal([]byte(line), &resp)
return &resp, err
}

func (ctl *EtcdctlV3) RoleRevokePermission(role string, key, rangeEnd string) (*clientv3.AuthRoleRevokePermissionResponse, error) {
line, err := SpawnCmdWithJsonPrinter(ctl.cmdArgs("role", "revoke-permission", role, key, rangeEnd), nil)
if err != nil {
return nil, err
}
var resp clientv3.AuthRoleRevokePermissionResponse
err = json.Unmarshal([]byte(line), &resp)
return &resp, err
}

func (ctl *EtcdctlV3) RoleDelete(role string) (*clientv3.AuthRoleDeleteResponse, error) {
line, err := SpawnCmdWithJsonPrinter(ctl.cmdArgs("role", "delete", role), nil)
if err != nil {
return nil, err
}
var resp clientv3.AuthRoleDeleteResponse
err = json.Unmarshal([]byte(line), &resp)
return &resp, err
}

// SpawnCmdWithJsonPrinter spawns an etcdctl process with provided command line arguments and environment variables
// with json printer output and capture header line as output.
func SpawnCmdWithJsonPrinter(args []string, envVars map[string]string) (string, error) {
args = append(args, "-w", "json")
cmd, err := SpawnCmd(args, envVars)
if err != nil {
return "", err
}
return cmd.ExpectFunc(func(txt string) bool {
// header substring is missing because of embedding in LeaseLeasesResponse.
// revision substring is missing because ClusterServer.header() does not fill revision in the header
return strings.Contains(txt, "header") || strings.Contains(txt, "revision")
})
}
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)
}
Loading

0 comments on commit a90c638

Please sign in to comment.