builder/digitalocean: Implement Artifact destroy
/cc @pearkes
This commit is contained in:
parent
c3de114585
commit
91253c4f32
|
@ -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 (
|
||||||
|
|
||||||
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 {
|
||||||
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)
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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 (
|
||||||
"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 {
|
||||||
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 {
|
if err != nil {
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
return multistep.ActionHalt
|
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
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue