Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions cmd/dbaas/dbaas_acl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package dbaas

import (
"github.com/spf13/cobra"
)

var dbaasAclCmd = &cobra.Command{
Use: "acl",
Short: "Manage DBaaS ACL configuration",
}

func init() {
dbaasCmd.AddCommand(dbaasAclCmd)
}
151 changes: 151 additions & 0 deletions cmd/dbaas/dbaas_acl_show.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package dbaas

import (
"bytes"
"fmt"

"github.com/spf13/cobra"

exocmd "github.com/exoscale/cli/cmd"
"github.com/exoscale/cli/pkg/globalstate"
"github.com/exoscale/cli/pkg/output"
"github.com/exoscale/cli/table"
"github.com/exoscale/cli/utils"
v3 "github.com/exoscale/egoscale/v3"
)

type dbaasAclShowOutput struct {
Users []dbaasAclUserOutput `json:"users"`
}

func (o *dbaasAclShowOutput) ToJSON() { output.JSON(o) }
func (o *dbaasAclShowOutput) ToText() { output.Text(o) }

type dbaasAclUserOutput struct {
Username string `json:"username"`
Roles []dbaasAclRoleOutput `json:"roles"`
Privileges []dbaasPrivOutput `json:"privileges"`
}

type dbaasAclRoleOutput struct {
Name string `json:"name"`
Default bool `json:"default,omitempty"`
WithAdminOption bool `json:"with-admin-option,omitempty"`
}

type dbaasPrivOutput struct {
AccessType string `json:"access-type"`
Database string `json:"database,omitempty"`
Table string `json:"table,omitempty"`
Column string `json:"column,omitempty"`
GrantOption bool `json:"grant-option,omitempty"`
PartialRevoke bool `json:"partial-revoke,omitempty"`
}

type dbaasAclShowCmd struct {
exocmd.CliCommandSettings `cli-cmd:"-"`

_ bool `cli-cmd:"show"`
Name string `cli-arg:"#" cli-usage:"NAME"`
Zone string `cli-short:"z" cli-usage:"Database Service zone"`
}

func (c *dbaasAclShowCmd) CmdAliases() []string { return nil }
func (c *dbaasAclShowCmd) CmdShort() string { return "Show ClickHouse ACL configuration" }
func (c *dbaasAclShowCmd) CmdLong() string { return "Show the current ClickHouse ACL configuration for a DBaaS service." }

func (c *dbaasAclShowCmd) CmdPreRun(cmd *cobra.Command, args []string) error {
exocmd.CmdSetZoneFlagFromDefault(cmd)
return exocmd.CliCommandDefaultPreRun(c, cmd, args)
}

func (c *dbaasAclShowCmd) CmdRun(_ *cobra.Command, _ []string) error {
ctx := exocmd.GContext

client, err := exocmd.SwitchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone))
if err != nil {
return err
}

acl, err := client.GetDBAASClickhouseAclConfig(ctx, c.Name)
if err != nil {
return err
}

out := &dbaasAclShowOutput{}
for _, u := range acl.Users {
userOut := dbaasAclUserOutput{
Username: string(u.Username),
}
for _, r := range u.Roles {
userOut.Roles = append(userOut.Roles, dbaasAclRoleOutput{
Name: r.Name,
Default: utils.DefaultBool(r.Default, false),
WithAdminOption: utils.DefaultBool(r.WithAdminOption, false),
})
}
for _, p := range u.Privileges {
userOut.Privileges = append(userOut.Privileges, dbaasPrivOutput{
AccessType: p.AccessType,
Database: p.Database,
Table: p.Table,
Column: p.Column,
GrantOption: utils.DefaultBool(p.GrantOption, false),
PartialRevoke: utils.DefaultBool(p.PartialRevoke, false),
})
}
out.Users = append(out.Users, userOut)
}

return c.OutputFunc(out, nil)
}

func (o *dbaasAclShowOutput) ToTable() {
t := table.NewTable(nil)
defer t.Render()

if len(o.Users) == 0 {
t.Append([]string{"No ACL configuration found", ""})
return
}

for _, u := range o.Users {
t.Append([]string{"User", u.Username})

buf := bytes.NewBuffer(nil)
rolesTable := table.NewEmbeddedTable(buf)
rolesTable.SetHeader([]string{"Role", "Default", "Admin"})
for _, r := range u.Roles {
rolesTable.Append([]string{
r.Name,
fmt.Sprintf("%v", r.Default),
fmt.Sprintf("%v", r.WithAdminOption),
})
}
rolesTable.Render()
t.Append([]string{"Roles", buf.String()})

buf.Reset()
privsTable := table.NewEmbeddedTable(buf)
privsTable.SetHeader([]string{"Access", "Database", "Table", "Column", "Grant", "Partial"})
for _, p := range u.Privileges {
privsTable.Append([]string{
p.AccessType,
p.Database,
p.Table,
p.Column,
fmt.Sprintf("%v", p.GrantOption),
fmt.Sprintf("%v", p.PartialRevoke),
})
}
privsTable.Render()
t.Append([]string{"Privileges", buf.String()})
t.Append([]string{"", ""})
}
}

func init() {
cobra.CheckErr(exocmd.RegisterCLICommand(dbaasAclCmd, &dbaasAclShowCmd{
CliCommandSettings: exocmd.DefaultCLICmdSettings(),
}))
}
19 changes: 16 additions & 3 deletions cmd/dbaas/dbaas_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ type dbaasServiceCreateCmd struct {
HelpOpensearch bool `cli-usage:"show usage for flags specific to the opensearch type"`
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"`
HelpValkey bool `cli-usage:"show usage for flags specific to the valkey type"`
HelpGrafana bool `cli-usage:"show usage for flags specific to the grafana type"`
HelpThanos bool `cli-usage:"show usage for flags specific to the thanos type"`
HelpValkey bool `cli-usage:"show usage for flags specific to the valkey type"`
HelpGrafana bool `cli-usage:"show usage for flags specific to the grafana type"`
HelpThanos bool `cli-usage:"show usage for flags specific to the thanos type"`
HelpClickhouse bool `cli-usage:"show usage for flags specific to the clickhouse type"`

MaintenanceDOW string `cli-flag:"maintenance-dow" cli-usage:"automated Database Service maintenance day-of-week"`
MaintenanceTime string `cli-usage:"automated Database Service maintenance time (format HH:MM:SS)"`
Expand Down Expand Up @@ -128,6 +129,13 @@ type dbaasServiceCreateCmd struct {
// "thanos" type specific flags
ThanosIPFilter []string `cli-flag:"thanos-ip-filter" cli-usage:"allow incoming connections from CIDR address block" cli-hidden:""`
ThanosSettings string `cli-flag:"thanos-settings" cli-usage:"Thanos configuration settings (JSON format)" cli-hidden:""`

// "clickhouse" type specific flags
ClickhouseForkFrom string `cli-flag:"clickhouse-fork-from" cli-usage:"name of a Database Service to fork from" cli-hidden:""`
ClickhouseIPFilter []string `cli-flag:"clickhouse-ip-filter" cli-usage:"allow incoming connections from CIDR address block" cli-hidden:""`
ClickhouseRecoveryBackupName string `cli-flag:"clickhouse-recovery-backup-name" cli-usage:"the name of the backup to restore when forking from a Database Service" cli-hidden:""`
ClickhouseSettings string `cli-flag:"clickhouse-settings" cli-usage:"ClickHouse configuration settings (JSON format)" cli-hidden:""`
ClickhouseVersion string `cli-flag:"clickhouse-version" cli-usage:"ClickHouse major version" cli-hidden:""`
}

func (c *dbaasServiceCreateCmd) CmdAliases() []string { return exocmd.GCreateAlias }
Expand Down Expand Up @@ -167,6 +175,9 @@ func (c *dbaasServiceCreateCmd) CmdPreRun(cmd *cobra.Command, args []string) err
case cmd.Flags().Changed("help-thanos"):
exocmd.CmdShowHelpFlags(cmd.Flags(), "thanos-")
os.Exit(0)
case cmd.Flags().Changed("help-clickhouse"):
exocmd.CmdShowHelpFlags(cmd.Flags(), "clickhouse-")
os.Exit(0)
}

exocmd.CmdSetZoneFlagFromDefault(cmd)
Expand Down Expand Up @@ -199,6 +210,8 @@ func (c *dbaasServiceCreateCmd) CmdRun(cmd *cobra.Command, args []string) error
return c.createValkey(cmd, args)
case "thanos":
return c.createThanos(cmd, args)
case "clickhouse":
return c.createClickhouse(cmd, args)
default:
return fmt.Errorf("unsupported service type %q", c.Type)
}
Expand Down
94 changes: 94 additions & 0 deletions cmd/dbaas/dbaas_create_clickhouse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// AI-modified by hermes-agent - not reviewed yet
package dbaas

