Add os-volume_upload_image action to volumeactions (#240)
* Add os-volume_upload_image action to volumeactions
* Code updates to align with style guide
diff --git a/openstack/blockstorage/extensions/volumeactions/requests.go b/openstack/blockstorage/extensions/volumeactions/requests.go
index 1aff494..e3c7df3 100644
--- a/openstack/blockstorage/extensions/volumeactions/requests.go
+++ b/openstack/blockstorage/extensions/volumeactions/requests.go
@@ -214,3 +214,40 @@
})
return
}
+
+// UploadImageOptsBuilder allows extensions to add additional parameters to the
+// UploadImage request.
+type UploadImageOptsBuilder interface {
+ ToVolumeUploadImageMap() (map[string]interface{}, error)
+}
+
+// UploadImageOpts contains options for uploading a Volume to image storage.
+type UploadImageOpts struct {
+ // Container format, may be bare, ofv, ova, etc.
+ ContainerFormat string `json:"container_format,omitempty"`
+ // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc.
+ DiskFormat string `json:"disk_format,omitempty"`
+ // The name of image that will be stored in glance
+ ImageName string `json:"image_name,omitempty"`
+ // Force image creation, usable if volume attached to instance
+ Force bool `json:"force,omitempty"`
+}
+
+// ToVolumeUploadImageMap assembles a request body based on the contents of a
+// UploadImageOpts.
+func (opts UploadImageOpts) ToVolumeUploadImageMap() (map[string]interface{}, error) {
+ return gophercloud.BuildRequestBody(opts, "os-volume_upload_image")
+}
+
+// UploadImage will upload image base on the values in UploadImageOptsBuilder
+func UploadImage(client *gophercloud.ServiceClient, id string, opts UploadImageOptsBuilder) (r UploadImageResult) {
+ b, err := opts.ToVolumeUploadImageMap()
+ if err != nil {
+ r.Err = err
+ return
+ }
+ _, r.Err = client.Post(uploadURL(client, id), b, nil, &gophercloud.RequestOpts{
+ OkCodes: []int{202},
+ })
+ return
+}
diff --git a/openstack/blockstorage/extensions/volumeactions/results.go b/openstack/blockstorage/extensions/volumeactions/results.go
index b5695b7..634b04d 100644
--- a/openstack/blockstorage/extensions/volumeactions/results.go
+++ b/openstack/blockstorage/extensions/volumeactions/results.go
@@ -17,6 +17,11 @@
gophercloud.ErrResult
}
+// UploadImageResult contains the response body and error from a UploadImage request.
+type UploadImageResult struct {
+ gophercloud.ErrResult
+}
+
// ReserveResult contains the response body and error from a Get request.
type ReserveResult struct {
gophercloud.ErrResult
diff --git a/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go b/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go
index 4c3c0dd..d914097 100644
--- a/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go
+++ b/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go
@@ -74,6 +74,31 @@
})
}
+func MockUploadImageResponse(t *testing.T) {
+ th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action",
+ func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "POST")
+ th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+ th.TestHeader(t, r, "Content-Type", "application/json")
+ th.TestHeader(t, r, "Accept", "application/json")
+ th.TestJSONRequest(t, r, `
+{
+ "os-volume_upload_image": {
+ "container_format": "bare",
+ "force": true,
+ "image_name": "test",
+ "disk_format": "raw"
+ }
+}
+ `)
+
+ w.Header().Add("Content-Type", "application/json")
+ w.WriteHeader(http.StatusAccepted)
+
+ fmt.Fprintf(w, `{}`)
+ })
+}
+
func MockReserveResponse(t *testing.T) {
th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action",
func(w http.ResponseWriter, r *http.Request) {
diff --git a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go
index b1f7af7..6132161 100644
--- a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go
+++ b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go
@@ -44,6 +44,21 @@
th.AssertNoErr(t, err)
}
+func TestUploadImage(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+ MockUploadImageResponse(t)
+ options := &volumeactions.UploadImageOpts{
+ ContainerFormat: "bare",
+ DiskFormat: "raw",
+ ImageName: "test",
+ Force: true,
+ }
+
+ err := volumeactions.UploadImage(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr()
+ th.AssertNoErr(t, err)
+}
+
func TestReserve(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
diff --git a/openstack/blockstorage/extensions/volumeactions/urls.go b/openstack/blockstorage/extensions/volumeactions/urls.go
index a172549..5efd2b2 100644
--- a/openstack/blockstorage/extensions/volumeactions/urls.go
+++ b/openstack/blockstorage/extensions/volumeactions/urls.go
@@ -14,6 +14,10 @@
return attachURL(c, id)
}
+func uploadURL(c *gophercloud.ServiceClient, id string) string {
+ return attachURL(c, id)
+}
+
func reserveURL(c *gophercloud.ServiceClient, id string) string {
return attachURL(c, id)
}