builder/digitalocean: Implement Artifact destroy

/cc @pearkes
This commit is contained in:
Mitchell Hashimoto 2013-06-18 21:54:15 -07:00
parent c3de114585
commit 91253c4f32
5 changed files with 81 additions and 8 deletions

View File

@ -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)

View File

@ -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)
}

View File

@ -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 {

View File

@ -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() {

View File

@ -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
}