Skip to content
Merged
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
6 changes: 6 additions & 0 deletions args.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,12 @@ const (
ArgDatabaseMaintenanceDay = "day"
// ArgDatabaseMaintenanceHour is the new hour for the maintenance window
ArgDatabaseMaintenanceHour = "hour"
// ArgDatabaseStorageAutoscaleEnabled enables or disables storage autoscaling for a database cluster
ArgDatabaseStorageAutoscaleEnabled = "enabled"
// ArgDatabaseStorageAutoscaleThresholdPercent is the storage usage percentage that triggers autoscaling
ArgDatabaseStorageAutoscaleThresholdPercent = "threshold-percent"
// ArgDatabaseStorageAutoscaleIncrementGib is the amount of storage, in GiB, to add when autoscaling triggers
ArgDatabaseStorageAutoscaleIncrementGib = "increment-gib"
// ArgDatabasePoolUserName is the name of user for use with connection pool
ArgDatabasePoolUserName = "user"
// ArgDatabasePoolDBName is the database for use with connection pool
Expand Down
138 changes: 137 additions & 1 deletion commands/databases.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ const (
defaultDatabaseNodeCount = 1
defaultDatabaseRegion = "nyc1"
defaultDatabaseEngine = "pg"
databaseListDetails = `

defaultDatabaseStorageAutoscaleThresholdPercent = 80
defaultDatabaseStorageAutoscaleIncrementGib = 10
databaseListDetails = `

This command requires the ID of a database cluster, which you can retrieve by calling:

Expand Down Expand Up @@ -154,6 +157,7 @@ For PostgreSQL and MySQL clusters, you can also provide a disk size in MiB to sc

cmd.AddCommand(databaseReplica())
cmd.AddCommand(databaseMaintenanceWindow())
cmd.AddCommand(databaseStorageAutoscale())
cmd.AddCommand(databaseUser())
cmd.AddCommand(databaseDB())
cmd.AddCommand(databasePool())
Expand Down Expand Up @@ -743,6 +747,138 @@ func buildDatabaseUpdateMaintenanceRequestFromArgs(c *CmdConfig) (*godo.Database
return r, nil
}

func databaseStorageAutoscale() *Command {
cmd := &Command{
Command: &cobra.Command{
Use: "storage-autoscale",
Aliases: []string{"autoscale", "storage"},
Short: "Display commands for managing database cluster storage autoscaling",
Long: `The ` + "`" + `doctl databases storage-autoscale` + "`" + ` commands allow you to view and update storage autoscaling settings for database clusters.

When enabled, storage autoscaling automatically increases disk capacity when usage crosses a configured threshold.`,
},
}

cmdStorageAutoscaleGet := CmdBuilder(cmd, RunDatabaseStorageAutoscaleGet, "get <database-cluster-id>",
"Retrieve storage autoscaling settings for a database cluster", `Retrieves the storage autoscaling configuration for the specified database cluster, including:

- Whether storage autoscaling is enabled
- The storage usage percentage that triggers autoscaling
- The amount of storage, in GiB, added when autoscaling triggers

To see a list of your databases and their IDs, run `+"`"+`doctl databases list`+"`"+`.`, Writer, aliasOpt("g"),
displayerType(&displayers.DatabaseStorageAutoscale{}))
cmdStorageAutoscaleGet.Example = `The following example retrieves storage autoscaling settings for a database cluster with the ID ` + "`" + `ca9f591d-f38h-5555-a0ef-1c02d1d1e35` + "`" + `: doctl databases storage-autoscale get ca9f591d-f38h-5555-a0ef-1c02d1d1e35`

cmdStorageAutoscaleUpdate := CmdBuilder(cmd, RunDatabaseStorageAutoscaleUpdate,
"update <database-cluster-id>", "Update storage autoscaling settings for a database cluster", `Updates the storage autoscaling configuration for the specified database cluster.

The `+"`"+`--enabled`+"`"+` is a required flag and must be `+"`"+`true`+"`"+` or `+"`"+`false`+"`"+`.

When `+"`"+`--enabled`+"`"+` is `+"`"+`false`+"`"+`, storage autoscaling is disabled and threshold/increment flags are ignored.

When `+"`"+`--enabled`+"`"+` is `+"`"+`true`+"`"+`, you may optionally set `+"`"+`--threshold-percent`+"`"+` and `+"`"+`--increment-gib`+"`"+`. When omitted, defaults of 80 (percent) and 10 (GiB) are used.

To see a list of your databases and their IDs, run `+"`"+`doctl databases list`+"`"+`.`, Writer, aliasOpt("u"))
AddStringFlag(cmdStorageAutoscaleUpdate, doctl.ArgDatabaseStorageAutoscaleEnabled, "", "",
"Whether storage autoscaling is enabled for the database cluster; must be `true` or `false`", requiredOpt())
AddIntFlag(cmdStorageAutoscaleUpdate, doctl.ArgDatabaseStorageAutoscaleThresholdPercent, "", 0,
"The storage usage percentage that triggers autoscaling (only used when --enabled is true)")
AddIntFlag(cmdStorageAutoscaleUpdate, doctl.ArgDatabaseStorageAutoscaleIncrementGib, "", 0,
"The amount of storage, in GiB, to add when autoscaling triggers (only used when --enabled is true)")
cmdStorageAutoscaleUpdate.Example = `The following example enables storage autoscaling for a database cluster with the ID ` + "`" + `ca9f591d-f38h-5555-a0ef-1c02d1d1e35` + "`" + ` with an 80% threshold and 10 GiB increment: doctl databases storage-autoscale update ca9f591d-f38h-5555-a0ef-1c02d1d1e35 --enabled true --threshold-percent 80 --increment-gib 10

The following example enables storage autoscaling with platform defaults: doctl databases storage-autoscale update ca9f591d-f38h-5555-a0ef-1c02d1d1e35 --enabled true

The following example disables storage autoscaling: doctl databases storage-autoscale update ca9f591d-f38h-5555-a0ef-1c02d1d1e35 --enabled false`

return cmd
}

// RunDatabaseStorageAutoscaleGet retrieves storage autoscaling settings for a database cluster.
func RunDatabaseStorageAutoscaleGet(c *CmdConfig) error {
if len(c.Args) == 0 {
return doctl.NewMissingArgsErr(c.NS)
}

id := c.Args[0]

autoscale, err := c.Databases().GetStorageAutoscale(id)
if err != nil {
return err
}

return displayDatabaseStorageAutoscale(c, *autoscale)
}

func displayDatabaseStorageAutoscale(c *CmdConfig, autoscale do.DatabaseStorageAutoscale) error {
item := &displayers.DatabaseStorageAutoscale{DatabaseStorageAutoscale: autoscale}
return c.Display(item)
}

// RunDatabaseStorageAutoscaleUpdate updates storage autoscaling settings for a database cluster.
func RunDatabaseStorageAutoscaleUpdate(c *CmdConfig) error {
if len(c.Args) == 0 {
return doctl.NewMissingArgsErr(c.NS)
}

id := c.Args[0]
r, err := buildDatabaseStorageAutoscaleRequestFromArgs(c)
if err != nil {
return err
}

return c.Databases().UpdateStorageAutoscale(id, r)
}

func buildDatabaseStorageAutoscaleRequestFromArgs(c *CmdConfig) (*godo.DatabaseStorageAutoscale, error) {
if c.Command == nil {
return nil, errors.New("command flags are not available")
}

flags := c.Command.Flags()

enabledStr, err := flags.GetString(doctl.ArgDatabaseStorageAutoscaleEnabled)
if err != nil {
return nil, err
}
if strings.TrimSpace(enabledStr) == "" {
return nil, doctl.NewMissingArgsErr(doctl.ArgDatabaseStorageAutoscaleEnabled)
}

enabled, err := strconv.ParseBool(enabledStr)
if err != nil {
return nil, fmt.Errorf("%q is not a valid boolean for --enabled (use true or false)", enabledStr)
}

if !enabled {
return &godo.DatabaseStorageAutoscale{Enabled: false}, nil
}

thresholdPercent := defaultDatabaseStorageAutoscaleThresholdPercent
if flags.Changed(doctl.ArgDatabaseStorageAutoscaleThresholdPercent) {
thresholdPercent, err = flags.GetInt(doctl.ArgDatabaseStorageAutoscaleThresholdPercent)
if err != nil {
return nil, err
}
}

incrementGib := uint64(defaultDatabaseStorageAutoscaleIncrementGib)
if flags.Changed(doctl.ArgDatabaseStorageAutoscaleIncrementGib) {
val, err := flags.GetInt(doctl.ArgDatabaseStorageAutoscaleIncrementGib)
if err != nil {
return nil, err
}
incrementGib = uint64(val)
}

return &godo.DatabaseStorageAutoscale{
Enabled: true,
ThresholdPercent: &thresholdPercent,
IncrementGib: &incrementGib,
}, nil
}

func databaseUser() *Command {
cmd := &Command{
Command: &cobra.Command{
Expand Down
92 changes: 92 additions & 0 deletions commands/databases_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ var (
Hour: "10:00",
}

testGODOStorageAutoscaleThresholdPercent = 70
testGODOStorageAutoscaleIncrementGib = uint64(20)
testGODOStorageAutoscale = &godo.DatabaseStorageAutoscale{
Enabled: true,
ThresholdPercent: &testGODOStorageAutoscaleThresholdPercent,
IncrementGib: &testGODOStorageAutoscaleIncrementGib,
}

testBackUpRestore = &godo.DatabaseBackupRestore{
DatabaseName: "sunny-db-cluster",
BackupCreatedAt: "2023-02-01T17:32:15Z",
Expand Down Expand Up @@ -128,6 +136,10 @@ var (
DatabaseMaintenanceWindow: testGODOMainWindow,
}

testDBStorageAutoscale = do.DatabaseStorageAutoscale{
DatabaseStorageAutoscale: testGODOStorageAutoscale,
}

testDBBackup = do.DatabaseBackup{
DatabaseBackup: &godo.DatabaseBackup{
CreatedAt: time.Now(),
Expand Down Expand Up @@ -275,6 +287,7 @@ func TestDatabasesCommand(t *testing.T) {
"replica",
"options",
"maintenance-window",
"storage-autoscale",
"user",
"pool",
"db",
Expand All @@ -295,6 +308,15 @@ func TestDatabaseMaintenanceWindowCommand(t *testing.T) {
)
}

func TestDatabaseStorageAutoscaleCommand(t *testing.T) {
cmd := databaseStorageAutoscale()
assert.NotNil(t, cmd)
assertCommandNames(t, cmd,
"update",
"get",
)
}

func TestDatabaseUserCommand(t *testing.T) {
cmd := databaseUser()
assert.NotNil(t, cmd)
Expand Down Expand Up @@ -934,6 +956,76 @@ func TestDatabaseUpdateMaintenance(t *testing.T) {
})
}

func TestDatabaseGetStorageAutoscale(t *testing.T) {
// Success
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
tm.databases.EXPECT().GetStorageAutoscale(testDBCluster.ID).Return(&testDBStorageAutoscale, nil)
config.Args = append(config.Args, testDBCluster.ID)

err := RunDatabaseStorageAutoscaleGet(config)
assert.NoError(t, err)
})

// Error
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
tm.databases.EXPECT().GetStorageAutoscale(testDBCluster.ID).Return(nil, errTest)
config.Args = append(config.Args, testDBCluster.ID)

err := RunDatabaseStorageAutoscaleGet(config)
assert.EqualError(t, err, errTest.Error())
})
}

func TestDatabaseUpdateStorageAutoscale(t *testing.T) {
defaultThreshold := defaultDatabaseStorageAutoscaleThresholdPercent
defaultIncrement := uint64(defaultDatabaseStorageAutoscaleIncrementGib)

rEnabled := &godo.DatabaseStorageAutoscale{
Enabled: true,
ThresholdPercent: &defaultThreshold,
IncrementGib: &defaultIncrement,
}
rDisabled := &godo.DatabaseStorageAutoscale{Enabled: false}

setStorageAutoscaleUpdateFlags := func(config *CmdConfig, enabled string) {
for _, child := range databaseStorageAutoscale().Commands() {
if child.Name() == "update" {
config.Command = child
break
}
}
assert.NotNil(t, config.Command)
assert.NoError(t, config.Command.Flags().Set(doctl.ArgDatabaseStorageAutoscaleEnabled, enabled))
}

withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
tm.databases.EXPECT().UpdateStorageAutoscale(testDBCluster.ID, rEnabled).Return(nil)
config.Args = append(config.Args, testDBCluster.ID)
setStorageAutoscaleUpdateFlags(config, "true")

err := RunDatabaseStorageAutoscaleUpdate(config)
assert.NoError(t, err)
})

withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
tm.databases.EXPECT().UpdateStorageAutoscale(testDBCluster.ID, rDisabled).Return(nil)
config.Args = append(config.Args, testDBCluster.ID)
setStorageAutoscaleUpdateFlags(config, "false")

err := RunDatabaseStorageAutoscaleUpdate(config)
assert.NoError(t, err)
})

withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
tm.databases.EXPECT().UpdateStorageAutoscale(testDBCluster.ID, rEnabled).Return(errTest)
config.Args = append(config.Args, testDBCluster.ID)
setStorageAutoscaleUpdateFlags(config, "true")

err := RunDatabaseStorageAutoscaleUpdate(config)
assert.EqualError(t, err, errTest.Error())
})
}

func TestDatabaseInstallUpdate(t *testing.T) {

// Success
Expand Down
41 changes: 41 additions & 0 deletions commands/displayers/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,47 @@ func (dmw *DatabaseMaintenanceWindow) KV() []map[string]any {
return []map[string]any{o}
}

type DatabaseStorageAutoscale struct {
DatabaseStorageAutoscale do.DatabaseStorageAutoscale
}

var _ Displayable = &DatabaseStorageAutoscale{}

func (dsa *DatabaseStorageAutoscale) JSON(out io.Writer) error {
return writeJSON(dsa.DatabaseStorageAutoscale, out)
}

func (dsa *DatabaseStorageAutoscale) Cols() []string {
return []string{
"Enabled",
"Threshold Percent",
"Increment GiB",
}
}

func (dsa *DatabaseStorageAutoscale) ColMap() map[string]string {
return map[string]string{
"Enabled": "Enabled",
"Threshold Percent": "Threshold Percent",
"Increment GiB": "Increment GiB",
}
}

func (dsa *DatabaseStorageAutoscale) KV() []map[string]any {
autoscale := dsa.DatabaseStorageAutoscale
o := map[string]any{
"Enabled": autoscale.Enabled,
}
if autoscale.ThresholdPercent != nil {
o["Threshold Percent"] = *autoscale.ThresholdPercent
}
if autoscale.IncrementGib != nil {
o["Increment GiB"] = *autoscale.IncrementGib
}

return []map[string]any{o}
}

type DatabaseDBs struct {
DatabaseDBs do.DatabaseDBs
}
Expand Down
23 changes: 23 additions & 0 deletions do/databases.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ type DatabaseMaintenanceWindow struct {
*godo.DatabaseMaintenanceWindow
}

// DatabaseStorageAutoscale is a wrapper for godo.DatabaseStorageAutoscale
type DatabaseStorageAutoscale struct {
*godo.DatabaseStorageAutoscale
}

// DatabaseFirewallRule is a wrapper for godo.DatabaseFirewallRule
type DatabaseFirewallRule struct {
*godo.DatabaseFirewallRule
Expand Down Expand Up @@ -185,6 +190,9 @@ type DatabasesService interface {
UpdateMaintenance(string, *godo.DatabaseUpdateMaintenanceRequest) error
InstallUpdate(string) error

GetStorageAutoscale(string) (*DatabaseStorageAutoscale, error)
UpdateStorageAutoscale(string, *godo.DatabaseStorageAutoscale) error

GetUser(string, string) (*DatabaseUser, error)
ListUsers(string) (DatabaseUsers, error)
CreateUser(string, *godo.DatabaseCreateUserRequest) (*DatabaseUser, error)
Expand Down Expand Up @@ -371,6 +379,21 @@ func (ds *databasesService) InstallUpdate(databaseID string) error {
return err
}

func (ds *databasesService) GetStorageAutoscale(databaseID string) (*DatabaseStorageAutoscale, error) {
autoscale, _, err := ds.client.Databases.GetStorageAutoscale(context.TODO(), databaseID)
if err != nil {
return nil, err
}

return &DatabaseStorageAutoscale{DatabaseStorageAutoscale: autoscale}, nil
}

func (ds *databasesService) UpdateStorageAutoscale(databaseID string, req *godo.DatabaseStorageAutoscale) error {
_, err := ds.client.Databases.UpdateStorageAutoscale(context.TODO(), databaseID, req)

return err
}

func (ds *databasesService) ListBackups(databaseID string) (DatabaseBackups, error) {
f := func(opt *godo.ListOptions) ([]any, *godo.Response, error) {
list, resp, err := ds.client.Databases.ListBackups(context.TODO(), databaseID, opt)
Expand Down
Loading
Loading