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
|
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.
|
// CreateImage registers a GCE Image with a project.
|
||||||
func (g *GoogleComputeClient) CreateImage(name, description, sourceURL string) (*compute.Operation, error) {
|
func (g *GoogleComputeClient) CreateImage(name, description, sourceURL string) (*compute.Operation, error) {
|
||||||
imageRawDisk := &compute.ImageRawDisk{
|
imageRawDisk := &compute.ImageRawDisk{
|
||||||
|
@ -98,26 +86,6 @@ func (g *GoogleComputeClient) CreateImage(name, description, sourceURL string) (
|
||||||
return operation, nil
|
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.
|
// ZoneOperationStatus returns the status for the named zone operation.
|
||||||
func (g *GoogleComputeClient) ZoneOperationStatus(zone, name string) (string, error) {
|
func (g *GoogleComputeClient) ZoneOperationStatus(zone, name string) (string, error) {
|
||||||
zoneOperationsGetCall := g.Service.ZoneOperations.Get(g.ProjectId, zone, name)
|
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{
|
steps := []multistep.Step{
|
||||||
new(StepCreateSSHKey),
|
new(StepCreateSSHKey),
|
||||||
new(StepCreateInstance),
|
new(StepCreateInstance),
|
||||||
|
new(StepInstanceInfo),
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
new(stepInstanceInfo),
|
|
||||||
&common.StepConnectSSH{
|
&common.StepConnectSSH{
|
||||||
SSHAddress: sshAddress,
|
SSHAddress: sshAddress,
|
||||||
SSHConfig: sshConfig,
|
SSHConfig: sshConfig,
|
||||||
|
|
|
@ -7,8 +7,14 @@ type Driver interface {
|
||||||
// DeleteInstance deletes the given instance.
|
// DeleteInstance deletes the given instance.
|
||||||
DeleteInstance(zone, name string) (<-chan error, error)
|
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 takes the given config and launches an instance.
|
||||||
RunInstance(*InstanceConfig) (<-chan error, error)
|
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 {
|
type InstanceConfig struct {
|
||||||
|
|
|
@ -70,6 +70,27 @@ func (d *driverGCE) DeleteInstance(zone, name string) (<-chan error, error) {
|
||||||
return errCh, nil
|
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) {
|
func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) {
|
||||||
// Get the zone
|
// Get the zone
|
||||||
d.ui.Message(fmt.Sprintf("Loading zone: %s", c.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
|
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) {
|
func (d *driverGCE) getImage(name string) (image *compute.Image, err error) {
|
||||||
projects := []string{d.projectId, "debian-cloud", "centos-cloud"}
|
projects := []string{d.projectId, "debian-cloud", "centos-cloud"}
|
||||||
for _, project := range projects {
|
for _, project := range projects {
|
||||||
|
@ -173,6 +200,16 @@ func (d *driverGCE) getImage(name string) (image *compute.Image, err error) {
|
||||||
return
|
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 {
|
func (d *driverGCE) refreshZoneOp(zone string, op *compute.Operation) stateRefreshFunc {
|
||||||
return func() (string, error) {
|
return func() (string, error) {
|
||||||
newOp, err := d.service.ZoneOperations.Get(d.projectId, zone, op.Name).Do()
|
newOp, err := d.service.ZoneOperations.Get(d.projectId, zone, op.Name).Do()
|
||||||
|
|
|
@ -8,9 +8,19 @@ type DriverMock struct {
|
||||||
DeleteInstanceErrCh <-chan error
|
DeleteInstanceErrCh <-chan error
|
||||||
DeleteInstanceErr error
|
DeleteInstanceErr error
|
||||||
|
|
||||||
|
GetNatIPZone string
|
||||||
|
GetNatIPName string
|
||||||
|
GetNatIPResult string
|
||||||
|
GetNatIPErr error
|
||||||
|
|
||||||
RunInstanceConfig *InstanceConfig
|
RunInstanceConfig *InstanceConfig
|
||||||
RunInstanceErrCh <-chan error
|
RunInstanceErrCh <-chan error
|
||||||
RunInstanceErr error
|
RunInstanceErr error
|
||||||
|
|
||||||
|
WaitForInstanceState string
|
||||||
|
WaitForInstanceZone string
|
||||||
|
WaitForInstanceName string
|
||||||
|
WaitForInstanceErrCh <-chan error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DriverMock) DeleteInstance(zone, name string) (<-chan error, 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
|
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) {
|
func (d *DriverMock) RunInstance(c *InstanceConfig) (<-chan error, error) {
|
||||||
d.RunInstanceConfig = c
|
d.RunInstanceConfig = c
|
||||||
|
|
||||||
|
@ -39,3 +55,18 @@ func (d *DriverMock) RunInstance(c *InstanceConfig) (<-chan error, error) {
|
||||||
|
|
||||||
return resultCh, d.RunInstanceErr
|
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
|
package googlecompute
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mitchellh/multistep"
|
"github.com/mitchellh/multistep"
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// stepInstanceInfo represents a Packer build step that gathers GCE instance info.
|
// 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.
|
// Run executes the Packer build step that gathers GCE instance info.
|
||||||
func (s *stepInstanceInfo) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *StepInstanceInfo) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
var (
|
config := state.Get("config").(*Config)
|
||||||
client = state.Get("client").(*GoogleComputeClient)
|
driver := state.Get("driver").(Driver)
|
||||||
config = state.Get("config").(*Config)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
ui = state.Get("ui").(packer.Ui)
|
|
||||||
)
|
|
||||||
instanceName := state.Get("instance_name").(string)
|
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 {
|
if err != nil {
|
||||||
err := fmt.Errorf("Error creating instance: %s", err)
|
err := fmt.Errorf("Error waiting for instance: %s", err)
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
ip, err := client.GetNatIP(config.Zone, instanceName)
|
|
||||||
|
ip, err := driver.GetNatIP(config.Zone, instanceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("Error retrieving instance nat ip address: %s", err)
|
err := fmt.Errorf("Error retrieving instance nat ip address: %s", err)
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui.Message(fmt.Sprintf("IP: %s", ip))
|
||||||
state.Put("instance_ip", ip)
|
state.Put("instance_ip", ip)
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup.
|
// 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