Skip to content

Commit 4dcf121

Browse files
feat: psql v2 (#657)
* temp: create cmd * add version commands * feat: backup and backup location * fix config override * feat: add tab completion * fix: duplicate l * fix: use convbytes * temp * fix: *string to fmt, MB when GB expected, -l conflict, --sync-mode vs --sync * doc: docs * feat: use new psql-v2 api changes * doc: regen docs * feat: use new printer pkg * fix: new api changes * fix docs and gofmt * test: fix psql-v2 tests * fix: sync-mode, postgres-v2 command * fix: fileconfig should contain psqlv2 * fix: changes to login and cfg generation to generate both psql and psqlv2 * test: use .invalid domain * test: wait longer * fix: db-password on update * doc: aliases, changelog * test: only use backup if active * review: migrate to new printer * review: add ranges and validations for ram, storage-size, cores, instances etc * review: add a nice error if recovery-time used without backup-id * review: move completions to a new completer pkg * nit: idOk should not be checked * fix: add cols DbUsername, DbDatabase, and StatusMessage * nit: use AddSetFlag for some flags * add --state for use with delete --all * fix: govulncheck * test: update tests * fix: --cols tab-completion, move to resource-level * test: ensure capital letter in password
1 parent 9723fb9 commit 4dcf121

136 files changed

Lines changed: 16946 additions & 95 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Changed
66
- Grouped all cloudapi-v6 commands under a `compute` subcommand, allowing custom behaviour or global flags for the entire API as a whole. All commands are also added as hidden commands onto the root command, to prevent breaking changes.
7+
- Change config generation logic to allow whitelisting or blacklisting by API version, not just name (e.g. `--whitelist postgresql:v2` or `--blacklist postgresql:v1`)
78

89
### Fixed
910
- Fixed typos in `alb rule httprule add` and `group user remove`.
@@ -13,7 +14,7 @@
1314

1415
### Added
1516
- Added `--wait-for-request` and `--timeout` flags to `group user remove`
16-
17+
- Added support for `dbaas postgres-v2`
1718

1819
## [v6.9.8] – March 2026
1920

commands/cfg/login.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -282,20 +282,21 @@ func addFilterFlags(cmd *core.Command) {
282282
"object-storage": fileconfiguration.ObjectStorage,
283283
"object-storage-management": fileconfiguration.ObjectStorageManagement,
284284
"mongodb": fileconfiguration.Mongo,
285-
"postgresql": fileconfiguration.PSQL,
285+
"postgresql:v1": fileconfiguration.PSQL,
286+
"postgresql:v2": fileconfiguration.PSQLV2,
286287
"in-memory-db": fileconfiguration.InMemoryDB,
287288
"observability-monitoring": fileconfiguration.Monitoring,
288289
"vmautoscaling": fileconfiguration.Autoscaling,
289290
},
290291
"Define custom names for each spec")
291292
cmd.AddStringFlag(FlagFilterVersion, "", "", "Filter by major spec version (e.g. v1)")
292-
cmd.AddStringSliceFlag(FlagWhitelist, "", []string{}, "Comma-separated list of API names to include")
293+
cmd.AddStringSliceFlag(FlagWhitelist, "", []string{}, "Comma-separated list of API names or name:version pairs to include (e.g. vpn,postgresql:v2)")
293294
cmd.AddStringSliceFlag(FlagBlacklist, "",
294295
[]string{"object-storage-user-owned-buckets", "object-storage-contract-owned-buckets",
295296
"identity-federation", "identity-provider", "identity-policy",
296297
"inference-modelhub", "inference-openai",
297298
"quota", "reseller", "tagging"},
298-
"Comma-separated list of API names to exclude")
299+
"Comma-separated list of API names or name:version pairs to exclude (e.g. postgresql:v1)")
299300
cmd.AddStringFlag(FlagVisibility, "", "public", "(hidden) Filter by index visibility")
300301
cmd.AddStringFlag(FlagGate, "", "", "(hidden) Filter by release gate")
301302

