diff --git a/etcdctl/ctlv3/command/printer_simple.go b/etcdctl/ctlv3/command/printer_simple.go index c5939fa47..e9a0b4103 100644 --- a/etcdctl/ctlv3/command/printer_simple.go +++ b/etcdctl/ctlv3/command/printer_simple.go @@ -24,6 +24,8 @@ import ( v3 "go.etcd.io/etcd/client/v3" ) +const rootRole = "root" + type simplePrinter struct { isHex bool valueOnly bool @@ -180,6 +182,14 @@ func (s *simplePrinter) RoleAdd(role string, r v3.AuthRoleAddResponse) { func (s *simplePrinter) RoleGet(role string, r v3.AuthRoleGetResponse) { fmt.Printf("Role %s\n", role) + if rootRole == role && r.Perm == nil { + fmt.Println("KV Read:") + fmt.Println("\t[, ") + fmt.Println("KV Write:") + fmt.Println("\t[, ") + return + } + fmt.Println("KV Read:") printRange := func(perm *v3.Permission) { diff --git a/tests/e2e/ctl_v3_role_test.go b/tests/e2e/ctl_v3_role_test.go index fb4e5de60..c0779b49b 100644 --- a/tests/e2e/ctl_v3_role_test.go +++ b/tests/e2e/ctl_v3_role_test.go @@ -20,6 +20,7 @@ import ( ) 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(*newConfigNoTLS())) } func TestCtlV3RoleAddClientTLS(t *testing.T) { testCtl(t, roleAddTest, withCfg(*newConfigClientTLS())) } func TestCtlV3RoleAddPeerTLS(t *testing.T) { testCtl(t, roleAddTest, withCfg(*newConfigPeerTLS())) } @@ -53,6 +54,49 @@ 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 [, + { + args: []string{"get", "root"}, + expectedStr: []string{"Role root\r\n", "KV Read:\r\n", "\t[, \r\n", "KV Write:\r\n", "\t[, \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", "\tfoo\r\n", "KV Write:\r\n", "\tfoo\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 @@ -92,6 +136,12 @@ func roleGrantTest(cx ctlCtx) { } } +func ctlV3RoleMultiExpect(cx ctlCtx, args []string, expStr ...string) error { + cmdArgs := append(cx.PrefixArgs(), "role") + cmdArgs = append(cmdArgs, args...) + + return spawnWithExpects(cmdArgs, expStr...) +} func ctlV3Role(cx ctlCtx, args []string, expStr string) error { cmdArgs := append(cx.PrefixArgs(), "role") cmdArgs = append(cmdArgs, args...)