diff --git a/builder/digitalocean/api.go b/builder/digitalocean/api.go index e04e86d08..8ebb2d5bd 100644 --- a/builder/digitalocean/api.go +++ b/builder/digitalocean/api.go @@ -8,6 +8,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/mitchellh/mapstructure" "io/ioutil" "log" "net/http" @@ -16,6 +17,16 @@ import ( const DIGITALOCEAN_API_URL = "https://api.digitalocean.com" +type Image struct { + Id uint + Name string + Distribution string +} + +type ImagesResp struct { + Images []Image +} + type DigitalOceanClient struct { // The http client for communicating client *http.Client @@ -107,6 +118,28 @@ func (d DigitalOceanClient) CreateSnapshot(id uint, name string) error { return err } +// Returns all available images. +func (d DigitalOceanClient) Images() ([]Image, error) { + resp, err := NewRequest(d, "images", "") + if err != nil { + return nil, err + } + + var result ImagesResp + if err := mapstructure.Decode(resp, &result); err != nil { + return nil, err + } + + return result.Images, nil +} + +// Destroys an image by its ID. +func (d DigitalOceanClient) DestroyImage(id uint) error { + path := fmt.Sprintf("images/%d/destroy", id) + _, err := NewRequest(d, path, "") + return err +} + // Returns DO's string representation of status "off" "new" "active" etc. func (d DigitalOceanClient) DropletStatus(id uint) (string, string, error) { path := fmt.Sprintf("droplets/%v", id) diff --git a/builder/digitalocean/artifact.go b/builder/digitalocean/artifact.go index ddd9cdfa3..292068c8f 100644 --- a/builder/digitalocean/artifact.go +++ b/builder/digitalocean/artifact.go @@ -1,13 +1,19 @@ package digitalocean import ( - "errors" "fmt" + "log" ) type Artifact struct { // The name of the snapshot snapshotName string + + // The ID of the image + snapshotId uint + + // The client for making API calls + client *DigitalOceanClient } func (*Artifact) BuilderId() string { @@ -28,5 +34,6 @@ func (a *Artifact) String() string { } func (a *Artifact) Destroy() error { - return errors.New("not implemented yet") + log.Printf("Destroying image: %d", a.snapshotId) + return a.client.DestroyImage(a.snapshotId) } diff --git a/builder/digitalocean/artifact_test.go b/builder/digitalocean/artifact_test.go index cad84f207..a28c3334c 100644 --- a/builder/digitalocean/artifact_test.go +++ b/builder/digitalocean/artifact_test.go @@ -14,7 +14,7 @@ func TestArtifact_Impl(t *testing.T) { } func TestArtifactString(t *testing.T) { - a := &Artifact{"packer-foobar"} + a := &Artifact{"packer-foobar", 42, nil} expected := "A snapshot was created: packer-foobar" if a.String() != expected { diff --git a/builder/digitalocean/builder.go b/builder/digitalocean/builder.go index 29c1b9f9e..5d77955bc 100644 --- a/builder/digitalocean/builder.go +++ b/builder/digitalocean/builder.go @@ -184,7 +184,18 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe b.runner.Run(state) - return &Artifact{b.config.SnapshotName}, nil + if _, ok := state["snapshot_name"]; !ok { + log.Println("Failed to find snapshot_name in state. Bug?") + return nil, nil + } + + artifact := &Artifact{ + snapshotName: state["snapshot_name"].(string), + snapshotId: state["snapshot_image_id"].(uint), + client: client, + } + + return artifact, nil } func (b *Builder) Cancel() { diff --git a/builder/digitalocean/step_snapshot.go b/builder/digitalocean/step_snapshot.go index 1abeb67b0..15fa02a1b 100644 --- a/builder/digitalocean/step_snapshot.go +++ b/builder/digitalocean/step_snapshot.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" + "log" ) type stepSnapshot struct{} @@ -15,23 +16,44 @@ func (s *stepSnapshot) Run(state map[string]interface{}) multistep.StepAction { dropletId := state["droplet_id"].(uint) ui.Say(fmt.Sprintf("Creating snapshot: %v", c.SnapshotName)) - err := client.CreateSnapshot(dropletId, c.SnapshotName) - if err != nil { ui.Error(err.Error()) return multistep.ActionHalt } ui.Say("Waiting for snapshot to complete...") - err = waitForDropletState("active", dropletId, client) - if err != nil { ui.Error(err.Error()) return multistep.ActionHalt } + log.Printf("Looking up snapshot ID for snapshot: %s", c.SnapshotName) + images, err := client.Images() + if err != nil { + ui.Error(err.Error()) + return multistep.ActionHalt + } + + var imageId uint + for _, image := range images { + if image.Name == c.SnapshotName { + imageId = image.Id + break + } + } + + if imageId == 0 { + ui.Error("Couldn't find snapshot to get the image ID. Bug?") + return multistep.ActionHalt + } + + log.Printf("Snapshot image ID: %d", imageId) + + state["snapshot_image_id"] = imageId + state["snapshot_name"] = c.SnapshotName + return multistep.ActionContinue }