github.com/jerryclinesmith/packer@v0.3.7/builder/amazon/common/instance.go (about)

     1  package common
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"github.com/mitchellh/goamz/ec2"
     7  	"github.com/mitchellh/multistep"
     8  	"log"
     9  	"time"
    10  )
    11  
    12  // StateRefreshFunc is a function type used for StateChangeConf that is
    13  // responsible for refreshing the item being watched for a state change.
    14  //
    15  // It returns three results. `result` is any object that will be returned
    16  // as the final object after waiting for state change. This allows you to
    17  // return the final updated object, for example an EC2 instance after refreshing
    18  // it.
    19  //
    20  // `state` is the latest state of that object. And `err` is any error that
    21  // may have happened while refreshing the state.
    22  type StateRefreshFunc func() (result interface{}, state string, err error)
    23  
    24  // StateChangeConf is the configuration struct used for `WaitForState`.
    25  type StateChangeConf struct {
    26  	Conn      *ec2.EC2
    27  	Pending   []string
    28  	Refresh   StateRefreshFunc
    29  	StepState multistep.StateBag
    30  	Target    string
    31  }
    32  
    33  // InstanceStateRefreshFunc returns a StateRefreshFunc that is used to watch
    34  // an EC2 instance.
    35  func InstanceStateRefreshFunc(conn *ec2.EC2, i *ec2.Instance) StateRefreshFunc {
    36  	return func() (interface{}, string, error) {
    37  		resp, err := conn.Instances([]string{i.InstanceId}, ec2.NewFilter())
    38  		if err != nil {
    39  			log.Printf("Error on InstanceStateRefresh: %s", err)
    40  			return nil, "", err
    41  		}
    42  
    43  		if len(resp.Reservations) == 0 || len(resp.Reservations[0].Instances) == 0 {
    44  			// Sometimes AWS just has consistency issues and doesn't see
    45  			// our instance yet. Return an empty state.
    46  			return nil, "", nil
    47  		}
    48  
    49  		i = &resp.Reservations[0].Instances[0]
    50  		return i, i.State.Name, nil
    51  	}
    52  }
    53  
    54  // WaitForState watches an object and waits for it to achieve a certain
    55  // state.
    56  func WaitForState(conf *StateChangeConf) (i interface{}, err error) {
    57  	log.Printf("Waiting for state to become: %s", conf.Target)
    58  
    59  	for {
    60  		var currentState string
    61  		i, currentState, err = conf.Refresh()
    62  		if err != nil {
    63  			return
    64  		}
    65  
    66  		// Check states only if we were able to refresh to an instance
    67  		// that exists.
    68  		if i != nil {
    69  			if currentState == conf.Target {
    70  				return
    71  			}
    72  
    73  			if conf.StepState != nil {
    74  				if _, ok := conf.StepState.GetOk(multistep.StateCancelled); ok {
    75  					return nil, errors.New("interrupted")
    76  				}
    77  			}
    78  
    79  			found := false
    80  			for _, allowed := range conf.Pending {
    81  				if currentState == allowed {
    82  					found = true
    83  					break
    84  				}
    85  			}
    86  
    87  			if !found {
    88  				fmt.Errorf("unexpected state '%s', wanted target '%s'", currentState, conf.Target)
    89  				return
    90  			}
    91  		}
    92  
    93  		time.Sleep(2 * time.Second)
    94  	}
    95  
    96  	return
    97  }