From 67d3ce84d30bcb66e1915290e53f9296fdf1a33a Mon Sep 17 00:00:00 2001 From: Predrag Janosevic Date: Wed, 15 Nov 2023 08:56:49 +0100 Subject: [PATCH] Remove iam org-policy update & refactor iam policy output --- cmd/iam.go | 58 ++++++++++- cmd/iam_org_policy_replace.go | 4 +- cmd/iam_org_policy_show.go | 64 ++---------- cmd/iam_org_policy_update.go | 187 ---------------------------------- 4 files changed, 65 insertions(+), 248 deletions(-) delete mode 100644 cmd/iam_org_policy_update.go diff --git a/cmd/iam.go b/cmd/iam.go index 230718ef..7e77aa51 100644 --- a/cmd/iam.go +++ b/cmd/iam.go @@ -1,6 +1,13 @@ package cmd -import "github.com/spf13/cobra" +import ( + "fmt" + "os" + + "github.com/exoscale/cli/pkg/output" + "github.com/exoscale/cli/table" + "github.com/spf13/cobra" +) var iamCmd = &cobra.Command{ Use: "iam", @@ -10,3 +17,52 @@ var iamCmd = &cobra.Command{ func init() { RootCmd.AddCommand(iamCmd) } + +type iamPolicyOutput struct { + DefaultServiceStrategy string `json:"default-service-strategy"` + Services map[string]iamPolicyServiceOutput `json:"services"` +} + +type iamPolicyServiceOutput struct { + Type string `json:"type"` + Rules []iamPolicyServiceRuleOutput `json:"rules"` +} + +type iamPolicyServiceRuleOutput struct { + Action string `json:"action"` + Expression string `json:"expression"` +} + +func (o *iamPolicyOutput) ToJSON() { output.JSON(o) } +func (o *iamPolicyOutput) ToText() { output.Text(o) } +func (o *iamPolicyOutput) ToTable() { + t := table.NewTable(os.Stdout) + t.SetAutoMergeCellsByColumnIndex([]int{0, 1}) + + t.SetHeader([]string{ + "Service", + fmt.Sprintf("Type (default strategy \"%s\")", o.DefaultServiceStrategy), + "Rule Action", + "Rule Expression", + }) + + // use underlying tablewriter.Render to display table even with empty rows + // as default strategy is in header. + defer t.Table.Render() + + for name, service := range o.Services { + if len(service.Rules) == 0 { + t.Append([]string{name, service.Type, "", "", ""}) + continue + } + + for _, rule := range service.Rules { + t.Append([]string{ + name, + service.Type, + rule.Action, + rule.Expression, + }) + } + } +} diff --git a/cmd/iam_org_policy_replace.go b/cmd/iam_org_policy_replace.go index 0fdf81c3..196d5243 100644 --- a/cmd/iam_org_policy_replace.go +++ b/cmd/iam_org_policy_replace.go @@ -38,7 +38,7 @@ Pro Tip: you can get policy in JSON format with command: exo iam org-policy show --output-format json Supported output template annotations: %s`, - strings.Join(output.TemplateAnnotations(&iamOrgPolicyShowOutput{}), ", ")) + strings.Join(output.TemplateAnnotations(&iamPolicyOutput{}), ", ")) } func (c *iamOrgPolicyReplaceCmd) cmdPreRun(cmd *cobra.Command, args []string) error { @@ -62,7 +62,7 @@ func (c *iamOrgPolicyReplaceCmd) cmdRun(cmd *cobra.Command, _ []string) error { c.Policy = string(b) } - var obj iamOrgPolicyShowOutput + var obj iamPolicyOutput err := json.Unmarshal([]byte(c.Policy), &obj) if err != nil { return fmt.Errorf("failed to parse policy: %w", err) diff --git a/cmd/iam_org_policy_show.go b/cmd/iam_org_policy_show.go index 33599530..0a262706 100644 --- a/cmd/iam_org_policy_show.go +++ b/cmd/iam_org_policy_show.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - "os" "strings" "github.com/spf13/cobra" @@ -10,61 +9,10 @@ import ( "github.com/exoscale/cli/pkg/account" "github.com/exoscale/cli/pkg/globalstate" "github.com/exoscale/cli/pkg/output" - "github.com/exoscale/cli/table" "github.com/exoscale/cli/utils" exoapi "github.com/exoscale/egoscale/v2/api" ) -type iamOrgPolicyShowOutput struct { - DefaultServiceStrategy string `json:"default-service-strategy"` - Services map[string]iamOrgPolicyServiceShowOutput `json:"services"` -} - -type iamOrgPolicyServiceShowOutput struct { - Type string `json:"type"` - Rules []iamOrgPolicyServiceRuleShowOutput `json:"rules"` -} - -type iamOrgPolicyServiceRuleShowOutput struct { - Action string `json:"action"` - Expression string `json:"expression"` -} - -func (o *iamOrgPolicyShowOutput) ToJSON() { output.JSON(o) } -func (o *iamOrgPolicyShowOutput) ToText() { output.Text(o) } -func (o *iamOrgPolicyShowOutput) ToTable() { - t := table.NewTable(os.Stdout) - t.SetAutoMergeCells(true) - t.SetAutoMergeCellsByColumnIndex([]int{0, 1}) - - t.SetHeader([]string{ - "Service", - fmt.Sprintf("Type (default strategy \"%s\")", o.DefaultServiceStrategy), - "Rule Action", - "Rule Expression", - }) - - // use underlying tablewriter.Render to display table even with empty rows - // as default strategy is in header. - defer t.Table.Render() - - for name, service := range o.Services { - if len(service.Rules) == 0 { - t.Append([]string{name, service.Type, "", "", ""}) - continue - } - - for _, rule := range service.Rules { - t.Append([]string{ - name, - service.Type, - rule.Action, - rule.Expression, - }) - } - } -} - type iamOrgPolicyShowCmd struct { cliCommandSettings `cli-cmd:"-"` @@ -81,7 +29,7 @@ func (c *iamOrgPolicyShowCmd) cmdLong() string { return fmt.Sprintf(`This command shows IAM Org Policy details. Supported output template annotations: %s`, - strings.Join(output.TemplateAnnotations(&iamOrgPolicyShowOutput{}), ", ")) + strings.Join(output.TemplateAnnotations(&iamPolicyServiceOutput{}), ", ")) } func (c *iamOrgPolicyShowCmd) cmdPreRun(cmd *cobra.Command, args []string) error { @@ -97,21 +45,21 @@ func (c *iamOrgPolicyShowCmd) cmdRun(_ *cobra.Command, _ []string) error { return err } - out := iamOrgPolicyShowOutput{ + out := iamPolicyOutput{ DefaultServiceStrategy: policy.DefaultServiceStrategy, - Services: map[string]iamOrgPolicyServiceShowOutput{}, + Services: map[string]iamPolicyServiceOutput{}, } for name, service := range policy.Services { - rules := []iamOrgPolicyServiceRuleShowOutput{} + rules := []iamPolicyServiceRuleOutput{} for _, rule := range service.Rules { - rules = append(rules, iamOrgPolicyServiceRuleShowOutput{ + rules = append(rules, iamPolicyServiceRuleOutput{ Action: utils.DefaultString(rule.Action, ""), Expression: utils.DefaultString(rule.Expression, ""), }) } - out.Services[name] = iamOrgPolicyServiceShowOutput{ + out.Services[name] = iamPolicyServiceOutput{ Type: utils.DefaultString(service.Type, ""), Rules: rules, } diff --git a/cmd/iam_org_policy_update.go b/cmd/iam_org_policy_update.go deleted file mode 100644 index 1c741626..00000000 --- a/cmd/iam_org_policy_update.go +++ /dev/null @@ -1,187 +0,0 @@ -package cmd - -import ( - "errors" - "fmt" - "strings" - - "github.com/spf13/cobra" - - "github.com/exoscale/cli/pkg/account" - "github.com/exoscale/cli/pkg/globalstate" - "github.com/exoscale/cli/pkg/output" - exoscale "github.com/exoscale/egoscale/v2" - exoapi "github.com/exoscale/egoscale/v2/api" - - "github.com/exoscale/cli/utils" -) - -type iamOrgPolicyUpdate struct { - cliCommandSettings `cli-cmd:"-"` - - DeleteService string `cli-flag:"delete-service" cli-usage:"Delete service class"` - AddService string `cli-flag:"add-service" cli-usage:"Add service class"` - UpdateService string `cli-flag:"update-service" cli-usage:"Update service class"` - - ServiceType string `cli-flag:"service-type" cli-usage:"Default Strategy for service type. Allowed values: 'allow', 'deny' and 'rules'. Used with --add-service"` - AppendServiceRuleAllow string `cli-flag:"append-service-rule-allow" cli-usage:"Append service rule of type 'allow' to the end of the rules list"` - AppendServiceRuleDeny string `cli-flag:"append-service-rule-deny" cli-usage:"Append service rule of type 'deny' to the end of the rules list"` - - _ bool `cli-cmd:"update"` -} - -func (c *iamOrgPolicyUpdate) cmdAliases() []string { return nil } - -func (c *iamOrgPolicyUpdate) cmdShort() string { - return "Update Org policy" -} - -func (c *iamOrgPolicyUpdate) cmdLong() string { - return fmt.Sprintf(`This command updates an IAM Organization Policy. - -Command can update only a single service and a single service rule. -For bigger changes to Org Policy command must be executed multiple times. - -Pro Tip: use 'exo iam org-policy replace' command to edit Org Policy as JSON document. - -Supported output template annotations: %s`, - strings.Join(output.TemplateAnnotations(&iamOrgPolicyShowOutput{}), ", ")) -} - -func (c *iamOrgPolicyUpdate) cmdPreRun(cmd *cobra.Command, args []string) error { - err := cliCommandDefaultPreRun(c, cmd, args) - if err != nil { - return err - } - - counter := 0 - if c.DeleteService != "" { - counter++ - } - if c.AddService != "" { - counter++ - } - if c.UpdateService != "" { - counter++ - } - - if counter != 1 { - return errors.New("only one service can be updated") - } - - if c.AddService != "" || c.UpdateService != "" { - if c.AppendServiceRuleAllow != "" && c.AppendServiceRuleDeny != "" { - return errors.New("only one service rule can be added") - } - } - - return nil -} - -func (c *iamOrgPolicyUpdate) cmdRun(cmd *cobra.Command, _ []string) error { - zone := account.CurrentAccount.DefaultZone - ctx := exoapi.WithEndpoint(gContext, exoapi.NewReqEndpoint(account.CurrentAccount.Environment, zone)) - - policy, err := globalstate.EgoscaleClient.GetIAMOrgPolicy(ctx, zone) - if err != nil { - return err - } - - switch { - case c.DeleteService != "": - if _, found := policy.Services[c.DeleteService]; !found { - return fmt.Errorf("service class %q not found", c.DeleteService) - } - - delete(policy.Services, c.DeleteService) - case c.AddService != "": - if _, found := policy.Services[c.AddService]; found { - return fmt.Errorf("service class %q already exists", c.AddService) - } - - service := exoscale.IAMPolicyService{} - - if c.ServiceType == "" { - return errors.New("--service-type must be specified when --add-service is used") - } - if c.ServiceType != "allow" && c.ServiceType != "deny" && c.ServiceType != "rules" { - return errors.New("allowed values for --service-type are: 'allow', 'deny' and 'rules'") - } - - service.Type = &c.ServiceType - - if c.ServiceType == "rules" { - if c.AppendServiceRuleAllow == "" && c.AppendServiceRuleDeny == "" { - return errors.New("service rule must be specified, use --append-service-rule-allow or --append-service-rule-deny") - } - - var rule exoscale.IAMPolicyServiceRule - - if c.AppendServiceRuleAllow != "" { - rule = exoscale.IAMPolicyServiceRule{ - Action: utils.NonEmptyStringPtr("allow"), - Expression: &c.AppendServiceRuleAllow, - } - } - - if c.AppendServiceRuleDeny != "" { - rule = exoscale.IAMPolicyServiceRule{ - Action: utils.NonEmptyStringPtr("deny"), - Expression: &c.AppendServiceRuleDeny, - } - } - - service.Rules = []exoscale.IAMPolicyServiceRule{rule} - } - - policy.Services[c.AddService] = service - case c.UpdateService != "": - service, found := policy.Services[c.UpdateService] - if !found { - return fmt.Errorf("service class %q does not exist", c.UpdateService) - } - - if c.AppendServiceRuleAllow == "" && c.AppendServiceRuleDeny == "" { - return errors.New("service rule must be specified, use --append-service-rule-allow or --append-service-rule-deny") - } - - var rule exoscale.IAMPolicyServiceRule - - if c.AppendServiceRuleAllow != "" { - rule = exoscale.IAMPolicyServiceRule{ - Action: utils.NonEmptyStringPtr("allow"), - Expression: &c.AppendServiceRuleAllow, - } - } - - if c.AppendServiceRuleDeny != "" { - rule = exoscale.IAMPolicyServiceRule{ - Action: utils.NonEmptyStringPtr("deny"), - Expression: &c.AppendServiceRuleDeny, - } - } - - service.Rules = append(service.Rules, rule) - - policy.Services[c.UpdateService] = service - } - - err = globalstate.EgoscaleClient.UpdateIAMOrgPolicy(ctx, zone, policy) - if err != nil { - return err - } - - if !globalstate.Quiet { - return (&iamOrgPolicyShowCmd{ - cliCommandSettings: c.cliCommandSettings, - }).cmdRun(nil, nil) - } - - return nil -} - -func init() { - cobra.CheckErr(registerCLICommand(iamOrgPolicyCmd, &iamOrgPolicyUpdate{ - cliCommandSettings: defaultCLICmdSettings(), - })) -}