github.com/askholme/packer@v0.7.2-0.20140924152349-70d9566a6852/builder/openstack/server.go (about)

     1  package openstack
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"github.com/mitchellh/multistep"
     7  	"github.com/racker/perigee"
     8  	"github.com/rackspace/gophercloud"
     9  	"log"
    10  	"time"
    11  )
    12  
    13  // StateRefreshFunc is a function type used for StateChangeConf that is
    14  // responsible for refreshing the item being watched for a state change.
    15  //
    16  // It returns three results. `result` is any object that will be returned
    17  // as the final object after waiting for state change. This allows you to
    18  // return the final updated object, for example an openstack instance after
    19  // refreshing it.
    20  //
    21  // `state` is the latest state of that object. And `err` is any error that
    22  // may have happened while refreshing the state.
    23  type StateRefreshFunc func() (result interface{}, state string, progress int, err error)
    24  
    25  // StateChangeConf is the configuration struct used for `WaitForState`.
    26  type StateChangeConf struct {
    27  	Pending   []string
    28  	Refresh   StateRefreshFunc
    29  	StepState multistep.StateBag
    30  	Target    string
    31  }
    32  
    33  // ServerStateRefreshFunc returns a StateRefreshFunc that is used to watch
    34  // an openstack server.
    35  func ServerStateRefreshFunc(csp gophercloud.CloudServersProvider, s *gophercloud.Server) StateRefreshFunc {
    36  	return func() (interface{}, string, int, error) {
    37  		resp, err := csp.ServerById(s.Id)
    38  		if err != nil {
    39  			urce, ok := err.(*perigee.UnexpectedResponseCodeError)
    40  			if ok && (urce.Actual == 404) {
    41  				log.Printf("404 on ServerStateRefresh, returning DELETED")
    42  
    43  				return nil, "DELETED", 0, nil
    44  			} else {
    45  				log.Printf("Error on ServerStateRefresh: %s", err)
    46  				return nil, "", 0, err
    47  			}
    48  		}
    49  		return resp, resp.Status, resp.Progress, nil
    50  	}
    51  }
    52  
    53  // WaitForState watches an object and waits for it to achieve a certain
    54  // state.
    55  func WaitForState(conf *StateChangeConf) (i interface{}, err error) {
    56  	log.Printf("Waiting for state to become: %s", conf.Target)
    57  
    58  	for {
    59  		var currentProgress int
    60  		var currentState string
    61  		i, currentState, currentProgress, err = conf.Refresh()
    62  		if err != nil {
    63  			return
    64  		}
    65  
    66  		if currentState == conf.Target {
    67  			return
    68  		}
    69  
    70  		if conf.StepState != nil {
    71  			if _, ok := conf.StepState.GetOk(multistep.StateCancelled); ok {
    72  				return nil, errors.New("interrupted")
    73  			}
    74  		}
    75  
    76  		found := false
    77  		for _, allowed := range conf.Pending {
    78  			if currentState == allowed {
    79  				found = true
    80  				break
    81  			}
    82  		}
    83  
    84  		if !found {
    85  			return nil, fmt.Errorf("unexpected state '%s', wanted target '%s'", currentState, conf.Target)
    86  		}
    87  
    88  		log.Printf("Waiting for state to become: %s currently %s (%d%%)", conf.Target, currentState, currentProgress)
    89  		time.Sleep(2 * time.Second)
    90  	}
    91  
    92  	return
    93  }