github.com/rothwerx/packer@v0.9.0/builder/openstack/server.go (about)

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