Skip to content

Commit 2a76f09

Browse files
authored
Merge pull request #73 from ovh/dev/aamstutz/pci-distant-snapshot
feat: Add cloud instance snapshots commands
2 parents 2455071 + 0f7f987 commit 2a76f09

9 files changed

Lines changed: 209 additions & 10 deletions

doc/ovhcloud_cloud_instance_create.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ ovhcloud cloud instance create <region (e.g. GRA9, BHS5, SBG3)> [flags]
6161
--backup-cron string Autobackup Unix Cron pattern (eg: '0 0 * * *')
6262
--backup-rotation int Number of backups to keep
6363
--billing-period string Billing period (hourly, monthly), default is hourly (default "hourly")
64-
--boot-from.image string Image ID to boot from (you can use 'ovhcloud cloud reference list-images' to get the image ID)
64+
--boot-from.image string Image ID to boot from (you can use 'ovhcloud cloud reference list-images' to get the image ID or 'ovhcloud cloud instance snapshot ls' to get the snapshots)
6565
--boot-from.volume string Volume ID to boot from
6666
--bulk int Number of instances to create
6767
--editor Use a text editor to define parameters

doc/ovhcloud_cloud_instance_snapshot.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,7 @@ Manage snapshots of the given instance
3232
* [ovhcloud cloud instance](ovhcloud_cloud_instance.md) - Manage instances in the given cloud project
3333
* [ovhcloud cloud instance snapshot abort](ovhcloud_cloud_instance_snapshot_abort.md) - Abort the snapshot creation of the given instance
3434
* [ovhcloud cloud instance snapshot create](ovhcloud_cloud_instance_snapshot_create.md) - Create a snapshot of the given instance
35+
* [ovhcloud cloud instance snapshot delete](ovhcloud_cloud_instance_snapshot_delete.md) - Delete a specific instance snapshot in the current cloud project
36+
* [ovhcloud cloud instance snapshot get](ovhcloud_cloud_instance_snapshot_get.md) - Get a specific instance snapshot in the current cloud project
37+
* [ovhcloud cloud instance snapshot list](ovhcloud_cloud_instance_snapshot_list.md) - List all instance snapshots in the current cloud project
3538

doc/ovhcloud_cloud_instance_snapshot_create.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ ovhcloud cloud instance snapshot create <instance_id> <snapshot_name> [flags]
99
### Options
1010

1111
```
12-
-h, --help help for create
12+
--distant-region-name string Name of the distant region (for cross region snapshot)
13+
--distant-snapshot-name string Name of the snapshot in the distant region (for cross region snapshot)
14+
-h, --help help for create
1315
```
1416

1517
### Options inherited from parent commands
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
## ovhcloud cloud instance snapshot delete
2+
3+
Delete a specific instance snapshot in the current cloud project
4+
5+
```
6+
ovhcloud cloud instance snapshot delete <snapshot_id> [flags]
7+
```
8+
9+
### Options
10+
11+
```
12+
-h, --help help for delete
13+
```
14+
15+
### Options inherited from parent commands
16+
17+
```
18+
--cloud-project string Cloud project ID
19+
-d, --debug Activate debug mode (will log all HTTP requests details)
20+
-f, --format string Output value according to given format (expression using https://github.com/PaesslerAG/gval syntax)
21+
Examples:
22+
--format 'id' (to extract a single field)
23+
--format 'nested.field.subfield' (to extract a nested field)
24+
--format '[id, 'name']' (to extract multiple fields as an array)
25+
--format '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
26+
--format 'name+","+type' (to extract and concatenate fields in a string)
27+
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
28+
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
29+
-i, --interactive Interactive output
30+
-j, --json Output in JSON
31+
-y, --yaml Output in YAML
32+
```
33+
34+
### SEE ALSO
35+
36+
* [ovhcloud cloud instance snapshot](ovhcloud_cloud_instance_snapshot.md) - Manage snapshots of the given instance
37+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
## ovhcloud cloud instance snapshot get
2+
3+
Get a specific instance snapshot in the current cloud project
4+
5+
```
6+
ovhcloud cloud instance snapshot get <snapshot_id> [flags]
7+
```
8+
9+
### Options
10+
11+
```
12+
-h, --help help for get
13+
```
14+
15+
### Options inherited from parent commands
16+
17+
```
18+
--cloud-project string Cloud project ID
19+
-d, --debug Activate debug mode (will log all HTTP requests details)
20+
-f, --format string Output value according to given format (expression using https://github.com/PaesslerAG/gval syntax)
21+
Examples:
22+
--format 'id' (to extract a single field)
23+
--format 'nested.field.subfield' (to extract a nested field)
24+
--format '[id, 'name']' (to extract multiple fields as an array)
25+
--format '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
26+
--format 'name+","+type' (to extract and concatenate fields in a string)
27+
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
28+
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
29+
-i, --interactive Interactive output
30+
-j, --json Output in JSON
31+
-y, --yaml Output in YAML
32+
```
33+
34+
### SEE ALSO
35+
36+
* [ovhcloud cloud instance snapshot](ovhcloud_cloud_instance_snapshot.md) - Manage snapshots of the given instance
37+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
## ovhcloud cloud instance snapshot list
2+
3+
List all instance snapshots in the current cloud project
4+
5+
```
6+
ovhcloud cloud instance snapshot list [flags]
7+
```
8+
9+
### Options
10+
11+
```
12+
--filter stringArray Filter results by any property using https://github.com/PaesslerAG/gval syntax
13+
Examples:
14+
--filter 'state="running"'
15+
--filter 'name=~"^my.*"'
16+
--filter 'nested.property.subproperty>10'
17+
--filter 'startDate>="2023-12-01"'
18+
--filter 'name=~"something" && nbField>10'
19+
-h, --help help for list
20+
```
21+
22+
### Options inherited from parent commands
23+
24+
```
25+
--cloud-project string Cloud project ID
26+
-d, --debug Activate debug mode (will log all HTTP requests details)
27+
-f, --format string Output value according to given format (expression using https://github.com/PaesslerAG/gval syntax)
28+
Examples:
29+
--format 'id' (to extract a single field)
30+
--format 'nested.field.subfield' (to extract a nested field)
31+
--format '[id, 'name']' (to extract multiple fields as an array)
32+
--format '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
33+
--format 'name+","+type' (to extract and concatenate fields in a string)
34+
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
35+
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
36+
-i, --interactive Interactive output
37+
-j, --json Output in JSON
38+
-y, --yaml Output in YAML
39+
```
40+
41+
### SEE ALSO
42+
43+
* [ovhcloud cloud instance snapshot](ovhcloud_cloud_instance_snapshot.md) - Manage snapshots of the given instance
44+

