diff --git a/cmd/completion_test.go b/cmd/completion_test.go index b2ea6c5..a2a32be 100644 --- a/cmd/completion_test.go +++ b/cmd/completion_test.go @@ -1047,3 +1047,179 @@ func TestCompleteVPCPeeringRouteID_TooManyArgs(t *testing.T) { t.Errorf("expected nil completions with too many args, got %v", comps) } } + +// ─── completeDBaaSDatabaseID parent-slot fixes ─────────────────────────────── + +func TestCompleteDBaaSDatabaseID_NoArgs_DelegatesToDBaaSID(t *testing.T) { + id, name := "dbaas-001", "my-dbaas" + srv := newArubaTestServer(t) + srv.OnGet("/projects/proj-123/providers/Aruba.Database/dbaas", jsonResponse(200, types.DBaaSListResponse{ + Values: []types.DBaaSResponse{ + {Metadata: types.ResourceMetadataResponse{ID: &id, Name: &name}}, + }, + })) + setClientForTesting(srv.Client()) + defer resetClientState() + + completions, _ := completeDBaaSDatabaseID(makeProjectCmd("proj-123"), []string{}, "") + if len(completions) == 0 { + t.Errorf("expected DBaaS IDs from delegation, got none") + } +} + +func TestCompleteDBaaSDatabaseID_NoArgs_NoProjectID(t *testing.T) { + cleanup := withTempHomeDir(t) + defer cleanup() + + comps, _ := completeDBaaSDatabaseID(&cobra.Command{}, []string{}, "") + if comps != nil { + t.Errorf("expected nil completions without project-id, got %v", comps) + } +} + +func TestCompleteDBaaSDatabaseID_TooManyArgs(t *testing.T) { + comps, _ := completeDBaaSDatabaseID(&cobra.Command{}, []string{"dbaas-001", "my-db"}, "") + if comps != nil { + t.Errorf("expected nil completions with too many args, got %v", comps) + } +} + +// ─── completeDBaaSUserID parent-slot fixes ──────────────────────────────────── + +func TestCompleteDBaaSUserID_NoArgs_DelegatesToDBaaSID(t *testing.T) { + id, name := "dbaas-001", "my-dbaas" + srv := newArubaTestServer(t) + srv.OnGet("/projects/proj-123/providers/Aruba.Database/dbaas", jsonResponse(200, types.DBaaSListResponse{ + Values: []types.DBaaSResponse{ + {Metadata: types.ResourceMetadataResponse{ID: &id, Name: &name}}, + }, + })) + setClientForTesting(srv.Client()) + defer resetClientState() + + completions, _ := completeDBaaSUserID(makeProjectCmd("proj-123"), []string{}, "") + if len(completions) == 0 { + t.Errorf("expected DBaaS IDs from delegation, got none") + } +} + +func TestCompleteDBaaSUserID_NoArgs_NoProjectID(t *testing.T) { + cleanup := withTempHomeDir(t) + defer cleanup() + + comps, _ := completeDBaaSUserID(&cobra.Command{}, []string{}, "") + if comps != nil { + t.Errorf("expected nil completions without project-id, got %v", comps) + } +} + +func TestCompleteDBaaSUserID_TooManyArgs(t *testing.T) { + comps, _ := completeDBaaSUserID(&cobra.Command{}, []string{"dbaas-001", "alice"}, "") + if comps != nil { + t.Errorf("expected nil completions with too many args, got %v", comps) + } +} + +// ─── completeGrantID parent-slot fixes ─────────────────────────────────────── + +func TestCompleteGrantID_NoArgs_DelegatesToDBaaSID(t *testing.T) { + id, name := "dbaas-001", "my-dbaas" + srv := newArubaTestServer(t) + srv.OnGet("/projects/proj-123/providers/Aruba.Database/dbaas", jsonResponse(200, types.DBaaSListResponse{ + Values: []types.DBaaSResponse{ + {Metadata: types.ResourceMetadataResponse{ID: &id, Name: &name}}, + }, + })) + setClientForTesting(srv.Client()) + defer resetClientState() + + completions, _ := completeGrantID(makeProjectCmd("proj-123"), []string{}, "") + if len(completions) == 0 { + t.Errorf("expected DBaaS IDs from delegation, got none") + } +} + +func TestCompleteGrantID_OneArg_DelegatesToDatabaseID(t *testing.T) { + srv := newArubaTestServer(t) + srv.OnGet("/projects/proj-123/providers/Aruba.Database/dbaas/dbaas-001/databases", jsonResponse(200, types.DatabaseListResponse{ + Values: []types.DatabaseResponse{ + {Name: "my-db"}, + }, + })) + setClientForTesting(srv.Client()) + defer resetClientState() + + completions, _ := completeGrantID(makeProjectCmd("proj-123"), []string{"dbaas-001"}, "") + if len(completions) == 0 { + t.Errorf("expected database names from delegation, got none") + } +} + +func TestCompleteGrantID_TooManyArgs(t *testing.T) { + comps, _ := completeGrantID(&cobra.Command{}, []string{"dbaas-001", "my-db", "grant-id"}, "") + if comps != nil { + t.Errorf("expected nil completions with too many args, got %v", comps) + } +} + +// ─── completeStorageRestoreCreateArgs ──────────────────────────────────────── + +func TestCompleteStorageRestoreCreateArgs_NoArgs_DelegatesToBackupID(t *testing.T) { + id, name := "bkp-001", "my-backup" + srv := newArubaTestServer(t) + srv.OnGet("/projects/proj-123/providers/Aruba.Storage/backups", jsonResponse(200, types.StorageBackupListResponse{ + Values: []types.StorageBackupResponse{ + {Metadata: types.ResourceMetadataResponse{ID: &id, Name: &name}}, + }, + })) + setClientForTesting(srv.Client()) + defer resetClientState() + + completions, _ := completeStorageRestoreCreateArgs(makeProjectCmd("proj-123"), []string{}, "") + if len(completions) == 0 { + t.Errorf("expected backup IDs from delegation, got none") + } +} + +func TestCompleteStorageRestoreCreateArgs_OneArg_DelegatesToBlockStorageID(t *testing.T) { + id, name := "vol-001", "my-volume" + srv := newArubaTestServer(t) + srv.OnGet("/projects/proj-123/providers/Aruba.Storage/blockStorages", jsonResponse(200, types.BlockStorageListResponse{ + Values: []types.BlockStorageResponse{ + {Metadata: types.ResourceMetadataResponse{ID: &id, Name: &name}}, + }, + })) + setClientForTesting(srv.Client()) + defer resetClientState() + + completions, _ := completeStorageRestoreCreateArgs(makeProjectCmd("proj-123"), []string{"bkp-001"}, "") + if len(completions) == 0 { + t.Errorf("expected volume IDs from delegation, got none") + } +} + +func TestCompleteStorageRestoreCreateArgs_TooManyArgs(t *testing.T) { + comps, _ := completeStorageRestoreCreateArgs(&cobra.Command{}, []string{"bkp-001", "vol-001"}, "") + if comps != nil { + t.Errorf("expected nil completions with too many args, got %v", comps) + } +} + +// ─── storageBackupCmd create ValidArgsFunction ─────────────────────────────── + +func TestCompleteBackupCreateArg_DelegatesToBlockStorageID(t *testing.T) { + id, name := "vol-001", "my-volume" + srv := newArubaTestServer(t) + srv.OnGet("/projects/proj-123/providers/Aruba.Storage/blockStorages", jsonResponse(200, types.BlockStorageListResponse{ + Values: []types.BlockStorageResponse{ + {Metadata: types.ResourceMetadataResponse{ID: &id, Name: &name}}, + }, + })) + setClientForTesting(srv.Client()) + defer resetClientState() + + completions, _ := completeBlockStorageID(makeProjectCmd("proj-123"), nil, "") + if len(completions) == 0 { + t.Errorf("expected volume IDs, got none") + } +} diff --git a/cmd/database.dbaas.database.go b/cmd/database.dbaas.database.go index 7821047..b6facd1 100644 --- a/cmd/database.dbaas.database.go +++ b/cmd/database.dbaas.database.go @@ -34,13 +34,18 @@ func init() { dbaasDatabaseListCmd.Flags().Int("limit", 0, "Maximum number of results to return (0 = no limit)") dbaasDatabaseListCmd.Flags().Int("offset", 0, "Number of results to skip") + dbaasDatabaseCreateCmd.ValidArgsFunction = completeDBaaSDatabaseID + dbaasDatabaseListCmd.ValidArgsFunction = completeDBaaSDatabaseID dbaasDatabaseGetCmd.ValidArgsFunction = completeDBaaSDatabaseID dbaasDatabaseUpdateCmd.ValidArgsFunction = completeDBaaSDatabaseID dbaasDatabaseDeleteCmd.ValidArgsFunction = completeDBaaSDatabaseID } func completeDBaaSDatabaseID(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) < 1 { + if len(args) == 0 { + return completeDBaaSID(cmd, args, toComplete) + } + if len(args) > 1 { return nil, cobra.ShellCompDirectiveNoFileComp } diff --git a/cmd/database.dbaas.grant.go b/cmd/database.dbaas.grant.go index 30a9edc..db89bdf 100644 --- a/cmd/database.dbaas.grant.go +++ b/cmd/database.dbaas.grant.go @@ -38,6 +38,11 @@ func init() { dbaasGrantDeleteCmd.Flags().String("project-id", "", "Project ID (uses context if not specified)") dbaasGrantDeleteCmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt") dbaasGrantDeleteCmd.Flags().Bool("dry-run", false, "Validate resource exists without deleting") + + dbaasGrantCreateCmd.ValidArgsFunction = completeGrantID + dbaasGrantListCmd.ValidArgsFunction = completeGrantID + dbaasGrantGetCmd.ValidArgsFunction = completeGrantID + dbaasGrantDeleteCmd.ValidArgsFunction = completeGrantID } // grantRef returns a Ref for a specific grant inside a database inside a DBaaS instance. @@ -46,7 +51,13 @@ func grantRef(projectID, dbaasID, dbName, grantID string) aruba.Ref { } func completeGrantID(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) < 2 { + if len(args) == 0 { + return completeDBaaSID(cmd, args, toComplete) + } + if len(args) == 1 { + return completeDBaaSDatabaseID(cmd, args, toComplete) + } + if len(args) > 2 { return nil, cobra.ShellCompDirectiveNoFileComp } projectID, err := GetProjectID(cmd) diff --git a/cmd/database.dbaas.user.go b/cmd/database.dbaas.user.go index 43360de..24b0e8b 100644 --- a/cmd/database.dbaas.user.go +++ b/cmd/database.dbaas.user.go @@ -47,13 +47,18 @@ func init() { dbaasUserListCmd.Flags().Int("limit", 0, "Maximum number of results to return (0 = no limit)") dbaasUserListCmd.Flags().Int("offset", 0, "Number of results to skip") + dbaasUserCreateCmd.ValidArgsFunction = completeDBaaSUserID + dbaasUserListCmd.ValidArgsFunction = completeDBaaSUserID dbaasUserGetCmd.ValidArgsFunction = completeDBaaSUserID dbaasUserUpdateCmd.ValidArgsFunction = completeDBaaSUserID dbaasUserDeleteCmd.ValidArgsFunction = completeDBaaSUserID } func completeDBaaSUserID(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) < 1 { + if len(args) == 0 { + return completeDBaaSID(cmd, args, toComplete) + } + if len(args) > 1 { return nil, cobra.ShellCompDirectiveNoFileComp } diff --git a/cmd/storage.backup.go b/cmd/storage.backup.go index 9aebae1..d6d6f25 100644 --- a/cmd/storage.backup.go +++ b/cmd/storage.backup.go @@ -48,6 +48,7 @@ func init() { storageBackupDeleteCmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt") storageBackupDeleteCmd.Flags().Bool("dry-run", false, "Validate resource exists without deleting") + storageBackupCmd.ValidArgsFunction = completeBlockStorageID storageBackupGetCmd.ValidArgsFunction = completeBackupID storageBackupUpdateCmd.ValidArgsFunction = completeBackupID storageBackupDeleteCmd.ValidArgsFunction = completeBackupID diff --git a/cmd/storage.restore.go b/cmd/storage.restore.go index ca6eacd..027407e 100644 --- a/cmd/storage.restore.go +++ b/cmd/storage.restore.go @@ -46,6 +46,7 @@ func init() { storageRestoreDeleteCmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt") storageRestoreDeleteCmd.Flags().Bool("dry-run", false, "Validate resource exists without deleting") + storageRestoreCmd.ValidArgsFunction = completeStorageRestoreCreateArgs storageRestoreGetCmd.ValidArgsFunction = completeRestoreID storageRestoreUpdateCmd.ValidArgsFunction = completeRestoreID storageRestoreDeleteCmd.ValidArgsFunction = completeRestoreID @@ -96,6 +97,19 @@ func completeRestoreID(cmd *cobra.Command, args []string, toComplete string) ([] return filterCompletions(completions, toComplete), cobra.ShellCompDirectiveNoFileComp } +// completeStorageRestoreCreateArgs completes the two positional args of +// "storage restore [backup-id] [volume-id]": backup IDs at args[0], block +// storage (volume) IDs at args[1]. +func completeStorageRestoreCreateArgs(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + return completeBackupID(cmd, args, toComplete) + } + if len(args) == 1 { + return completeBlockStorageID(cmd, args, toComplete) + } + return nil, cobra.ShellCompDirectiveNoFileComp +} + var storageRestoreCmd = &cobra.Command{ Use: "restore [backup-id] [volume-id]", Short: "Restore a block storage volume from a backup", diff --git a/docs/getting-started.md b/docs/getting-started.md index 2bb5af9..9089bc6 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -322,14 +322,30 @@ The auto-completion system provides: # 696c9edce63c1af07d60d0c8 BackupSnapshot # ... - # Backups + # Backup create — completes volume ID + acloud storage backup + # Shows: + # 6965a6c3ffc0fd1ef8ba5612 MyVolume + # ... + + # Backup get/update/delete — completes backup ID acloud storage backup get # Shows: # 67649dac8c7bb1c5d7c80631 MyBackup # 67649dac8c7bb1c5d7c80632 DailyBackup # ... - # Restores (hierarchical: backup-id then restore-id) + # Restore create — hierarchical: backup-id then volume-id + acloud storage restore + # First shows backup IDs: + # 67649dac8c7bb1c5d7c80631 MyBackup + # ... + acloud storage restore 67649dac8c7bb1c5d7c80631 + # Then shows volume IDs: + # 6965a6c3ffc0fd1ef8ba5612 MyVolume + # ... + + # Restore get/update/delete — hierarchical: backup-id then restore-id acloud storage restore get # First shows backup IDs: # 67649dac8c7bb1c5d7c80631 MyBackup @@ -340,7 +356,35 @@ The auto-completion system provides: # ... ``` - Auto-completion works with `get`, `update`, and `delete` commands for all resources. + **Database Resources:** + ```bash + # DBaaS database commands — hierarchical: dbaas-id then database-name + acloud database dbaas database list + # Shows DBaaS instance IDs: + # 69455aa70d0972656501d45d my-dbaas + # ... + acloud database dbaas database get 69455aa70d0972656501d45d + # Shows database names in that instance: + # appdb + # reporting + # ... + + # DBaaS user commands — hierarchical: dbaas-id then username + acloud database dbaas user list + # Shows DBaaS instance IDs + acloud database dbaas user get 69455aa70d0972656501d45d + # Shows usernames in that instance + + # DBaaS grant commands — hierarchical: dbaas-id, database-name, grant-id + acloud database dbaas grant list + # Shows DBaaS instance IDs + acloud database dbaas grant list 69455aa70d0972656501d45d + # Shows database names + acloud database dbaas grant get 69455aa70d0972656501d45d appdb + # Shows grant IDs for that database + ``` + + Auto-completion works with `get`, `update`, `delete`, `list`, and `create` commands for all resources. ## Verifying Installation diff --git a/docs/website/docs/resources/database/dbaas.database.md b/docs/website/docs/resources/database/dbaas.database.md index 3a415d6..9ea5745 100644 --- a/docs/website/docs/resources/database/dbaas.database.md +++ b/docs/website/docs/resources/database/dbaas.database.md @@ -146,6 +146,22 @@ acloud database dbaas database delete [--yes] [flags] acloud database dbaas database delete 69455aa70d0972656501d45d "my-database" --yes ``` +## Shell Auto-completion + +Database commands support hierarchical auto-completion: the first TAB completes +DBaaS instance IDs, the second completes database names scoped to that instance. + +```bash +# First argument — shows available DBaaS instance IDs +acloud database dbaas database list +acloud database dbaas database get + +# Second argument — shows database names for the given DBaaS instance +acloud database dbaas database get +acloud database dbaas database update +acloud database dbaas database delete +``` + ## Related Resources - [DBaaS](dbaas.md) - Manage DBaaS instances diff --git a/docs/website/docs/resources/database/dbaas.grant.md b/docs/website/docs/resources/database/dbaas.grant.md index 124f9dd..4fc6106 100644 --- a/docs/website/docs/resources/database/dbaas.grant.md +++ b/docs/website/docs/resources/database/dbaas.grant.md @@ -145,6 +145,25 @@ acloud database dbaas grant create "appdb" --username "restapi" --rol acloud database dbaas grant list "appdb" ``` +## Shell Auto-completion + +Grant commands support hierarchical auto-completion across all three positional +arguments: + +```bash +# First argument — shows available DBaaS instance IDs +acloud database dbaas grant list +acloud database dbaas grant get + +# Second argument — shows database names for the given DBaaS instance +acloud database dbaas grant list +acloud database dbaas grant get + +# Third argument — shows grant IDs for the given DBaaS instance and database +acloud database dbaas grant get +acloud database dbaas grant delete +``` + ## Related Resources - [DBaaS](dbaas.md) - Manage DBaaS instances diff --git a/docs/website/docs/resources/database/dbaas.user.md b/docs/website/docs/resources/database/dbaas.user.md index e106691..58a8387 100644 --- a/docs/website/docs/resources/database/dbaas.user.md +++ b/docs/website/docs/resources/database/dbaas.user.md @@ -156,6 +156,22 @@ acloud database dbaas user delete 69455aa70d0972656501d45d "app-user" --yes - Never share passwords or commit them to version control - Consider using a password manager +## Shell Auto-completion + +User commands support hierarchical auto-completion: the first TAB completes +DBaaS instance IDs, the second completes usernames scoped to that instance. + +```bash +# First argument — shows available DBaaS instance IDs +acloud database dbaas user list +acloud database dbaas user get + +# Second argument — shows usernames for the given DBaaS instance +acloud database dbaas user get +acloud database dbaas user update +acloud database dbaas user delete +``` + ## Related Resources - [DBaaS](dbaas.md) - Manage DBaaS instances diff --git a/docs/website/docs/resources/network/securitygroup.md b/docs/website/docs/resources/network/securitygroup.md index 4d26fe1..0d3665f 100644 --- a/docs/website/docs/resources/network/securitygroup.md +++ b/docs/website/docs/resources/network/securitygroup.md @@ -126,7 +126,20 @@ Security Group 1234567890abcdef deleted successfully! ## Shell Auto-completion -The security group commands support auto-completion for VPC IDs and security group IDs. +Security group commands support hierarchical auto-completion: the first TAB +completes VPC IDs, the second completes security group IDs scoped to the +selected VPC. + +```bash +# First argument — shows available VPC IDs +acloud network securitygroup list +acloud network securitygroup get + +# Second argument — shows security group IDs for the given VPC +acloud network securitygroup get +acloud network securitygroup update +acloud network securitygroup delete +``` ## Best Practices - Use descriptive names and descriptions for security groups. diff --git a/docs/website/docs/resources/network/securityrule.md b/docs/website/docs/resources/network/securityrule.md index 894ec0d..9d30d47 100644 --- a/docs/website/docs/resources/network/securityrule.md +++ b/docs/website/docs/resources/network/securityrule.md @@ -250,13 +250,19 @@ ID STATUS ## Shell Auto-completion -The security rule commands support intelligent auto-completion for security rule IDs: +Security rule commands support hierarchical auto-completion across all three +positional arguments: ```bash -# Enable completion (bash) -source <(acloud completion bash) +# First argument — shows available VPC IDs +acloud network securityrule get +acloud network securityrule list -# Type command and press TAB to see available security rule IDs +# Second argument — shows security group IDs for the given VPC +acloud network securityrule get +acloud network securityrule list + +# Third argument — shows rule IDs for the given VPC and security group acloud network securityrule get acloud network securityrule update acloud network securityrule delete diff --git a/docs/website/docs/resources/network/subnet.md b/docs/website/docs/resources/network/subnet.md index c9cec83..22d5411 100644 --- a/docs/website/docs/resources/network/subnet.md +++ b/docs/website/docs/resources/network/subnet.md @@ -171,7 +171,19 @@ Subnet 1234567890abcdef deleted successfully! ## Shell Auto-completion -The subnet commands support auto-completion for VPC IDs and subnet IDs. +Subnet commands support hierarchical auto-completion: the first TAB completes +VPC IDs, the second completes subnet IDs scoped to the selected VPC. + +```bash +# First argument — shows available VPC IDs +acloud network subnet list +acloud network subnet get + +# Second argument — shows subnet IDs for the given VPC +acloud network subnet get +acloud network subnet update +acloud network subnet delete +``` ## Best Practices - Use descriptive names for subnets based on their purpose. diff --git a/docs/website/docs/resources/network/vpcpeering.md b/docs/website/docs/resources/network/vpcpeering.md index fcb6c66..e6c0037 100644 --- a/docs/website/docs/resources/network/vpcpeering.md +++ b/docs/website/docs/resources/network/vpcpeering.md @@ -134,7 +134,19 @@ VPC Peering 6949666e4d0cdc87949b7204 deleted successfully! ## Shell Auto-completion -The VPC Peering commands support auto-completion for VPC IDs and peering IDs. +VPC Peering commands support hierarchical auto-completion: the first TAB +completes VPC IDs, the second completes peering IDs scoped to the selected VPC. + +```bash +# First argument — shows available VPC IDs +acloud network vpcpeering list +acloud network vpcpeering get + +# Second argument — shows peering IDs for the given VPC +acloud network vpcpeering get +acloud network vpcpeering update +acloud network vpcpeering delete +``` ## Best Practices - Use descriptive names for peering connections. diff --git a/docs/website/docs/resources/network/vpcpeeringroute.md b/docs/website/docs/resources/network/vpcpeeringroute.md index fa0daf6..52639d6 100644 --- a/docs/website/docs/resources/network/vpcpeeringroute.md +++ b/docs/website/docs/resources/network/vpcpeeringroute.md @@ -217,13 +217,19 @@ ID STATUS ## Shell Auto-completion -The VPC Peering Route commands support intelligent auto-completion for route IDs: +VPC Peering Route commands support hierarchical auto-completion across all three +positional arguments: ```bash -# Enable completion (bash) -source <(acloud completion bash) +# First argument — shows available VPC IDs +acloud network vpcpeeringroute get +acloud network vpcpeeringroute list -# Type command and press TAB to see available route IDs +# Second argument — shows peering IDs for the given VPC +acloud network vpcpeeringroute get +acloud network vpcpeeringroute list + +# Third argument — shows route IDs for the given VPC and peering acloud network vpcpeeringroute get acloud network vpcpeeringroute update acloud network vpcpeeringroute delete diff --git a/docs/website/docs/resources/storage/backup.md b/docs/website/docs/resources/storage/backup.md index 792b5d8..20b0453 100644 --- a/docs/website/docs/resources/storage/backup.md +++ b/docs/website/docs/resources/storage/backup.md @@ -32,7 +32,7 @@ acloud storage backup --name [flags] ### Arguments -- `volume-id` - The ID of the block storage volume to backup +- `volume-id` - The ID of the block storage volume to backup (supports auto-completion) ### Required Flags diff --git a/docs/website/docs/resources/storage/restore.md b/docs/website/docs/resources/storage/restore.md index 4bddb1d..dd7b4ae 100644 --- a/docs/website/docs/resources/storage/restore.md +++ b/docs/website/docs/resources/storage/restore.md @@ -33,8 +33,20 @@ acloud storage restore --name [flags] ### Arguments -- `backup-id` - The ID of the backup to restore from -- `volume-id` - The ID of the target volume (will be overwritten) +- `backup-id` - The ID of the backup to restore from (supports auto-completion) +- `volume-id` - The ID of the target volume (supports auto-completion; will be overwritten) + +### Auto-completion + +The create command supports hierarchical auto-completion: + +```bash +# First argument — shows available backup IDs +acloud storage restore + +# Second argument — shows volume IDs once a backup is selected +acloud storage restore +``` ### Required Flags