diff --git a/CHANGELOG.md b/CHANGELOG.md index 95e6152e..963925af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased ### Bug fixes - config: fixing bug sosEndpoint lost after user switch account #652 +- dbaas: added commands for managing service users #654 ## 1.81.0 diff --git a/cmd/dbaas.go b/cmd/dbaas.go index 33b9ae46..0ad44f02 100644 --- a/cmd/dbaas.go +++ b/cmd/dbaas.go @@ -16,6 +16,7 @@ import ( "github.com/exoscale/cli/pkg/globalstate" "github.com/exoscale/cli/table" + v3 "github.com/exoscale/egoscale/v3" ) var dbServiceMaintenanceDOWs = []string{ @@ -176,3 +177,24 @@ func dbaasGetType(ctx context.Context, name, zone string) (string, error) { return "", fmt.Errorf("%q Database Service not found in zone %q", name, zone) } + +func dbaasGetV3(ctx context.Context, name, zone string) (v3.DBAASServiceCommon, error) { + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(zone)) + if err != nil { + return v3.DBAASServiceCommon{}, err + } + + dbs, err := client.ListDBAASServices(ctx) + if err != nil { + return v3.DBAASServiceCommon{}, err + } + + for _, db := range dbs.DBAASServices { + if string(db.Name) == name { + return db, nil + } + } + + return v3.DBAASServiceCommon{}, fmt.Errorf("%q Database Service not found in zone %q", name, zone) +} diff --git a/cmd/dbaas_user.go b/cmd/dbaas_user.go new file mode 100644 index 00000000..a2465e64 --- /dev/null +++ b/cmd/dbaas_user.go @@ -0,0 +1,14 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +var dbaasUserCmd = &cobra.Command{ + Use: "user", + Short: "Manage DBaaS users", +} + +func init() { + dbaasCmd.AddCommand(dbaasUserCmd) +} diff --git a/cmd/dbaas_user_create.go b/cmd/dbaas_user_create.go new file mode 100644 index 00000000..2b4f0922 --- /dev/null +++ b/cmd/dbaas_user_create.go @@ -0,0 +1,79 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" +) + +type dbaasUserCreateCmd struct { + cliCommandSettings `cli-cmd:"-"` + + _ bool `cli-cmd:"create"` + + Name string `cli-arg:"#"` + Username string `cli-arg:"#"` + + HelpMysql bool `cli-usage:"show usage for flags specific to the mysql type"` + HelpPg bool `cli-usage:"show usage for flags specific to the pg type"` + Zone string `cli-short:"z" cli-usage:"Database Service zone"` + + // "mysql" type specific flags + MysqlAuthenticationMethod string `cli-flag:"mysql-authentication-method" cli-usage:"authentication method to be used (\"caching_sha2_password\" or \"mysql_native_password\")." cli-hidden:""` + + // "kafka" type specific flags + PostgresAllowReplication bool `cli-flag:"pg-allow-replication" cli-usage:"" cli-hidden:""` +} + +func (c *dbaasUserCreateCmd) cmdAliases() []string { return nil } + +func (c *dbaasUserCreateCmd) cmdShort() string { return "Create DBAAS user" } + +func (c *dbaasUserCreateCmd) cmdLong() string { + return `This command creates a DBAAS user for the specified service.` +} + +func (c *dbaasUserCreateCmd) cmdPreRun(cmd *cobra.Command, args []string) error { + switch { + + case cmd.Flags().Changed("help-mysql"): + cmdShowHelpFlags(cmd.Flags(), "mysql-") + os.Exit(0) + case cmd.Flags().Changed("help-pg"): + cmdShowHelpFlags(cmd.Flags(), "pg-") + os.Exit(0) + } + + cmdSetZoneFlagFromDefault(cmd) + return cliCommandDefaultPreRun(c, cmd, args) +} + +func (c *dbaasUserCreateCmd) cmdRun(cmd *cobra.Command, args []string) error { + + ctx := gContext + db, err := dbaasGetV3(ctx, c.Name, c.Zone) + if err != nil { + return err + } + + switch db.Type { + case "mysql": + return c.createMysql(cmd, args) + case "kafka": + return c.createKafka(cmd, args) + case "pg": + return c.createPg(cmd, args) + case "opensearch": + return c.createOpensearch(cmd, args) + default: + return fmt.Errorf("creating user unsupported for service of type %q", db.Type) + } + +} + +func init() { + cobra.CheckErr(registerCLICommand(dbaasUserCmd, &dbaasUserCreateCmd{ + cliCommandSettings: defaultCLICmdSettings(), + })) +} diff --git a/cmd/dbaas_user_create_kafka.go b/cmd/dbaas_user_create_kafka.go new file mode 100644 index 00000000..e0693268 --- /dev/null +++ b/cmd/dbaas_user_create_kafka.go @@ -0,0 +1,48 @@ +package cmd + +import ( + "fmt" + + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +func (c *dbaasUserCreateCmd) createKafka(cmd *cobra.Command, _ []string) error { + + ctx := gContext + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return err + } + + req := v3.CreateDBAASKafkaUserRequest{Username: v3.DBAASUserUsername(c.Username)} + + op, err := client.CreateDBAASKafkaUser(ctx, c.Name, req) + + if err != nil { + return err + } + + decorateAsyncOperation(fmt.Sprintf("Creating DBaaS user %q", c.Username), func() { + op, err = client.Wait(ctx, op, v3.OperationStateSuccess) + }) + + if err != nil { + return err + } + + if !globalstate.Quiet { + + return c.outputFunc((&dbaasUserShowCmd{ + Name: c.Name, + Zone: c.Zone, + Username: c.Username, + }).showKafka(ctx)) + + } + + return nil + +} diff --git a/cmd/dbaas_user_create_mysql.go b/cmd/dbaas_user_create_mysql.go new file mode 100644 index 00000000..f88f776c --- /dev/null +++ b/cmd/dbaas_user_create_mysql.go @@ -0,0 +1,48 @@ +package cmd + +import ( + "fmt" + + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +func (c *dbaasUserCreateCmd) createMysql(cmd *cobra.Command, _ []string) error { + + ctx := gContext + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return err + } + + req := v3.CreateDBAASMysqlUserRequest{Username: v3.DBAASUserUsername(c.Username)} + if c.MysqlAuthenticationMethod != "" { + req.Authentication = v3.EnumMysqlAuthenticationPlugin(c.MysqlAuthenticationMethod) + } + + op, err := client.CreateDBAASMysqlUser(ctx, c.Name, req) + + if err != nil { + return err + } + + decorateAsyncOperation(fmt.Sprintf("Creating DBaaS user %q", c.Username), func() { + op, err = client.Wait(ctx, op, v3.OperationStateSuccess) + }) + + if err != nil { + return err + } + + if !globalstate.Quiet { + return c.outputFunc((&dbaasUserShowCmd{ + Name: c.Name, + Zone: c.Zone, + Username: c.Username, + }).showMysql(ctx)) + } + + return nil +} diff --git a/cmd/dbaas_user_create_opensearch.go b/cmd/dbaas_user_create_opensearch.go new file mode 100644 index 00000000..d7157e6f --- /dev/null +++ b/cmd/dbaas_user_create_opensearch.go @@ -0,0 +1,45 @@ +package cmd + +import ( + "fmt" + + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +func (c *dbaasUserCreateCmd) createOpensearch(cmd *cobra.Command, _ []string) error { + + ctx := gContext + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return err + } + + req := v3.CreateDBAASOpensearchUserRequest{Username: v3.DBAASUserUsername(c.Username)} + + op, err := client.CreateDBAASOpensearchUser(ctx, c.Name, req) + + if err != nil { + return err + } + + decorateAsyncOperation(fmt.Sprintf("Creating DBaaS user %q", c.Username), func() { + op, err = client.Wait(ctx, op, v3.OperationStateSuccess) + }) + + if err != nil { + return err + } + + if !globalstate.Quiet { + return c.outputFunc((&dbaasUserShowCmd{ + Name: c.Name, + Zone: c.Zone, + Username: c.Username, + }).showOpensearch(ctx)) + } + + return nil +} diff --git a/cmd/dbaas_user_create_pg.go b/cmd/dbaas_user_create_pg.go new file mode 100644 index 00000000..446a8b94 --- /dev/null +++ b/cmd/dbaas_user_create_pg.go @@ -0,0 +1,45 @@ +package cmd + +import ( + "fmt" + + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +func (c *dbaasUserCreateCmd) createPg(cmd *cobra.Command, _ []string) error { + + ctx := gContext + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return err + } + + req := v3.CreateDBAASPostgresUserRequest{Username: v3.DBAASUserUsername(c.Username), AllowReplication: &c.PostgresAllowReplication} + + op, err := client.CreateDBAASPostgresUser(ctx, c.Name, req) + + if err != nil { + return err + } + + decorateAsyncOperation(fmt.Sprintf("Creating DBaaS user %q", c.Username), func() { + op, err = client.Wait(ctx, op, v3.OperationStateSuccess) + }) + + if err != nil { + return err + } + + if !globalstate.Quiet { + return c.outputFunc((&dbaasUserShowCmd{ + Name: c.Name, + Zone: c.Zone, + Username: c.Username, + }).showPG(ctx)) + } + + return nil +} diff --git a/cmd/dbaas_user_delete.go b/cmd/dbaas_user_delete.go new file mode 100644 index 00000000..733b9975 --- /dev/null +++ b/cmd/dbaas_user_delete.go @@ -0,0 +1,61 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +type dbaasUserDeleteCmd struct { + cliCommandSettings `cli-cmd:"-"` + + _ bool `cli-cmd:"delete"` + + Name string `cli-arg:"#"` + Username string `cli-arg:"#"` + Zone string `cli-short:"z" cli-usage:"Database Service zone"` + + Force bool `cli-short:"f" cli-usage:"don't prompt for confirmation"` +} + +func (c *dbaasUserDeleteCmd) cmdAliases() []string { return nil } + +func (c *dbaasUserDeleteCmd) cmdShort() string { return "Delete DBAAS user" } + +func (c *dbaasUserDeleteCmd) cmdLong() string { + return `This command deletes a DBAAS user for the specified service.` +} + +func (c *dbaasUserDeleteCmd) cmdPreRun(cmd *cobra.Command, args []string) error { + cmdSetZoneFlagFromDefault(cmd) + return cliCommandDefaultPreRun(c, cmd, args) +} + +func (c *dbaasUserDeleteCmd) cmdRun(cmd *cobra.Command, args []string) error { + + ctx := gContext + db, err := dbaasGetV3(ctx, c.Name, c.Zone) + if err != nil { + return err + } + + switch db.Type { + case "mysql": + return c.deleteMysql(cmd, args) + case "kafka": + return c.deleteKafka(cmd, args) + case "pg": + return c.deletePg(cmd, args) + case "opensearch": + return c.deleteOpensearch(cmd, args) + default: + return fmt.Errorf("deleting user unsupported for service of type %q", db.Type) + } + +} + +func init() { + cobra.CheckErr(registerCLICommand(dbaasUserCmd, &dbaasUserDeleteCmd{ + cliCommandSettings: defaultCLICmdSettings(), + })) +} diff --git a/cmd/dbaas_user_delete_kafka.go b/cmd/dbaas_user_delete_kafka.go new file mode 100644 index 00000000..1e5e82ad --- /dev/null +++ b/cmd/dbaas_user_delete_kafka.go @@ -0,0 +1,65 @@ +package cmd + +import ( + "fmt" + + "github.com/exoscale/cli/pkg/account" + "github.com/exoscale/cli/pkg/globalstate" + exoapi "github.com/exoscale/egoscale/v2/api" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +func (c *dbaasUserDeleteCmd) deleteKafka(cmd *cobra.Command, _ []string) error { + + ctx := gContext + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return err + } + + s, err := client.GetDBAASServiceKafka(ctx, c.Name) + if err != nil { + return err + } + userFound := false + for _, u := range s.Users { + if u.Username == c.Username { + break + } + } + if !userFound { + return fmt.Errorf("user %q not found for service %q", c.Username, c.Name) + } + if !c.Force { + if !askQuestion(fmt.Sprintf( + "Are you sure you want to delete user %q", c.Username)) { + return nil + } + } + + op, err := client.DeleteDBAASKafkaUser(ctx, c.Name, c.Username) + + if err != nil { + return err + } + + decorateAsyncOperation(fmt.Sprintf("Deletng DBaaS user %q", c.Username), func() { + op, err = client.Wait(ctx, op, v3.OperationStateSuccess) + }) + + if err != nil { + return err + } + + if !globalstate.Quiet { + return c.outputFunc((&dbaasServiceShowCmd{ + Name: c.Name, + Zone: c.Zone, + }).showDatabaseServiceKafka(exoapi.WithEndpoint(gContext, exoapi.NewReqEndpoint(account.CurrentAccount.Environment, c.Zone)))) + } + + return nil + +} diff --git a/cmd/dbaas_user_delete_mysql.go b/cmd/dbaas_user_delete_mysql.go new file mode 100644 index 00000000..7fb3e0c3 --- /dev/null +++ b/cmd/dbaas_user_delete_mysql.go @@ -0,0 +1,66 @@ +package cmd + +import ( + "fmt" + + "github.com/exoscale/cli/pkg/account" + "github.com/exoscale/cli/pkg/globalstate" + exoapi "github.com/exoscale/egoscale/v2/api" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +func (c *dbaasUserDeleteCmd) deleteMysql(cmd *cobra.Command, _ []string) error { + + ctx := gContext + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return err + } + + s, err := client.GetDBAASServiceMysql(ctx, c.Name) + if err != nil { + return err + } + userFound := false + for _, u := range s.Users { + if u.Username == c.Username { + userFound = true + break + } + } + if !userFound { + return fmt.Errorf("user %q not found for service %q", c.Username, c.Name) + } + if !c.Force { + if !askQuestion(fmt.Sprintf( + "Are you sure you want to delete user %q", c.Username)) { + return nil + } + } + + op, err := client.DeleteDBAASMysqlUser(ctx, c.Name, c.Username) + + if err != nil { + return err + } + + decorateAsyncOperation(fmt.Sprintf("Deletng DBaaS user %q", c.Username), func() { + op, err = client.Wait(ctx, op, v3.OperationStateSuccess) + }) + + if err != nil { + return err + } + + if !globalstate.Quiet { + return c.outputFunc((&dbaasServiceShowCmd{ + Name: c.Name, + Zone: c.Zone, + }).showDatabaseServiceMysql(exoapi.WithEndpoint(gContext, exoapi.NewReqEndpoint(account.CurrentAccount.Environment, c.Zone)))) + } + + return nil + +} diff --git a/cmd/dbaas_user_delete_opensearch.go b/cmd/dbaas_user_delete_opensearch.go new file mode 100644 index 00000000..3990be30 --- /dev/null +++ b/cmd/dbaas_user_delete_opensearch.go @@ -0,0 +1,65 @@ +package cmd + +import ( + "fmt" + + "github.com/exoscale/cli/pkg/account" + "github.com/exoscale/cli/pkg/globalstate" + exoapi "github.com/exoscale/egoscale/v2/api" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +func (c *dbaasUserDeleteCmd) deleteOpensearch(cmd *cobra.Command, _ []string) error { + + ctx := gContext + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return err + } + + s, err := client.GetDBAASServiceOpensearch(ctx, c.Name) + if err != nil { + return err + } + userFound := false + for _, u := range s.Users { + if u.Username == c.Username { + break + } + } + if !userFound { + return fmt.Errorf("user %q not found for service %q", c.Username, c.Name) + } + if !c.Force { + if !askQuestion(fmt.Sprintf( + "Are you sure you want to delete user %q", c.Username)) { + return nil + } + } + + op, err := client.DeleteDBAASOpensearchUser(ctx, c.Name, c.Username) + + if err != nil { + return err + } + + decorateAsyncOperation(fmt.Sprintf("Deletng DBaaS user %q", c.Username), func() { + op, err = client.Wait(ctx, op, v3.OperationStateSuccess) + }) + + if err != nil { + return err + } + + if !globalstate.Quiet { + return c.outputFunc((&dbaasServiceShowCmd{ + Name: c.Name, + Zone: c.Zone, + }).showDatabaseServiceOpensearch(exoapi.WithEndpoint(gContext, exoapi.NewReqEndpoint(account.CurrentAccount.Environment, c.Zone)))) + } + + return nil + +} diff --git a/cmd/dbaas_user_delete_pg.go b/cmd/dbaas_user_delete_pg.go new file mode 100644 index 00000000..53dc4f81 --- /dev/null +++ b/cmd/dbaas_user_delete_pg.go @@ -0,0 +1,65 @@ +package cmd + +import ( + "fmt" + + "github.com/exoscale/cli/pkg/account" + "github.com/exoscale/cli/pkg/globalstate" + exoapi "github.com/exoscale/egoscale/v2/api" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +func (c *dbaasUserDeleteCmd) deletePg(cmd *cobra.Command, _ []string) error { + + ctx := gContext + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return err + } + + s, err := client.GetDBAASServicePG(ctx, c.Name) + if err != nil { + return err + } + userFound := false + for _, u := range s.Users { + if u.Username == c.Username { + break + } + } + if !userFound { + return fmt.Errorf("user %q not found for service %q", c.Username, c.Name) + } + if !c.Force { + if !askQuestion(fmt.Sprintf( + "Are you sure you want to delete user %q", c.Username)) { + return nil + } + } + + op, err := client.DeleteDBAASPostgresUser(ctx, c.Name, c.Username) + + if err != nil { + return err + } + + decorateAsyncOperation(fmt.Sprintf("Deletng DBaaS user %q", c.Username), func() { + op, err = client.Wait(ctx, op, v3.OperationStateSuccess) + }) + + if err != nil { + return err + } + + if !globalstate.Quiet { + return c.outputFunc((&dbaasServiceShowCmd{ + Name: c.Name, + Zone: c.Zone, + }).showDatabaseServicePG(exoapi.WithEndpoint(gContext, exoapi.NewReqEndpoint(account.CurrentAccount.Environment, c.Zone)))) + } + + return nil + +} diff --git a/cmd/dbaas_user_list.go b/cmd/dbaas_user_list.go new file mode 100644 index 00000000..9ea79ee5 --- /dev/null +++ b/cmd/dbaas_user_list.go @@ -0,0 +1,76 @@ +package cmd + +import ( + "fmt" + + "github.com/exoscale/cli/pkg/output" + "github.com/spf13/cobra" +) + +type dbaasUsersListItemOutput struct { + Username string `json:"username,omitempty"` + Type string `json:"type,omitempty"` +} +type dbaasUsersListOutput []dbaasUsersListItemOutput + +func (o *dbaasUsersListOutput) ToJSON() { output.JSON(o) } +func (o *dbaasUsersListOutput) ToText() { output.Text(o) } + +func (o *dbaasUsersListOutput) ToTable() { + output.Table(o) +} + +type dbaasUserListCmd struct { + cliCommandSettings `cli-cmd:"-"` + + _ bool `cli-cmd:"list"` + + Name string `cli-arg:"#"` + Zone string `cli-short:"z" cli-usage:"Database Service zone"` +} + +func (c *dbaasUserListCmd) cmdAliases() []string { return nil } + +func (c *dbaasUserListCmd) cmdShort() string { return "List users of a DBAAS service" } + +func (c *dbaasUserListCmd) cmdLong() string { + return `This command list users and their role for a specified DBAAS service.` +} + +func (c *dbaasUserListCmd) cmdPreRun(cmd *cobra.Command, args []string) error { + cmdSetZoneFlagFromDefault(cmd) + + return cliCommandDefaultPreRun(c, cmd, args) +} + +func (c *dbaasUserListCmd) cmdRun(cmd *cobra.Command, args []string) error { + + ctx := gContext + db, err := dbaasGetV3(ctx, c.Name, c.Zone) + if err != nil { + return err + } + + switch db.Type { + case "mysql": + return c.listMysql(cmd, args) + case "kafka": + return c.listKafka(cmd, args) + case "pg": + return c.listPG(cmd, args) + case "opensearch": + return c.listOpensearch(cmd, args) + case "grafana": + return c.listGrafana(cmd, args) + default: + return fmt.Errorf("listing users unsupported for service of type %q", db.Type) + + } + +} + +func init() { + cobra.CheckErr(registerCLICommand(dbaasUserCmd, &dbaasUserListCmd{ + cliCommandSettings: defaultCLICmdSettings(), + })) +} diff --git a/cmd/dbaas_user_list_grafana.go b/cmd/dbaas_user_list_grafana.go new file mode 100644 index 00000000..f4ddcaf3 --- /dev/null +++ b/cmd/dbaas_user_list_grafana.go @@ -0,0 +1,33 @@ +package cmd + +import ( + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +func (c *dbaasUserListCmd) listGrafana(cmd *cobra.Command, _ []string) error { + + ctx := gContext + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return err + } + + s, err := client.GetDBAASServiceGrafana(ctx, c.Name) + if err != nil { + return err + } + + res := make(dbaasUsersListOutput, 0) + + for _, u := range s.Users { + res = append(res, dbaasUsersListItemOutput{ + Username: u.Username, + Type: u.Type, + }) + } + + return c.outputFunc(&res, nil) +} diff --git a/cmd/dbaas_user_list_kafka.go b/cmd/dbaas_user_list_kafka.go new file mode 100644 index 00000000..b89ada23 --- /dev/null +++ b/cmd/dbaas_user_list_kafka.go @@ -0,0 +1,33 @@ +package cmd + +import ( + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +func (c *dbaasUserListCmd) listKafka(cmd *cobra.Command, _ []string) error { + + ctx := gContext + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return err + } + + s, err := client.GetDBAASServiceKafka(ctx, c.Name) + if err != nil { + return err + } + + res := make(dbaasUsersListOutput, 0) + + for _, u := range s.Users { + res = append(res, dbaasUsersListItemOutput{ + Username: u.Username, + Type: u.Type, + }) + } + + return c.outputFunc(&res, nil) +} diff --git a/cmd/dbaas_user_list_mysql.go b/cmd/dbaas_user_list_mysql.go new file mode 100644 index 00000000..0d83d079 --- /dev/null +++ b/cmd/dbaas_user_list_mysql.go @@ -0,0 +1,33 @@ +package cmd + +import ( + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +func (c *dbaasUserListCmd) listMysql(cmd *cobra.Command, _ []string) error { + + ctx := gContext + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return err + } + + s, err := client.GetDBAASServiceMysql(ctx, c.Name) + if err != nil { + return err + } + + res := make(dbaasUsersListOutput, 0) + + for _, u := range s.Users { + res = append(res, dbaasUsersListItemOutput{ + Username: u.Username, + Type: u.Type, + }) + } + + return c.outputFunc(&res, nil) +} diff --git a/cmd/dbaas_user_list_opensearch.go b/cmd/dbaas_user_list_opensearch.go new file mode 100644 index 00000000..6cd02834 --- /dev/null +++ b/cmd/dbaas_user_list_opensearch.go @@ -0,0 +1,33 @@ +package cmd + +import ( + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +func (c *dbaasUserListCmd) listOpensearch(cmd *cobra.Command, _ []string) error { + + ctx := gContext + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return err + } + + s, err := client.GetDBAASServiceOpensearch(ctx, c.Name) + if err != nil { + return err + } + + res := make(dbaasUsersListOutput, 0) + + for _, u := range s.Users { + res = append(res, dbaasUsersListItemOutput{ + Username: u.Username, + Type: u.Type, + }) + } + + return c.outputFunc(&res, nil) +} diff --git a/cmd/dbaas_user_list_pg.go b/cmd/dbaas_user_list_pg.go new file mode 100644 index 00000000..27ffebd9 --- /dev/null +++ b/cmd/dbaas_user_list_pg.go @@ -0,0 +1,33 @@ +package cmd + +import ( + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +func (c *dbaasUserListCmd) listPG(cmd *cobra.Command, _ []string) error { + + ctx := gContext + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return err + } + + s, err := client.GetDBAASServicePG(ctx, c.Name) + if err != nil { + return err + } + + res := make(dbaasUsersListOutput, 0) + + for _, u := range s.Users { + res = append(res, dbaasUsersListItemOutput{ + Username: u.Username, + Type: u.Type, + }) + } + + return c.outputFunc(&res, nil) +} diff --git a/cmd/dbaas_user_reset.go b/cmd/dbaas_user_reset.go new file mode 100644 index 00000000..5096c3d8 --- /dev/null +++ b/cmd/dbaas_user_reset.go @@ -0,0 +1,77 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" +) + +type dbaasUserResetCmd struct { + cliCommandSettings `cli-cmd:"-"` + + _ bool `cli-cmd:"reset-credentials"` + + Name string `cli-arg:"#"` + Username string `cli-arg:"#"` + Zone string `cli-short:"z" cli-usage:"Database Service zone"` + + Password string `cli-flag:"password" cli-usage:"Use a specific password instead of an automatically generated one"` + + HelpMysql bool `cli-usage:"show usage for flags specific to the mysql type"` + + // "mysql" type specific flags + MysqlAuthenticationMethod string `cli-flag:"mysql-auhentication-method" cli-usage:"authentication method to be used (\"caching_sha2_password\" or \"mysql_native_password\")." cli-hidden:""` +} + +func (c *dbaasUserResetCmd) cmdAliases() []string { return nil } + +func (c *dbaasUserResetCmd) cmdShort() string { return "Reset the credentials of a DBAAS user" } + +func (c *dbaasUserResetCmd) cmdLong() string { + return `This command resets the credentials of a DBAAS user for the specified service.` +} + +func (c *dbaasUserResetCmd) cmdPreRun(cmd *cobra.Command, args []string) error { + cmdSetZoneFlagFromDefault(cmd) + + switch { + + case cmd.Flags().Changed("help-mysql"): + cmdShowHelpFlags(cmd.Flags(), "mysql-") + os.Exit(0) + } + + return cliCommandDefaultPreRun(c, cmd, args) +} + +func (c *dbaasUserResetCmd) cmdRun(cmd *cobra.Command, args []string) error { + + ctx := gContext + db, err := dbaasGetV3(ctx, c.Name, c.Zone) + if err != nil { + return err + } + + switch db.Type { + case "mysql": + return c.resetMysql(cmd, args) + case "kafka": + return c.resetKafka(cmd, args) + case "pg": + return c.resetPG(cmd, args) + case "opensearch": + return c.resetOpensearch(cmd, args) + case "grafana": + return c.resetGrafana(cmd, args) + default: + return fmt.Errorf("reseting user credentials unsupported for service of type %q", db.Type) + } + +} + +func init() { + cobra.CheckErr(registerCLICommand(dbaasUserCmd, &dbaasUserResetCmd{ + cliCommandSettings: defaultCLICmdSettings(), + })) +} diff --git a/cmd/dbaas_user_reset_grafana.go b/cmd/dbaas_user_reset_grafana.go new file mode 100644 index 00000000..e531be58 --- /dev/null +++ b/cmd/dbaas_user_reset_grafana.go @@ -0,0 +1,48 @@ +package cmd + +import ( + "fmt" + + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +func (c *dbaasUserResetCmd) resetGrafana(cmd *cobra.Command, _ []string) error { + + ctx := gContext + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return err + } + + req := v3.ResetDBAASGrafanaUserPasswordRequest{} + if c.Password != "" { + req.Password = v3.DBAASUserPassword(c.Password) + } + + op, err := client.ResetDBAASGrafanaUserPassword(ctx, c.Name, c.Username, req) + + if err != nil { + return err + } + + decorateAsyncOperation(fmt.Sprintf("Resetting DBaaS user %q", c.Username), func() { + op, err = client.Wait(ctx, op, v3.OperationStateSuccess) + }) + + if err != nil { + return err + } + + if !globalstate.Quiet { + return c.outputFunc((&dbaasUserShowCmd{ + Name: c.Name, + Zone: c.Zone, + Username: c.Username, + }).showGrafana(ctx)) + } + + return nil +} diff --git a/cmd/dbaas_user_reset_kafka.go b/cmd/dbaas_user_reset_kafka.go new file mode 100644 index 00000000..ae903425 --- /dev/null +++ b/cmd/dbaas_user_reset_kafka.go @@ -0,0 +1,48 @@ +package cmd + +import ( + "fmt" + + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +func (c *dbaasUserResetCmd) resetKafka(cmd *cobra.Command, _ []string) error { + + ctx := gContext + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return err + } + + req := v3.ResetDBAASKafkaUserPasswordRequest{} + if c.Password != "" { + req.Password = v3.DBAASUserPassword(c.Password) + } + + op, err := client.ResetDBAASKafkaUserPassword(ctx, c.Name, c.Username, req) + + if err != nil { + return err + } + + decorateAsyncOperation(fmt.Sprintf("Resetting DBaaS user %q", c.Username), func() { + op, err = client.Wait(ctx, op, v3.OperationStateSuccess) + }) + + if err != nil { + return err + } + + if !globalstate.Quiet { + return c.outputFunc((&dbaasUserShowCmd{ + Name: c.Name, + Zone: c.Zone, + Username: c.Username, + }).showKafka(ctx)) + } + + return nil +} diff --git a/cmd/dbaas_user_reset_mysql.go b/cmd/dbaas_user_reset_mysql.go new file mode 100644 index 00000000..8d39307f --- /dev/null +++ b/cmd/dbaas_user_reset_mysql.go @@ -0,0 +1,52 @@ +package cmd + +import ( + "fmt" + + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +func (c *dbaasUserResetCmd) resetMysql(cmd *cobra.Command, _ []string) error { + + ctx := gContext + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return err + } + + req := v3.ResetDBAASMysqlUserPasswordRequest{} + + if c.Password != "" { + req.Password = v3.DBAASUserPassword(c.Password) + } + if c.MysqlAuthenticationMethod != "" { + req.Authentication = v3.EnumMysqlAuthenticationPlugin(c.MysqlAuthenticationMethod) + } + + op, err := client.ResetDBAASMysqlUserPassword(ctx, c.Name, c.Username, req) + + if err != nil { + return err + } + + decorateAsyncOperation(fmt.Sprintf("Resetting DBaaS user %q", c.Username), func() { + op, err = client.Wait(ctx, op, v3.OperationStateSuccess) + }) + + if err != nil { + return err + } + + if !globalstate.Quiet { + return c.outputFunc((&dbaasUserShowCmd{ + Name: c.Name, + Zone: c.Zone, + Username: c.Username, + }).showMysql(ctx)) + } + + return nil +} diff --git a/cmd/dbaas_user_reset_opensearch.go b/cmd/dbaas_user_reset_opensearch.go new file mode 100644 index 00000000..80f4d382 --- /dev/null +++ b/cmd/dbaas_user_reset_opensearch.go @@ -0,0 +1,48 @@ +package cmd + +import ( + "fmt" + + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +func (c *dbaasUserResetCmd) resetOpensearch(cmd *cobra.Command, _ []string) error { + + ctx := gContext + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return err + } + + req := v3.ResetDBAASOpensearchUserPasswordRequest{} + if c.Password != "" { + req.Password = v3.DBAASUserPassword(c.Password) + } + + op, err := client.ResetDBAASOpensearchUserPassword(ctx, c.Name, c.Username, req) + + if err != nil { + return err + } + + decorateAsyncOperation(fmt.Sprintf("Resetting DBaaS user %q", c.Username), func() { + op, err = client.Wait(ctx, op, v3.OperationStateSuccess) + }) + + if err != nil { + return err + } + + if !globalstate.Quiet { + return c.outputFunc((&dbaasUserShowCmd{ + Name: c.Name, + Zone: c.Zone, + Username: c.Username, + }).showOpensearch(ctx)) + } + + return nil +} diff --git a/cmd/dbaas_user_reset_pg.go b/cmd/dbaas_user_reset_pg.go new file mode 100644 index 00000000..7d924e26 --- /dev/null +++ b/cmd/dbaas_user_reset_pg.go @@ -0,0 +1,48 @@ +package cmd + +import ( + "fmt" + + "github.com/exoscale/cli/pkg/globalstate" + v3 "github.com/exoscale/egoscale/v3" + "github.com/spf13/cobra" +) + +func (c *dbaasUserResetCmd) resetPG(cmd *cobra.Command, _ []string) error { + + ctx := gContext + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return err + } + + req := v3.ResetDBAASPostgresUserPasswordRequest{} + if c.Password != "" { + req.Password = v3.DBAASUserPassword(c.Password) + } + + op, err := client.ResetDBAASPostgresUserPassword(ctx, c.Name, c.Username, req) + + if err != nil { + return err + } + + decorateAsyncOperation(fmt.Sprintf("Resetting DBaaS user %q", c.Username), func() { + op, err = client.Wait(ctx, op, v3.OperationStateSuccess) + }) + + if err != nil { + return err + } + + if !globalstate.Quiet { + return c.outputFunc((&dbaasUserShowCmd{ + Name: c.Name, + Zone: c.Zone, + Username: c.Username, + }).showPG(ctx)) + } + + return nil +} diff --git a/cmd/dbaas_user_reveal.go b/cmd/dbaas_user_reveal.go new file mode 100644 index 00000000..3cfdea80 --- /dev/null +++ b/cmd/dbaas_user_reveal.go @@ -0,0 +1,93 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/exoscale/cli/pkg/output" + "github.com/exoscale/cli/table" + "github.com/spf13/cobra" +) + +type dbaasUserRevealOutput struct { + Username string `json:"username,omitempty"` + Type string `json:"type,omitempty"` + Password string `json:"password,omitempty"` + + // Additional user info for some DBAAS Services + Kafka *dbaasKafkaUserRevealOutput `json:"kafka,omitempty"` +} + +func (o *dbaasUserRevealOutput) ToJSON() { output.JSON(o) } +func (o *dbaasUserRevealOutput) ToText() { output.Text(o) } + +func (o *dbaasUserRevealOutput) ToTable() { + + t := table.NewTable(os.Stdout) + t.SetHeader([]string{"Secrets"}) + defer t.Render() + + t.Append([]string{"Password", o.Password}) + + switch { + case o.Kafka != nil: + o.Kafka.formatUser(t) + } + +} + +type dbaasUserRevealCmd struct { + cliCommandSettings `cli-cmd:"-"` + + _ bool `cli-cmd:"reveal-secrets"` + + Name string `cli-arg:"#"` + Username string `cli-arg:"#"` + Zone string `cli-short:"z" cli-usage:"Database Service zone"` +} + +func (c *dbaasUserRevealCmd) cmdAliases() []string { return nil } + +func (c *dbaasUserRevealCmd) cmdShort() string { return "Show the secrets of a user" } + +func (c *dbaasUserRevealCmd) cmdLong() string { + return `This command reveals a user's password and other possible secrets, depending on the service type.` +} + +func (c *dbaasUserRevealCmd) cmdPreRun(cmd *cobra.Command, args []string) error { + cmdSetZoneFlagFromDefault(cmd) + + return cliCommandDefaultPreRun(c, cmd, args) +} + +func (c *dbaasUserRevealCmd) cmdRun(cmd *cobra.Command, args []string) error { + + ctx := gContext + db, err := dbaasGetV3(ctx, c.Name, c.Zone) + if err != nil { + return err + } + + switch db.Type { + case "mysql": + return c.outputFunc(c.revealMysql(ctx)) + case "kafka": + return c.outputFunc(c.revealKafka(ctx)) + case "pg": + return c.outputFunc(c.revealPG(ctx)) + case "opensearch": + return c.outputFunc(c.revealOpensearch(ctx)) + case "grafana": + return c.outputFunc(c.revealGrafana(ctx)) + default: + return fmt.Errorf("listing users unsupported for service of type %q", db.Type) + + } + +} + +func init() { + cobra.CheckErr(registerCLICommand(dbaasUserCmd, &dbaasUserRevealCmd{ + cliCommandSettings: defaultCLICmdSettings(), + })) +} diff --git a/cmd/dbaas_user_reveal_grafana.go b/cmd/dbaas_user_reveal_grafana.go new file mode 100644 index 00000000..fe77fe0b --- /dev/null +++ b/cmd/dbaas_user_reveal_grafana.go @@ -0,0 +1,27 @@ +package cmd + +import ( + "context" + + "github.com/exoscale/cli/pkg/globalstate" + "github.com/exoscale/cli/pkg/output" + v3 "github.com/exoscale/egoscale/v3" +) + +func (c *dbaasUserRevealCmd) revealGrafana(ctx context.Context) (output.Outputter, error) { + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return &dbaasUserRevealOutput{}, err + } + + s, err := client.RevealDBAASGrafanaUserPassword(ctx, c.Name, c.Username) + if err != nil { + return &dbaasUserRevealOutput{}, err + } + + return &dbaasUserRevealOutput{ + Password: s.Password, + }, nil + +} diff --git a/cmd/dbaas_user_reveal_kafka.go b/cmd/dbaas_user_reveal_kafka.go new file mode 100644 index 00000000..93d6066f --- /dev/null +++ b/cmd/dbaas_user_reveal_kafka.go @@ -0,0 +1,46 @@ +package cmd + +import ( + "context" + "time" + + "github.com/exoscale/cli/pkg/globalstate" + "github.com/exoscale/cli/pkg/output" + "github.com/exoscale/cli/table" + v3 "github.com/exoscale/egoscale/v3" +) + +type dbaasKafkaUserRevealOutput struct { + AccessKey string `json:"access-key,omitempty"` + AccessCert string `json:"access-cert,omitempty"` + AccessCertExpiry time.Time `json:"access-cert-expiry,omitempty"` +} + +func (o *dbaasKafkaUserRevealOutput) formatUser(t *table.Table) { + t.Append([]string{"Access Cert", o.AccessCert}) + t.Append([]string{"Access Key", o.AccessKey}) + t.Append([]string{"Access Cert Expiry", o.AccessCertExpiry.String()}) + +} + +func (c *dbaasUserRevealCmd) revealKafka(ctx context.Context) (output.Outputter, error) { + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return &dbaasUserRevealOutput{}, err + } + + s, err := client.RevealDBAASKafkaUserPassword(ctx, c.Name, c.Username) + if err != nil { + return &dbaasUserRevealOutput{}, err + } + + return &dbaasUserRevealOutput{ + Password: s.Password, + Kafka: &dbaasKafkaUserRevealOutput{ + AccessKey: s.AccessKey, + AccessCert: s.AccessCert, + AccessCertExpiry: s.AccessCertExpiry, + }, + }, nil +} diff --git a/cmd/dbaas_user_reveal_mysql.go b/cmd/dbaas_user_reveal_mysql.go new file mode 100644 index 00000000..f40a2bc6 --- /dev/null +++ b/cmd/dbaas_user_reveal_mysql.go @@ -0,0 +1,27 @@ +package cmd + +import ( + "context" + + "github.com/exoscale/cli/pkg/globalstate" + "github.com/exoscale/cli/pkg/output" + v3 "github.com/exoscale/egoscale/v3" +) + +func (c *dbaasUserRevealCmd) revealMysql(ctx context.Context) (output.Outputter, error) { + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return &dbaasUserRevealOutput{}, err + } + + s, err := client.RevealDBAASMysqlUserPassword(ctx, c.Name, c.Username) + if err != nil { + return &dbaasUserRevealOutput{}, err + } + + return &dbaasUserRevealOutput{ + Password: s.Password, + }, nil + +} diff --git a/cmd/dbaas_user_reveal_opensearch.go b/cmd/dbaas_user_reveal_opensearch.go new file mode 100644 index 00000000..3c06175f --- /dev/null +++ b/cmd/dbaas_user_reveal_opensearch.go @@ -0,0 +1,27 @@ +package cmd + +import ( + "context" + + "github.com/exoscale/cli/pkg/globalstate" + "github.com/exoscale/cli/pkg/output" + v3 "github.com/exoscale/egoscale/v3" +) + +func (c *dbaasUserRevealCmd) revealOpensearch(ctx context.Context) (output.Outputter, error) { + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return &dbaasUserRevealOutput{}, err + } + + s, err := client.RevealDBAASOpensearchUserPassword(ctx, c.Name, c.Username) + if err != nil { + return &dbaasUserRevealOutput{}, err + } + + return &dbaasUserRevealOutput{ + Password: s.Password, + }, nil + +} diff --git a/cmd/dbaas_user_reveal_pg.go b/cmd/dbaas_user_reveal_pg.go new file mode 100644 index 00000000..55517e7a --- /dev/null +++ b/cmd/dbaas_user_reveal_pg.go @@ -0,0 +1,27 @@ +package cmd + +import ( + "context" + + "github.com/exoscale/cli/pkg/globalstate" + "github.com/exoscale/cli/pkg/output" + v3 "github.com/exoscale/egoscale/v3" +) + +func (c *dbaasUserRevealCmd) revealPG(ctx context.Context) (output.Outputter, error) { + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return &dbaasUserRevealOutput{}, err + } + + s, err := client.RevealDBAASPostgresUserPassword(ctx, c.Name, c.Username) + if err != nil { + return &dbaasUserRevealOutput{}, err + } + + return &dbaasUserRevealOutput{ + Password: s.Password, + }, nil + +} diff --git a/cmd/dbaas_user_show.go b/cmd/dbaas_user_show.go new file mode 100644 index 00000000..f4ef3856 --- /dev/null +++ b/cmd/dbaas_user_show.go @@ -0,0 +1,96 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/exoscale/cli/pkg/output" + "github.com/exoscale/cli/table" + "github.com/spf13/cobra" +) + +type dbaasUserShowOutput struct { + Username string `json:"username,omitempty"` + Type string `json:"type,omitempty"` + + // Additional user info for some DBAAS Services + MySQL *dbaasMysqlUserShowOutput `json:"mysql,omitempty"` + PG *dbaasPGUserShowOutput `json:"pg,omitempty"` +} + +func (o *dbaasUserShowOutput) ToJSON() { output.JSON(o) } +func (o *dbaasUserShowOutput) ToText() { output.Text(o) } + +func (o *dbaasUserShowOutput) ToTable() { + + t := table.NewTable(os.Stdout) + t.SetHeader([]string{"Service User"}) + defer t.Render() + + t.Append([]string{"Username", o.Username}) + t.Append([]string{"Type", o.Type}) + + switch { + case o.MySQL != nil: + o.MySQL.formatUser(t) + case o.PG != nil: + o.PG.formatUser(t) + } + +} + +type dbaasUserShowCmd struct { + cliCommandSettings `cli-cmd:"-"` + + _ bool `cli-cmd:"show"` + + Name string `cli-arg:"#"` + Username string `cli-arg:"#"` + Zone string `cli-short:"z" cli-usage:"Database Service zone"` +} + +func (c *dbaasUserShowCmd) cmdAliases() []string { return nil } + +func (c *dbaasUserShowCmd) cmdShort() string { return "Show the details of a user" } + +func (c *dbaasUserShowCmd) cmdLong() string { + return `This command show a user and their details for a specified DBAAS service.` +} + +func (c *dbaasUserShowCmd) cmdPreRun(cmd *cobra.Command, args []string) error { + cmdSetZoneFlagFromDefault(cmd) + + return cliCommandDefaultPreRun(c, cmd, args) +} + +func (c *dbaasUserShowCmd) cmdRun(cmd *cobra.Command, args []string) error { + + ctx := gContext + db, err := dbaasGetV3(ctx, c.Name, c.Zone) + if err != nil { + return err + } + + switch db.Type { + case "mysql": + return c.outputFunc(c.showMysql(ctx)) + case "kafka": + return c.outputFunc(c.showKafka(ctx)) + case "pg": + return c.outputFunc(c.showPG(ctx)) + case "opensearch": + return c.outputFunc(c.showOpensearch(ctx)) + case "grafana": + return c.outputFunc(c.showGrafana(ctx)) + default: + return fmt.Errorf("listing users unsupported for service of type %q", db.Type) + + } + +} + +func init() { + cobra.CheckErr(registerCLICommand(dbaasUserCmd, &dbaasUserShowCmd{ + cliCommandSettings: defaultCLICmdSettings(), + })) +} diff --git a/cmd/dbaas_user_show_grafana.go b/cmd/dbaas_user_show_grafana.go new file mode 100644 index 00000000..faa95145 --- /dev/null +++ b/cmd/dbaas_user_show_grafana.go @@ -0,0 +1,36 @@ +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 *dbaasUserShowCmd) showGrafana(ctx context.Context) (output.Outputter, error) { + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return &dbaasUserShowOutput{}, err + } + + s, err := client.GetDBAASServiceGrafana(ctx, c.Name) + if err != nil { + return &dbaasUserShowOutput{}, err + } + + for _, u := range s.Users { + + if u.Username == c.Username { + return &dbaasUserShowOutput{ + Username: c.Username, + Type: u.Type, + }, nil + } + + } + + return &dbaasUserShowOutput{}, fmt.Errorf("user %q not found for service %q", c.Username, c.Name) +} diff --git a/cmd/dbaas_user_show_kafka.go b/cmd/dbaas_user_show_kafka.go new file mode 100644 index 00000000..5f8d1949 --- /dev/null +++ b/cmd/dbaas_user_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 *dbaasUserShowCmd) showKafka(ctx context.Context) (output.Outputter, error) { + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return &dbaasUserShowOutput{}, err + } + + s, err := client.GetDBAASServiceKafka(ctx, c.Name) + if err != nil { + return &dbaasUserShowOutput{}, err + } + + for _, u := range s.Users { + if u.Username == c.Username { + return &dbaasUserShowOutput{ + Username: c.Username, + Type: u.Type, + }, nil + } + + } + + return &dbaasUserShowOutput{}, fmt.Errorf("user %q not found for service %q", c.Username, c.Name) +} diff --git a/cmd/dbaas_user_show_mysql.go b/cmd/dbaas_user_show_mysql.go new file mode 100644 index 00000000..9e8e4cbe --- /dev/null +++ b/cmd/dbaas_user_show_mysql.go @@ -0,0 +1,49 @@ +package cmd + +import ( + "context" + "fmt" + + "github.com/exoscale/cli/pkg/globalstate" + "github.com/exoscale/cli/pkg/output" + "github.com/exoscale/cli/table" + v3 "github.com/exoscale/egoscale/v3" +) + +type dbaasMysqlUserShowOutput struct { + Authentication string `json:"authentication,omitempty"` +} + +func (o *dbaasMysqlUserShowOutput) formatUser(t *table.Table) { + t.Append([]string{"Authentication", o.Authentication}) +} + +func (c *dbaasUserShowCmd) showMysql(ctx context.Context) (output.Outputter, error) { + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return &dbaasUserShowOutput{}, err + } + + s, err := client.GetDBAASServiceMysql(ctx, c.Name) + if err != nil { + return &dbaasUserShowOutput{}, err + } + + for _, u := range s.Users { + + if u.Username == c.Username { + return &dbaasUserShowOutput{ + Username: c.Username, + + Type: u.Type, + MySQL: &dbaasMysqlUserShowOutput{ + Authentication: u.Authentication, + }, + }, nil + } + + } + + return &dbaasUserShowOutput{}, fmt.Errorf("user %q not found for service %q", c.Username, c.Name) +} diff --git a/cmd/dbaas_user_show_opensearch.go b/cmd/dbaas_user_show_opensearch.go new file mode 100644 index 00000000..e32daf14 --- /dev/null +++ b/cmd/dbaas_user_show_opensearch.go @@ -0,0 +1,37 @@ +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 *dbaasUserShowCmd) showOpensearch(ctx context.Context) (output.Outputter, error) { + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return &dbaasUserShowOutput{}, err + } + + s, err := client.GetDBAASServiceOpensearch(ctx, c.Name) + if err != nil { + return &dbaasUserShowOutput{}, err + } + + for _, u := range s.Users { + + if u.Username == c.Username { + return &dbaasUserShowOutput{ + Username: c.Username, + + Type: u.Type, + }, nil + } + + } + + return &dbaasUserShowOutput{}, fmt.Errorf("user %q not found for service %q", c.Username, c.Name) +} diff --git a/cmd/dbaas_user_show_pg.go b/cmd/dbaas_user_show_pg.go new file mode 100644 index 00000000..2727b366 --- /dev/null +++ b/cmd/dbaas_user_show_pg.go @@ -0,0 +1,51 @@ +package cmd + +import ( + "context" + "fmt" + "strconv" + + "github.com/exoscale/cli/pkg/globalstate" + "github.com/exoscale/cli/pkg/output" + "github.com/exoscale/cli/table" + v3 "github.com/exoscale/egoscale/v3" +) + +type dbaasPGUserShowOutput struct { + AllowReplication *bool `json:"allow-replication,omitempty"` +} + +func (o *dbaasPGUserShowOutput) formatUser(t *table.Table) { + if o.AllowReplication != nil { + t.Append([]string{"Allow Replication", strconv.FormatBool(*o.AllowReplication)}) + } +} + +func (c *dbaasUserShowCmd) showPG(ctx context.Context) (output.Outputter, error) { + + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone)) + if err != nil { + return &dbaasUserShowOutput{}, err + } + + s, err := client.GetDBAASServicePG(ctx, c.Name) + if err != nil { + return &dbaasUserShowOutput{}, err + } + + for _, u := range s.Users { + + if u.Username == c.Username { + return &dbaasUserShowOutput{ + Username: c.Username, + Type: u.Type, + PG: &dbaasPGUserShowOutput{ + AllowReplication: u.AllowReplication, + }, + }, nil + } + + } + + return &dbaasUserShowOutput{}, fmt.Errorf("user %q not found for service %q", c.Username, c.Name) +}