From a08b9939c4a086c53bc36e86bdf9f971ca908dae Mon Sep 17 00:00:00 2001 From: elkezza Date: Thu, 19 Dec 2024 22:01:42 +0000 Subject: [PATCH 01/20] creating the structure, and implementation of acl_show and acl_show_kafka --- cmd/dbaas_acl.go | 14 ++++++ cmd/dbaas_acl_create.go | 1 + cmd/dbaas_acl_delete.go | 1 + cmd/dbaas_acl_list.go | 1 + cmd/dbaas_acl_show.go | 88 +++++++++++++++++++++++++++++++++++++ cmd/dbaas_acl_show_kafka.go | 35 +++++++++++++++ 6 files changed, 140 insertions(+) create mode 100644 cmd/dbaas_acl.go create mode 100644 cmd/dbaas_acl_create.go create mode 100644 cmd/dbaas_acl_delete.go create mode 100644 cmd/dbaas_acl_list.go create mode 100644 cmd/dbaas_acl_show.go create mode 100644 cmd/dbaas_acl_show_kafka.go diff --git a/cmd/dbaas_acl.go b/cmd/dbaas_acl.go new file mode 100644 index 000000000..a2f1ab061 --- /dev/null +++ b/cmd/dbaas_acl.go @@ -0,0 +1,14 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +var dbaasAclCmd = &cobra.Command{ + Use: "acl", + Short: "Manage DBaaS acl", +} + +func init() { + dbaasCmd.AddCommand(dbaasAclCmd) +} diff --git a/cmd/dbaas_acl_create.go b/cmd/dbaas_acl_create.go new file mode 100644 index 000000000..1d619dd05 --- /dev/null +++ b/cmd/dbaas_acl_create.go @@ -0,0 +1 @@ +package cmd diff --git a/cmd/dbaas_acl_delete.go b/cmd/dbaas_acl_delete.go new file mode 100644 index 000000000..1d619dd05 --- /dev/null +++ b/cmd/dbaas_acl_delete.go @@ -0,0 +1 @@ +package cmd diff --git a/cmd/dbaas_acl_list.go b/cmd/dbaas_acl_list.go new file mode 100644 index 000000000..1d619dd05 --- /dev/null +++ b/cmd/dbaas_acl_list.go @@ -0,0 +1 @@ +package cmd diff --git a/cmd/dbaas_acl_show.go b/cmd/dbaas_acl_show.go new file mode 100644 index 000000000..7430b0c1f --- /dev/null +++ b/cmd/dbaas_acl_show.go @@ -0,0 +1,88 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/exoscale/cli/pkg/output" + "github.com/exoscale/cli/table" + "github.com/spf13/cobra" +) + +type dbaasAclShowOutput struct { + Username string `json:"username,omitempty"` + Permission string `json:"permission,omitempty"` + Topic string `json:"topic,omitempty"` +} + +func (o *dbaasAclShowOutput) ToJSON() { output.JSON(o) } +func (o *dbaasAclShowOutput) ToText() { output.Text(o) } + +func (o *dbaasAclShowOutput) ToTable() { + t := table.NewTable(os.Stdout) + t.SetHeader([]string{"ACL Entry"}) + defer t.Render() + + t.Append([]string{"Username", o.Username}) + t.Append([]string{"Topic", o.Topic}) + t.Append([]string{"Permission", o.Permission}) +} + +type dbaasAclShowCmd struct { + cliCommandSettings `cli-cmd:"-"` + + _ bool `cli-cmd:"show"` + Name string `cli-flag:"name" cli-usage:"Name of the DBaaS service"` + Username string `cli-flag:"username" cli-usage:"Username of the ACL entry"` + ServiceType string `cli-short:"t" cli-usage:"type of the DBaaS service (e.g., kafka, opensearch)"` + Zone string `cli-short:"z" cli-usage:"Database Service zone"` +} + +func (c *dbaasAclShowCmd) cmdAliases() []string { return nil } + +func (c *dbaasAclShowCmd) cmdShort() string { return "Show the details of an acl" } + +func (c *dbaasAclShowCmd) cmdLong() string { + return `This command show an acl entty and its details for a specified DBAAS service.` +} + +func (c *dbaasAclShowCmd) cmdPreRun(cmd *cobra.Command, args []string) error { + cmdSetZoneFlagFromDefault(cmd) + + return cliCommandDefaultPreRun(c, cmd, args) +} + +func (c *dbaasAclShowCmd) cmdRun(cmd *cobra.Command, args []string) error { + ctx := gContext + + if c.Name == "" || c.Username == "" { + return fmt.Errorf("both --name and --username flags must be specified") + } + + db, err := dbaasGetV3(ctx, c.Name, c.Zone) + if err != nil { + return fmt.Errorf("error retrieving DBaaS service %q in zone %q: %w", c.Name, c.Zone, err) + } + + var output output.Outputter + switch db.Type { + case "kafka": + output, err = c.showKafka(ctx, c.Name) + //case "opensearch": + //output, err = c.showOpensearch(ctx) + default: + return fmt.Errorf("listing ACL unsupported for service of type %q", db.Type) + } + + if err != nil { + return err + } + + return c.outputFunc(output, nil) +} + +func init() { + cobra.CheckErr(registerCLICommand(dbaasAclCmd, &dbaasAclShowCmd{ + cliCommandSettings: defaultCLICmdSettings(), + })) +} diff --git a/cmd/dbaas_acl_show_kafka.go b/cmd/dbaas_acl_show_kafka.go new file mode 100644 index 000000000..508b5f5c0 --- /dev/null +++ b/cmd/dbaas_acl_show_kafka.go @@ -0,0 +1,35 @@ +package cmd + +import ( + "context" + "fmt" + "github.com/exoscale/cli/pkg/globalstate" + "github.com/exoscale/cli/pkg/output" + v3 "github.com/exoscale/egoscale/v3" +) + +func (c *dbaasAclShowCmd) showKafka(ctx context.Context, serviceName string) (output.Outputter, error) { + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return nil, fmt.Errorf("error initializing client for zone %s: %w", c.Zone, err) + } + + // Fetch Kafka ACLs for the specified service + acls, err := client.GetDBAASKafkaAclConfig(ctx, serviceName) + if err != nil { + return nil, fmt.Errorf("error fetching ACL configuration for service %q: %w", serviceName, err) + } + + // Search for the specific username in the fetched ACLs + for _, acl := range acls.TopicAcl { + if acl.Username == c.Username { + return &dbaasAclShowOutput{ + Username: acl.Username, + Topic: acl.Topic, + Permission: string(acl.Permission), + }, nil + } + } + + return nil, fmt.Errorf("ACL entry for username %q not found in service %q", c.Username, serviceName) +} From a05e812e886b81f9e6d90efc1a117eaaa51c593e Mon Sep 17 00:00:00 2001 From: elkezza Date: Fri, 20 Dec 2024 15:08:28 +0000 Subject: [PATCH 02/20] Adding the open search-logic for the show sub-command --- cmd/dbaas_acl_show.go | 16 ++++--- cmd/dbaas_acl_show_opensearch.go | 71 ++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 cmd/dbaas_acl_show_opensearch.go diff --git a/cmd/dbaas_acl_show.go b/cmd/dbaas_acl_show.go index 7430b0c1f..2995a5596 100644 --- a/cmd/dbaas_acl_show.go +++ b/cmd/dbaas_acl_show.go @@ -34,8 +34,8 @@ type dbaasAclShowCmd struct { _ bool `cli-cmd:"show"` Name string `cli-flag:"name" cli-usage:"Name of the DBaaS service"` Username string `cli-flag:"username" cli-usage:"Username of the ACL entry"` - ServiceType string `cli-short:"t" cli-usage:"type of the DBaaS service (e.g., kafka, opensearch)"` - Zone string `cli-short:"z" cli-usage:"Database Service zone"` + ServiceType string `cli-flag:"type" cli-short:"t" cli-usage:"type of the DBaaS service (e.g., kafka, opensearch)"` + Zone string `cli-flag:"zone" cli-short:"z" cli-usage:"Database Service zone"` } func (c *dbaasAclShowCmd) cmdAliases() []string { return nil } @@ -55,8 +55,8 @@ func (c *dbaasAclShowCmd) cmdPreRun(cmd *cobra.Command, args []string) error { func (c *dbaasAclShowCmd) cmdRun(cmd *cobra.Command, args []string) error { ctx := gContext - if c.Name == "" || c.Username == "" { - return fmt.Errorf("both --name and --username flags must be specified") + if c.Name == "" || c.Username == "" || c.ServiceType == "" { + return fmt.Errorf("both --name, --username and --type flags must be specified") } db, err := dbaasGetV3(ctx, c.Name, c.Zone) @@ -64,12 +64,16 @@ func (c *dbaasAclShowCmd) cmdRun(cmd *cobra.Command, args []string) error { return fmt.Errorf("error retrieving DBaaS service %q in zone %q: %w", c.Name, c.Zone, err) } + if string(db.Type) != c.ServiceType { + return fmt.Errorf("mismatched service type: expected %q but got %q for service %q", c.ServiceType, db.Type, c.Name) + } + var output output.Outputter switch db.Type { case "kafka": output, err = c.showKafka(ctx, c.Name) - //case "opensearch": - //output, err = c.showOpensearch(ctx) + case "opensearch": + output, err = c.showOpensearch(ctx, c.Name) default: return fmt.Errorf("listing ACL unsupported for service of type %q", db.Type) } diff --git a/cmd/dbaas_acl_show_opensearch.go b/cmd/dbaas_acl_show_opensearch.go new file mode 100644 index 000000000..a0e116082 --- /dev/null +++ b/cmd/dbaas_acl_show_opensearch.go @@ -0,0 +1,71 @@ +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/exoscale/cli/pkg/globalstate" + "github.com/exoscale/cli/pkg/output" + "github.com/exoscale/cli/table" + v3 "github.com/exoscale/egoscale/v3" +) + +type dbaasAclShowOpensearchOutput struct { + Username string `json:"username,omitempty"` + Rules []v3.DBAASOpensearchAclConfigAclsRules `json:"rules,omitempty"` + AclEnabled bool `json:"acl_enabled,omitempty"` + ExtendedAclEnabled bool `json:"extended_acl_enabled,omitempty"` +} + +func (o *dbaasAclShowOpensearchOutput) ToJSON() { output.JSON(o) } + +func (o *dbaasAclShowOpensearchOutput) ToText() { output.Text(o) } + +func (o *dbaasAclShowOpensearchOutput) ToTable() { + t := table.NewTable(os.Stdout) + t.SetHeader([]string{"Field", "Value"}) + defer t.Render() + + t.Append([]string{"ACL Enabled", fmt.Sprintf("%t", o.AclEnabled)}) + t.Append([]string{"Extended ACL Enabled", fmt.Sprintf("%t", o.ExtendedAclEnabled)}) + + for _, rule := range o.Rules { + t.Append([]string{"Rule", fmt.Sprintf("ACL pattern: %s, Permission: %s", rule.Index, rule.Permission)}) + } +} + +func (c *dbaasAclShowCmd) showOpensearch(ctx context.Context, serviceName string) (output.Outputter, error) { + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return nil, fmt.Errorf("error initializing client for zone %s: %w", c.Zone, err) + } + + aclsConfig, err := client.GetDBAASOpensearchAclConfig(ctx, serviceName) + if err != nil { + return nil, fmt.Errorf("error fetching ACL configuration for service %q: %w", serviceName, err) + } + + aclEnabled := false + if aclsConfig.AclEnabled != nil { + aclEnabled = *aclsConfig.AclEnabled + } + + extendedAclEnabled := false + if aclsConfig.ExtendedAclEnabled != nil { + extendedAclEnabled = *aclsConfig.ExtendedAclEnabled + } + + for _, acl := range aclsConfig.Acls { + if string(acl.Username) == c.Username { + return &dbaasAclShowOpensearchOutput{ + Username: string(acl.Username), + Rules: acl.Rules, + AclEnabled: aclEnabled, + ExtendedAclEnabled: extendedAclEnabled, + }, nil + } + } + + return nil, fmt.Errorf("ACL entry for username %q not found in service %q", c.Username, serviceName) +} From 0dff30c75e5bbf3ea2de5668ca108bb1991ed7a4 Mon Sep 17 00:00:00 2001 From: elkezza Date: Fri, 20 Dec 2024 15:17:59 +0000 Subject: [PATCH 03/20] Adding comment to the code for better readability. --- cmd/dbaas_acl_show.go | 17 ++++++++++++++--- cmd/dbaas_acl_show_opensearch.go | 12 +++++++++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/cmd/dbaas_acl_show.go b/cmd/dbaas_acl_show.go index 2995a5596..b7ea548b6 100644 --- a/cmd/dbaas_acl_show.go +++ b/cmd/dbaas_acl_show.go @@ -28,6 +28,7 @@ func (o *dbaasAclShowOutput) ToTable() { t.Append([]string{"Permission", o.Permission}) } +// Main command for showing ACLs type dbaasAclShowCmd struct { cliCommandSettings `cli-cmd:"-"` @@ -38,36 +39,44 @@ type dbaasAclShowCmd struct { Zone string `cli-flag:"zone" cli-short:"z" cli-usage:"Database Service zone"` } +// Command aliases (none in this case) func (c *dbaasAclShowCmd) cmdAliases() []string { return nil } +// Short description for the command func (c *dbaasAclShowCmd) cmdShort() string { return "Show the details of an acl" } +// Long description for the command func (c *dbaasAclShowCmd) cmdLong() string { return `This command show an acl entty and its details for a specified DBAAS service.` } +// Pre-run validation for required flags and default zone setting func (c *dbaasAclShowCmd) cmdPreRun(cmd *cobra.Command, args []string) error { - cmdSetZoneFlagFromDefault(cmd) - - return cliCommandDefaultPreRun(c, cmd, args) + cmdSetZoneFlagFromDefault(cmd) // Set the default zone if not specified + return cliCommandDefaultPreRun(c, cmd, args) // Run default validations } +// Main run logic for showing ACL details func (c *dbaasAclShowCmd) cmdRun(cmd *cobra.Command, args []string) error { ctx := gContext + // Validate required flags if c.Name == "" || c.Username == "" || c.ServiceType == "" { return fmt.Errorf("both --name, --username and --type flags must be specified") } + // Fetch DBaaS service details db, err := dbaasGetV3(ctx, c.Name, c.Zone) if err != nil { return fmt.Errorf("error retrieving DBaaS service %q in zone %q: %w", c.Name, c.Zone, err) } + // Validate that the service type matches the expected type if string(db.Type) != c.ServiceType { return fmt.Errorf("mismatched service type: expected %q but got %q for service %q", c.ServiceType, db.Type, c.Name) } + // Call the appropriate method based on the service type var output output.Outputter switch db.Type { case "kafka": @@ -82,9 +91,11 @@ func (c *dbaasAclShowCmd) cmdRun(cmd *cobra.Command, args []string) error { return err } + // Output the fetched details return c.outputFunc(output, nil) } +// Register the command func init() { cobra.CheckErr(registerCLICommand(dbaasAclCmd, &dbaasAclShowCmd{ cliCommandSettings: defaultCLICmdSettings(), diff --git a/cmd/dbaas_acl_show_opensearch.go b/cmd/dbaas_acl_show_opensearch.go index a0e116082..18950d24e 100644 --- a/cmd/dbaas_acl_show_opensearch.go +++ b/cmd/dbaas_acl_show_opensearch.go @@ -22,42 +22,52 @@ func (o *dbaasAclShowOpensearchOutput) ToJSON() { output.JSON(o) } func (o *dbaasAclShowOpensearchOutput) ToText() { output.Text(o) } +// ToTable Define table output formatting for OpenSearch func (o *dbaasAclShowOpensearchOutput) ToTable() { t := table.NewTable(os.Stdout) t.SetHeader([]string{"Field", "Value"}) defer t.Render() + // Display whether ACL and extended ACL are enabled t.Append([]string{"ACL Enabled", fmt.Sprintf("%t", o.AclEnabled)}) t.Append([]string{"Extended ACL Enabled", fmt.Sprintf("%t", o.ExtendedAclEnabled)}) + // Iterate over rules and display each for _, rule := range o.Rules { t.Append([]string{"Rule", fmt.Sprintf("ACL pattern: %s, Permission: %s", rule.Index, rule.Permission)}) } } +// Fetch OpenSearch ACL configuration and process its details func (c *dbaasAclShowCmd) showOpensearch(ctx context.Context, serviceName string) (output.Outputter, error) { + // Switch to the appropriate client for the specified zone client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) if err != nil { return nil, fmt.Errorf("error initializing client for zone %s: %w", c.Zone, err) } + // Fetch OpenSearch ACL configuration for the specified service aclsConfig, err := client.GetDBAASOpensearchAclConfig(ctx, serviceName) if err != nil { return nil, fmt.Errorf("error fetching ACL configuration for service %q: %w", serviceName, err) } + // Check if ACLs are enabled aclEnabled := false if aclsConfig.AclEnabled != nil { aclEnabled = *aclsConfig.AclEnabled } + // Check if extended ACLs are enabled extendedAclEnabled := false if aclsConfig.ExtendedAclEnabled != nil { extendedAclEnabled = *aclsConfig.ExtendedAclEnabled } + // Search for the specific username in the fetched ACLs for _, acl := range aclsConfig.Acls { if string(acl.Username) == c.Username { + // Return the ACL details for the matched username return &dbaasAclShowOpensearchOutput{ Username: string(acl.Username), Rules: acl.Rules, @@ -66,6 +76,6 @@ func (c *dbaasAclShowCmd) showOpensearch(ctx context.Context, serviceName string }, nil } } - + // If no matching username is found, return an error return nil, fmt.Errorf("ACL entry for username %q not found in service %q", c.Username, serviceName) } From 52a8caf905c412b3d6aef035fe7aca7a7c0cd57a Mon Sep 17 00:00:00 2001 From: elkezza Date: Fri, 20 Dec 2024 15:20:11 +0000 Subject: [PATCH 04/20] Improve readability. --- cmd/dbaas_acl_show.go | 12 ++++++------ cmd/dbaas_acl_show_opensearch.go | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cmd/dbaas_acl_show.go b/cmd/dbaas_acl_show.go index b7ea548b6..0289fae42 100644 --- a/cmd/dbaas_acl_show.go +++ b/cmd/dbaas_acl_show.go @@ -19,13 +19,13 @@ func (o *dbaasAclShowOutput) ToJSON() { output.JSON(o) } func (o *dbaasAclShowOutput) ToText() { output.Text(o) } func (o *dbaasAclShowOutput) ToTable() { - t := table.NewTable(os.Stdout) - t.SetHeader([]string{"ACL Entry"}) - defer t.Render() + table := table.NewTable(os.Stdout) + table.SetHeader([]string{"ACL Entry"}) + defer table.Render() - t.Append([]string{"Username", o.Username}) - t.Append([]string{"Topic", o.Topic}) - t.Append([]string{"Permission", o.Permission}) + table.Append([]string{"Username", o.Username}) + table.Append([]string{"Topic", o.Topic}) + table.Append([]string{"Permission", o.Permission}) } // Main command for showing ACLs diff --git a/cmd/dbaas_acl_show_opensearch.go b/cmd/dbaas_acl_show_opensearch.go index 18950d24e..4a09c2006 100644 --- a/cmd/dbaas_acl_show_opensearch.go +++ b/cmd/dbaas_acl_show_opensearch.go @@ -24,17 +24,17 @@ func (o *dbaasAclShowOpensearchOutput) ToText() { output.Text(o) } // ToTable Define table output formatting for OpenSearch func (o *dbaasAclShowOpensearchOutput) ToTable() { - t := table.NewTable(os.Stdout) - t.SetHeader([]string{"Field", "Value"}) - defer t.Render() + table := table.NewTable(os.Stdout) + table.SetHeader([]string{"Field", "Value"}) + defer table.Render() // Display whether ACL and extended ACL are enabled - t.Append([]string{"ACL Enabled", fmt.Sprintf("%t", o.AclEnabled)}) - t.Append([]string{"Extended ACL Enabled", fmt.Sprintf("%t", o.ExtendedAclEnabled)}) + table.Append([]string{"ACL Enabled", fmt.Sprintf("%t", o.AclEnabled)}) + table.Append([]string{"Extended ACL Enabled", fmt.Sprintf("%t", o.ExtendedAclEnabled)}) // Iterate over rules and display each for _, rule := range o.Rules { - t.Append([]string{"Rule", fmt.Sprintf("ACL pattern: %s, Permission: %s", rule.Index, rule.Permission)}) + table.Append([]string{"Rule", fmt.Sprintf("ACL pattern: %s, Permission: %s", rule.Index, rule.Permission)}) } } From 58b4dd2089be4b8609d4db402c04afbc0deff948 Mon Sep 17 00:00:00 2001 From: elkezza Date: Fri, 20 Dec 2024 15:20:54 +0000 Subject: [PATCH 05/20] Improve readability. --- cmd/dbaas_acl_show_kafka.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/dbaas_acl_show_kafka.go b/cmd/dbaas_acl_show_kafka.go index 508b5f5c0..c4ca8defa 100644 --- a/cmd/dbaas_acl_show_kafka.go +++ b/cmd/dbaas_acl_show_kafka.go @@ -8,6 +8,7 @@ import ( v3 "github.com/exoscale/egoscale/v3" ) +// Fetch OpenSearch ACL configuration and process its details func (c *dbaasAclShowCmd) showKafka(ctx context.Context, serviceName string) (output.Outputter, error) { client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) if err != nil { From be8f14fd4f0859a8629231fc0b7ad8c5d58fa6c9 Mon Sep 17 00:00:00 2001 From: elkezza Date: Mon, 23 Dec 2024 01:37:45 +0000 Subject: [PATCH 06/20] Adding the list sub-commands logic. --- cmd/dbaas_acl_list.go | 103 +++++++++++++++++++++++++++++++ cmd/dbaas_acl_list_kafka.go | 47 ++++++++++++++ cmd/dbaas_acl_list_opensearch.go | 58 +++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 cmd/dbaas_acl_list_kafka.go create mode 100644 cmd/dbaas_acl_list_opensearch.go diff --git a/cmd/dbaas_acl_list.go b/cmd/dbaas_acl_list.go index 1d619dd05..49066f989 100644 --- a/cmd/dbaas_acl_list.go +++ b/cmd/dbaas_acl_list.go @@ -1 +1,104 @@ package cmd + +import ( + "context" + "fmt" + "github.com/spf13/cobra" + + "github.com/exoscale/cli/pkg/globalstate" + "github.com/exoscale/cli/pkg/output" + v3 "github.com/exoscale/egoscale/v3" +) + +type dbaasAclListCmd struct { + cliCommandSettings `cli-cmd:"-"` + + _ bool `cli-cmd:"list"` + Name string `cli-flag:"name" cli-usage:"Name of the DBaaS service"` + ServiceType string `cli-flag:"type" cli-short:"t" cli-usage:"Type of the DBaaS service (e.g., kafka, opensearch)"` +} + +// Command aliases (none in this case) +func (c *dbaasAclListCmd) cmdAliases() []string { return nil } + +// Short description for the command +func (c *dbaasAclListCmd) cmdShort() string { return "List ACL entries for a DBaaS service" } + +// Long description for the command +func (c *dbaasAclListCmd) cmdLong() string { + return `This command lists ACL entries for a specified DBaaS service, including Kafka and OpenSearch, across all available zones.` +} + +// Pre-run validation for required flags +func (c *dbaasAclListCmd) cmdPreRun(cmd *cobra.Command, args []string) error { + return cliCommandDefaultPreRun(c, cmd, args) // Run default validations +} + +// Main run logic for listing ACLs +func (c *dbaasAclListCmd) cmdRun(cmd *cobra.Command, args []string) error { + ctx := context.Background() + + // Validate required flags + if c.Name == "" || c.ServiceType == "" { + return fmt.Errorf("both --name and --type flags must be specified") + } + + // Fetch all available zones + zones, err := globalstate.EgoscaleV3Client.ListZones(ctx) + if err != nil { + return fmt.Errorf("error fetching zones: %w", err) + } + + // Iterate through zones to find the service + var found bool + var serviceZone string + var dbType v3.DBAASDatabaseName + for _, zone := range zones.Zones { + db, err := dbaasGetV3(ctx, c.Name, string(zone.Name)) + if err == nil { + dbType = v3.DBAASDatabaseName(db.Type) + found = true + serviceZone = string(zone.Name) + break + } + } + + // Handle case where service is not found in any zone + if !found { + return fmt.Errorf("service %q not found in any zone", c.Name) + } + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(serviceZone)) + if err != nil { + return fmt.Errorf("error initializing client for zone %s: %w", serviceZone, err) + } + + // Validate the service type + if string(dbType) != c.ServiceType { + return fmt.Errorf("mismatched service type: expected %q but got %q for service %q", c.ServiceType, dbType, c.Name) + } + + // Determine the appropriate listing logic based on the service type + var output output.Outputter + switch dbType { + case "kafka": + output, err = c.listKafkaACL(ctx, client, c.Name) + case "opensearch": + output, err = c.listOpenSearchACL(ctx, client, c.Name) + default: + return fmt.Errorf("listing ACL unsupported for service type %q", dbType) + } + + if err != nil { + return err + } + + // Output the fetched details + return c.outputFunc(output, nil) +} + +func init() { + cobra.CheckErr(registerCLICommand(dbaasAclCmd, &dbaasAclListCmd{ + cliCommandSettings: defaultCLICmdSettings(), + })) +} diff --git a/cmd/dbaas_acl_list_kafka.go b/cmd/dbaas_acl_list_kafka.go new file mode 100644 index 000000000..f1d156f97 --- /dev/null +++ b/cmd/dbaas_acl_list_kafka.go @@ -0,0 +1,47 @@ +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/exoscale/cli/pkg/output" + "github.com/exoscale/cli/table" + v3 "github.com/exoscale/egoscale/v3" +) + +// dbaasAclListKafkaOutput defines the Kafka ACL output structure. +type dbaasAclListKafkaOutput struct { + TopicAcls []v3.DBAASKafkaTopicAclEntry `json:"topic_acls,omitempty"` +} + +// ToJSON outputs the result in JSON format. +func (o *dbaasAclListKafkaOutput) ToJSON() { output.JSON(o) } + +// ToText outputs the result in plain text format. +func (o *dbaasAclListKafkaOutput) ToText() { output.Text(o) } + +// ToTable outputs the result in a tabular format. +func (o *dbaasAclListKafkaOutput) ToTable() { + tabular := table.NewTable(os.Stdout) + tabular.SetHeader([]string{"Username", "Topic", "Permission"}) + + // Display Topic ACL entries. + for _, acl := range o.TopicAcls { + tabular.Append([]string{acl.Username, acl.Topic, string(acl.Permission)}) + } + + tabular.Render() +} + +// listKafkaACL fetches Kafka ACLs and prepares the output. +func (c *dbaasAclListCmd) listKafkaACL(ctx context.Context, client *v3.Client, serviceName string) (output.Outputter, error) { + acls, err := client.GetDBAASKafkaAclConfig(ctx, serviceName) + if err != nil { + return nil, fmt.Errorf("error fetching Kafka ACL configuration: %w", err) + } + + return &dbaasAclListKafkaOutput{ + TopicAcls: acls.TopicAcl, + }, nil +} diff --git a/cmd/dbaas_acl_list_opensearch.go b/cmd/dbaas_acl_list_opensearch.go new file mode 100644 index 000000000..88c63bd16 --- /dev/null +++ b/cmd/dbaas_acl_list_opensearch.go @@ -0,0 +1,58 @@ +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/exoscale/cli/pkg/output" + "github.com/exoscale/cli/table" + v3 "github.com/exoscale/egoscale/v3" +) + +// dbaasAclListOpenSearchOutput defines the OpenSearch ACL output structure. +type dbaasAclListOpenSearchOutput struct { + Acls []v3.DBAASOpensearchAclConfigAcls `json:"acls,omitempty"` // ACLs grouped by username + AclEnabled bool `json:"acl_enabled,omitempty"` // ACL Enabled status + ExtendedAclEnabled bool `json:"extended_acl_enabled,omitempty"` // Extended ACL Enabled status +} + +// ToJSON outputs the result in JSON format. +func (o *dbaasAclListOpenSearchOutput) ToJSON() { output.JSON(o) } + +// ToText outputs the result in plain text format. +func (o *dbaasAclListOpenSearchOutput) ToText() { output.Text(o) } + +// ToTable outputs the result in a tabular format. +func (o *dbaasAclListOpenSearchOutput) ToTable() { + tabular := table.NewTable(os.Stdout) + tabular.SetHeader([]string{"Field", "Value"}) + + // Display general ACL configurations. + tabular.Append([]string{"ACL Enabled", fmt.Sprintf("%t", o.AclEnabled)}) + tabular.Append([]string{"Extended ACL Enabled", fmt.Sprintf("%t", o.ExtendedAclEnabled)}) + + // Display rules grouped under usernames. + for _, acl := range o.Acls { + tabular.Append([]string{"Username", string(acl.Username)}) + for _, rule := range acl.Rules { + tabular.Append([]string{" Rule", fmt.Sprintf("Pattern: %s, Permission: %s", rule.Index, string(rule.Permission))}) + } + } + + tabular.Render() +} + +// listOpenSearchACL fetches OpenSearch ACLs and prepares the output. +func (c *dbaasAclListCmd) listOpenSearchACL(ctx context.Context, client *v3.Client, serviceName string) (output.Outputter, error) { + aclsConfig, err := client.GetDBAASOpensearchAclConfig(ctx, serviceName) + if err != nil { + return nil, fmt.Errorf("error fetching OpenSearch ACL configuration: %w", err) + } + + return &dbaasAclListOpenSearchOutput{ + Acls: aclsConfig.Acls, + AclEnabled: *aclsConfig.AclEnabled, + ExtendedAclEnabled: *aclsConfig.ExtendedAclEnabled, + }, nil +} From a8703f67554de1ad7d54079d792b259cc76440b9 Mon Sep 17 00:00:00 2001 From: elkezza Date: Mon, 23 Dec 2024 02:07:36 +0000 Subject: [PATCH 07/20] adding the delete sub-command tree logic, still missing the opensearch becuase the openapi does not support delete opensearch acl entry. --- cmd/dbaas_acl_delete.go | 101 +++++++++++++++++++++++++++++ cmd/dbaas_acl_delete_kafka.go | 42 ++++++++++++ cmd/dbaas_acl_delete_opensearch.go | 1 + 3 files changed, 144 insertions(+) create mode 100644 cmd/dbaas_acl_delete_kafka.go create mode 100644 cmd/dbaas_acl_delete_opensearch.go diff --git a/cmd/dbaas_acl_delete.go b/cmd/dbaas_acl_delete.go index 1d619dd05..13402e986 100644 --- a/cmd/dbaas_acl_delete.go +++ b/cmd/dbaas_acl_delete.go @@ -1 +1,102 @@ package cmd + +import ( + "context" + "fmt" + + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +type dbaasAclDeleteCmd struct { + cliCommandSettings `cli-cmd:"-"` + + _ bool `cli-cmd:"delete"` + Name string `cli-flag:"name" cli-usage:"Name of the DBaaS service"` + ServiceType string `cli-flag:"type" cli-short:"t" cli-usage:"DBaaS service type (e.g., kafka, opensearch)"` + Username string `cli-flag:"username" cli-usage:"Username of the ACL entry"` +} + +// Command aliases (none in this case) +func (c *dbaasAclDeleteCmd) cmdAliases() []string { return nil } + +// Short description for the command +func (c *dbaasAclDeleteCmd) cmdShort() string { return "Delete an ACL entry for a DBaaS service" } + +// Long description for the command +func (c *dbaasAclDeleteCmd) cmdLong() string { + return `This command deletes a specified ACL entry for a DBaaS service, such as Kafka or OpenSearch, across all available zones.` +} + +func (c *dbaasAclDeleteCmd) cmdPreRun(cmd *cobra.Command, args []string) error { + return cliCommandDefaultPreRun(c, cmd, args) // Default validations +} + +// Main run logic for showing ACL details +func (c *dbaasAclDeleteCmd) cmdRun(cmd *cobra.Command, args []string) error { + ctx := context.Background() + + // Validate required flags + if c.Name == "" || c.ServiceType == "" || c.Username == "" { + return fmt.Errorf("all flags --name, --type, and --username must be specified") + } + + // Fetch all available zones + zones, err := globalstate.EgoscaleV3Client.ListZones(ctx) + if err != nil { + return fmt.Errorf("error fetching zones: %w", err) + } + + // Iterate through zones to find the service + var found bool + var serviceZone string + var dbType v3.DBAASDatabaseName // Use DBAASDatabaseName for consistency + for _, zone := range zones.Zones { + db, err := dbaasGetV3(ctx, c.Name, string(zone.Name)) + if err == nil { + dbType = v3.DBAASDatabaseName(db.Type) // Save the type for validation + found = true + serviceZone = string(zone.Name) + break + } + } + + // Handle case where service is not found in any zone + if !found { + return fmt.Errorf("service %q not found in any zone", c.Name) + } + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(serviceZone)) + if err != nil { + return fmt.Errorf("error initializing client for zone %s: %w", serviceZone, err) + } + + // Validate the service type + if string(dbType) != c.ServiceType { + return fmt.Errorf("mismatched service type: expected %q but got %q for service %q", c.ServiceType, dbType, c.Name) + } + + // Call the appropriate delete logic based on the service type + switch dbType { + case "kafka": + err = c.deleteKafkaACL(ctx, client, c.Name, c.Username) + case "opensearch": //TODO + //err = c.deleteOpenSearchACL(ctx, client, c.Name, c.Username) + default: + return fmt.Errorf("deleting ACL unsupported for service type %q", dbType) + } + + if err != nil { + return err + } + + cmd.Println(fmt.Sprintf("Successfully deleted ACL entry for username %q in service %q.", c.Username, c.Name)) + return nil +} + +func init() { + cobra.CheckErr(registerCLICommand(dbaasAclCmd, &dbaasAclDeleteCmd{ + cliCommandSettings: defaultCLICmdSettings(), + })) +} diff --git a/cmd/dbaas_acl_delete_kafka.go b/cmd/dbaas_acl_delete_kafka.go new file mode 100644 index 000000000..636d8c4e2 --- /dev/null +++ b/cmd/dbaas_acl_delete_kafka.go @@ -0,0 +1,42 @@ +package cmd + +import ( + "context" + "fmt" + + v3 "github.com/exoscale/egoscale/v3" +) + +// deleteKafkaACL deletes a Kafka ACL entry for the specified username. +func (c *dbaasAclDeleteCmd) deleteKafkaACL(ctx context.Context, client *v3.Client, serviceName, username string) error { + // Fetch Kafka ACLs for the service + acls, err := client.GetDBAASKafkaAclConfig(ctx, serviceName) + if err != nil { + return fmt.Errorf("error fetching Kafka ACL configuration: %w", err) + } + + // Find ACL entries for the given username and delete them + var found bool + for _, acl := range acls.TopicAcl { + if acl.Username == username { + found = true + // Use the correct delete function to remove the topic ACL + op, err := client.DeleteDBAASKafkaTopicAclConfig(ctx, serviceName, string(acl.ID)) + if err != nil { + return fmt.Errorf("error deleting ACL entry %q for topic %q: %w", acl.ID, acl.Topic, err) + } + + // Wait for the operation to complete (if applicable) + _, waitErr := client.Wait(ctx, op, v3.OperationStateSuccess) + if waitErr != nil { + return fmt.Errorf("error waiting for ACL deletion operation: %w", waitErr) + } + } + } + + if !found { + return fmt.Errorf("no ACL entry found for username %q in service %q", username, serviceName) + } + + return nil +} diff --git a/cmd/dbaas_acl_delete_opensearch.go b/cmd/dbaas_acl_delete_opensearch.go new file mode 100644 index 000000000..1d619dd05 --- /dev/null +++ b/cmd/dbaas_acl_delete_opensearch.go @@ -0,0 +1 @@ +package cmd From 7884c912ef6de3daa3db6f30c3bdf47c22df431b Mon Sep 17 00:00:00 2001 From: elkezza Date: Thu, 2 Jan 2025 09:15:48 +0000 Subject: [PATCH 08/20] adding the update sub-command tree logic, still missing the kafka because the openapi does not support update for opensearch acl entry. --- cmd/dbaas_acl_delete.go | 6 +- cmd/dbaas_acl_delete_opensearch.go | 1 - cmd/dbaas_acl_update.go | 92 ++++++++++++++++++++++++++++++ cmd/dbaas_acl_update_opensearch.go | 65 +++++++++++++++++++++ 4 files changed, 159 insertions(+), 5 deletions(-) delete mode 100644 cmd/dbaas_acl_delete_opensearch.go create mode 100644 cmd/dbaas_acl_update.go create mode 100644 cmd/dbaas_acl_update_opensearch.go diff --git a/cmd/dbaas_acl_delete.go b/cmd/dbaas_acl_delete.go index 13402e986..c0fafcf62 100644 --- a/cmd/dbaas_acl_delete.go +++ b/cmd/dbaas_acl_delete.go @@ -51,11 +51,11 @@ func (c *dbaasAclDeleteCmd) cmdRun(cmd *cobra.Command, args []string) error { // Iterate through zones to find the service var found bool var serviceZone string - var dbType v3.DBAASDatabaseName // Use DBAASDatabaseName for consistency + var dbType v3.DBAASDatabaseName for _, zone := range zones.Zones { db, err := dbaasGetV3(ctx, c.Name, string(zone.Name)) if err == nil { - dbType = v3.DBAASDatabaseName(db.Type) // Save the type for validation + dbType = v3.DBAASDatabaseName(db.Type) found = true serviceZone = string(zone.Name) break @@ -81,8 +81,6 @@ func (c *dbaasAclDeleteCmd) cmdRun(cmd *cobra.Command, args []string) error { switch dbType { case "kafka": err = c.deleteKafkaACL(ctx, client, c.Name, c.Username) - case "opensearch": //TODO - //err = c.deleteOpenSearchACL(ctx, client, c.Name, c.Username) default: return fmt.Errorf("deleting ACL unsupported for service type %q", dbType) } diff --git a/cmd/dbaas_acl_delete_opensearch.go b/cmd/dbaas_acl_delete_opensearch.go deleted file mode 100644 index 1d619dd05..000000000 --- a/cmd/dbaas_acl_delete_opensearch.go +++ /dev/null @@ -1 +0,0 @@ -package cmd diff --git a/cmd/dbaas_acl_update.go b/cmd/dbaas_acl_update.go new file mode 100644 index 000000000..e202e666c --- /dev/null +++ b/cmd/dbaas_acl_update.go @@ -0,0 +1,92 @@ +package cmd + +import ( + "context" + "fmt" + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +type dbaasAclUpdateCmd struct { + cliCommandSettings `cli-cmd:"-"` + + _ bool `cli-cmd:"update"` + Name string `cli-flag:"name" cli-usage:"Name of the DBaaS service"` + Username string `cli-flag:"username" cli-usage:"Current username of the ACL entry to update"` + NewUsername string `cli-flag:"new-username" cli-usage:"New username to replace the current one (optional)"` + ServiceType string `cli-flag:"type" cli-short:"t" cli-usage:"Type of the DBaaS service (e.g., opensearch)"` + Index string `cli-flag:"index" cli-usage:"The index pattern for the ACL rule"` + Permission string `cli-flag:"permission" cli-usage:"Permission to apply (should be one of admin, read, readwrite, write, or deny (only for OpenSearch))"` +} + +// Command aliases (none in this case) +func (c *dbaasAclUpdateCmd) cmdAliases() []string { return nil } + +// Short description for the command +func (c *dbaasAclUpdateCmd) cmdShort() string { + return "Update an ACL entry for a DBaaS service" +} + +// Long description for the command +func (c *dbaasAclUpdateCmd) cmdLong() string { + return `This command updates an ACL entry for a specified DBaaS service. You can also update the username with the --new-username flag.` +} + +func (c *dbaasAclUpdateCmd) cmdPreRun(cmd *cobra.Command, args []string) error { + return cliCommandDefaultPreRun(c, cmd, args) +} + +// Main run logic for showing ACL details +func (c *dbaasAclUpdateCmd) cmdRun(cmd *cobra.Command, args []string) error { + ctx := context.Background() + + // Validate required flags + if c.Name == "" || c.Username == "" || c.ServiceType == "" { + return fmt.Errorf("both --name, --username, and --type flags must be specified") + } + + // Fetch all available zones + zones, err := globalstate.EgoscaleV3Client.ListZones(ctx) + if err != nil { + return fmt.Errorf("error fetching zones: %w", err) + } + + // Iterate through zones to find the service + var found bool + var serviceZone string + var dbType v3.DBAASDatabaseName + for _, zone := range zones.Zones { + db, err := dbaasGetV3(ctx, c.Name, string(zone.Name)) + if err == nil { + dbType = v3.DBAASDatabaseName(db.Type) + found = true + serviceZone = string(zone.Name) + break + } + } + + // Handle case where service is not found in any zone + if !found { + return fmt.Errorf("service %q not found in any zone", c.Name) + } + + // Validate the service type + if string(dbType) != c.ServiceType { + return fmt.Errorf("service type mismatch: expected %q but got %q for service %q", c.ServiceType, dbType, c.Name) + } + + // Determine the appropriate update logic based on the service type + switch dbType { + case "opensearch": + return c.updateOpensearch(ctx, serviceZone, c.Name) + default: + return fmt.Errorf("update ACL unsupported for service type %q", dbType) + } +} + +func init() { + cobra.CheckErr(registerCLICommand(dbaasAclCmd, &dbaasAclUpdateCmd{ + cliCommandSettings: defaultCLICmdSettings(), + })) +} diff --git a/cmd/dbaas_acl_update_opensearch.go b/cmd/dbaas_acl_update_opensearch.go new file mode 100644 index 000000000..ab8f49d8c --- /dev/null +++ b/cmd/dbaas_acl_update_opensearch.go @@ -0,0 +1,65 @@ +package cmd + +import ( + "context" + "fmt" + + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" +) + +func (c *dbaasAclUpdateCmd) updateOpensearch(ctx context.Context, zone, serviceName string) error { + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(zone)) + if err != nil { + return fmt.Errorf("error initializing client for zone %s: %w", zone, err) + } + + aclsConfig, err := client.GetDBAASOpensearchAclConfig(ctx, serviceName) + if err != nil { + return fmt.Errorf("error fetching ACL configuration for service %q: %w", serviceName, err) + } + + // Ensure ACL entry for the specified username exists + var updatedAcls []v3.DBAASOpensearchAclConfigAcls + var updatedEntry *v3.DBAASOpensearchAclConfigAcls + found := false + + for _, acl := range aclsConfig.Acls { + if string(acl.Username) == c.Username { + found = true + + // Update username if --new-username is provided + newUsername := c.Username + if c.NewUsername != "" { + newUsername = c.NewUsername + } + + updatedEntry = &v3.DBAASOpensearchAclConfigAcls{ + Username: v3.DBAASUserUsername(newUsername), + Rules: []v3.DBAASOpensearchAclConfigAclsRules{ + {Index: c.Index, Permission: v3.EnumOpensearchRulePermission(c.Permission)}, + }, + } + } else { + updatedAcls = append(updatedAcls, acl) + } + } + + if !found { + return fmt.Errorf("ACL entry for username %q not found in service %q", c.Username, serviceName) + } + + if updatedEntry != nil { + updatedAcls = append(updatedAcls, *updatedEntry) + } + + // Update the configuration + aclsConfig.Acls = updatedAcls + _, err = client.UpdateDBAASOpensearchAclConfig(ctx, serviceName, *aclsConfig) + if err != nil { + return fmt.Errorf("error updating ACL configuration for service %q: %w", serviceName, err) + } + + fmt.Printf("ACL entry for username %q updated successfully in service %q\n", c.Username, serviceName) + return nil +} From 80ebe7889d9ef9eeed14cce9b57465b61dbaa0c6 Mon Sep 17 00:00:00 2001 From: elkezza Date: Thu, 2 Jan 2025 13:35:59 +0000 Subject: [PATCH 09/20] adding the `create` sub-command tree logic. --- cmd/dbaas_acl_create.go | 94 ++++++++++++++++++++++++++++++ cmd/dbaas_acl_create_kafka.go | 35 +++++++++++ cmd/dbaas_acl_create_opensearch.go | 46 +++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 cmd/dbaas_acl_create_kafka.go create mode 100644 cmd/dbaas_acl_create_opensearch.go diff --git a/cmd/dbaas_acl_create.go b/cmd/dbaas_acl_create.go index 1d619dd05..3bba8ef5a 100644 --- a/cmd/dbaas_acl_create.go +++ b/cmd/dbaas_acl_create.go @@ -1 +1,95 @@ package cmd + +import ( + "context" + "fmt" + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +type dbaasAclCreateCmd struct { + cliCommandSettings `cli-cmd:"-"` + + _ bool `cli-cmd:"create"` + Name string `cli-flag:"name" cli-usage:"Name of the DBaaS service"` + Username string `cli-flag:"username" cli-usage:"Username for the ACL entry"` + ServiceType string `cli-flag:"type" cli-short:"t" cli-usage:"Type of the DBaaS service (e.g., kafka opensearch)"` + Pattern string `cli-flag:"pattern" cli-usage:"The pattern for the ACL rule (index* for OpenSearch or topic for Kafka, max 249 characters)"` + Permission string `cli-flag:"permission" cli-usage:"Permission to apply (should be one of admin, read, readwrite, write, or deny (only for OpenSearch))"` +} + +// Command aliases (none in this case) +func (c *dbaasAclCreateCmd) cmdAliases() []string { return nil } + +// Short description for the command +func (c *dbaasAclCreateCmd) cmdShort() string { + return "Create an ACL entry for a DBaaS service" +} + +// Long description for the command +func (c *dbaasAclCreateCmd) cmdLong() string { + return `This command creates an ACL entry for a specified DBaaS service, automatically searching for the service across all available zones.` +} + +func (c *dbaasAclCreateCmd) cmdPreRun(cmd *cobra.Command, args []string) error { + return cliCommandDefaultPreRun(c, cmd, args) +} + +// Main run logic for showing ACL details +func (c *dbaasAclCreateCmd) cmdRun(cmd *cobra.Command, args []string) error { + ctx := context.Background() + + // Validate required inputs + if c.Name == "" || c.Username == "" || c.ServiceType == "" || c.Permission == "" || c.Pattern == "" { + return fmt.Errorf("all --name, --username, --type, --permission and --pattern flags must be specified") + } + + // Fetch all available zones + zones, err := globalstate.EgoscaleV3Client.ListZones(ctx) + if err != nil { + return fmt.Errorf("error fetching zones: %w", err) + } + + // Iterate through zones to find the service + var serviceZone string + var dbType v3.DBAASDatabaseName + var client *v3.Client + found := false + + for _, zone := range zones.Zones { + db, err := dbaasGetV3(ctx, c.Name, string(zone.Name)) + if err == nil { + dbType = v3.DBAASDatabaseName(db.Type) + serviceZone = string(zone.Name) + client, err = switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(serviceZone)) + if err != nil { + return fmt.Errorf("error initializing client for zone %s: %w", serviceZone, err) + } + found = true + break + } + } + + if !found { + return fmt.Errorf("service %q not found in any zone", c.Name) + } + // Validate the service type + if string(dbType) != c.ServiceType { + return fmt.Errorf("service type mismatch: expected %q but got %q for service %q", c.ServiceType, dbType, c.Name) + } + + switch dbType { + case "kafka": + return c.createKafka(ctx, client, c.Name) + case "opensearch": + return c.createOpensearch(ctx, client, c.Name) + default: + return fmt.Errorf("create ACL unsupported for service type %q", dbType) + } +} +func init() { + cobra.CheckErr(registerCLICommand(dbaasAclCmd, &dbaasAclCreateCmd{ + cliCommandSettings: defaultCLICmdSettings(), + })) +} diff --git a/cmd/dbaas_acl_create_kafka.go b/cmd/dbaas_acl_create_kafka.go new file mode 100644 index 000000000..573aaa53c --- /dev/null +++ b/cmd/dbaas_acl_create_kafka.go @@ -0,0 +1,35 @@ +package cmd + +import ( + "context" + "fmt" + + v3 "github.com/exoscale/egoscale/v3" +) + +func (c *dbaasAclCreateCmd) createKafka(ctx context.Context, client *v3.Client, serviceName string) error { + // Define the new Kafka ACL entry + newAcl := v3.DBAASKafkaTopicAclEntry{ + Username: c.Username, + Topic: c.Pattern, + Permission: v3.DBAASKafkaTopicAclEntryPermission(c.Permission), + } + + // Trigger the creation of the ACL entry + op, err := client.CreateDBAASKafkaTopicAclConfig(ctx, serviceName, newAcl) + if err != nil { + return fmt.Errorf("error creating ACL entry for service %q: %w", serviceName, err) + } + + // Use decorateAsyncOperation to handle the operation and provide user feedback + decorateAsyncOperation(fmt.Sprintf("Creating Kafka ACL entry for user %q", c.Username), func() { + op, err = client.Wait(ctx, op, v3.OperationStateSuccess) + }) + + if err != nil { + return fmt.Errorf("error completing ACL creation: %w", err) + } + + fmt.Printf("Kafka ACL entry for user %q successfully created in service %q\n", c.Username, serviceName) + return nil +} diff --git a/cmd/dbaas_acl_create_opensearch.go b/cmd/dbaas_acl_create_opensearch.go new file mode 100644 index 000000000..abc4d0d1b --- /dev/null +++ b/cmd/dbaas_acl_create_opensearch.go @@ -0,0 +1,46 @@ +package cmd + +import ( + "context" + "fmt" + v3 "github.com/exoscale/egoscale/v3" +) + +func (c *dbaasAclCreateCmd) createOpensearch(ctx context.Context, client *v3.Client, serviceName string) error { + aclsConfig, err := client.GetDBAASOpensearchAclConfig(ctx, serviceName) + if err != nil { + return fmt.Errorf("error fetching ACL configuration for service %q: %w", serviceName, err) + } + + // Check if an entry with the same username already exists + for _, acl := range aclsConfig.Acls { + if string(acl.Username) == c.Username { + return fmt.Errorf("ACL entry for username %q already exists in service %q", c.Username, serviceName) + } + } + + // Create a new ACL entry + newAcl := v3.DBAASOpensearchAclConfigAcls{ + Username: v3.DBAASUserUsername(c.Username), + Rules: []v3.DBAASOpensearchAclConfigAclsRules{ + {Index: c.Pattern, Permission: v3.EnumOpensearchRulePermission(c.Permission)}, + }, + } + + // Append the new entry to the existing ACLs + aclsConfig.Acls = append(aclsConfig.Acls, newAcl) + + // Update the configuration with the new entry + op, err := client.UpdateDBAASOpensearchAclConfig(ctx, serviceName, *aclsConfig) + if err != nil { + return fmt.Errorf("error updating ACL configuration for service %q: %w", serviceName, err) + } + + // Use decorateAsyncOperation to wait for the operation and provide user feedback + decorateAsyncOperation(fmt.Sprintf("Creating ACL entry for user %q", c.Username), func() { + op, err = client.Wait(ctx, op, v3.OperationStateSuccess) + }) + + fmt.Printf("ACL entry for username %q created successfully in service %q\n", c.Username, serviceName) + return nil +} From a234463d28e7a07dd63f5298cd9bdc251cc4a1d1 Mon Sep 17 00:00:00 2001 From: elkezza Date: Thu, 2 Jan 2025 13:41:07 +0000 Subject: [PATCH 10/20] use `pattern` instead of `index` as it is more general name, because in kafka `topic` is used. --- cmd/dbaas_acl_update.go | 2 +- cmd/dbaas_acl_update_opensearch.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/dbaas_acl_update.go b/cmd/dbaas_acl_update.go index e202e666c..ddb9dbf17 100644 --- a/cmd/dbaas_acl_update.go +++ b/cmd/dbaas_acl_update.go @@ -16,7 +16,7 @@ type dbaasAclUpdateCmd struct { Username string `cli-flag:"username" cli-usage:"Current username of the ACL entry to update"` NewUsername string `cli-flag:"new-username" cli-usage:"New username to replace the current one (optional)"` ServiceType string `cli-flag:"type" cli-short:"t" cli-usage:"Type of the DBaaS service (e.g., opensearch)"` - Index string `cli-flag:"index" cli-usage:"The index pattern for the ACL rule"` + Pattern string `cli-flag:"pattern" cli-usage:"The pattern for the ACL rule (index* for OpenSearch or topic for Kafka, max 249 characters)"` Permission string `cli-flag:"permission" cli-usage:"Permission to apply (should be one of admin, read, readwrite, write, or deny (only for OpenSearch))"` } diff --git a/cmd/dbaas_acl_update_opensearch.go b/cmd/dbaas_acl_update_opensearch.go index ab8f49d8c..ea099fffb 100644 --- a/cmd/dbaas_acl_update_opensearch.go +++ b/cmd/dbaas_acl_update_opensearch.go @@ -37,7 +37,7 @@ func (c *dbaasAclUpdateCmd) updateOpensearch(ctx context.Context, zone, serviceN updatedEntry = &v3.DBAASOpensearchAclConfigAcls{ Username: v3.DBAASUserUsername(newUsername), Rules: []v3.DBAASOpensearchAclConfigAclsRules{ - {Index: c.Index, Permission: v3.EnumOpensearchRulePermission(c.Permission)}, + {Index: c.Pattern, Permission: v3.EnumOpensearchRulePermission(c.Permission)}, }, } } else { From 3d748237c979d397207e57754e776eb99eb23dc8 Mon Sep 17 00:00:00 2001 From: elkezza Date: Thu, 2 Jan 2025 14:55:21 +0000 Subject: [PATCH 11/20] defining `FindServiceAcrossZones` function instead of repeating the same code block in each sub-command for acl management. --- cmd/dbaas.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/cmd/dbaas.go b/cmd/dbaas.go index 0ad44f025..9c819e03a 100644 --- a/cmd/dbaas.go +++ b/cmd/dbaas.go @@ -198,3 +198,22 @@ func dbaasGetV3(ctx context.Context, name, zone string) (v3.DBAASServiceCommon, return v3.DBAASServiceCommon{}, fmt.Errorf("%q Database Service not found in zone %q", name, zone) } + +// FindServiceAcrossZones searches for a DBaaS service across all available zones. +func FindServiceAcrossZones(ctx context.Context, client *v3.Client, serviceName string) (v3.DBAASServiceCommon, string, error) { + // Fetch all available zones + zones, err := client.ListZones(ctx) + if err != nil { + return v3.DBAASServiceCommon{}, "", fmt.Errorf("error fetching zones: %w", err) + } + + // Iterate through zones to find the service + for _, zone := range zones.Zones { + db, err := dbaasGetV3(ctx, serviceName, string(zone.Name)) + if err == nil { + return db, string(zone.Name), nil + } + } + + return v3.DBAASServiceCommon{}, "", fmt.Errorf("service %q not found in any zone", serviceName) +} From c7b3194b0a8b3aebde67ba82828c56b291f2a73e Mon Sep 17 00:00:00 2001 From: elkezza Date: Thu, 2 Jan 2025 14:56:13 +0000 Subject: [PATCH 12/20] using `FindServiceAcrossZones` function instead of repeating the same code block in each sub-command for acl management. --- cmd/dbaas_acl_create.go | 39 ++++++++++----------------------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/cmd/dbaas_acl_create.go b/cmd/dbaas_acl_create.go index 3bba8ef5a..e00912d0c 100644 --- a/cmd/dbaas_acl_create.go +++ b/cmd/dbaas_acl_create.go @@ -45,47 +45,28 @@ func (c *dbaasAclCreateCmd) cmdRun(cmd *cobra.Command, args []string) error { return fmt.Errorf("all --name, --username, --type, --permission and --pattern flags must be specified") } - // Fetch all available zones - zones, err := globalstate.EgoscaleV3Client.ListZones(ctx) + service, zone, err := FindServiceAcrossZones(ctx, globalstate.EgoscaleV3Client, c.Name) if err != nil { - return fmt.Errorf("error fetching zones: %w", err) + return fmt.Errorf("error finding service: %w", err) } - // Iterate through zones to find the service - var serviceZone string - var dbType v3.DBAASDatabaseName - var client *v3.Client - found := false - - for _, zone := range zones.Zones { - db, err := dbaasGetV3(ctx, c.Name, string(zone.Name)) - if err == nil { - dbType = v3.DBAASDatabaseName(db.Type) - serviceZone = string(zone.Name) - client, err = switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(serviceZone)) - if err != nil { - return fmt.Errorf("error initializing client for zone %s: %w", serviceZone, err) - } - found = true - break - } - } - - if !found { - return fmt.Errorf("service %q not found in any zone", c.Name) + // Switch client to the appropriate zone + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(zone)) + if err != nil { + return fmt.Errorf("error initializing client for zone %s: %w", zone, err) } // Validate the service type - if string(dbType) != c.ServiceType { - return fmt.Errorf("service type mismatch: expected %q but got %q for service %q", c.ServiceType, dbType, c.Name) + if string(service.Type) != c.ServiceType { + return fmt.Errorf("service type mismatch: expected %q but got %q for service %q", c.ServiceType, service.Type, c.Name) } - switch dbType { + switch service.Type { case "kafka": return c.createKafka(ctx, client, c.Name) case "opensearch": return c.createOpensearch(ctx, client, c.Name) default: - return fmt.Errorf("create ACL unsupported for service type %q", dbType) + return fmt.Errorf("create ACL unsupported for service type %q", service.Type) } } func init() { From 09982d57ec9730ff970c25af84a941f391fbb340 Mon Sep 17 00:00:00 2001 From: elkezza Date: Thu, 2 Jan 2025 15:05:41 +0000 Subject: [PATCH 13/20] using `FindServiceAcrossZones` function in update sub-commands instead of repeating the same code block in each sub-command for acl management. --- cmd/dbaas_acl_create.go | 1 + cmd/dbaas_acl_update.go | 37 ++++++++++-------------------- cmd/dbaas_acl_update_opensearch.go | 7 +----- 3 files changed, 14 insertions(+), 31 deletions(-) diff --git a/cmd/dbaas_acl_create.go b/cmd/dbaas_acl_create.go index e00912d0c..3028e4205 100644 --- a/cmd/dbaas_acl_create.go +++ b/cmd/dbaas_acl_create.go @@ -45,6 +45,7 @@ func (c *dbaasAclCreateCmd) cmdRun(cmd *cobra.Command, args []string) error { return fmt.Errorf("all --name, --username, --type, --permission and --pattern flags must be specified") } + // Search for the service in each zone service, zone, err := FindServiceAcrossZones(ctx, globalstate.EgoscaleV3Client, c.Name) if err != nil { return fmt.Errorf("error finding service: %w", err) diff --git a/cmd/dbaas_acl_update.go b/cmd/dbaas_acl_update.go index ddb9dbf17..e5060c828 100644 --- a/cmd/dbaas_acl_update.go +++ b/cmd/dbaas_acl_update.go @@ -46,42 +46,29 @@ func (c *dbaasAclUpdateCmd) cmdRun(cmd *cobra.Command, args []string) error { return fmt.Errorf("both --name, --username, and --type flags must be specified") } - // Fetch all available zones - zones, err := globalstate.EgoscaleV3Client.ListZones(ctx) + // Search for the service in each zone + service, zone, err := FindServiceAcrossZones(ctx, globalstate.EgoscaleV3Client, c.Name) if err != nil { - return fmt.Errorf("error fetching zones: %w", err) + return fmt.Errorf("error finding service: %w", err) } - // Iterate through zones to find the service - var found bool - var serviceZone string - var dbType v3.DBAASDatabaseName - for _, zone := range zones.Zones { - db, err := dbaasGetV3(ctx, c.Name, string(zone.Name)) - if err == nil { - dbType = v3.DBAASDatabaseName(db.Type) - found = true - serviceZone = string(zone.Name) - break - } - } - - // Handle case where service is not found in any zone - if !found { - return fmt.Errorf("service %q not found in any zone", c.Name) + // Switch client to the appropriate zone + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(zone)) + if err != nil { + return fmt.Errorf("error initializing client for zone %s: %w", zone, err) } // Validate the service type - if string(dbType) != c.ServiceType { - return fmt.Errorf("service type mismatch: expected %q but got %q for service %q", c.ServiceType, dbType, c.Name) + if string(service.Type) != c.ServiceType { + return fmt.Errorf("service type mismatch: expected %q but got %q for service %q", c.ServiceType, service.Type, c.Name) } // Determine the appropriate update logic based on the service type - switch dbType { + switch service.Type { case "opensearch": - return c.updateOpensearch(ctx, serviceZone, c.Name) + return c.updateOpensearch(ctx, client, c.Name) default: - return fmt.Errorf("update ACL unsupported for service type %q", dbType) + return fmt.Errorf("update ACL unsupported for service type %q", service.Type) } } diff --git a/cmd/dbaas_acl_update_opensearch.go b/cmd/dbaas_acl_update_opensearch.go index ea099fffb..c089d8f98 100644 --- a/cmd/dbaas_acl_update_opensearch.go +++ b/cmd/dbaas_acl_update_opensearch.go @@ -4,15 +4,10 @@ import ( "context" "fmt" - "github.com/exoscale/cli/pkg/globalstate" v3 "github.com/exoscale/egoscale/v3" ) -func (c *dbaasAclUpdateCmd) updateOpensearch(ctx context.Context, zone, serviceName string) error { - client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(zone)) - if err != nil { - return fmt.Errorf("error initializing client for zone %s: %w", zone, err) - } +func (c *dbaasAclUpdateCmd) updateOpensearch(ctx context.Context, client *v3.Client, serviceName string) error { aclsConfig, err := client.GetDBAASOpensearchAclConfig(ctx, serviceName) if err != nil { From 1dbcc110446eedf3481144292e50041614847465 Mon Sep 17 00:00:00 2001 From: elkezza Date: Thu, 2 Jan 2025 15:11:01 +0000 Subject: [PATCH 14/20] using `FindServiceAcrossZones` function in delete sub-commands instead of repeating the same code block in each sub-command for acl management. --- cmd/dbaas_acl_delete.go | 37 +++++++++---------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/cmd/dbaas_acl_delete.go b/cmd/dbaas_acl_delete.go index c0fafcf62..2b46722ab 100644 --- a/cmd/dbaas_acl_delete.go +++ b/cmd/dbaas_acl_delete.go @@ -42,47 +42,28 @@ func (c *dbaasAclDeleteCmd) cmdRun(cmd *cobra.Command, args []string) error { return fmt.Errorf("all flags --name, --type, and --username must be specified") } - // Fetch all available zones - zones, err := globalstate.EgoscaleV3Client.ListZones(ctx) + // Search for the service in each zone + service, zone, err := FindServiceAcrossZones(ctx, globalstate.EgoscaleV3Client, c.Name) if err != nil { - return fmt.Errorf("error fetching zones: %w", err) + return fmt.Errorf("error finding service: %w", err) } - // Iterate through zones to find the service - var found bool - var serviceZone string - var dbType v3.DBAASDatabaseName - for _, zone := range zones.Zones { - db, err := dbaasGetV3(ctx, c.Name, string(zone.Name)) - if err == nil { - dbType = v3.DBAASDatabaseName(db.Type) - found = true - serviceZone = string(zone.Name) - break - } - } - - // Handle case where service is not found in any zone - if !found { - return fmt.Errorf("service %q not found in any zone", c.Name) - } - - client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(serviceZone)) + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(zone)) if err != nil { - return fmt.Errorf("error initializing client for zone %s: %w", serviceZone, err) + return fmt.Errorf("error initializing client for zone %s: %w", zone, err) } // Validate the service type - if string(dbType) != c.ServiceType { - return fmt.Errorf("mismatched service type: expected %q but got %q for service %q", c.ServiceType, dbType, c.Name) + if string(service.Type) != c.ServiceType { + return fmt.Errorf("mismatched service type: expected %q but got %q for service %q", c.ServiceType, service.Type, c.Name) } // Call the appropriate delete logic based on the service type - switch dbType { + switch service.Type { case "kafka": err = c.deleteKafkaACL(ctx, client, c.Name, c.Username) default: - return fmt.Errorf("deleting ACL unsupported for service type %q", dbType) + return fmt.Errorf("deleting ACL unsupported for service type %q", service.Type) } if err != nil { From 79bb0794bb84d50de3796a81d2437e1209dfb4dc Mon Sep 17 00:00:00 2001 From: elkezza Date: Thu, 2 Jan 2025 15:13:37 +0000 Subject: [PATCH 15/20] using `FindServiceAcrossZones` function in list sub-commands instead of repeating the same code block in each sub-command for acl management. --- cmd/dbaas_acl_list.go | 37 +++++++++---------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/cmd/dbaas_acl_list.go b/cmd/dbaas_acl_list.go index 49066f989..14adc46a4 100644 --- a/cmd/dbaas_acl_list.go +++ b/cmd/dbaas_acl_list.go @@ -43,50 +43,31 @@ func (c *dbaasAclListCmd) cmdRun(cmd *cobra.Command, args []string) error { return fmt.Errorf("both --name and --type flags must be specified") } - // Fetch all available zones - zones, err := globalstate.EgoscaleV3Client.ListZones(ctx) + // Search for the service in each zone + service, zone, err := FindServiceAcrossZones(ctx, globalstate.EgoscaleV3Client, c.Name) if err != nil { - return fmt.Errorf("error fetching zones: %w", err) + return fmt.Errorf("error finding service: %w", err) } - // Iterate through zones to find the service - var found bool - var serviceZone string - var dbType v3.DBAASDatabaseName - for _, zone := range zones.Zones { - db, err := dbaasGetV3(ctx, c.Name, string(zone.Name)) - if err == nil { - dbType = v3.DBAASDatabaseName(db.Type) - found = true - serviceZone = string(zone.Name) - break - } - } - - // Handle case where service is not found in any zone - if !found { - return fmt.Errorf("service %q not found in any zone", c.Name) - } - - client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(serviceZone)) + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(zone)) if err != nil { - return fmt.Errorf("error initializing client for zone %s: %w", serviceZone, err) + return fmt.Errorf("error initializing client for zone %s: %w", zone, err) } // Validate the service type - if string(dbType) != c.ServiceType { - return fmt.Errorf("mismatched service type: expected %q but got %q for service %q", c.ServiceType, dbType, c.Name) + if string(service.Type) != c.ServiceType { + return fmt.Errorf("mismatched service type: expected %q but got %q for service %q", c.ServiceType, service.Type, c.Name) } // Determine the appropriate listing logic based on the service type var output output.Outputter - switch dbType { + switch service.Type { case "kafka": output, err = c.listKafkaACL(ctx, client, c.Name) case "opensearch": output, err = c.listOpenSearchACL(ctx, client, c.Name) default: - return fmt.Errorf("listing ACL unsupported for service type %q", dbType) + return fmt.Errorf("listing ACL unsupported for service type %q", service.Type) } if err != nil { From 637677179192e36aeb2b45854819efd8b95c84c4 Mon Sep 17 00:00:00 2001 From: elkezza Date: Thu, 2 Jan 2025 15:33:46 +0000 Subject: [PATCH 16/20] removing the zone flag from the show command by using `FindServiceAcrossZones` function. --- cmd/dbaas_acl_show.go | 30 ++++++++++++++++++------------ cmd/dbaas_acl_show_kafka.go | 7 +------ cmd/dbaas_acl_show_opensearch.go | 8 +------- 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/cmd/dbaas_acl_show.go b/cmd/dbaas_acl_show.go index 0289fae42..be71b4875 100644 --- a/cmd/dbaas_acl_show.go +++ b/cmd/dbaas_acl_show.go @@ -2,6 +2,8 @@ package cmd import ( "fmt" + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" "os" "github.com/exoscale/cli/pkg/output" @@ -36,7 +38,6 @@ type dbaasAclShowCmd struct { Name string `cli-flag:"name" cli-usage:"Name of the DBaaS service"` Username string `cli-flag:"username" cli-usage:"Username of the ACL entry"` ServiceType string `cli-flag:"type" cli-short:"t" cli-usage:"type of the DBaaS service (e.g., kafka, opensearch)"` - Zone string `cli-flag:"zone" cli-short:"z" cli-usage:"Database Service zone"` } // Command aliases (none in this case) @@ -52,7 +53,6 @@ func (c *dbaasAclShowCmd) cmdLong() string { // Pre-run validation for required flags and default zone setting func (c *dbaasAclShowCmd) cmdPreRun(cmd *cobra.Command, args []string) error { - cmdSetZoneFlagFromDefault(cmd) // Set the default zone if not specified return cliCommandDefaultPreRun(c, cmd, args) // Run default validations } @@ -65,26 +65,32 @@ func (c *dbaasAclShowCmd) cmdRun(cmd *cobra.Command, args []string) error { return fmt.Errorf("both --name, --username and --type flags must be specified") } - // Fetch DBaaS service details - db, err := dbaasGetV3(ctx, c.Name, c.Zone) + // Search for the service in each zone + service, zone, err := FindServiceAcrossZones(ctx, globalstate.EgoscaleV3Client, c.Name) if err != nil { - return fmt.Errorf("error retrieving DBaaS service %q in zone %q: %w", c.Name, c.Zone, err) + return fmt.Errorf("error finding service: %w", err) } - // Validate that the service type matches the expected type - if string(db.Type) != c.ServiceType { - return fmt.Errorf("mismatched service type: expected %q but got %q for service %q", c.ServiceType, db.Type, c.Name) + // Switch client to the appropriate zone + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(zone)) + if err != nil { + return fmt.Errorf("error initializing client for zone %s: %w", zone, err) + } + + // Validate the service type + if string(service.Type) != c.ServiceType { + return fmt.Errorf("service type mismatch: expected %q but got %q for service %q", c.ServiceType, service.Type, c.Name) } // Call the appropriate method based on the service type var output output.Outputter - switch db.Type { + switch service.Type { case "kafka": - output, err = c.showKafka(ctx, c.Name) + output, err = c.showKafka(ctx, client, c.Name) case "opensearch": - output, err = c.showOpensearch(ctx, c.Name) + output, err = c.showOpensearch(ctx, client, c.Name) default: - return fmt.Errorf("listing ACL unsupported for service of type %q", db.Type) + return fmt.Errorf("listing ACL unsupported for service of type %q", service.Type) } if err != nil { diff --git a/cmd/dbaas_acl_show_kafka.go b/cmd/dbaas_acl_show_kafka.go index c4ca8defa..2f3310c7c 100644 --- a/cmd/dbaas_acl_show_kafka.go +++ b/cmd/dbaas_acl_show_kafka.go @@ -3,17 +3,12 @@ package cmd import ( "context" "fmt" - "github.com/exoscale/cli/pkg/globalstate" "github.com/exoscale/cli/pkg/output" v3 "github.com/exoscale/egoscale/v3" ) // Fetch OpenSearch ACL configuration and process its details -func (c *dbaasAclShowCmd) showKafka(ctx context.Context, serviceName string) (output.Outputter, error) { - client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) - if err != nil { - return nil, fmt.Errorf("error initializing client for zone %s: %w", c.Zone, err) - } +func (c *dbaasAclShowCmd) showKafka(ctx context.Context, client *v3.Client, serviceName string) (output.Outputter, error) { // Fetch Kafka ACLs for the specified service acls, err := client.GetDBAASKafkaAclConfig(ctx, serviceName) diff --git a/cmd/dbaas_acl_show_opensearch.go b/cmd/dbaas_acl_show_opensearch.go index 4a09c2006..85fb6d843 100644 --- a/cmd/dbaas_acl_show_opensearch.go +++ b/cmd/dbaas_acl_show_opensearch.go @@ -5,7 +5,6 @@ import ( "fmt" "os" - "github.com/exoscale/cli/pkg/globalstate" "github.com/exoscale/cli/pkg/output" "github.com/exoscale/cli/table" v3 "github.com/exoscale/egoscale/v3" @@ -39,12 +38,7 @@ func (o *dbaasAclShowOpensearchOutput) ToTable() { } // Fetch OpenSearch ACL configuration and process its details -func (c *dbaasAclShowCmd) showOpensearch(ctx context.Context, serviceName string) (output.Outputter, error) { - // Switch to the appropriate client for the specified zone - client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) - if err != nil { - return nil, fmt.Errorf("error initializing client for zone %s: %w", c.Zone, err) - } +func (c *dbaasAclShowCmd) showOpensearch(ctx context.Context, client *v3.Client, serviceName string) (output.Outputter, error) { // Fetch OpenSearch ACL configuration for the specified service aclsConfig, err := client.GetDBAASOpensearchAclConfig(ctx, serviceName) From 31cde211e17a83b90f064e4dd42803942678ad2a Mon Sep 17 00:00:00 2001 From: elkezza Date: Thu, 2 Jan 2025 15:55:47 +0000 Subject: [PATCH 17/20] updating the CHANGELOG.md. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9348a1bed..d2ab47171 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - storage: Adding recursive feature to the storage command #653 - Update help for instance protection #658 +- dbaas: added commands for dbaas acl management #659 ## Unreleased From f0cdd7f7b1ae97e1ff3083d0e5da725eb1498bc7 Mon Sep 17 00:00:00 2001 From: elkezza Date: Thu, 2 Jan 2025 16:30:27 +0000 Subject: [PATCH 18/20] using `v3.ZoneName`to be returned instead of string. --- cmd/dbaas.go | 4 ++-- cmd/dbaas_acl_create.go | 3 +-- cmd/dbaas_acl_delete.go | 3 +-- cmd/dbaas_acl_list.go | 3 +-- cmd/dbaas_acl_show.go | 3 +-- cmd/dbaas_acl_update.go | 3 +-- 6 files changed, 7 insertions(+), 12 deletions(-) diff --git a/cmd/dbaas.go b/cmd/dbaas.go index 9c819e03a..3ef7ebc81 100644 --- a/cmd/dbaas.go +++ b/cmd/dbaas.go @@ -200,7 +200,7 @@ func dbaasGetV3(ctx context.Context, name, zone string) (v3.DBAASServiceCommon, } // FindServiceAcrossZones searches for a DBaaS service across all available zones. -func FindServiceAcrossZones(ctx context.Context, client *v3.Client, serviceName string) (v3.DBAASServiceCommon, string, error) { +func FindServiceAcrossZones(ctx context.Context, client *v3.Client, serviceName string) (v3.DBAASServiceCommon, v3.ZoneName, error) { // Fetch all available zones zones, err := client.ListZones(ctx) if err != nil { @@ -211,7 +211,7 @@ func FindServiceAcrossZones(ctx context.Context, client *v3.Client, serviceName for _, zone := range zones.Zones { db, err := dbaasGetV3(ctx, serviceName, string(zone.Name)) if err == nil { - return db, string(zone.Name), nil + return db, zone.Name, nil } } diff --git a/cmd/dbaas_acl_create.go b/cmd/dbaas_acl_create.go index 3028e4205..81acd94cf 100644 --- a/cmd/dbaas_acl_create.go +++ b/cmd/dbaas_acl_create.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "github.com/exoscale/cli/pkg/globalstate" - v3 "github.com/exoscale/egoscale/v3" "github.com/spf13/cobra" ) @@ -52,7 +51,7 @@ func (c *dbaasAclCreateCmd) cmdRun(cmd *cobra.Command, args []string) error { } // Switch client to the appropriate zone - client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(zone)) + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, zone) if err != nil { return fmt.Errorf("error initializing client for zone %s: %w", zone, err) } diff --git a/cmd/dbaas_acl_delete.go b/cmd/dbaas_acl_delete.go index 2b46722ab..e7a4504c3 100644 --- a/cmd/dbaas_acl_delete.go +++ b/cmd/dbaas_acl_delete.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/exoscale/cli/pkg/globalstate" - v3 "github.com/exoscale/egoscale/v3" "github.com/spf13/cobra" ) @@ -48,7 +47,7 @@ func (c *dbaasAclDeleteCmd) cmdRun(cmd *cobra.Command, args []string) error { return fmt.Errorf("error finding service: %w", err) } - client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(zone)) + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, zone) if err != nil { return fmt.Errorf("error initializing client for zone %s: %w", zone, err) } diff --git a/cmd/dbaas_acl_list.go b/cmd/dbaas_acl_list.go index 14adc46a4..ea4d47b27 100644 --- a/cmd/dbaas_acl_list.go +++ b/cmd/dbaas_acl_list.go @@ -7,7 +7,6 @@ import ( "github.com/exoscale/cli/pkg/globalstate" "github.com/exoscale/cli/pkg/output" - v3 "github.com/exoscale/egoscale/v3" ) type dbaasAclListCmd struct { @@ -49,7 +48,7 @@ func (c *dbaasAclListCmd) cmdRun(cmd *cobra.Command, args []string) error { return fmt.Errorf("error finding service: %w", err) } - client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(zone)) + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, zone) if err != nil { return fmt.Errorf("error initializing client for zone %s: %w", zone, err) } diff --git a/cmd/dbaas_acl_show.go b/cmd/dbaas_acl_show.go index be71b4875..222613818 100644 --- a/cmd/dbaas_acl_show.go +++ b/cmd/dbaas_acl_show.go @@ -3,7 +3,6 @@ package cmd import ( "fmt" "github.com/exoscale/cli/pkg/globalstate" - v3 "github.com/exoscale/egoscale/v3" "os" "github.com/exoscale/cli/pkg/output" @@ -72,7 +71,7 @@ func (c *dbaasAclShowCmd) cmdRun(cmd *cobra.Command, args []string) error { } // Switch client to the appropriate zone - client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(zone)) + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, zone) if err != nil { return fmt.Errorf("error initializing client for zone %s: %w", zone, err) } diff --git a/cmd/dbaas_acl_update.go b/cmd/dbaas_acl_update.go index e5060c828..61b020b96 100644 --- a/cmd/dbaas_acl_update.go +++ b/cmd/dbaas_acl_update.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "github.com/exoscale/cli/pkg/globalstate" - v3 "github.com/exoscale/egoscale/v3" "github.com/spf13/cobra" ) @@ -53,7 +52,7 @@ func (c *dbaasAclUpdateCmd) cmdRun(cmd *cobra.Command, args []string) error { } // Switch client to the appropriate zone - client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(zone)) + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, zone) if err != nil { return fmt.Errorf("error initializing client for zone %s: %w", zone, err) } From dba696d83b7848528848eca3f875538d77f0a57d Mon Sep 17 00:00:00 2001 From: elkezza Date: Thu, 2 Jan 2025 17:22:20 +0000 Subject: [PATCH 19/20] using global context. --- cmd/dbaas_acl_create.go | 3 +-- cmd/dbaas_acl_delete.go | 3 +-- cmd/dbaas_acl_list.go | 3 +-- cmd/dbaas_acl_update.go | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/cmd/dbaas_acl_create.go b/cmd/dbaas_acl_create.go index 81acd94cf..925f14882 100644 --- a/cmd/dbaas_acl_create.go +++ b/cmd/dbaas_acl_create.go @@ -1,7 +1,6 @@ package cmd import ( - "context" "fmt" "github.com/exoscale/cli/pkg/globalstate" "github.com/spf13/cobra" @@ -37,7 +36,7 @@ func (c *dbaasAclCreateCmd) cmdPreRun(cmd *cobra.Command, args []string) error { // Main run logic for showing ACL details func (c *dbaasAclCreateCmd) cmdRun(cmd *cobra.Command, args []string) error { - ctx := context.Background() + ctx := gContext // Validate required inputs if c.Name == "" || c.Username == "" || c.ServiceType == "" || c.Permission == "" || c.Pattern == "" { diff --git a/cmd/dbaas_acl_delete.go b/cmd/dbaas_acl_delete.go index e7a4504c3..8d613936b 100644 --- a/cmd/dbaas_acl_delete.go +++ b/cmd/dbaas_acl_delete.go @@ -1,7 +1,6 @@ package cmd import ( - "context" "fmt" "github.com/exoscale/cli/pkg/globalstate" @@ -34,7 +33,7 @@ func (c *dbaasAclDeleteCmd) cmdPreRun(cmd *cobra.Command, args []string) error { // Main run logic for showing ACL details func (c *dbaasAclDeleteCmd) cmdRun(cmd *cobra.Command, args []string) error { - ctx := context.Background() + ctx := gContext // Validate required flags if c.Name == "" || c.ServiceType == "" || c.Username == "" { diff --git a/cmd/dbaas_acl_list.go b/cmd/dbaas_acl_list.go index ea4d47b27..ad93bd54a 100644 --- a/cmd/dbaas_acl_list.go +++ b/cmd/dbaas_acl_list.go @@ -1,7 +1,6 @@ package cmd import ( - "context" "fmt" "github.com/spf13/cobra" @@ -35,7 +34,7 @@ func (c *dbaasAclListCmd) cmdPreRun(cmd *cobra.Command, args []string) error { // Main run logic for listing ACLs func (c *dbaasAclListCmd) cmdRun(cmd *cobra.Command, args []string) error { - ctx := context.Background() + ctx := gContext // Validate required flags if c.Name == "" || c.ServiceType == "" { diff --git a/cmd/dbaas_acl_update.go b/cmd/dbaas_acl_update.go index 61b020b96..39cf57087 100644 --- a/cmd/dbaas_acl_update.go +++ b/cmd/dbaas_acl_update.go @@ -1,7 +1,6 @@ package cmd import ( - "context" "fmt" "github.com/exoscale/cli/pkg/globalstate" "github.com/spf13/cobra" @@ -38,7 +37,7 @@ func (c *dbaasAclUpdateCmd) cmdPreRun(cmd *cobra.Command, args []string) error { // Main run logic for showing ACL details func (c *dbaasAclUpdateCmd) cmdRun(cmd *cobra.Command, args []string) error { - ctx := context.Background() + ctx := gContext // Validate required flags if c.Name == "" || c.Username == "" || c.ServiceType == "" { From 6fb70329d311ea777e48c67e2dddde933ff49bf9 Mon Sep 17 00:00:00 2001 From: elkezza <47871727+elkezza@users.noreply.github.com> Date: Mon, 13 Jan 2025 10:34:51 +0100 Subject: [PATCH 20/20] Update cmd/dbaas_acl_delete.go Co-authored-by: Predrag Janosevic --- cmd/dbaas_acl_delete.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/dbaas_acl_delete.go b/cmd/dbaas_acl_delete.go index 8d613936b..326b96cdb 100644 --- a/cmd/dbaas_acl_delete.go +++ b/cmd/dbaas_acl_delete.go @@ -12,7 +12,7 @@ type dbaasAclDeleteCmd struct { _ bool `cli-cmd:"delete"` Name string `cli-flag:"name" cli-usage:"Name of the DBaaS service"` - ServiceType string `cli-flag:"type" cli-short:"t" cli-usage:"DBaaS service type (e.g., kafka, opensearch)"` + ServiceType string `cli-flag:"type" cli-short:"t" cli-usage:"DBaaS service type (currently only kafka supported)"` Username string `cli-flag:"username" cli-usage:"Username of the ACL entry"` }