server.go 2.53 KB
Newer Older
1 2 3 4 5 6
package openstack

import (
	"errors"
	"fmt"
	"github.com/mitchellh/multistep"
7
	"github.com/racker/perigee"
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
	"github.com/rackspace/gophercloud"
	"log"
	"time"
)

// StateRefreshFunc is a function type used for StateChangeConf that is
// responsible for refreshing the item being watched for a state change.
//
// It returns three results. `result` is any object that will be returned
// as the final object after waiting for state change. This allows you to
// return the final updated object, for example an openstack instance after
// refreshing it.
//
// `state` is the latest state of that object. And `err` is any error that
// may have happened while refreshing the state.
type StateRefreshFunc func() (result interface{}, state string, progress int, err error)

// StateChangeConf is the configuration struct used for `WaitForState`.
type StateChangeConf struct {
	Pending   []string
	Refresh   StateRefreshFunc
29
	StepState multistep.StateBag
30 31 32 33
	Target    string
}

// ServerStateRefreshFunc returns a StateRefreshFunc that is used to watch
Ian Delahorne's avatar
Ian Delahorne committed
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 40 41 42 43 44 45 46
			urce, ok := err.(*perigee.UnexpectedResponseCodeError)
			if ok && (urce.Actual == 404) {
				log.Printf("404 on ServerStateRefresh, returning DELETED")

				return nil, "DELETED", 0, nil
			} else {
				log.Printf("Error on ServerStateRefresh: %s", err)
				return nil, "", 0, err
47 48
			}
		}
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
		return resp, resp.Status, resp.Progress, nil
	}
}

// WaitForState watches an object and waits for it to achieve a certain
// state.
func WaitForState(conf *StateChangeConf) (i interface{}, err error) {
	log.Printf("Waiting for state to become: %s", conf.Target)

	for {
		var currentProgress int
		var currentState string
		i, currentState, currentProgress, err = conf.Refresh()
		if err != nil {
			return
		}

		if currentState == conf.Target {
			return
		}

		if conf.StepState != nil {
71
			if _, ok := conf.StepState.GetOk(multistep.StateCancelled); ok {
72 73 74 75 76 77 78 79 80 81 82 83 84
				return nil, errors.New("interrupted")
			}
		}

		found := false
		for _, allowed := range conf.Pending {
			if currentState == allowed {
				found = true
				break
			}
		}

		if !found {
85
			return nil, fmt.Errorf("unexpected state '%s', wanted target '%s'", currentState, conf.Target)
86 87 88 89 90 91 92 93
		}

		log.Printf("Waiting for state to become: %s currently %s (%d%%)", conf.Target, currentState, currentProgress)
		time.Sleep(2 * time.Second)
	}

	return
}