packer-cn/builder/triton/driver_triton.go

186 lines
4.8 KiB
Go

package triton
import (
"context"
"errors"
"net/http"
"time"
"github.com/hashicorp/packer/packer"
"github.com/joyent/triton-go"
)
type driverTriton struct {
client *triton.Client
ui packer.Ui
}
func NewDriverTriton(ui packer.Ui, config Config) (Driver, error) {
client, err := config.AccessConfig.CreateTritonClient()
if err != nil {
return nil, err
}
return &driverTriton{
client: client,
ui: ui,
}, nil
}
func (d *driverTriton) CreateImageFromMachine(machineId string, config Config) (string, error) {
image, err := d.client.Images().CreateImageFromMachine(context.Background(), &triton.CreateImageFromMachineInput{
MachineID: machineId,
Name: config.ImageName,
Version: config.ImageVersion,
Description: config.ImageDescription,
HomePage: config.ImageHomepage,
EULA: config.ImageEULA,
ACL: config.ImageACL,
Tags: config.ImageTags,
})
if err != nil {
return "", err
}
return image.ID, err
}
func (d *driverTriton) CreateMachine(config Config) (string, error) {
input := &triton.CreateMachineInput{
Package: config.MachinePackage,
Image: config.MachineImage,
Metadata: config.MachineMetadata,
Tags: config.MachineTags,
FirewallEnabled: config.MachineFirewallEnabled,
}
if config.MachineName == "" {
// If not supplied generate a name for the source VM: "packer-builder-[image_name]".
// The version is not used because it can contain characters invalid for a VM name.
input.Name = "packer-builder-" + config.ImageName
} else {
input.Name = config.MachineName
}
if len(config.MachineNetworks) > 0 {
input.Networks = config.MachineNetworks
}
machine, err := d.client.Machines().CreateMachine(context.Background(), input)
if err != nil {
return "", err
}
return machine.ID, nil
}
func (d *driverTriton) DeleteImage(imageId string) error {
return d.client.Images().DeleteImage(context.Background(), &triton.DeleteImageInput{
ImageID: imageId,
})
}
func (d *driverTriton) DeleteMachine(machineId string) error {
return d.client.Machines().DeleteMachine(context.Background(), &triton.DeleteMachineInput{
ID: machineId,
})
}
func (d *driverTriton) GetMachineIP(machineId string) (string, error) {
machine, err := d.client.Machines().GetMachine(context.Background(), &triton.GetMachineInput{
ID: machineId,
})
if err != nil {
return "", err
}
return machine.PrimaryIP, nil
}
func (d *driverTriton) StopMachine(machineId string) error {
return d.client.Machines().StopMachine(context.Background(), &triton.StopMachineInput{
MachineID: machineId,
})
}
// waitForMachineState uses the supplied client to wait for the state of
// the machine with the given ID to reach the state described in state.
// If timeout is reached before the machine reaches the required state, an
// error is returned. If the machine reaches the target state within the
// timeout, nil is returned.
func (d *driverTriton) WaitForMachineState(machineId string, state string, timeout time.Duration) error {
return waitFor(
func() (bool, error) {
machine, err := d.client.Machines().GetMachine(context.Background(), &triton.GetMachineInput{
ID: machineId,
})
if machine == nil {
return false, err
}
return machine.State == state, err
},
3*time.Second,
timeout,
)
}
// waitForMachineDeletion uses the supplied client to wait for the machine
// with the given ID to be deleted. It is expected that the API call to delete
// the machine has already been issued at this point.
func (d *driverTriton) WaitForMachineDeletion(machineId string, timeout time.Duration) error {
return waitFor(
func() (bool, error) {
_, err := d.client.Machines().GetMachine(context.Background(), &triton.GetMachineInput{
ID: machineId,
})
if err != nil {
// Return true only when we receive a 410 (Gone) response. A 404
// indicates that the machine is being deleted whereas a 410 indicates
// that this process has completed.
if triErr, ok := err.(*triton.TritonError); ok && triErr.StatusCode == http.StatusGone {
return true, nil
}
}
return false, err
},
3*time.Second,
timeout,
)
}
func (d *driverTriton) WaitForImageCreation(imageId string, timeout time.Duration) error {
return waitFor(
func() (bool, error) {
image, err := d.client.Images().GetImage(context.Background(), &triton.GetImageInput{
ImageID: imageId,
})
if image == nil {
return false, err
}
return image.OS != "", err
},
3*time.Second,
timeout,
)
}
func waitFor(f func() (bool, error), every, timeout time.Duration) error {
start := time.Now()
for time.Since(start) <= timeout {
stop, err := f()
if err != nil {
return err
}
if stop {
return nil
}
time.Sleep(every)
}
return errors.New("Timed out while waiting for resource change")
}