waiter.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package waiter
  2. import (
  3. "fmt"
  4. "reflect"
  5. "time"
  6. "github.com/aws/aws-sdk-go/aws"
  7. "github.com/aws/aws-sdk-go/aws/awserr"
  8. "github.com/aws/aws-sdk-go/aws/awsutil"
  9. "github.com/aws/aws-sdk-go/aws/request"
  10. )
  11. // A Config provides a collection of configuration values to setup a generated
  12. // waiter code with.
  13. type Config struct {
  14. Name string
  15. Delay int
  16. MaxAttempts int
  17. Operation string
  18. Acceptors []WaitAcceptor
  19. }
  20. // A WaitAcceptor provides the information needed to wait for an API operation
  21. // to complete.
  22. type WaitAcceptor struct {
  23. Expected interface{}
  24. Matcher string
  25. State string
  26. Argument string
  27. }
  28. // A Waiter provides waiting for an operation to complete.
  29. type Waiter struct {
  30. Config
  31. Client interface{}
  32. Input interface{}
  33. }
  34. // Wait waits for an operation to complete, expire max attempts, or fail. Error
  35. // is returned if the operation fails.
  36. func (w *Waiter) Wait() error {
  37. client := reflect.ValueOf(w.Client)
  38. in := reflect.ValueOf(w.Input)
  39. method := client.MethodByName(w.Config.Operation + "Request")
  40. for i := 0; i < w.MaxAttempts; i++ {
  41. res := method.Call([]reflect.Value{in})
  42. req := res[0].Interface().(*request.Request)
  43. req.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Waiter"))
  44. err := req.Send()
  45. for _, a := range w.Acceptors {
  46. result := false
  47. var vals []interface{}
  48. switch a.Matcher {
  49. case "pathAll", "path":
  50. // Require all matches to be equal for result to match
  51. vals, _ = awsutil.ValuesAtPath(req.Data, a.Argument)
  52. if len(vals) == 0 {
  53. break
  54. }
  55. result = true
  56. for _, val := range vals {
  57. if !awsutil.DeepEqual(val, a.Expected) {
  58. result = false
  59. break
  60. }
  61. }
  62. case "pathAny":
  63. // Only a single match needs to equal for the result to match
  64. vals, _ = awsutil.ValuesAtPath(req.Data, a.Argument)
  65. for _, val := range vals {
  66. if awsutil.DeepEqual(val, a.Expected) {
  67. result = true
  68. break
  69. }
  70. }
  71. case "status":
  72. s := a.Expected.(int)
  73. result = s == req.HTTPResponse.StatusCode
  74. case "error":
  75. if aerr, ok := err.(awserr.Error); ok {
  76. result = aerr.Code() == a.Expected.(string)
  77. }
  78. case "pathList":
  79. // ignored matcher
  80. default:
  81. logf(client, "WARNING: Waiter for %s encountered unexpected matcher: %s",
  82. w.Config.Operation, a.Matcher)
  83. }
  84. if !result {
  85. // If there was no matching result found there is nothing more to do
  86. // for this response, retry the request.
  87. continue
  88. }
  89. switch a.State {
  90. case "success":
  91. // waiter completed
  92. return nil
  93. case "failure":
  94. // Waiter failure state triggered
  95. return awserr.New("ResourceNotReady",
  96. fmt.Sprintf("failed waiting for successful resource state"), err)
  97. case "retry":
  98. // clear the error and retry the operation
  99. err = nil
  100. default:
  101. logf(client, "WARNING: Waiter for %s encountered unexpected state: %s",
  102. w.Config.Operation, a.State)
  103. }
  104. }
  105. if err != nil {
  106. return err
  107. }
  108. time.Sleep(time.Second * time.Duration(w.Delay))
  109. }
  110. return awserr.New("ResourceNotReady",
  111. fmt.Sprintf("exceeded %d wait attempts", w.MaxAttempts), nil)
  112. }
  113. func logf(client reflect.Value, msg string, args ...interface{}) {
  114. cfgVal := client.FieldByName("Config")
  115. if !cfgVal.IsValid() {
  116. return
  117. }
  118. if cfg, ok := cfgVal.Interface().(*aws.Config); ok && cfg.Logger != nil {
  119. cfg.Logger.Log(fmt.Sprintf(msg, args...))
  120. }
  121. }