doc/ovhcloud_cloud_rancher_edit.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ ovhcloud cloud rancher edit <rancher_id> [flags]
99
### Options
1010

1111
```
12+
--editor Use a text editor to define parameters
1213
-h, --help help for edit
1314
--iam-auth-enabled Allow Rancher to use identities managed by OVHcloud IAM (Identity and Access Management) to control access
1415
--name string Name of the managed Rancher service

internal/cmd/cloud_instance.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ There are three ways to define the creation parameters:
7575
instanceCreateCmd.Flags().StringVar(&cloud.InstanceCreationParameters.Name, "name", "", "Instance name")
7676

7777
// Boot options
78-
instanceCreateCmd.Flags().StringVar(&cloud.InstanceCreationParameters.BootFrom.ImageID, "boot-from.image", "", "Image ID to boot from (you can use 'ovhcloud cloud reference list-images' to get the image ID)")
78+
instanceCreateCmd.Flags().StringVar(&cloud.InstanceCreationParameters.BootFrom.ImageID, "boot-from.image", "", "Image ID to boot from (you can use 'ovhcloud cloud reference list-images' to get the image ID or 'ovhcloud cloud instance snapshot ls' to get the snapshots)")
7979
instanceCreateCmd.Flags().StringVar(&cloud.InstanceCreationParameters.BootFrom.VolumeID, "boot-from.volume", "", "Volume ID to boot from")
8080
instanceCreateCmd.MarkFlagsMutuallyExclusive("boot-from.image", "boot-from.volume")
8181

@@ -367,12 +367,16 @@ There are three ways to define the installation parameters:
367367
}
368368
instanceCmd.AddCommand(snapshotCmd)
369369

370-
snapshotCmd.AddCommand(&cobra.Command{
370+
snapshotCreateCmd := &cobra.Command{
371371
Use: "create <instance_id> <snapshot_name>",
372372
Short: "Create a snapshot of the given instance",
373373
Run: cloud.CreateInstanceSnapshot,
374374
Args: cobra.ExactArgs(2),
375-
})
375+
}
376+
snapshotCreateCmd.Flags().StringVar(&cloud.InstanceSnapshotSpec.DistantSnapshotName, "distant-snapshot-name", "", "Name of the snapshot in the distant region (for cross region snapshot)")
377+
snapshotCreateCmd.Flags().StringVar(&cloud.InstanceSnapshotSpec.DistantRegionName, "distant-region-name", "", "Name of the distant region (for cross region snapshot)")
378+
snapshotCreateCmd.MarkFlagsRequiredTogether("distant-snapshot-name", "distant-region-name")
379+
snapshotCmd.AddCommand(snapshotCreateCmd)
376380

377381
snapshotCmd.AddCommand(&cobra.Command{
378382
Use: "abort <instance_id>",
@@ -381,5 +385,27 @@ There are three ways to define the installation parameters:
381385
Args: cobra.ExactArgs(1),
382386
})
383387

388+
snapshotCmd.AddCommand(withFilterFlag(&cobra.Command{
389+
Use: "list",
390+
Aliases: []string{"ls"},
391+
Short: "List all instance snapshots in the current cloud project",
392+
Run: cloud.ListInstanceSnapshots,
393+
Args: cobra.NoArgs,
394+
}))
395+
396+
snapshotCmd.AddCommand(&cobra.Command{
397+
Use: "get <snapshot_id>",
398+
Short: "Get a specific instance snapshot in the current cloud project",
399+
Run: cloud.GetInstanceSnapshot,
400+
Args: cobra.ExactArgs(1),
401+
})
402+
403+
snapshotCmd.AddCommand(&cobra.Command{
404+
Use: "delete <snapshot_id>",
405+
Short: "Delete a specific instance snapshot in the current cloud project",
406+
Run: cloud.DeleteInstanceSnapshot,
407+
Args: cobra.ExactArgs(1),
408+
})
409+
384410
cloudCmd.AddCommand(instanceCmd)
385411
}

internal/services/cloud/cloud_instance.go

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,12 @@ var (
115115
} `json:"sshKeyCreate,omitzero"`
116116
UserData string `json:"userData,omitempty"`
117117
}{}
118+
119+
InstanceSnapshotSpec struct {
120+
SnapshotName string `json:"snapshotName,omitempty"`
121+
DistantSnapshotName string `json:"distantSnapshotName,omitempty"`
122+
DistantRegionName string `json:"distantRegionName,omitempty"`
123+
}
118124
)
119125

120126
func ListInstances(_ *cobra.Command, _ []string) {
@@ -807,19 +813,25 @@ func CreateInstanceSnapshot(_ *cobra.Command, args []string) {
807813
return
808814
}
809815

810-
body := map[string]any{
811-
"snapshotName": args[1],
816+
// Fetch instance details to get its region
817+
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s", projectID, url.PathEscape(args[0]))
818+
var instance map[string]any
819+
if err := httpLib.Client.Get(endpoint, &instance); err != nil {
820+
display.OutputError(&flags.OutputFormatConfig, "failed to fetch instance details: %s", err)
821+
return
812822
}
823+
region := instance["region"].(string)
813824

814-
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s/snapshot", projectID, url.PathEscape(args[0]))
825+
InstanceSnapshotSpec.SnapshotName = args[1]
815826

827+
endpoint = fmt.Sprintf("/cloud/project/%s/region/%s/instance/%s/snapshot", projectID, url.PathEscape(region), url.PathEscape(args[0]))
816828
var response map[string]any
817-
if err := httpLib.Client.Post(endpoint, body, &response); err != nil {
829+
if err := httpLib.Client.Post(endpoint, InstanceSnapshotSpec, &response); err != nil {
818830
display.OutputError(&flags.OutputFormatConfig, "error creating snapshot for instance %q: %s", args[0], err)
819831
return
820832
}
821833

822-
display.OutputInfo(&flags.OutputFormatConfig, response, "✅ Snapshot created successfully with ID: %s", response["snapshotId"])
834+
display.OutputInfo(&flags.OutputFormatConfig, response, "✅ Snapshot created successfully with ID: %s", response["imageId"])
823835
}
824836

825837
func AbortInstanceSnapshot(_ *cobra.Command, args []string) {
@@ -847,3 +859,40 @@ func AbortInstanceSnapshot(_ *cobra.Command, args []string) {
847859

848860
display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ Snapshot aborted successfully")
849861
}
862+
863+
func ListInstanceSnapshots(_ *cobra.Command, _ []string) {
864+
projectID, err := getConfiguredCloudProject()
865+
if err != nil {
866+
display.OutputError(&flags.OutputFormatConfig, "%s", err)
867+
return
868+
}
869+
870+
common.ManageListRequestNoExpand(fmt.Sprintf("/cloud/project/%s/snapshot", projectID), []string{"id", "name", "type", "status", "region"}, flags.GenericFilters)
871+
}
872+
873+
func GetInstanceSnapshot(_ *cobra.Command, args []string) {
874+
projectID, err := getConfiguredCloudProject()
875+
if err != nil {
876+
display.OutputError(&flags.OutputFormatConfig, "%s", err)
877+
return
878+
}
879+
880+
common.ManageObjectRequest(fmt.Sprintf("/cloud/project/%s/snapshot", projectID), args[0], "")
881+
}
882+
883+
func DeleteInstanceSnapshot(_ *cobra.Command, args []string) {
884+
projectID, err := getConfiguredCloudProject()
885+
if err != nil {
886+
display.OutputError(&flags.OutputFormatConfig, "%s", err)
887+
return
888+
}
889+
890+
endpoint := fmt.Sprintf("/cloud/project/%s/snapshot/%s", projectID, url.PathEscape(args[0]))
891+
892+
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
893+
display.OutputError(&flags.OutputFormatConfig, "error deleting snapshot %q: %s", args[0], err)
894+
return
895+
}
896+
897+
display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ Snapshot successfully deleted")
898+
}

0 commit comments

Comments
 (0)