From 3013b5def2237537e1446c8b28c8a5c191b96741 Mon Sep 17 00:00:00 2001 From: Natalie Perret <11332444+natalie-o-perret@users.noreply.github.com> Date: Mon, 31 Aug 2026 18:01:44 +0200 Subject: [PATCH 1/4] iam: include policy in role show output --- cmd/iam/iam_role_show.go | 71 ++++++++++++++++++++++------------- cmd/iam/iam_role_show_test.go | 57 ++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 27 deletions(-) create mode 100644 cmd/iam/iam_role_show_test.go diff --git a/cmd/iam/iam_role_show.go b/cmd/iam/iam_role_show.go index 133e5ce62..5021c84a5 100644 --- a/cmd/iam/iam_role_show.go +++ b/cmd/iam/iam_role_show.go @@ -22,11 +22,47 @@ type iamRoleShowOutput struct { Editable bool `json:"editable"` Labels map[string]string `json:"labels"` Permissions []string `json:"permission"` + Policy *iamPolicyOutput `json:"policy" output:"-"` } -func (o *iamRoleShowOutput) ToJSON() { output.JSON(o) } -func (o *iamRoleShowOutput) ToText() { output.Text(o) } -func (o *iamRoleShowOutput) ToTable() { output.Table(o) } +func (o *iamRoleShowOutput) ToJSON() { output.JSON(o) } +func (o *iamRoleShowOutput) ToText() { output.Text(o) } +func (o *iamRoleShowOutput) ToTable() { + output.Table(o) + if o.Policy != nil { + o.Policy.ToTable() + } +} + +func iamPolicyToOutput(policy *v3.IAMPolicy) *iamPolicyOutput { + if policy == nil { + return nil + } + + out := &iamPolicyOutput{ + DefaultServiceStrategy: string(policy.DefaultServiceStrategy), + Services: map[string]iamPolicyServiceOutput{}, + } + + for name, service := range policy.Services { + rules := []iamPolicyServiceRuleOutput{} + if service.Type == "rules" { + for _, rule := range service.Rules { + rules = append(rules, iamPolicyServiceRuleOutput{ + Action: string(rule.Action), + Expression: rule.Expression, + }) + } + } + + out.Services[name] = iamPolicyServiceOutput{ + Type: string(service.Type), + Rules: rules, + } + } + + return out +} type iamRoleShowCmd struct { exocmd.CliCommandSettings `cli-cmd:"-"` @@ -76,32 +112,12 @@ func (c *iamRoleShowCmd) CmdRun(_ *cobra.Command, _ []string) error { return err } + policy := iamPolicyToOutput(role.Policy) if c.Policy { - policy := role.Policy - - out := iamPolicyOutput{ - DefaultServiceStrategy: string(policy.DefaultServiceStrategy), - Services: map[string]iamPolicyServiceOutput{}, - } - - for name, service := range policy.Services { - rules := []iamPolicyServiceRuleOutput{} - if service.Type == "rules" { - for _, rule := range service.Rules { - rules = append(rules, iamPolicyServiceRuleOutput{ - Action: string(rule.Action), - Expression: rule.Expression, - }) - } - } - - out.Services[name] = iamPolicyServiceOutput{ - Type: string(service.Type), - Rules: rules, - } + if policy == nil { + return errors.New("role policy not found") } - - return c.OutputFunc(&out, nil) + return c.OutputFunc(policy, nil) } out := iamRoleShowOutput{ @@ -111,6 +127,7 @@ func (c *iamRoleShowCmd) CmdRun(_ *cobra.Command, _ []string) error { Labels: role.Labels, Name: role.Name, Permissions: role.Permissions, + Policy: policy, } return c.OutputFunc(&out, nil) diff --git a/cmd/iam/iam_role_show_test.go b/cmd/iam/iam_role_show_test.go new file mode 100644 index 000000000..8248849e0 --- /dev/null +++ b/cmd/iam/iam_role_show_test.go @@ -0,0 +1,57 @@ +package iam + +import ( + "net/http" + "net/http/httptest" + "testing" + + exocmd "github.com/exoscale/cli/cmd" + "github.com/exoscale/cli/pkg/account" + "github.com/exoscale/cli/pkg/output" + "github.com/exoscale/cli/pkg/testutils" + v3 "github.com/exoscale/egoscale/v3" +) + +func TestIAMRoleShowIncludesPolicy(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet || r.URL.Path != "/iam-role" { + w.WriteHeader(http.StatusNotFound) + return + } + + testutils.WriteJSON(t, w, http.StatusOK, v3.ListIAMRolesResponse{IAMRoles: []v3.IAMRole{{ + ID: v3.UUID("0e324aaf-4b7e-45ff-a8c9-5830e19484cc"), + Name: "vpc-canary-iam-role", + Policy: &v3.IAMPolicy{ + DefaultServiceStrategy: v3.IAMPolicyDefaultServiceStrategyDeny, + Services: map[string]v3.IAMServicePolicy{ + "networking": {Type: v3.IAMServicePolicyTypeAllow}, + "compute": {Type: v3.IAMServicePolicyTypeAllow}, + }, + }, + }}}) + })) + defer server.Close() + + testutils.SetupV3Client(t, server.URL) + account.CurrentAccount = &account.Account{} + cmd := &iamRoleShowCmd{ + CliCommandSettings: exocmd.DefaultCLICmdSettings(), + Role: "vpc-canary-iam-role", + } + var got *iamRoleShowOutput + cmd.OutputFunc = func(o output.Outputter, err error) error { + got = o.(*iamRoleShowOutput) + return err + } + + if err := cmd.CmdRun(nil, nil); err != nil { + t.Fatal(err) + } + if got.Policy == nil || got.Policy.DefaultServiceStrategy != "deny" { + t.Fatalf("unexpected policy: %+v", got.Policy) + } + if got.Policy.Services["networking"].Type != "allow" || got.Policy.Services["compute"].Type != "allow" { + t.Fatalf("unexpected policy services: %+v", got.Policy.Services) + } +} From 31d34cc8e093c938a5f0da2a46362072a1519d36 Mon Sep 17 00:00:00 2001 From: Natalie Perret <11332444+natalie-o-perret@users.noreply.github.com> Date: Mon, 31 Aug 2026 18:02:51 +0200 Subject: [PATCH 2/4] changelog: mention IAM role policy output --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9880f068c..ace09b31d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Bug fixes +- iam: include policies in role show output (#903) - dbaas: fix empty table output for user create, role list and acl show (output routed to a nil writer) (#894) - dbaas: fix show --uri for clickhouse printing a raw host:port instead of a connection URI (#894) From 7b14668dd67b2e21bd8cfdd524e15d08b059cb55 Mon Sep 17 00:00:00 2001 From: Natalie Perret <11332444+natalie-o-perret@users.noreply.github.com> Date: Thu, 3 Sep 2026 10:53:54 +0200 Subject: [PATCH 3/4] iam: keep role policy out of table output --- CHANGELOG.md | 2 +- cmd/iam/iam_role_show.go | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ace09b31d..473ef3ffd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ ### Bug fixes -- iam: include policies in role show output (#903) +- iam: include policies in JSON role show output (#903) - dbaas: fix empty table output for user create, role list and acl show (output routed to a nil writer) (#894) - dbaas: fix show --uri for clickhouse printing a raw host:port instead of a connection URI (#894) diff --git a/cmd/iam/iam_role_show.go b/cmd/iam/iam_role_show.go index 5021c84a5..73a402595 100644 --- a/cmd/iam/iam_role_show.go +++ b/cmd/iam/iam_role_show.go @@ -25,14 +25,9 @@ type iamRoleShowOutput struct { Policy *iamPolicyOutput `json:"policy" output:"-"` } -func (o *iamRoleShowOutput) ToJSON() { output.JSON(o) } -func (o *iamRoleShowOutput) ToText() { output.Text(o) } -func (o *iamRoleShowOutput) ToTable() { - output.Table(o) - if o.Policy != nil { - o.Policy.ToTable() - } -} +func (o *iamRoleShowOutput) ToJSON() { output.JSON(o) } +func (o *iamRoleShowOutput) ToText() { output.Text(o) } +func (o *iamRoleShowOutput) ToTable() { output.Table(o) } func iamPolicyToOutput(policy *v3.IAMPolicy) *iamPolicyOutput { if policy == nil { From ce2963b0e8219ebee43a1793747ab4bd9b3c20e0 Mon Sep 17 00:00:00 2001 From: Natalie Perret <11332444+natalie-o-perret@users.noreply.github.com> Date: Thu, 3 Sep 2026 10:59:10 +0200 Subject: [PATCH 4/4] iam: label separate policy table --- CHANGELOG.md | 2 +- cmd/iam/iam_role_show.go | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 473ef3ffd..ace09b31d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ ### Bug fixes -- iam: include policies in JSON role show output (#903) +- iam: include policies in role show output (#903) - dbaas: fix empty table output for user create, role list and acl show (output routed to a nil writer) (#894) - dbaas: fix show --uri for clickhouse printing a raw host:port instead of a connection URI (#894) diff --git a/cmd/iam/iam_role_show.go b/cmd/iam/iam_role_show.go index 73a402595..ee34cc389 100644 --- a/cmd/iam/iam_role_show.go +++ b/cmd/iam/iam_role_show.go @@ -25,9 +25,15 @@ type iamRoleShowOutput struct { Policy *iamPolicyOutput `json:"policy" output:"-"` } -func (o *iamRoleShowOutput) ToJSON() { output.JSON(o) } -func (o *iamRoleShowOutput) ToText() { output.Text(o) } -func (o *iamRoleShowOutput) ToTable() { output.Table(o) } +func (o *iamRoleShowOutput) ToJSON() { output.JSON(o) } +func (o *iamRoleShowOutput) ToText() { output.Text(o) } +func (o *iamRoleShowOutput) ToTable() { + output.Table(o) + if o.Policy != nil { + fmt.Println("\nPolicy:") + o.Policy.ToTable() + } +} func iamPolicyToOutput(policy *v3.IAMPolicy) *iamPolicyOutput { if policy == nil {