builder/googlecompute: get instance info
This commit is contained in:
parent
57f707dfcc
commit
a72d31fb5b
|
@ -66,18 +66,6 @@ func New(projectId string, zone string, c *clientSecrets, pemKey []byte) (*Googl
|
|||
return googleComputeClient, nil
|
||||
}
|
||||
|
||||
// InstanceStatus returns a string representing the status of the named instance.
|
||||
// Status will be one of: "PROVISIONING", "STAGING", "RUNNING", "STOPPING",
|
||||
// "STOPPED", "TERMINATED".
|
||||
func (g *GoogleComputeClient) InstanceStatus(zone, name string) (string, error) {
|
||||
instanceGetCall := g.Service.Instances.Get(g.ProjectId, zone, name)
|
||||
instance, err := instanceGetCall.Do()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return instance.Status, nil
|
||||
}
|
||||
|
||||
// CreateImage registers a GCE Image with a project.
|
||||
func (g *GoogleComputeClient) CreateImage(name, description, sourceURL string) (*compute.Operation, error) {
|
||||
imageRawDisk := &compute.ImageRawDisk{
|
||||
|
@ -98,26 +86,6 @@ func (g *GoogleComputeClient) CreateImage(name, description, sourceURL string) (
|
|||
return operation, nil
|
||||
}
|
||||
|
||||
// GetNatIp returns the public IPv4 address for named GCE instance.
|
||||
func (g *GoogleComputeClient) GetNatIP(zone, name string) (string, error) {
|
||||
instanceGetCall := g.Service.Instances.Get(g.ProjectId, zone, name)
|
||||
instance, err := instanceGetCall.Do()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, ni := range instance.NetworkInterfaces {
|
||||
if ni.AccessConfigs == nil {
|
||||
continue
|
||||
}
|
||||
for _, ac := range ni.AccessConfigs {
|
||||
if ac.NatIP != "" {
|
||||
return ac.NatIP, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// ZoneOperationStatus returns the status for the named zone operation.
|
||||
func (g *GoogleComputeClient) ZoneOperationStatus(zone, name string) (string, error) {
|
||||
zoneOperationsGetCall := g.Service.ZoneOperations.Get(g.ProjectId, zone, name)
|
||||
|
|
|
@ -50,9 +50,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
steps := []multistep.Step{
|
||||
new(StepCreateSSHKey),
|
||||
new(StepCreateInstance),
|
||||
new(StepInstanceInfo),
|
||||
}
|
||||
/*
|
||||
new(stepInstanceInfo),
|
||||
&common.StepConnectSSH{
|
||||
SSHAddress: sshAddress,
|
||||
SSHConfig: sshConfig,
|
||||
|
|
|
@ -7,8 +7,14 @@ type Driver interface {
|
|||
// DeleteInstance deletes the given instance.
|
||||
DeleteInstance(zone, name string) (<-chan error, error)
|
||||
|
||||
// GetNatIP gets the NAT IP address for the instance.
|
||||
GetNatIP(zone, name string) (string, error)
|
||||
|
||||
// RunInstance takes the given config and launches an instance.
|
||||
RunInstance(*InstanceConfig) (<-chan error, error)
|
||||
|
||||
// WaitForInstance waits for an instance to reach the given state.
|
||||
WaitForInstance(state, zone, name string) <-chan error
|
||||
}
|
||||
|
||||
type InstanceConfig struct {
|
||||
|
|
|
@ -70,6 +70,27 @@ func (d *driverGCE) DeleteInstance(zone, name string) (<-chan error, error) {
|
|||
return errCh, nil
|
||||
}
|
||||
|
||||
func (d *driverGCE) GetNatIP(zone, name string) (string, error) {
|
||||
instance, err := d.service.Instances.Get(d.projectId, zone, name).Do()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, ni := range instance.NetworkInterfaces {
|
||||
if ni.AccessConfigs == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, ac := range ni.AccessConfigs {
|
||||
if ac.NatIP != "" {
|
||||
return ac.NatIP, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) {
|
||||
// Get the zone
|
||||
d.ui.Message(fmt.Sprintf("Loading zone: %s", c.Zone))
|
||||
|
@ -156,6 +177,12 @@ func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) {
|
|||
return errCh, nil
|
||||
}
|
||||
|
||||
func (d *driverGCE) WaitForInstance(state, zone, name string) <-chan error {
|
||||
errCh := make(chan error, 1)
|
||||
go waitForState(errCh, state, d.refreshInstanceState(zone, name))
|
||||
return errCh
|
||||
}
|
||||
|
||||
func (d *driverGCE) getImage(name string) (image *compute.Image, err error) {
|
||||
projects := []string{d.projectId, "debian-cloud", "centos-cloud"}
|
||||
for _, project := range projects {
|
||||
|
@ -173,6 +200,16 @@ func (d *driverGCE) getImage(name string) (image *compute.Image, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (d *driverGCE) refreshInstanceState(zone, name string) stateRefreshFunc {
|
||||
return func() (string, error) {
|
||||
instance, err := d.service.Instances.Get(d.projectId, zone, name).Do()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return instance.Status, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d *driverGCE) refreshZoneOp(zone string, op *compute.Operation) stateRefreshFunc {
|
||||
return func() (string, error) {
|
||||
newOp, err := d.service.ZoneOperations.Get(d.projectId, zone, op.Name).Do()
|
||||
|
|
|
@ -8,9 +8,19 @@ type DriverMock struct {
|
|||
DeleteInstanceErrCh <-chan error
|
||||
DeleteInstanceErr error
|
||||
|
||||
GetNatIPZone string
|
||||
GetNatIPName string
|
||||
GetNatIPResult string
|
||||
GetNatIPErr error
|
||||
|
||||
RunInstanceConfig *InstanceConfig
|
||||
RunInstanceErrCh <-chan error
|
||||
RunInstanceErr error
|
||||
|
||||
WaitForInstanceState string
|
||||
WaitForInstanceZone string
|
||||
WaitForInstanceName string
|
||||
WaitForInstanceErrCh <-chan error
|
||||
}
|
||||
|
||||
func (d *DriverMock) DeleteInstance(zone, name string) (<-chan error, error) {
|
||||
|
@ -27,6 +37,12 @@ func (d *DriverMock) DeleteInstance(zone, name string) (<-chan error, error) {
|
|||
return resultCh, d.DeleteInstanceErr
|
||||
}
|
||||
|
||||
func (d *DriverMock) GetNatIP(zone, name string) (string, error) {
|
||||
d.GetNatIPZone = zone
|
||||
d.GetNatIPName = name
|
||||
return d.GetNatIPResult, d.GetNatIPErr
|
||||
}
|
||||
|
||||
func (d *DriverMock) RunInstance(c *InstanceConfig) (<-chan error, error) {
|
||||
d.RunInstanceConfig = c
|
||||
|
||||
|
@ -39,3 +55,18 @@ func (d *DriverMock) RunInstance(c *InstanceConfig) (<-chan error, error) {
|
|||
|
||||
return resultCh, d.RunInstanceErr
|
||||
}
|
||||
|
||||
func (d *DriverMock) WaitForInstance(state, zone, name string) <-chan error {
|
||||
d.WaitForInstanceState = state
|
||||
d.WaitForInstanceZone = zone
|
||||
d.WaitForInstanceName = name
|
||||
|
||||
resultCh := d.WaitForInstanceErrCh
|
||||
if resultCh == nil {
|
||||
ch := make(chan error)
|
||||
close(ch)
|
||||
resultCh = ch
|
||||
}
|
||||
|
||||
return resultCh
|
||||
}
|
||||
|
|
|
@ -1,40 +1,53 @@
|
|||
package googlecompute
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
)
|
||||
|
||||
// stepInstanceInfo represents a Packer build step that gathers GCE instance info.
|
||||
type stepInstanceInfo int
|
||||
type StepInstanceInfo int
|
||||
|
||||
// Run executes the Packer build step that gathers GCE instance info.
|
||||
func (s *stepInstanceInfo) Run(state multistep.StateBag) multistep.StepAction {
|
||||
var (
|
||||
client = state.Get("client").(*GoogleComputeClient)
|
||||
config = state.Get("config").(*Config)
|
||||
ui = state.Get("ui").(packer.Ui)
|
||||
)
|
||||
func (s *StepInstanceInfo) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
instanceName := state.Get("instance_name").(string)
|
||||
err := waitForInstanceState("RUNNING", config.Zone, instanceName, client, config.stateTimeout)
|
||||
|
||||
ui.Say("Waiting for the instance to become running...")
|
||||
errCh := driver.WaitForInstance("RUNNING", config.Zone, instanceName)
|
||||
var err error
|
||||
select {
|
||||
case err = <-errCh:
|
||||
case <-time.After(config.stateTimeout):
|
||||
err = errors.New("time out while waiting for instance to become running")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating instance: %s", err)
|
||||
err := fmt.Errorf("Error waiting for instance: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ip, err := client.GetNatIP(config.Zone, instanceName)
|
||||
|
||||
ip, err := driver.GetNatIP(config.Zone, instanceName)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error retrieving instance nat ip address: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("IP: %s", ip))
|
||||
state.Put("instance_ip", ip)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Cleanup.
|
||||
func (s *stepInstanceInfo) Cleanup(state multistep.StateBag) {}
|
||||
func (s *StepInstanceInfo) Cleanup(state multistep.StateBag) {}
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
package googlecompute
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/mitchellh/multistep"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestStepInstanceInfo_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepInstanceInfo)
|
||||
}
|
||||
|
||||
func TestStepInstanceInfo(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepInstanceInfo)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("instance_name", "foo")
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
driver.GetNatIPResult = "1.2.3.4"
|
||||
|
||||
// run the step
|
||||
if action := step.Run(state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify state
|
||||
if driver.WaitForInstanceState != "RUNNING" {
|
||||
t.Fatalf("bad: %#v", driver.WaitForInstanceState)
|
||||
}
|
||||
if driver.WaitForInstanceZone != config.Zone {
|
||||
t.Fatalf("bad: %#v", driver.WaitForInstanceZone)
|
||||
}
|
||||
if driver.WaitForInstanceName != "foo" {
|
||||
t.Fatalf("bad: %#v", driver.WaitForInstanceName)
|
||||
}
|
||||
|
||||
ipRaw, ok := state.GetOk("instance_ip")
|
||||
if !ok {
|
||||
t.Fatal("should have ip")
|
||||
}
|
||||
if ip, ok := ipRaw.(string); !ok {
|
||||
t.Fatal("ip is not a string")
|
||||
} else if ip != "1.2.3.4" {
|
||||
t.Fatalf("bad ip: %s", ip)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepInstanceInfo_getNatIPError(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepInstanceInfo)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("instance_name", "foo")
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
driver.GetNatIPErr = errors.New("error")
|
||||
|
||||
// run the step
|
||||
if action := step.Run(state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify state
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
if _, ok := state.GetOk("instance_ip"); ok {
|
||||
t.Fatal("should NOT have instance IP")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepInstanceInfo_waitError(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepInstanceInfo)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("instance_name", "foo")
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
errCh <- errors.New("error")
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
driver.WaitForInstanceErrCh = errCh
|
||||
|
||||
// run the step
|
||||
if action := step.Run(state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify state
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
if _, ok := state.GetOk("instance_ip"); ok {
|
||||
t.Fatal("should NOT have instance IP")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepInstanceInfo_errorTimeout(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepInstanceInfo)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
<-time.After(10 * time.Millisecond)
|
||||
errCh <- nil
|
||||
}()
|
||||
|
||||
state.Put("instance_name", "foo")
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
config.stateTimeout = 1 * time.Microsecond
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
driver.WaitForInstanceErrCh = errCh
|
||||
|
||||
// run the step
|
||||
if action := step.Run(state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify state
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
if _, ok := state.GetOk("instance_ip"); ok {
|
||||
t.Fatal("should NOT have instance IP")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue