Merge pull request #82 from markpeek/markpeek-deleteimage
Add DeleteImageById and acceptance test for CreateImage and DeleteImageById
diff --git a/acceptance/17-create-delete-image.go b/acceptance/17-create-delete-image.go
new file mode 100644
index 0000000..0ad9b14
--- /dev/null
+++ b/acceptance/17-create-delete-image.go
@@ -0,0 +1,50 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "github.com/rackspace/gophercloud"
+)
+
+var quiet = flag.Bool("quiet", false, "Quiet mode for acceptance testing. $? non-zero on error though.")
+var rgn = flag.String("r", "DFW", "Datacenter region to interrogate.")
+
+func main() {
+ flag.Parse()
+
+ withIdentity(false, func(auth gophercloud.AccessProvider) {
+ withServerApi(auth, func(servers gophercloud.CloudServersProvider) {
+ log("Creating server")
+ serverId, err := createServer(servers, "", "", "", "")
+ if err != nil {
+ panic(err)
+ }
+ waitForServerState(servers, serverId, "ACTIVE")
+
+ log("Creating image")
+ name := randomString("ACPTTEST", 16)
+ createImage := gophercloud.CreateImage{
+ Name: name,
+ }
+ imageId, err := servers.CreateImage(serverId, createImage)
+ if err != nil {
+ panic(err)
+ }
+ waitForImageState(servers, imageId, "ACTIVE")
+
+ log("Deleting server")
+ servers.DeleteServerById(serverId)
+
+ log("Deleting image")
+ servers.DeleteImageById(imageId)
+
+ log("Done")
+ })
+ })
+}
+
+func log(s string) {
+ if !*quiet {
+ fmt.Println(s)
+ }
+}
diff --git a/acceptance/libargs.go b/acceptance/libargs.go
index 5edb445..b1e79ff 100644
--- a/acceptance/libargs.go
+++ b/acceptance/libargs.go
@@ -1,10 +1,10 @@
package main
import (
- "fmt"
- "os"
"crypto/rand"
+ "fmt"
"github.com/rackspace/gophercloud"
+ "os"
"time"
)
@@ -32,13 +32,13 @@
// (Implementation from Even Shaw's contribution on
// http://stackoverflow.com/questions/12771930/what-is-the-fastest-way-to-generate-a-long-random-string-in-go).
func randomString(prefix string, n int) string {
- const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
- var bytes = make([]byte, n)
- rand.Read(bytes)
- for i, b := range bytes {
- bytes[i] = alphanum[b % byte(len(alphanum))]
- }
- return prefix + string(bytes)
+ const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ var bytes = make([]byte, n)
+ rand.Read(bytes)
+ for i, b := range bytes {
+ bytes[i] = alphanum[b%byte(len(alphanum))]
+ }
+ return prefix + string(bytes)
}
// aSuitableImage finds a minimal image for use in dynamically creating servers.
@@ -127,7 +127,7 @@
// findAlternativeFlavor locates a flavor to resize a server to. It is guaranteed to be different
// than what aSuitableFlavor() returns. If none could be found, this function will panic.
func findAlternativeFlavor() string {
- return "3" // 1GB image, up from 512MB image
+ return "3" // 1GB image, up from 512MB image
}
// findAlternativeImage locates an image to resize or rebuild a server with. It is guaranteed to be
@@ -143,8 +143,8 @@
acc, err := gophercloud.Authenticate(
provider,
gophercloud.AuthOptions{
- Username: username,
- Password: password,
+ Username: username,
+ Password: password,
AllowReauth: ar,
},
)
@@ -185,4 +185,21 @@
time.Sleep(10 * time.Second)
}
panic("Impossible")
-}
\ No newline at end of file
+}
+
+// waitForImageState polls, every 10 seconds, for a given image to appear in the indicated state.
+// This call will block forever if it never appears in the desired state, so if a timeout is required,
+// make sure to call this function in a goroutine.
+func waitForImageState(api gophercloud.CloudServersProvider, id, state string) error {
+ for {
+ s, err := api.ImageById(id)
+ if err != nil {
+ return err
+ }
+ if s.Status == state {
+ return nil
+ }
+ time.Sleep(10 * time.Second)
+ }
+ panic("Impossible")
+}
diff --git a/images.go b/images.go
index 61a3369..a23e0bb 100644
--- a/images.go
+++ b/images.go
@@ -37,6 +37,20 @@
return is, err
}
+func (gsp *genericServersProvider) DeleteImageById(id string) error {
+ err := gsp.context.WithReauth(gsp.access, func() error {
+ url := gsp.endpoint + "/images/" + id
+ _, err := perigee.Request("DELETE", url, perigee.Options{
+ CustomClient: gsp.context.httpClient,
+ MoreHeaders: map[string]string{
+ "X-Auth-Token": gsp.access.AuthToken(),
+ },
+ })
+ return err
+ })
+ return err
+}
+
// ImageLink provides a reference to a image by either ID or by direct URL.
// Some services use just the ID, others use just the URL.
// This structure provides a common means of expressing both in a single field.
diff --git a/interfaces.go b/interfaces.go
index 6786d39..4982937 100644
--- a/interfaces.go
+++ b/interfaces.go
@@ -148,6 +148,9 @@
// ImageById yields details about a specific image.
ImageById(id string) (*Image, error)
+ // DeleteImageById will delete the specific image.
+ DeleteImageById(id string) error
+
// Flavors
// ListFlavors yields the list of available system flavors. This function
diff --git a/servers.go b/servers.go
index 798666a..6ca8d24 100644
--- a/servers.go
+++ b/servers.go
@@ -326,8 +326,7 @@
MoreHeaders: map[string]string{
"X-Auth-Token": gsp.access.AuthToken(),
},
- OkCodes: []int{200, 202},
- DumpReqJson: true,
+ OkCodes: []int{200, 202},
})
})