import (
"encoding/json"
"fmt"

"github.com/spf13/cobra"

exocmd "github.com/exoscale/cli/cmd"
"github.com/exoscale/cli/pkg/globalstate"
"github.com/exoscale/cli/utils"
v3 "github.com/exoscale/egoscale/v3"
)

func (c *dbaasServiceCreateCmd) createClickhouse(_ *cobra.Command, _ []string) error {
var err error

ctx := exocmd.GContext

client, err := exocmd.SwitchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone))
if err != nil {
return fmt.Errorf("unable to create client: %w", err)
}

databaseService := v3.CreateDBAASServiceClickhouseRequest{
Plan: c.Plan,
TerminationProtection: &c.TerminationProtection,
Version: c.ClickhouseVersion,
}

if c.ClickhouseForkFrom != "" {
databaseService.ForkFromService = v3.DBAASServiceName(c.ClickhouseForkFrom)
if c.ClickhouseRecoveryBackupName != "" {
databaseService.RecoveryBackupName = c.ClickhouseRecoveryBackupName
}
}

if len(c.ClickhouseIPFilter) > 0 {
databaseService.IPFilter = c.ClickhouseIPFilter
}

if c.MaintenanceDOW != "" && c.MaintenanceTime != "" {
databaseService.Maintenance = &v3.CreateDBAASServiceClickhouseRequestMaintenance{
Dow: v3.CreateDBAASServiceClickhouseRequestMaintenanceDow(c.MaintenanceDOW),
Time: c.MaintenanceTime,
}
}

if c.ClickhouseSettings != "" {
settingsSchema, err := client.GetDBAASSettingsClickhouse(ctx)
if err != nil {
return fmt.Errorf("unable to retrieve Database Service settings: %w", err)
}
_, err = validateDatabaseServiceSettings(
c.ClickhouseSettings,
settingsSchema.Settings.Clickhouse.Properties,
)
if err != nil {
return fmt.Errorf("invalid settings: %w", err)
}

settings := &v3.JSONSchemaClickhouse{}
if err := json.Unmarshal([]byte(c.ClickhouseSettings), &settings); err != nil {
return err
}

databaseService.ClickhouseSettings = settings
}

op, err := client.CreateDBAASServiceClickhouse(ctx, c.Name, databaseService)
if err != nil {
return err
}

utils.DecorateAsyncOperation(fmt.Sprintf("Creating DBaaS ClickHouse service %q", c.Name), func() {
op, err = client.Wait(ctx, op, v3.OperationStateSuccess)
})

if err != nil {
return err
}

serviceName := op.Reference.ID.String()

if !globalstate.Quiet {
return c.OutputFunc((&dbaasServiceShowCmd{
Name: serviceName,
Zone: c.Zone,
}).showDatabaseServiceClickhouse(ctx))
}

return nil
}
7 changes: 6 additions & 1 deletion cmd/dbaas/dbaas_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,12 @@ func (c *dbaasServiceDeleteCmd) CmdRun(_ *cobra.Command, _ []string) error {
c.Name, strings.Join(readReplicaNames, ", "))
}