commands/dbaas/dbaas.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"github.com/ionos-cloud/ionosctl/v6/commands/dbaas/mariadb"
66
"github.com/ionos-cloud/ionosctl/v6/commands/dbaas/mongo"
77
"github.com/ionos-cloud/ionosctl/v6/commands/dbaas/postgres"
8+
"github.com/ionos-cloud/ionosctl/v6/commands/dbaas/postgres-v2"
89
"github.com/ionos-cloud/ionosctl/v6/internal/core"
910
"github.com/spf13/cobra"
1011
)
@@ -20,6 +21,7 @@ func DataBaseServiceCmd() *core.Command {
2021
},
2122
}
2223
dbaasCmd.AddCommand(postgres.DBaaSPostgresCmd())
24+
dbaasCmd.AddCommand(postgres_v2.Root())
2325
dbaasCmd.AddCommand(mongo.DBaaSMongoCmd())
2426
dbaasCmd.AddCommand(mariadb.Root())
2527
dbaasCmd.AddCommand(inmemorydb.Root())
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package backup
2+
3+
import (
4+
"context"
5+
6+
"github.com/ionos-cloud/ionosctl/v6/commands/dbaas/postgres-v2/backup/location"
7+
"github.com/ionos-cloud/ionosctl/v6/internal/client"
8+
"github.com/ionos-cloud/ionosctl/v6/internal/constants"
9+
"github.com/ionos-cloud/ionosctl/v6/internal/core"
10+
"github.com/ionos-cloud/ionosctl/v6/internal/printer/table"
11+
psqlv2 "github.com/ionos-cloud/sdk-go-bundle/products/dbaas/psql/v3"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
var backupCols = []table.Column{
16+
{Name: "BackupId", JSONPath: "id", Default: true},
17+
{Name: "ClusterId", JSONPath: "properties.clusterId", Default: true},
18+
{Name: "PostgresClusterVersion", JSONPath: "properties.postgresClusterVersion", Default: true},
19+
{Name: "Location", JSONPath: "properties.location", Default: true},
20+
{Name: "IsActive", JSONPath: "properties.isActive", Default: true},
21+
{Name: "EarliestRecoveryTargetTime", JSONPath: "properties.earliestRecoveryTargetTime", Default: true},
22+
{Name: "LatestRecoveryTargetTime", JSONPath: "properties.latestRecoveryTargetTime", Default: true},
23+
{Name: "State", JSONPath: "metadata.state"},
24+
{Name: "CreatedDate", JSONPath: "metadata.createdDate"},
25+
}
26+
27+
func BackupCmd() *core.Command {
28+
backupCmd := &core.Command{
29+
Command: &cobra.Command{
30+
Use: "backup",
31+
Aliases: []string{"b"},
32+
Short: "PostgreSQL Backup Operations",
33+
Long: "The sub-commands of `ionosctl dbaas postgres-v2 backup` allow you to list and get DBaaS PostgreSQL Backups.",
34+
TraverseChildren: true,
35+
},
36+
}
37+
38+
backupCmd.Command.PersistentFlags().StringSlice(constants.ArgCols, nil, table.ColsMessage(backupCols))
39+
_ = backupCmd.Command.RegisterFlagCompletionFunc(
40+
constants.ArgCols, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
41+
return table.AllCols(backupCols), cobra.ShellCompDirectiveNoFileComp
42+
},
43+
)
44+
45+
backupCmd.AddCommand(BackupListCmd())
46+
backupCmd.AddCommand(BackupGetCmd())
47+
backupCmd.AddCommand(location.BackupLocationCmd())
48+
49+
return backupCmd
50+
}
51+
52+
func Backups(fs ...Filter) (psqlv2.BackupReadList, error) {
53+
req := client.Must().PostgresClientV2.BackupsApi.BackupsGet(context.Background())
54+
55+
for _, f := range fs {
56+
var err error
57+
req, err = f(req)
58+
if err != nil {
59+
return psqlv2.BackupReadList{}, err
60+
}
61+
}
62+
63+
ls, _, err := req.Execute()
64+
if err != nil {
65+
return psqlv2.BackupReadList{}, err
66+
}
67+
return ls, nil
68+
}
69+
70+
type Filter func(request psqlv2.ApiBackupsGetRequest) (psqlv2.ApiBackupsGetRequest, error)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package backup
2+
3+
import (
4+
"context"
5+
6+
"github.com/ionos-cloud/ionosctl/v6/commands/dbaas/postgres-v2/completer"
7+
"github.com/ionos-cloud/ionosctl/v6/internal/client"
8+
"github.com/ionos-cloud/ionosctl/v6/internal/constants"
9+
"github.com/ionos-cloud/ionosctl/v6/internal/core"
10+
"github.com/ionos-cloud/ionosctl/v6/internal/printer/table"
11+
"github.com/spf13/viper"
12+
)
13+
14+
func BackupGetCmd() *core.Command {
15+
ctx := context.TODO()
16+
get := core.NewCommand(ctx, nil, core.CommandBuilder{
17+
Namespace: "dbaas-postgres-v2",
18+
Resource: "backup",
19+
Verb: "get",
20+
Aliases: []string{"g"},
21+
ShortDesc: "Get a PostgreSQL Backup",
22+
LongDesc: "Use this command to retrieve details about a PostgreSQL Backup by using its ID.\n\nRequired values to run command:\n\n* Backup Id",
23+
Example: "ionosctl dbaas postgres-v2 backup get --backup-id <backup-id>",
24+
PreCmdRun: PreRunBackupId,
25+
CmdRun: RunBackupGet,
26+
InitClient: true,
27+
})
28+
get.AddUUIDFlag(constants.FlagBackupId, constants.FlagIdShort, "", "The unique ID of the Backup", core.RequiredFlagOption(),
29+
core.WithCompletion(completer.BackupIds, constants.PostgresApiRegionalURL, constants.PostgresLocations),
30+
)
31+
return get
32+
}
33+
34+
func PreRunBackupId(c *core.PreCommandConfig) error {
35+
return core.CheckRequiredFlags(c.Command, c.NS, constants.FlagBackupId)
36+
}
37+
38+
func RunBackupGet(c *core.CommandConfig) error {
39+
backupId := viper.GetString(core.GetFlagName(c.NS, constants.FlagBackupId))
40+
41+
c.Verbose(constants.FlagBackupId, backupId)
42+
43+
backup, _, err := client.Must().PostgresClientV2.BackupsApi.BackupsFindById(context.Background(), backupId).Execute()
44+
if err != nil {
45+
return err
46+
}
47+
48+
cols := viper.GetStringSlice(core.GetFlagName(c.NS, constants.ArgCols))
49+
return c.Out(table.Sprint(backupCols, backup, cols))
50+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package backup
2+
3+
import (
4+
"context"
5+
6+
"github.com/ionos-cloud/ionosctl/v6/commands/dbaas/postgres-v2/completer"
7+
"github.com/ionos-cloud/ionosctl/v6/internal/client"
8+
"github.com/ionos-cloud/ionosctl/v6/internal/constants"
9+
"github.com/ionos-cloud/ionosctl/v6/internal/core"
10+
"github.com/ionos-cloud/ionosctl/v6/internal/printer/table"
11+
"github.com/spf13/viper"
12+
)
13+
14+
func BackupListCmd() *core.Command {
15+
ctx := context.TODO()
16+
list := core.NewCommand(ctx, nil, core.CommandBuilder{
17+
Namespace: "dbaas-postgres-v2",
18+
Resource: "backup",
19+
Verb: "list",
20+
Aliases: []string{"ls"},
21+
ShortDesc: "List PostgreSQL Backups",
22+
LongDesc: "Use this command to retrieve a list of PostgreSQL Backups.",
23+
Example: "ionosctl dbaas postgres-v2 backup list",
24+
PreCmdRun: core.NoPreRun,
25+
CmdRun: RunBackupList,
26+
InitClient: true,
27+
})
28+
list.AddStringFlag(constants.FlagClusterId, constants.FlagIdShort, "", "Filter backups by Cluster ID",
29+
core.WithCompletion(completer.ClusterIds, constants.PostgresApiRegionalURL, constants.PostgresLocations),
30+
)
31+
list.AddInt32Flag(constants.FlagLimit, "", 100, "The limit of the number of items to return")
32+
list.AddInt32Flag(constants.FlagOffset, "", 0, "The offset of the listing")
33+
34+
return list
35+
}
36+
37+
func RunBackupList(c *core.CommandConfig) error {
38+
req := client.Must().PostgresClientV2.BackupsApi.BackupsGet(context.Background())
39+
40+
if viper.IsSet(core.GetFlagName(c.NS, constants.FlagClusterId)) {
41+
req = req.FilterClusterId(viper.GetString(core.GetFlagName(c.NS, constants.FlagClusterId)))
42+
}
43+
if viper.IsSet(core.GetFlagName(c.NS, constants.FlagLimit)) {
44+
req = req.Limit(viper.GetInt32(core.GetFlagName(c.NS, constants.FlagLimit)))
45+
}
46+
if viper.IsSet(core.GetFlagName(c.NS, constants.FlagOffset)) {
47+
req = req.Offset(viper.GetInt32(core.GetFlagName(c.NS, constants.FlagOffset)))
48+
}
49+
50+
backups, _, err := req.Execute()
51+
if err != nil {
52+
return err
53+
}
54+
55+
cols := viper.GetStringSlice(core.GetFlagName(c.NS, constants.ArgCols))
56+
return c.Out(table.Sprint(backupCols, backups, cols, table.WithPrefix("items")))
57+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package location
2+
3+
import (
4+
"context"
5+
6+
"github.com/ionos-cloud/ionosctl/v6/commands/dbaas/postgres-v2/completer"
7+
"github.com/ionos-cloud/ionosctl/v6/internal/client"
8+
"github.com/ionos-cloud/ionosctl/v6/internal/constants"
9+
"github.com/ionos-cloud/ionosctl/v6/internal/core"
10+
"github.com/ionos-cloud/ionosctl/v6/internal/printer/table"
11+
"github.com/spf13/cobra"
12+
"github.com/spf13/viper"
13+
)
14+
15+
func BackupLocationGetCmd() *core.Command {
16+
ctx := context.TODO()
17+
get := core.NewCommand(ctx, nil, core.CommandBuilder{
18+
Namespace: "dbaas-postgres-v2",
19+
Resource: "backup-location",
20+
Verb: "get",
21+
Aliases: []string{"g"},
22+
ShortDesc: "Get a PostgreSQL Backup Location",
23+
LongDesc: "Use this command to retrieve details about a PostgreSQL Backup Location by using its ID.\n\nRequired values to run command:\n\n* Backup Location Id",
24+
Example: "ionosctl dbaas postgres-v2 backup location get --backup-location-id <backup-location-id>",
25+
PreCmdRun: PreRunBackupLocationId,
26+
CmdRun: RunBackupLocationGet,
27+
InitClient: true,
28+
})
29+
get.AddStringFlag(constants.FlagBackupLocationId, constants.FlagIdShort, "", "The unique ID of the Backup Location", core.RequiredFlagOption())
30+
_ = get.Command.RegisterFlagCompletionFunc(constants.FlagBackupLocationId, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
31+
return completer.BackupLocationIds(), cobra.ShellCompDirectiveNoFileComp
32+
})
33+
return get
34+
}
35+
36+
func PreRunBackupLocationId(c *core.PreCommandConfig) error {
37+
return core.CheckRequiredFlags(c.Command, c.NS, constants.FlagBackupLocationId)
38+
}
39+
40+
func RunBackupLocationGet(c *core.CommandConfig) error {
41+
locationId := viper.GetString(core.GetFlagName(c.NS, constants.FlagBackupLocationId))
42+
43+
c.Verbose(constants.FlagBackupLocationId, locationId)
44+
45+
location, _, err := client.Must().PostgresClientV2.BackupLocationsApi.BackuplocationsFindById(context.Background(), locationId).Execute()
46+
if err != nil {
47+
return err
48+
}
49+
50+
cols := viper.GetStringSlice(core.GetFlagName(c.NS, constants.ArgCols))
51+
return c.Out(table.Sprint(backupLocationCols, location, cols))
52+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package location
2+
3+
import (
4+
"context"
5+
6+
"github.com/ionos-cloud/ionosctl/v6/internal/client"
7+
"github.com/ionos-cloud/ionosctl/v6/internal/constants"
8+
"github.com/ionos-cloud/ionosctl/v6/internal/core"
9+
"github.com/ionos-cloud/ionosctl/v6/internal/printer/table"
10+
"github.com/spf13/viper"
11+
)
12+
13+
func BackupLocationListCmd() *core.Command {
14+
ctx := context.TODO()
15+
list := core.NewCommand(ctx, nil, core.CommandBuilder{
16+
Namespace: "dbaas-postgres-v2",
17+
Resource: "backup-location",
18+
Verb: "list",
19+
Aliases: []string{"ls"},
20+
ShortDesc: "List PostgreSQL Backup Locations",
21+
LongDesc: "Use this command to retrieve a list of PostgreSQL Backup Locations.",
22+
Example: "ionosctl dbaas postgres-v2 backup location list",
23+
PreCmdRun: core.NoPreRun,
24+
CmdRun: RunBackupLocationList,
25+
InitClient: true,
26+
})
27+
list.AddInt32Flag(constants.FlagLimit, "", 100, "The limit of the number of items to return")
28+
list.AddInt32Flag(constants.FlagOffset, "", 0, "The offset of the listing")
29+
30+
return list
31+
}
32+
33+
func RunBackupLocationList(c *core.CommandConfig) error {
34+
req := client.Must().PostgresClientV2.BackupLocationsApi.BackuplocationsGet(context.Background())
35+
36+
if viper.IsSet(core.GetFlagName(c.NS, constants.FlagLimit)) {
37+
req = req.Limit(viper.GetInt32(core.GetFlagName(c.NS, constants.FlagLimit)))
38+
}
39+
if viper.IsSet(core.GetFlagName(c.NS, constants.FlagOffset)) {
40+
req = req.Offset(viper.GetInt32(core.GetFlagName(c.NS, constants.FlagOffset)))
41+
}
42+
43+
locations, _, err := req.Execute()
44+
if err != nil {
45+
return err
46+
}
47+
48+
cols := viper.GetStringSlice(core.GetFlagName(c.NS, constants.ArgCols))
49+
return c.Out(table.Sprint(backupLocationCols, locations, cols, table.WithPrefix("items")))
50+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package location
2+
3+
import (
4+
"github.com/ionos-cloud/ionosctl/v6/internal/constants"
5+
"github.com/ionos-cloud/ionosctl/v6/internal/core"
6+
"github.com/ionos-cloud/ionosctl/v6/internal/printer/table"
7+
"github.com/spf13/cobra"
8+
)
9+
10+
var backupLocationCols = []table.Column{
11+
{Name: "LocationId", JSONPath: "id", Default: true},
12+
{Name: "Location", JSONPath: "properties.location", Default: true},
13+
}
14+
15+
func BackupLocationCmd() *core.Command {
16+
locationCmd := &core.Command{
17+
Command: &cobra.Command{
18+
Use: "location",
19+
Aliases: []string{"loc"},
20+
Short: "PostgreSQL Backup Location Operations",
21+
Long: "The sub-commands of `ionosctl dbaas postgres-v2 backup location` allow you to list and get DBaaS PostgreSQL Backup Locations.",
22+
TraverseChildren: true,
23+
},
24+
}
25+
26+
locationCmd.Command.PersistentFlags().StringSlice(constants.ArgCols, nil, table.ColsMessage(backupLocationCols))
27+
_ = locationCmd.Command.RegisterFlagCompletionFunc(
28+
constants.ArgCols, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
29+
return table.AllCols(backupLocationCols), cobra.ShellCompDirectiveNoFileComp
30+
},
31+
)
32+
33+
locationCmd.AddCommand(BackupLocationListCmd())
34+
locationCmd.AddCommand(BackupLocationGetCmd())
35+
36+
return locationCmd
37+
}

0 commit comments

Comments
 (0)