From 0f1cd5e78ed4b84c699d571afa874c51dcd95acc Mon Sep 17 00:00:00 2001 From: Thomas Leterme Date: Fri, 4 Sep 2026 12:58:45 +0000 Subject: [PATCH 1/5] fix: remove ACL Signed-off-by: Thomas Leterme --- internal/services/cloud/cloud_storage_file.go | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/internal/services/cloud/cloud_storage_file.go b/internal/services/cloud/cloud_storage_file.go index a8325bae..234434bf 100644 --- a/internal/services/cloud/cloud_storage_file.go +++ b/internal/services/cloud/cloud_storage_file.go @@ -37,12 +37,7 @@ var ( "resourceStatus status", } shareSnapshotColumnsToDisplay = []string{"id", "name", "shareId", "size", "status"} - shareACLColumnsToDisplay = []string{ - "id", - "currentState.accessLevel accessLevel", - "currentState.accessTo accessTo", - "resourceStatus status", - } + shareACLColumnsToDisplay = []string{"id", "accessLevel", "accessTo", "accessType", "status"} //go:embed templates/cloud_storage_file_share.tmpl shareTemplate string @@ -104,10 +99,8 @@ var ( } ShareACLSpec struct { - TargetSpec struct { - AccessLevel string `json:"accessLevel,omitempty"` - AccessTo string `json:"accessTo,omitempty"` - } `json:"targetSpec"` + AccessLevel string `json:"accessLevel,omitempty"` + AccessTo string `json:"accessTo,omitempty"` } ShareRegion string @@ -314,26 +307,36 @@ func DeleteShare(_ *cobra.Command, args []string) { // ACL commands func ListShareACLs(_ *cobra.Command, args []string) { - projectID, err := getConfiguredCloudProject() + endpoint, _, err := findShare(args[0]) if err != nil { display.OutputError(&flags.OutputFormatConfig, "%s", err) return } - endpoint := fmt.Sprintf("%s/%s/acl", shareV2Endpoint(projectID), url.PathEscape(args[0])) - common.ManageListRequestNoExpand(endpoint, shareACLColumnsToDisplay, flags.GenericFilters) + var acls []map[string]any + if err := httpLib.Client.Get(endpoint+"/acl", &acls); err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to fetch share ACLs: %s", err) + return + } + + acls, err = filtersLib.FilterLines(acls, flags.GenericFilters) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to filter results: %s", err) + return + } + + display.RenderTable(acls, shareACLColumnsToDisplay, &flags.OutputFormatConfig) } func GetShareACL(_ *cobra.Command, args []string) { - projectID, err := getConfiguredCloudProject() + endpoint, _, err := findShare(args[0]) if err != nil { display.OutputError(&flags.OutputFormatConfig, "%s", err) return } - endpoint := fmt.Sprintf("%s/%s/acl/%s", shareV2Endpoint(projectID), url.PathEscape(args[0]), url.PathEscape(args[1])) var acl map[string]any - if err := httpLib.Client.Get(endpoint, &acl); err != nil { + if err := httpLib.Client.Get(fmt.Sprintf("%s/acl/%s", endpoint, url.PathEscape(args[1])), &acl); err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to fetch share ACL: %s", err) return } @@ -342,15 +345,14 @@ func GetShareACL(_ *cobra.Command, args []string) { } func CreateShareACL(_ *cobra.Command, args []string) { - projectID, err := getConfiguredCloudProject() + endpoint, _, err := findShare(args[0]) if err != nil { display.OutputError(&flags.OutputFormatConfig, "%s", err) return } - endpoint := fmt.Sprintf("%s/%s/acl", shareV2Endpoint(projectID), url.PathEscape(args[0])) var response map[string]any - if err := httpLib.Client.Post(endpoint, ShareACLSpec, &response); err != nil { + if err := httpLib.Client.Post(endpoint+"/acl", ShareACLSpec, &response); err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to create share ACL: %s", err) return } @@ -359,14 +361,13 @@ func CreateShareACL(_ *cobra.Command, args []string) { } func DeleteShareACL(_ *cobra.Command, args []string) { - projectID, err := getConfiguredCloudProject() + endpoint, _, err := findShare(args[0]) if err != nil { display.OutputError(&flags.OutputFormatConfig, "%s", err) return } - endpoint := fmt.Sprintf("%s/%s/acl/%s", shareV2Endpoint(projectID), url.PathEscape(args[0]), url.PathEscape(args[1])) - if err := httpLib.Client.Delete(endpoint, nil); err != nil { + if err := httpLib.Client.Delete(fmt.Sprintf("%s/acl/%s", endpoint, url.PathEscape(args[1])), nil); err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to delete share ACL: %s", err) return } From 8fcfd05ca91f529210034ae6f5db2748238fafbb Mon Sep 17 00:00:00 2001 From: Thomas Leterme Date: Fri, 4 Sep 2026 12:16:32 +0000 Subject: [PATCH 2/5] feat(ovhcloud-cli): migrate ACL (file storage share) endpoints to v2 Signed-off-by: Thomas Leterme --- internal/services/cloud/cloud_storage_file.go | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/internal/services/cloud/cloud_storage_file.go b/internal/services/cloud/cloud_storage_file.go index 234434bf..a8325bae 100644 --- a/internal/services/cloud/cloud_storage_file.go +++ b/internal/services/cloud/cloud_storage_file.go @@ -37,7 +37,12 @@ var ( "resourceStatus status", } shareSnapshotColumnsToDisplay = []string{"id", "name", "shareId", "size", "status"} - shareACLColumnsToDisplay = []string{"id", "accessLevel", "accessTo", "accessType", "status"} + shareACLColumnsToDisplay = []string{ + "id", + "currentState.accessLevel accessLevel", + "currentState.accessTo accessTo", + "resourceStatus status", + } //go:embed templates/cloud_storage_file_share.tmpl shareTemplate string @@ -99,8 +104,10 @@ var ( } ShareACLSpec struct { - AccessLevel string `json:"accessLevel,omitempty"` - AccessTo string `json:"accessTo,omitempty"` + TargetSpec struct { + AccessLevel string `json:"accessLevel,omitempty"` + AccessTo string `json:"accessTo,omitempty"` + } `json:"targetSpec"` } ShareRegion string @@ -307,36 +314,26 @@ func DeleteShare(_ *cobra.Command, args []string) { // ACL commands func ListShareACLs(_ *cobra.Command, args []string) { - endpoint, _, err := findShare(args[0]) + projectID, err := getConfiguredCloudProject() if err != nil { display.OutputError(&flags.OutputFormatConfig, "%s", err) return } - var acls []map[string]any - if err := httpLib.Client.Get(endpoint+"/acl", &acls); err != nil { - display.OutputError(&flags.OutputFormatConfig, "failed to fetch share ACLs: %s", err) - return - } - - acls, err = filtersLib.FilterLines(acls, flags.GenericFilters) - if err != nil { - display.OutputError(&flags.OutputFormatConfig, "failed to filter results: %s", err) - return - } - - display.RenderTable(acls, shareACLColumnsToDisplay, &flags.OutputFormatConfig) + endpoint := fmt.Sprintf("%s/%s/acl", shareV2Endpoint(projectID), url.PathEscape(args[0])) + common.ManageListRequestNoExpand(endpoint, shareACLColumnsToDisplay, flags.GenericFilters) } func GetShareACL(_ *cobra.Command, args []string) { - endpoint, _, err := findShare(args[0]) + projectID, err := getConfiguredCloudProject() if err != nil { display.OutputError(&flags.OutputFormatConfig, "%s", err) return } + endpoint := fmt.Sprintf("%s/%s/acl/%s", shareV2Endpoint(projectID), url.PathEscape(args[0]), url.PathEscape(args[1])) var acl map[string]any - if err := httpLib.Client.Get(fmt.Sprintf("%s/acl/%s", endpoint, url.PathEscape(args[1])), &acl); err != nil { + if err := httpLib.Client.Get(endpoint, &acl); err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to fetch share ACL: %s", err) return } @@ -345,14 +342,15 @@ func GetShareACL(_ *cobra.Command, args []string) { } func CreateShareACL(_ *cobra.Command, args []string) { - endpoint, _, err := findShare(args[0]) + projectID, err := getConfiguredCloudProject() if err != nil { display.OutputError(&flags.OutputFormatConfig, "%s", err) return } + endpoint := fmt.Sprintf("%s/%s/acl", shareV2Endpoint(projectID), url.PathEscape(args[0])) var response map[string]any - if err := httpLib.Client.Post(endpoint+"/acl", ShareACLSpec, &response); err != nil { + if err := httpLib.Client.Post(endpoint, ShareACLSpec, &response); err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to create share ACL: %s", err) return } @@ -361,13 +359,14 @@ func CreateShareACL(_ *cobra.Command, args []string) { } func DeleteShareACL(_ *cobra.Command, args []string) { - endpoint, _, err := findShare(args[0]) + projectID, err := getConfiguredCloudProject() if err != nil { display.OutputError(&flags.OutputFormatConfig, "%s", err) return } - if err := httpLib.Client.Delete(fmt.Sprintf("%s/acl/%s", endpoint, url.PathEscape(args[1])), nil); err != nil { + endpoint := fmt.Sprintf("%s/%s/acl/%s", shareV2Endpoint(projectID), url.PathEscape(args[0]), url.PathEscape(args[1])) + if err := httpLib.Client.Delete(endpoint, nil); err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to delete share ACL: %s", err) return } From 44b5a56374631032ff1a2911397ec53fb9817ac6 Mon Sep 17 00:00:00 2001 From: Thomas Leterme Date: Mon, 7 Sep 2026 06:59:48 +0000 Subject: [PATCH 3/5] feat(ovhcloud-cli): migrate snapshot file storage to API v2 Signed-off-by: Thomas Leterme --- internal/assets/api-schemas/cloud_v2.json | 50 +++++++--- internal/cmd/cloud_storage_file.go | 4 +- internal/services/cloud/cloud_storage_file.go | 91 ++++++++++++++----- .../cloud_storage_file_share_snapshot.tmpl | 12 +-- 4 files changed, 110 insertions(+), 47 deletions(-) diff --git a/internal/assets/api-schemas/cloud_v2.json b/internal/assets/api-schemas/cloud_v2.json index 96101ee0..cd9127e9 100644 --- a/internal/assets/api-schemas/cloud_v2.json +++ b/internal/assets/api-schemas/cloud_v2.json @@ -9526,10 +9526,17 @@ "nullable": true, "readOnly": true }, - "shareId": { - "type": "string", - "description": "ID of the parent file storage share", - "format": "uuid", + "share": { + "type": "object", + "description": "Parent file storage share", + "properties": { + "id": { + "type": "string", + "description": "ID of the parent file storage share", + "format": "uuid", + "readOnly": true + } + }, "readOnly": true }, "shareProto": { @@ -9546,7 +9553,7 @@ "description": "Size of the parent share in GB at the time of the snapshot", "readOnly": true }, - "snapshotSize": { + "size": { "type": "integer", "description": "Size of the snapshot in GB", "readOnly": true @@ -9575,15 +9582,24 @@ "description": "Desired snapshot name", "nullable": true }, - "shareId": { - "type": "string", - "description": "ID of the parent file storage share", - "format": "uuid" + "share": { + "type": "object", + "description": "Parent file storage share", + "properties": { + "id": { + "type": "string", + "description": "ID of the parent file storage share", + "format": "uuid" + } + }, + "required": [ + "id" + ] } }, "required": [ "location", - "shareId" + "share" ] }, "publicCloud.storage.file.FileStorageSnapshotUpdate": { @@ -27243,7 +27259,9 @@ "region": "GRA1" }, "name": "my-share-snapshot", - "shareId": "d1e2f3a4-b5c6-7890-abcd-ef1234567890" + "share": { + "id": "d1e2f3a4-b5c6-7890-abcd-ef1234567890" + } } }, "summary": "Create a snapshot of a file storage" @@ -27544,10 +27562,12 @@ "region": "GRA1" }, "name": "my-share-snapshot", - "shareId": "d1e2f3a4-b5c6-7890-abcd-ef1234567890", + "share": { + "id": "d1e2f3a4-b5c6-7890-abcd-ef1234567890" + }, "shareProto": "NFS", "shareSize": 100, - "snapshotSize": 1 + "size": 1 }, "currentTasks": null, "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", @@ -27558,7 +27578,9 @@ "region": "GRA1" }, "name": "my-share-snapshot", - "shareId": "d1e2f3a4-b5c6-7890-abcd-ef1234567890" + "share": { + "id": "d1e2f3a4-b5c6-7890-abcd-ef1234567890" + } }, "updatedAt": "2026-02-10T15:05:00Z" }, diff --git a/internal/cmd/cloud_storage_file.go b/internal/cmd/cloud_storage_file.go index c6e7940f..3f43ffc2 100644 --- a/internal/cmd/cloud_storage_file.go +++ b/internal/cmd/cloud_storage_file.go @@ -176,8 +176,8 @@ func initCloudStorageFileCommand(cloudCmd *cobra.Command) { Run: cloud.CreateShareSnapshot, Args: cobra.ExactArgs(1), } - snapshotCreateCmd.Flags().StringVar(&cloud.ShareSnapshotSpec.Description, "description", "", "Snapshot description") - snapshotCreateCmd.Flags().StringVar(&cloud.ShareSnapshotSpec.Name, "name", "", "Snapshot name") + snapshotCreateCmd.Flags().StringVar(&cloud.ShareSnapshotSpec.TargetSpec.Description, "description", "", "Snapshot description") + snapshotCreateCmd.Flags().StringVar(&cloud.ShareSnapshotSpec.TargetSpec.Name, "name", "", "Snapshot name") snapshotCmd.AddCommand(snapshotCreateCmd) snapshotCmd.AddCommand(&cobra.Command{ diff --git a/internal/services/cloud/cloud_storage_file.go b/internal/services/cloud/cloud_storage_file.go index a8325bae..f0361602 100644 --- a/internal/services/cloud/cloud_storage_file.go +++ b/internal/services/cloud/cloud_storage_file.go @@ -12,7 +12,6 @@ import ( "github.com/ovh/ovhcloud-cli/internal/assets" "github.com/ovh/ovhcloud-cli/internal/display" - filtersLib "github.com/ovh/ovhcloud-cli/internal/filters" "github.com/ovh/ovhcloud-cli/internal/flags" httpLib "github.com/ovh/ovhcloud-cli/internal/http" "github.com/ovh/ovhcloud-cli/internal/services/common" @@ -36,8 +35,14 @@ var ( "targetSpec.subnet.id subnetId", "resourceStatus status", } - shareSnapshotColumnsToDisplay = []string{"id", "name", "shareId", "size", "status"} - shareACLColumnsToDisplay = []string{ + shareSnapshotColumnsToDisplay = []string{ + "id", + "targetSpec.name name", + "targetSpec.share.id shareId", + "targetSpec.location.region region", + "resourceStatus status", + } + shareACLColumnsToDisplay = []string{ "id", "currentState.accessLevel accessLevel", "currentState.accessTo accessTo", @@ -99,8 +104,16 @@ var ( } ShareSnapshotSpec struct { - Description string `json:"description,omitempty"` - Name string `json:"name,omitempty"` + TargetSpec struct { + Description string `json:"description,omitempty"` + Name string `json:"name,omitempty"` + Share struct { + Id string `json:"id,omitempty"` + } `json:"share,omitzero"` + Location struct { + Region string `json:"region,omitempty"` + } `json:"location,omitzero"` + } `json:"targetSpec"` } ShareACLSpec struct { @@ -311,6 +324,30 @@ func DeleteShare(_ *cobra.Command, args []string) { display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ Share %s deleted successfully", args[0]) } +func getShareRegionV2(projectID, shareID string) (string, error) { + var share map[string]any + endpoint := fmt.Sprintf("%s/%s", shareV2Endpoint(projectID), url.PathEscape(shareID)) + if err := httpLib.Client.Get(endpoint, &share); err != nil { + return "", fmt.Errorf("failed to fetch share %s: %w", shareID, err) + } + + for _, state := range []string{"targetSpec", "currentState"} { + stateValue, ok := share[state].(map[string]any) + if !ok { + continue + } + location, ok := stateValue["location"].(map[string]any) + if !ok { + continue + } + if region, ok := location["region"].(string); ok && region != "" { + return region, nil + } + } + + return "", fmt.Errorf("share %s has no region", shareID) +} + // ACL commands func ListShareACLs(_ *cobra.Command, args []string) { @@ -377,36 +414,31 @@ func DeleteShareACL(_ *cobra.Command, args []string) { // Snapshot commands func ListShareSnapshots(_ *cobra.Command, args []string) { - endpoint, _, err := findShare(args[0]) + projectID, err := getConfiguredCloudProject() if err != nil { display.OutputError(&flags.OutputFormatConfig, "%s", err) return } - var snapshots []map[string]any - if err := httpLib.Client.Get(endpoint+"/snapshot", &snapshots); err != nil { - display.OutputError(&flags.OutputFormatConfig, "failed to fetch share snapshots: %s", err) - return - } - - snapshots, err = filtersLib.FilterLines(snapshots, flags.GenericFilters) - if err != nil { - display.OutputError(&flags.OutputFormatConfig, "failed to filter results: %s", err) - return - } - - display.RenderTable(snapshots, shareSnapshotColumnsToDisplay, &flags.OutputFormatConfig) + filters := append([]string{}, flags.GenericFilters...) + filters = append(filters, fmt.Sprintf("targetSpec.share.id==%q", args[0])) + common.ManageListRequestNoExpand( + fmt.Sprintf("/v2/publicCloud/project/%s/storage/file/snapshot", projectID), + shareSnapshotColumnsToDisplay, + filters, + ) } func GetShareSnapshot(_ *cobra.Command, args []string) { - endpoint, _, err := findShare(args[0]) + projectID, err := getConfiguredCloudProject() if err != nil { display.OutputError(&flags.OutputFormatConfig, "%s", err) return } + endpoint := fmt.Sprintf("/v2/publicCloud/project/%s/storage/file/snapshot/%s", projectID, url.PathEscape(args[1])) var snapshot map[string]any - if err := httpLib.Client.Get(fmt.Sprintf("%s/snapshot/%s", endpoint, url.PathEscape(args[1])), &snapshot); err != nil { + if err := httpLib.Client.Get(endpoint, &snapshot); err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to fetch share snapshot: %s", err) return } @@ -415,14 +447,22 @@ func GetShareSnapshot(_ *cobra.Command, args []string) { } func CreateShareSnapshot(_ *cobra.Command, args []string) { - endpoint, _, err := findShare(args[0]) + projectID, err := getConfiguredCloudProject() if err != nil { display.OutputError(&flags.OutputFormatConfig, "%s", err) return } + region, err := getShareRegionV2(projectID, args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + ShareSnapshotSpec.TargetSpec.Share.Id = args[0] + ShareSnapshotSpec.TargetSpec.Location.Region = region + endpoint := fmt.Sprintf("/v2/publicCloud/project/%s/storage/file/snapshot", projectID) var response map[string]any - if err := httpLib.Client.Post(endpoint+"/snapshot", ShareSnapshotSpec, &response); err != nil { + if err := httpLib.Client.Post(endpoint, ShareSnapshotSpec, &response); err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to create share snapshot: %s", err) return } @@ -431,13 +471,14 @@ func CreateShareSnapshot(_ *cobra.Command, args []string) { } func DeleteShareSnapshot(_ *cobra.Command, args []string) { - endpoint, _, err := findShare(args[0]) + projectID, err := getConfiguredCloudProject() if err != nil { display.OutputError(&flags.OutputFormatConfig, "%s", err) return } - if err := httpLib.Client.Delete(fmt.Sprintf("%s/snapshot/%s", endpoint, url.PathEscape(args[1])), nil); err != nil { + endpoint := fmt.Sprintf("/v2/publicCloud/project/%s/storage/file/snapshot/%s", projectID, url.PathEscape(args[1])) + if err := httpLib.Client.Delete(endpoint, nil); err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to delete share snapshot: %s", err) return } diff --git a/internal/services/cloud/templates/cloud_storage_file_share_snapshot.tmpl b/internal/services/cloud/templates/cloud_storage_file_share_snapshot.tmpl index 4fefa633..75d60868 100644 --- a/internal/services/cloud/templates/cloud_storage_file_share_snapshot.tmpl +++ b/internal/services/cloud/templates/cloud_storage_file_share_snapshot.tmpl @@ -5,12 +5,12 @@ _{{index .Result "description"}}_ ## General information -**Name**: {{index .Result "name"}} -**Share ID**: {{index .Result "shareId"}} -**Share protocol**: {{index .Result "shareProtocol"}} -**Share size**: {{index .Result "shareSize"}}GB -**Size**: {{index .Result "size"}}GB -**Status**: {{index .Result "status"}} +**Name**: {{index .Result "currentState" "name"}} +**Share ID**: {{index .Result "currentState" "share" "id"}} +**Share protocol**: {{index .Result "currentState" "shareProto"}} +**Share size**: {{index .Result "currentState" "shareSize"}}GB +**Size**: {{index .Result "currentState" "snapshotSize"}}GB +**Status**: {{index .Result "resourceStatus"}} **Created at**: {{index .Result "createdAt"}} 💡 Use option -o json or -o yaml to get the raw output with all information \ No newline at end of file From 9a3a862a6e88cadda3e8d0fc50ccb087e4ea6901 Mon Sep 17 00:00:00 2001 From: Thomas Leterme Date: Thu, 10 Sep 2026 14:56:33 +0000 Subject: [PATCH 4/5] fix(ovhcloud-cli): align file storage snapshots with the APIv2 contract Signed-off-by: Thomas Leterme --- internal/assets/api-schemas/cloud_v2.json | 31 --- internal/cmd/cloud_storage_file.go | 18 +- internal/services/cloud/cloud_storage_file.go | 210 +++++++++++------- .../storage-file-share-snapshot-create.json | 9 + .../cloud_storage_file_share_snapshot.tmpl | 19 +- 5 files changed, 162 insertions(+), 125 deletions(-) create mode 100644 internal/services/cloud/parameter-samples/storage-file-share-snapshot-create.json diff --git a/internal/assets/api-schemas/cloud_v2.json b/internal/assets/api-schemas/cloud_v2.json index cd9127e9..4544acf4 100644 --- a/internal/assets/api-schemas/cloud_v2.json +++ b/internal/assets/api-schemas/cloud_v2.json @@ -9539,20 +9539,6 @@ }, "readOnly": true }, - "shareProto": { - "allOf": [ - { - "$ref": "#/components/schemas/publicCloud.storage.file.FileStorageProtocolEnum" - } - ], - "description": "File sharing protocol of the parent share", - "readOnly": true - }, - "shareSize": { - "type": "integer", - "description": "Size of the parent share in GB at the time of the snapshot", - "readOnly": true - }, "size": { "type": "integer", "description": "Size of the snapshot in GB", @@ -9569,14 +9555,6 @@ "description": "Description of the snapshot", "nullable": true }, - "location": { - "allOf": [ - { - "$ref": "#/components/schemas/publicCloud.common.Location" - } - ], - "description": "Location of the snapshot" - }, "name": { "type": "string", "description": "Desired snapshot name", @@ -9598,7 +9576,6 @@ } }, "required": [ - "location", "share" ] }, @@ -27255,9 +27232,6 @@ "value": { "targetSpec": { "description": "Daily backup snapshot", - "location": { - "region": "GRA1" - }, "name": "my-share-snapshot", "share": { "id": "d1e2f3a4-b5c6-7890-abcd-ef1234567890" @@ -27565,8 +27539,6 @@ "share": { "id": "d1e2f3a4-b5c6-7890-abcd-ef1234567890" }, - "shareProto": "NFS", - "shareSize": 100, "size": 1 }, "currentTasks": null, @@ -27574,9 +27546,6 @@ "resourceStatus": "READY", "targetSpec": { "description": "Daily backup snapshot", - "location": { - "region": "GRA1" - }, "name": "my-share-snapshot", "share": { "id": "d1e2f3a4-b5c6-7890-abcd-ef1234567890" diff --git a/internal/cmd/cloud_storage_file.go b/internal/cmd/cloud_storage_file.go index 3f43ffc2..a349d689 100644 --- a/internal/cmd/cloud_storage_file.go +++ b/internal/cmd/cloud_storage_file.go @@ -6,6 +6,7 @@ package cmd import ( "github.com/ovh/ovhcloud-cli/internal/assets" + "github.com/ovh/ovhcloud-cli/internal/flags" "github.com/ovh/ovhcloud-cli/internal/services/cloud" "github.com/spf13/cobra" ) @@ -16,7 +17,6 @@ func initCloudStorageFileCommand(cloudCmd *cobra.Command) { Short: "Manage file storage shares in the given cloud project", } storageFileCmd.PersistentFlags().StringVar(&cloud.CloudProject, "cloud-project", "", "Cloud project ID") - storageFileCmd.PersistentFlags().StringVar(&cloud.ShareRegion, "region", "", "Region (skip region discovery if set)") networkCmd := &cobra.Command{ Use: "network", @@ -178,8 +178,24 @@ func initCloudStorageFileCommand(cloudCmd *cobra.Command) { } snapshotCreateCmd.Flags().StringVar(&cloud.ShareSnapshotSpec.TargetSpec.Description, "description", "", "Snapshot description") snapshotCreateCmd.Flags().StringVar(&cloud.ShareSnapshotSpec.TargetSpec.Name, "name", "", "Snapshot name") + snapshotCreateCmd.Flags().BoolVar(&flags.WaitForTask, "wait", false, "Wait for the snapshot to be ready before exiting") + addParameterFileFlags(snapshotCreateCmd, false, assets.CloudV2OpenapiSchema, "/publicCloud/project/{projectId}/storage/file/snapshot", "post", cloud.ShareSnapshotCreateExample, nil) + addInteractiveEditorFlag(snapshotCreateCmd) + markFlagsMutuallyExclusive(snapshotCreateCmd, "from-file", "editor") snapshotCmd.AddCommand(snapshotCreateCmd) + snapshotEditCmd := &cobra.Command{ + Use: "edit ", + Short: "Edit a snapshot of the given share", + Run: cloud.EditShareSnapshot, + Args: cobra.ExactArgs(2), + } + snapshotEditCmd.Flags().StringVar(&cloud.ShareSnapshotEditSpec.TargetSpec.Description, "description", "", "Snapshot description") + snapshotEditCmd.Flags().StringVar(&cloud.ShareSnapshotEditSpec.TargetSpec.Name, "name", "", "Snapshot name") + snapshotEditCmd.Flags().BoolVar(&flags.WaitForTask, "wait", false, "Wait for the snapshot to be ready before exiting") + addInteractiveEditorFlag(snapshotEditCmd) + snapshotCmd.AddCommand(snapshotEditCmd) + snapshotCmd.AddCommand(&cobra.Command{ Use: "delete ", Short: "Delete a snapshot from the given share", diff --git a/internal/services/cloud/cloud_storage_file.go b/internal/services/cloud/cloud_storage_file.go index f0361602..ae8a7b7c 100644 --- a/internal/services/cloud/cloud_storage_file.go +++ b/internal/services/cloud/cloud_storage_file.go @@ -39,7 +39,6 @@ var ( "id", "targetSpec.name name", "targetSpec.share.id shareId", - "targetSpec.location.region region", "resourceStatus status", } shareACLColumnsToDisplay = []string{ @@ -61,6 +60,9 @@ var ( //go:embed parameter-samples/storage-file-share-network-create.json ShareNetworkCreateExample string + //go:embed parameter-samples/storage-file-share-snapshot-create.json + ShareSnapshotCreateExample string + ShareSpec struct { TargetSpec struct { Description string `json:"description,omitempty"` @@ -110,20 +112,22 @@ var ( Share struct { Id string `json:"id,omitempty"` } `json:"share,omitzero"` - Location struct { - Region string `json:"region,omitempty"` - } `json:"location,omitzero"` } `json:"targetSpec"` } + ShareSnapshotEditSpec struct { + TargetSpec struct { + Description string `json:"description,omitempty"` + Name string `json:"name,omitempty"` + } `json:"targetSpec,omitzero"` + } + ShareACLSpec struct { TargetSpec struct { AccessLevel string `json:"accessLevel,omitempty"` AccessTo string `json:"accessTo,omitempty"` } `json:"targetSpec"` } - - ShareRegion string ) func shareV2Endpoint(projectID string) string { @@ -134,39 +138,8 @@ func shareNetworkV2Endpoint(projectID string) string { return fmt.Sprintf("/v2/publicCloud/project/%s/storage/file/network", projectID) } -// getShareRegions returns a single-element slice if --region is set, -// otherwise discovers all regions with the share feature available. -func getShareRegions(projectID string) ([]any, error) { - if ShareRegion != "" { - return []any{ShareRegion}, nil - } - return getCloudRegionsWithFeatureAvailable(projectID, "share") -} - -// findShare searches for a share across all regions and returns its endpoint and data. -func findShare(shareID string) (string, map[string]any, error) { - projectID, err := getConfiguredCloudProject() - if err != nil { - return "", nil, err - } - - regions, err := getShareRegions(projectID) - if err != nil { - return "", nil, fmt.Errorf("failed to fetch regions with share feature available: %w", err) - } - - for _, region := range regions { - var ( - share map[string]any - endpoint = fmt.Sprintf("/v1/cloud/project/%s/region/%s/share/%s", - projectID, url.PathEscape(region.(string)), url.PathEscape(shareID)) - ) - if err := httpLib.Client.Get(endpoint, &share); err == nil { - return endpoint, share, nil - } - } - - return "", nil, fmt.Errorf("no share found with ID %s", shareID) +func shareSnapshotV2Endpoint(projectID string) string { + return fmt.Sprintf("/v2/publicCloud/project/%s/storage/file/snapshot", projectID) } func ListShares(_ *cobra.Command, _ []string) { @@ -324,30 +297,6 @@ func DeleteShare(_ *cobra.Command, args []string) { display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ Share %s deleted successfully", args[0]) } -func getShareRegionV2(projectID, shareID string) (string, error) { - var share map[string]any - endpoint := fmt.Sprintf("%s/%s", shareV2Endpoint(projectID), url.PathEscape(shareID)) - if err := httpLib.Client.Get(endpoint, &share); err != nil { - return "", fmt.Errorf("failed to fetch share %s: %w", shareID, err) - } - - for _, state := range []string{"targetSpec", "currentState"} { - stateValue, ok := share[state].(map[string]any) - if !ok { - continue - } - location, ok := stateValue["location"].(map[string]any) - if !ok { - continue - } - if region, ok := location["region"].(string); ok && region != "" { - return region, nil - } - } - - return "", fmt.Errorf("share %s has no region", shareID) -} - // ACL commands func ListShareACLs(_ *cobra.Command, args []string) { @@ -413,6 +362,49 @@ func DeleteShareACL(_ *cobra.Command, args []string) { // Snapshot commands +// shareSnapshotParentID returns the identifier of the share a snapshot was +// taken from. The parent share is set at creation and never changes, so +// targetSpec is authoritative and is also the only one available while the +// snapshot is still being created. +func shareSnapshotParentID(snapshot map[string]any) string { + for _, state := range []string{"targetSpec", "currentState"} { + stateValue, ok := snapshot[state].(map[string]any) + if !ok { + continue + } + share, ok := stateValue["share"].(map[string]any) + if !ok { + continue + } + if id, ok := share["id"].(string); ok && id != "" { + return id + } + } + + return "" +} + +// fetchShareSnapshot fetches a snapshot and checks that it really belongs to +// the given share. Snapshot routes are project scoped in the v2 API, so +// without this check any snapshot of the project would be reachable (and +// deletable) through any share ID. +func fetchShareSnapshot(projectID, shareID, snapshotID string) (map[string]any, error) { + var snapshot map[string]any + endpoint := fmt.Sprintf("%s/%s", shareSnapshotV2Endpoint(projectID), url.PathEscape(snapshotID)) + if err := httpLib.Client.Get(endpoint, &snapshot); err != nil { + return nil, fmt.Errorf("failed to fetch share snapshot: %w", err) + } + + switch parent := shareSnapshotParentID(snapshot); parent { + case shareID: + return snapshot, nil + case "": + return nil, fmt.Errorf("failed to determine the parent share of snapshot %s", snapshotID) + default: + return nil, fmt.Errorf("snapshot %s belongs to share %s, not to share %s", snapshotID, parent, shareID) + } +} + func ListShareSnapshots(_ *cobra.Command, args []string) { projectID, err := getConfiguredCloudProject() if err != nil { @@ -422,11 +414,7 @@ func ListShareSnapshots(_ *cobra.Command, args []string) { filters := append([]string{}, flags.GenericFilters...) filters = append(filters, fmt.Sprintf("targetSpec.share.id==%q", args[0])) - common.ManageListRequestNoExpand( - fmt.Sprintf("/v2/publicCloud/project/%s/storage/file/snapshot", projectID), - shareSnapshotColumnsToDisplay, - filters, - ) + common.ManageListRequestNoExpand(shareSnapshotV2Endpoint(projectID), shareSnapshotColumnsToDisplay, filters) } func GetShareSnapshot(_ *cobra.Command, args []string) { @@ -436,38 +424,89 @@ func GetShareSnapshot(_ *cobra.Command, args []string) { return } - endpoint := fmt.Sprintf("/v2/publicCloud/project/%s/storage/file/snapshot/%s", projectID, url.PathEscape(args[1])) - var snapshot map[string]any - if err := httpLib.Client.Get(endpoint, &snapshot); err != nil { - display.OutputError(&flags.OutputFormatConfig, "failed to fetch share snapshot: %s", err) + snapshot, err := fetchShareSnapshot(projectID, args[0], args[1]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) return } display.OutputObject(snapshot, args[1], shareSnapshotTemplate, &flags.OutputFormatConfig) } -func CreateShareSnapshot(_ *cobra.Command, args []string) { +func CreateShareSnapshot(cmd *cobra.Command, args []string) { projectID, err := getConfiguredCloudProject() if err != nil { display.OutputError(&flags.OutputFormatConfig, "%s", err) return } - region, err := getShareRegionV2(projectID, args[0]) + ShareSnapshotSpec.TargetSpec.Share.Id = args[0] + endpoint := shareSnapshotV2Endpoint(projectID) + snapshot, err := common.CreateResource( + cmd, + "/publicCloud/project/{projectId}/storage/file/snapshot", + endpoint, + ShareSnapshotCreateExample, + ShareSnapshotSpec, + assets.CloudV2OpenapiSchema, + []string{"targetSpec.share.id"}, + ) if err != nil { display.OutputError(&flags.OutputFormatConfig, "%s", err) return } - ShareSnapshotSpec.TargetSpec.Share.Id = args[0] - ShareSnapshotSpec.TargetSpec.Location.Region = region - endpoint := fmt.Sprintf("/v2/publicCloud/project/%s/storage/file/snapshot", projectID) - var response map[string]any - if err := httpLib.Client.Post(endpoint, ShareSnapshotSpec, &response); err != nil { - display.OutputError(&flags.OutputFormatConfig, "failed to create share snapshot: %s", err) + + snapshotID, _ := snapshot["id"].(string) + + if !flags.WaitForTask { + display.OutputInfo(&flags.OutputFormatConfig, snapshot, "✅ Snapshot creation started successfully for share %s (id: %s)", args[0], snapshotID) + return + } + + ready, err := waitForCloudResourceReady(fmt.Sprintf("%s/%s", endpoint, url.PathEscape(snapshotID)), 10*time.Minute) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to wait for snapshot creation: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, ready, "✅ Snapshot %s created successfully for share %s", snapshotID, args[0]) +} + +func EditShareSnapshot(cmd *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + if _, err := fetchShareSnapshot(projectID, args[0], args[1]); err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("%s/%s", shareSnapshotV2Endpoint(projectID), url.PathEscape(args[1])) + if err := common.EditResource( + cmd, + "/publicCloud/project/{projectId}/storage/file/snapshot/{snapshotId}", + endpoint, + ShareSnapshotEditSpec, + assets.CloudV2OpenapiSchema, + ); err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) return } - display.OutputInfo(&flags.OutputFormatConfig, response, "✅ Snapshot created successfully for share %s (id: %s)", args[0], response["id"]) + if !flags.WaitForTask { + return + } + + ready, err := waitForCloudResourceReady(endpoint, 10*time.Minute) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to wait for snapshot to be ready: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, ready, "✅ Snapshot %s is now ready", args[1]) } func DeleteShareSnapshot(_ *cobra.Command, args []string) { @@ -477,11 +516,16 @@ func DeleteShareSnapshot(_ *cobra.Command, args []string) { return } - endpoint := fmt.Sprintf("/v2/publicCloud/project/%s/storage/file/snapshot/%s", projectID, url.PathEscape(args[1])) + if _, err := fetchShareSnapshot(projectID, args[0], args[1]); err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("%s/%s", shareSnapshotV2Endpoint(projectID), url.PathEscape(args[1])) if err := httpLib.Client.Delete(endpoint, nil); err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to delete share snapshot: %s", err) return } - display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ Snapshot %s deleted successfully from share %s", args[1], args[0]) + display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ Snapshot %s of share %s is being deleted", args[1], args[0]) } diff --git a/internal/services/cloud/parameter-samples/storage-file-share-snapshot-create.json b/internal/services/cloud/parameter-samples/storage-file-share-snapshot-create.json new file mode 100644 index 00000000..d2c9b461 --- /dev/null +++ b/internal/services/cloud/parameter-samples/storage-file-share-snapshot-create.json @@ -0,0 +1,9 @@ +{ + "targetSpec": { + "name": "my-share-snapshot", + "description": "My file share snapshot", + "share": { + "id": "00000000-0000-0000-0000-000000000000" + } + } +} diff --git a/internal/services/cloud/templates/cloud_storage_file_share_snapshot.tmpl b/internal/services/cloud/templates/cloud_storage_file_share_snapshot.tmpl index 75d60868..43e29c5f 100644 --- a/internal/services/cloud/templates/cloud_storage_file_share_snapshot.tmpl +++ b/internal/services/cloud/templates/cloud_storage_file_share_snapshot.tmpl @@ -1,16 +1,15 @@ 🚀 Share Snapshot {{.ServiceName}} ======= +{{$state := index .Result "targetSpec"}}{{with index .Result "currentState"}}{{$state = .}}{{end}} +{{with index $state "description"}}_{{.}}_ -_{{index .Result "description"}}_ +{{end}}## General information -## General information - -**Name**: {{index .Result "currentState" "name"}} -**Share ID**: {{index .Result "currentState" "share" "id"}} -**Share protocol**: {{index .Result "currentState" "shareProto"}} -**Share size**: {{index .Result "currentState" "shareSize"}}GB -**Size**: {{index .Result "currentState" "snapshotSize"}}GB -**Status**: {{index .Result "resourceStatus"}} -**Created at**: {{index .Result "createdAt"}} +{{with index $state "name"}}**Name**: {{.}} +{{end}}**Share ID**: {{index $state "share" "id"}} +**Status**: {{index .Result "resourceStatus"}} +{{with index .Result "currentState"}}**Region**: {{index . "location" "region"}} +**Size**: {{index . "size"}}GB +{{end}}**Created at**: {{index .Result "createdAt"}} 💡 Use option -o json or -o yaml to get the raw output with all information \ No newline at end of file From a9ecc5ff1b305eedd2a76ab08cfcdec4075978a9 Mon Sep 17 00:00:00 2001 From: Thomas Leterme Date: Thu, 10 Sep 2026 15:13:27 +0000 Subject: [PATCH 5/5] doc(share): make doc --> remove unused --region flag Signed-off-by: Thomas Leterme --- doc/ovhcloud_cloud_storage_file.md | 1 - doc/ovhcloud_cloud_storage_file_network.md | 1 - ...cloud_cloud_storage_file_network_create.md | 1 - ...cloud_cloud_storage_file_network_delete.md | 1 - ...ovhcloud_cloud_storage_file_network_get.md | 1 - ...vhcloud_cloud_storage_file_network_list.md | 1 - doc/ovhcloud_cloud_storage_file_share.md | 1 - doc/ovhcloud_cloud_storage_file_share_acl.md | 1 - ...oud_cloud_storage_file_share_acl_create.md | 1 - ...oud_cloud_storage_file_share_acl_delete.md | 1 - ...hcloud_cloud_storage_file_share_acl_get.md | 1 - ...cloud_cloud_storage_file_share_acl_list.md | 1 - ...vhcloud_cloud_storage_file_share_create.md | 1 - ...vhcloud_cloud_storage_file_share_delete.md | 1 - doc/ovhcloud_cloud_storage_file_share_edit.md | 1 - doc/ovhcloud_cloud_storage_file_share_get.md | 1 - doc/ovhcloud_cloud_storage_file_share_list.md | 1 - ...cloud_cloud_storage_file_share_snapshot.md | 2 +- ...loud_storage_file_share_snapshot_create.md | 6 ++- ...loud_storage_file_share_snapshot_delete.md | 1 - ..._cloud_storage_file_share_snapshot_edit.md | 42 +++++++++++++++++++ ...d_cloud_storage_file_share_snapshot_get.md | 1 - ..._cloud_storage_file_share_snapshot_list.md | 1 - 23 files changed, 48 insertions(+), 22 deletions(-) create mode 100644 doc/ovhcloud_cloud_storage_file_share_snapshot_edit.md diff --git a/doc/ovhcloud_cloud_storage_file.md b/doc/ovhcloud_cloud_storage_file.md index 57c7aa51..ed320de0 100644 --- a/doc/ovhcloud_cloud_storage_file.md +++ b/doc/ovhcloud_cloud_storage_file.md @@ -7,7 +7,6 @@ Manage file storage shares in the given cloud project ``` --cloud-project string Cloud project ID -h, --help help for file - --region string Region (skip region discovery if set) ``` ### Options inherited from parent commands diff --git a/doc/ovhcloud_cloud_storage_file_network.md b/doc/ovhcloud_cloud_storage_file_network.md index d9f63e0a..6add6078 100644 --- a/doc/ovhcloud_cloud_storage_file_network.md +++ b/doc/ovhcloud_cloud_storage_file_network.md @@ -26,7 +26,6 @@ Manage file storage share networks --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_network_create.md b/doc/ovhcloud_cloud_storage_file_network_create.md index 6dfbbb06..ae569b48 100644 --- a/doc/ovhcloud_cloud_storage_file_network_create.md +++ b/doc/ovhcloud_cloud_storage_file_network_create.md @@ -39,7 +39,6 @@ ovhcloud cloud storage file network create [flags] --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_network_delete.md b/doc/ovhcloud_cloud_storage_file_network_delete.md index e624ed04..44053a24 100644 --- a/doc/ovhcloud_cloud_storage_file_network_delete.md +++ b/doc/ovhcloud_cloud_storage_file_network_delete.md @@ -30,7 +30,6 @@ ovhcloud cloud storage file network delete [flags] --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_network_get.md b/doc/ovhcloud_cloud_storage_file_network_get.md index 7a47c258..07eddefa 100644 --- a/doc/ovhcloud_cloud_storage_file_network_get.md +++ b/doc/ovhcloud_cloud_storage_file_network_get.md @@ -30,7 +30,6 @@ ovhcloud cloud storage file network get [flags] --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_network_list.md b/doc/ovhcloud_cloud_storage_file_network_list.md index 7ffed53d..3ad1a16f 100644 --- a/doc/ovhcloud_cloud_storage_file_network_list.md +++ b/doc/ovhcloud_cloud_storage_file_network_list.md @@ -37,7 +37,6 @@ ovhcloud cloud storage file network list [flags] --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_share.md b/doc/ovhcloud_cloud_storage_file_share.md index aa8cddb9..60eaf6fc 100644 --- a/doc/ovhcloud_cloud_storage_file_share.md +++ b/doc/ovhcloud_cloud_storage_file_share.md @@ -26,7 +26,6 @@ Manage file storage shares --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_share_acl.md b/doc/ovhcloud_cloud_storage_file_share_acl.md index ee15b11a..f922503d 100644 --- a/doc/ovhcloud_cloud_storage_file_share_acl.md +++ b/doc/ovhcloud_cloud_storage_file_share_acl.md @@ -26,7 +26,6 @@ Manage share access control lists --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_share_acl_create.md b/doc/ovhcloud_cloud_storage_file_share_acl_create.md index ff020b7b..2574e49e 100644 --- a/doc/ovhcloud_cloud_storage_file_share_acl_create.md +++ b/doc/ovhcloud_cloud_storage_file_share_acl_create.md @@ -32,7 +32,6 @@ ovhcloud cloud storage file share acl create [flags] --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_share_acl_delete.md b/doc/ovhcloud_cloud_storage_file_share_acl_delete.md index ddd58e83..72cc015e 100644 --- a/doc/ovhcloud_cloud_storage_file_share_acl_delete.md +++ b/doc/ovhcloud_cloud_storage_file_share_acl_delete.md @@ -30,7 +30,6 @@ ovhcloud cloud storage file share acl delete [flags] --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_share_acl_get.md b/doc/ovhcloud_cloud_storage_file_share_acl_get.md index 10e6f18a..742a10f4 100644 --- a/doc/ovhcloud_cloud_storage_file_share_acl_get.md +++ b/doc/ovhcloud_cloud_storage_file_share_acl_get.md @@ -30,7 +30,6 @@ ovhcloud cloud storage file share acl get [flags] --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_share_acl_list.md b/doc/ovhcloud_cloud_storage_file_share_acl_list.md index 62b3f345..c68af1be 100644 --- a/doc/ovhcloud_cloud_storage_file_share_acl_list.md +++ b/doc/ovhcloud_cloud_storage_file_share_acl_list.md @@ -37,7 +37,6 @@ ovhcloud cloud storage file share acl list [flags] --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_share_create.md b/doc/ovhcloud_cloud_storage_file_share_create.md index 8f78d94f..d270d8c4 100644 --- a/doc/ovhcloud_cloud_storage_file_share_create.md +++ b/doc/ovhcloud_cloud_storage_file_share_create.md @@ -41,7 +41,6 @@ ovhcloud cloud storage file share create [flags] --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_share_delete.md b/doc/ovhcloud_cloud_storage_file_share_delete.md index 064f73dc..0395c769 100644 --- a/doc/ovhcloud_cloud_storage_file_share_delete.md +++ b/doc/ovhcloud_cloud_storage_file_share_delete.md @@ -30,7 +30,6 @@ ovhcloud cloud storage file share delete [flags] --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_share_edit.md b/doc/ovhcloud_cloud_storage_file_share_edit.md index 085eec56..b27389d7 100644 --- a/doc/ovhcloud_cloud_storage_file_share_edit.md +++ b/doc/ovhcloud_cloud_storage_file_share_edit.md @@ -34,7 +34,6 @@ ovhcloud cloud storage file share edit [flags] --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_share_get.md b/doc/ovhcloud_cloud_storage_file_share_get.md index be1bbf9a..fc74e13e 100644 --- a/doc/ovhcloud_cloud_storage_file_share_get.md +++ b/doc/ovhcloud_cloud_storage_file_share_get.md @@ -30,7 +30,6 @@ ovhcloud cloud storage file share get [flags] --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_share_list.md b/doc/ovhcloud_cloud_storage_file_share_list.md index 8dad6874..190c8c47 100644 --- a/doc/ovhcloud_cloud_storage_file_share_list.md +++ b/doc/ovhcloud_cloud_storage_file_share_list.md @@ -37,7 +37,6 @@ ovhcloud cloud storage file share list [flags] --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_share_snapshot.md b/doc/ovhcloud_cloud_storage_file_share_snapshot.md index 21f31289..b0124730 100644 --- a/doc/ovhcloud_cloud_storage_file_share_snapshot.md +++ b/doc/ovhcloud_cloud_storage_file_share_snapshot.md @@ -26,7 +26,6 @@ Manage share snapshots --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO @@ -34,6 +33,7 @@ Manage share snapshots * [ovhcloud cloud storage file share](ovhcloud_cloud_storage_file_share.md) - Manage file storage shares * [ovhcloud cloud storage file share snapshot create](ovhcloud_cloud_storage_file_share_snapshot_create.md) - Create a snapshot of the given share * [ovhcloud cloud storage file share snapshot delete](ovhcloud_cloud_storage_file_share_snapshot_delete.md) - Delete a snapshot from the given share +* [ovhcloud cloud storage file share snapshot edit](ovhcloud_cloud_storage_file_share_snapshot_edit.md) - Edit a snapshot of the given share * [ovhcloud cloud storage file share snapshot get](ovhcloud_cloud_storage_file_share_snapshot_get.md) - Get a specific snapshot for the given share * [ovhcloud cloud storage file share snapshot list](ovhcloud_cloud_storage_file_share_snapshot_list.md) - List snapshots for the given share diff --git a/doc/ovhcloud_cloud_storage_file_share_snapshot_create.md b/doc/ovhcloud_cloud_storage_file_share_snapshot_create.md index 1ac780ed..fbd8a54c 100644 --- a/doc/ovhcloud_cloud_storage_file_share_snapshot_create.md +++ b/doc/ovhcloud_cloud_storage_file_share_snapshot_create.md @@ -10,8 +10,13 @@ ovhcloud cloud storage file share snapshot create [flags] ``` --description string Snapshot description + --editor Use a text editor to define parameters + --from-file string File containing parameters -h, --help help for create + --init-file string Create a file with example parameters --name string Snapshot name + --replace Replace parameters file if it already exists + --wait Wait for the snapshot to be ready before exiting ``` ### Options inherited from parent commands @@ -32,7 +37,6 @@ ovhcloud cloud storage file share snapshot create [flags] --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_share_snapshot_delete.md b/doc/ovhcloud_cloud_storage_file_share_snapshot_delete.md index bf3649e5..884d406f 100644 --- a/doc/ovhcloud_cloud_storage_file_share_snapshot_delete.md +++ b/doc/ovhcloud_cloud_storage_file_share_snapshot_delete.md @@ -30,7 +30,6 @@ ovhcloud cloud storage file share snapshot delete [flag --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_share_snapshot_edit.md b/doc/ovhcloud_cloud_storage_file_share_snapshot_edit.md new file mode 100644 index 00000000..b2f98a35 --- /dev/null +++ b/doc/ovhcloud_cloud_storage_file_share_snapshot_edit.md @@ -0,0 +1,42 @@ +## ovhcloud cloud storage file share snapshot edit + +Edit a snapshot of the given share + +``` +ovhcloud cloud storage file share snapshot edit [flags] +``` + +### Options + +``` + --description string Snapshot description + --editor Use a text editor to define parameters + -h, --help help for edit + --name string Snapshot name + --wait Wait for the snapshot to be ready before exiting +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) + --profile string Use a specific profile from the configuration file +``` + +### SEE ALSO + +* [ovhcloud cloud storage file share snapshot](ovhcloud_cloud_storage_file_share_snapshot.md) - Manage share snapshots + diff --git a/doc/ovhcloud_cloud_storage_file_share_snapshot_get.md b/doc/ovhcloud_cloud_storage_file_share_snapshot_get.md index f86954e5..dc1360ce 100644 --- a/doc/ovhcloud_cloud_storage_file_share_snapshot_get.md +++ b/doc/ovhcloud_cloud_storage_file_share_snapshot_get.md @@ -30,7 +30,6 @@ ovhcloud cloud storage file share snapshot get [flags] --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO diff --git a/doc/ovhcloud_cloud_storage_file_share_snapshot_list.md b/doc/ovhcloud_cloud_storage_file_share_snapshot_list.md index 18811e2e..28380201 100644 --- a/doc/ovhcloud_cloud_storage_file_share_snapshot_list.md +++ b/doc/ovhcloud_cloud_storage_file_share_snapshot_list.md @@ -37,7 +37,6 @@ ovhcloud cloud storage file share snapshot list [flags] --output 'name+","+type' (to extract and concatenate fields in a string) --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) --profile string Use a specific profile from the configuration file - --region string Region (skip region discovery if set) ``` ### SEE ALSO