op, err := client.DeleteDBAASService(ctx, c.Name)
op, err := func() (*v3.Operation, error) {
if string(svc.Type) == "clickhouse" {
return client.DeleteDBAASServiceClickhouse(ctx, c.Name)
}
return client.DeleteDBAASService(ctx, c.Name)
}()
if err != nil {
if errors.Is(err, v3.ErrNotFound) {
return fmt.Errorf("resource not found in zone %q", c.Zone)
Expand Down
14 changes: 14 additions & 0 deletions cmd/dbaas/dbaas_role.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package dbaas

import (
"github.com/spf13/cobra"
)

var dbaasRoleCmd = &cobra.Command{
Use: "role",
Short: "Manage DBaaS roles",
}

func init() {
dbaasCmd.AddCommand(dbaasRoleCmd)
}
56 changes: 56 additions & 0 deletions cmd/dbaas/dbaas_role_delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package dbaas

import (
"fmt"

"github.com/spf13/cobra"

exocmd "github.com/exoscale/cli/cmd"
"github.com/exoscale/cli/pkg/globalstate"
"github.com/exoscale/cli/utils"
v3 "github.com/exoscale/egoscale/v3"
)

type dbaasRoleDeleteCmd struct {
exocmd.CliCommandSettings `cli-cmd:"-"`

_ bool `cli-cmd:"delete"`
Name string `cli-arg:"#" cli-usage:"NAME"`
RoleUUID string `cli-arg:"#" cli-usage:"ROLE-UUID"`
Zone string `cli-short:"z" cli-usage:"Database Service zone"`
}

func (c *dbaasRoleDeleteCmd) CmdAliases() []string { return nil }
func (c *dbaasRoleDeleteCmd) CmdShort() string { return "Delete a ClickHouse role" }
func (c *dbaasRoleDeleteCmd) CmdLong() string { return "Delete a role from a ClickHouse DBaaS service by UUID." }

func (c *dbaasRoleDeleteCmd) CmdPreRun(cmd *cobra.Command, args []string) error {
exocmd.CmdSetZoneFlagFromDefault(cmd)
return exocmd.CliCommandDefaultPreRun(c, cmd, args)
}

func (c *dbaasRoleDeleteCmd) CmdRun(_ *cobra.Command, _ []string) error {
ctx := exocmd.GContext

client, err := exocmd.SwitchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone))
if err != nil {
return err
}

op, err := client.DeleteDBAASClickhouseRole(ctx, c.Name, v3.UUID(c.RoleUUID))

Check failure on line 40 in cmd/dbaas/dbaas_role_delete.go

View workflow job for this annotation

GitHub Actions / Build + Run Unit Tests

client.DeleteDBAASClickhouseRole undefined (type *v3.Client has no field or method DeleteDBAASClickhouseRole)

Check failure on line 40 in cmd/dbaas/dbaas_role_delete.go

View workflow job for this annotation

GitHub Actions / Build + Run Unit Tests

client.DeleteDBAASClickhouseRole undefined (type *v3.Client has no field or method DeleteDBAASClickhouseRole)

Check failure on line 40 in cmd/dbaas/dbaas_role_delete.go

View workflow job for this annotation

GitHub Actions / lint

client.DeleteDBAASClickhouseRole undefined (type *v3.Client has no field or method DeleteDBAASClickhouseRole)

Check failure on line 40 in cmd/dbaas/dbaas_role_delete.go

View workflow job for this annotation

GitHub Actions / Run E2E (Testscript) Tests / Without API

client.DeleteDBAASClickhouseRole undefined (type *v3.Client has no field or method DeleteDBAASClickhouseRole)

Check failure on line 40 in cmd/dbaas/dbaas_role_delete.go

View workflow job for this annotation

GitHub Actions / Run E2E (Testscript) Tests / With API / dbaas

client.DeleteDBAASClickhouseRole undefined (type *v3.Client has no field or method DeleteDBAASClickhouseRole)

Check failure on line 40 in cmd/dbaas/dbaas_role_delete.go

View workflow job for this annotation

GitHub Actions / Run E2E (Testscript) Tests / With API / compute

client.DeleteDBAASClickhouseRole undefined (type *v3.Client has no field or method DeleteDBAASClickhouseRole)

Check failure on line 40 in cmd/dbaas/dbaas_role_delete.go

View workflow job for this annotation

GitHub Actions / Run E2E (Testscript) Tests / With API / storage

client.DeleteDBAASClickhouseRole undefined (type *v3.Client has no field or method DeleteDBAASClickhouseRole)

Check failure on line 40 in cmd/dbaas/dbaas_role_delete.go

View workflow job for this annotation

GitHub Actions / govulncheck

client.DeleteDBAASClickhouseRole undefined (type *v3.Client has no field or method DeleteDBAASClickhouseRole)
if err != nil {
return err
}

utils.DecorateAsyncOperation(fmt.Sprintf("Deleting role %q from service %q", c.RoleUUID, c.Name), func() {
op, err = client.Wait(ctx, op, v3.OperationStateSuccess)
})

return err
}

func init() {
cobra.CheckErr(exocmd.RegisterCLICommand(dbaasRoleCmd, &dbaasRoleDeleteCmd{
CliCommandSettings: exocmd.DefaultCLICmdSettings(),
}))
}
Loading
Loading