Commit 264e59d7 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

builder/digitalocean: Implement Artifact destroy

/cc @pearkes
parent 0a90d3e7
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/mitchellh/mapstructure"
"io/ioutil" "io/ioutil"
"log" "log"
"net/http" "net/http"
...@@ -16,6 +17,16 @@ import ( ...@@ -16,6 +17,16 @@ import (
const DIGITALOCEAN_API_URL = "https://api.digitalocean.com" 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 { type DigitalOceanClient struct {
// The http client for communicating // The http client for communicating
client *http.Client client *http.Client
...@@ -107,6 +118,28 @@ func (d DigitalOceanClient) CreateSnapshot(id uint, name string) error { ...@@ -107,6 +118,28 @@ func (d DigitalOceanClient) CreateSnapshot(id uint, name string) error {
return err 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. // Returns DO's string representation of status "off" "new" "active" etc.
func (d DigitalOceanClient) DropletStatus(id uint) (string, string, error) { func (d DigitalOceanClient) DropletStatus(id uint) (string, string, error) {
path := fmt.Sprintf("droplets/%v", id) path := fmt.Sprintf("droplets/%v", id)
......
package digitalocean package digitalocean
import ( import (
"errors"
"fmt" "fmt"
"log"
) )
type Artifact struct { type Artifact struct {
// The name of the snapshot // The name of the snapshot
snapshotName string snapshotName string
// The ID of the image
snapshotId uint
// The client for making API calls
client *DigitalOceanClient
} }
func (*Artifact) BuilderId() string { func (*Artifact) BuilderId() string {
...@@ -28,5 +34,6 @@ func (a *Artifact) String() string { ...@@ -28,5 +34,6 @@ func (a *Artifact) String() string {
} }
func (a *Artifact) Destroy() error { func (a *Artifact) Destroy() error {
return errors.New("not implemented yet") log.Printf("Destroying image: %d", a.snapshotId)
return a.client.DestroyImage(a.snapshotId)
} }
...@@ -14,7 +14,7 @@ func TestArtifact_Impl(t *testing.T) { ...@@ -14,7 +14,7 @@ func TestArtifact_Impl(t *testing.T) {
} }
func TestArtifactString(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" expected := "A snapshot was created: packer-foobar"
if a.String() != expected { if a.String() != expected {
......
...@@ -184,7 +184,18 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe ...@@ -184,7 +184,18 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
b.runner.Run(state) 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() { func (b *Builder) Cancel() {
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"log"
) )
type stepSnapshot struct{} type stepSnapshot struct{}
...@@ -15,23 +16,44 @@ func (s *stepSnapshot) Run(state map[string]interface{}) multistep.StepAction { ...@@ -15,23 +16,44 @@ func (s *stepSnapshot) Run(state map[string]interface{}) multistep.StepAction {
dropletId := state["droplet_id"].(uint) dropletId := state["droplet_id"].(uint)
ui.Say(fmt.Sprintf("Creating snapshot: %v", c.SnapshotName)) ui.Say(fmt.Sprintf("Creating snapshot: %v", c.SnapshotName))
err := client.CreateSnapshot(dropletId, c.SnapshotName) err := client.CreateSnapshot(dropletId, c.SnapshotName)
if err != nil { if err != nil {
ui.Error(err.Error()) ui.Error(err.Error())
return multistep.ActionHalt return multistep.ActionHalt
} }
ui.Say("Waiting for snapshot to complete...") ui.Say("Waiting for snapshot to complete...")
err = waitForDropletState("active", dropletId, client) 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 { if err != nil {
ui.Error(err.Error()) ui.Error(err.Error())
return multistep.ActionHalt 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 return multistep.ActionContinue
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment