waiters.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. // +build codegen
  2. package api
  3. import (
  4. "bytes"
  5. "encoding/json"
  6. "fmt"
  7. "os"
  8. "sort"
  9. "strings"
  10. "text/template"
  11. )
  12. // WaiterAcceptor is the acceptors defined in the model the SDK will use
  13. // to wait on resource states with.
  14. type WaiterAcceptor struct {
  15. State string
  16. Matcher string
  17. Argument string
  18. Expected interface{}
  19. }
  20. // ExpectedString returns the string that was expected by the WaiterAcceptor
  21. func (a *WaiterAcceptor) ExpectedString() string {
  22. switch a.Expected.(type) {
  23. case string:
  24. return fmt.Sprintf("%q", a.Expected)
  25. default:
  26. return fmt.Sprintf("%v", a.Expected)
  27. }
  28. }
  29. // A Waiter is an individual waiter definition.
  30. type Waiter struct {
  31. Name string
  32. Delay int
  33. MaxAttempts int
  34. OperationName string `json:"operation"`
  35. Operation *Operation
  36. Acceptors []WaiterAcceptor
  37. }
  38. // WaitersGoCode generates and returns Go code for each of the waiters of
  39. // this API.
  40. func (a *API) WaitersGoCode() string {
  41. var buf bytes.Buffer
  42. fmt.Fprintf(&buf, "import (\n%q\n\n%q\n%q\n)",
  43. "time",
  44. "github.com/aws/aws-sdk-go/aws",
  45. "github.com/aws/aws-sdk-go/aws/request",
  46. )
  47. for _, w := range a.Waiters {
  48. buf.WriteString(w.GoCode())
  49. }
  50. return buf.String()
  51. }
  52. // used for unmarshaling from the waiter JSON file
  53. type waiterDefinitions struct {
  54. *API
  55. Waiters map[string]Waiter
  56. }
  57. // AttachWaiters reads a file of waiter definitions, and adds those to the API.
  58. // Will panic if an error occurs.
  59. func (a *API) AttachWaiters(filename string) {
  60. p := waiterDefinitions{API: a}
  61. f, err := os.Open(filename)
  62. defer f.Close()
  63. if err != nil {
  64. panic(err)
  65. }
  66. err = json.NewDecoder(f).Decode(&p)
  67. if err != nil {
  68. panic(err)
  69. }
  70. p.setup()
  71. }
  72. func (p *waiterDefinitions) setup() {
  73. p.API.Waiters = []Waiter{}
  74. i, keys := 0, make([]string, len(p.Waiters))
  75. for k := range p.Waiters {
  76. keys[i] = k
  77. i++
  78. }
  79. sort.Strings(keys)
  80. for _, n := range keys {
  81. e := p.Waiters[n]
  82. n = p.ExportableName(n)
  83. e.Name = n
  84. e.OperationName = p.ExportableName(e.OperationName)
  85. e.Operation = p.API.Operations[e.OperationName]
  86. if e.Operation == nil {
  87. panic("unknown operation " + e.OperationName + " for waiter " + n)
  88. }
  89. p.API.Waiters = append(p.API.Waiters, e)
  90. }
  91. }
  92. var waiterTmpls = template.Must(template.New("waiterTmpls").Funcs(
  93. template.FuncMap{
  94. "titleCase": func(v string) string {
  95. return strings.Title(v)
  96. },
  97. },
  98. ).Parse(`
  99. {{ define "waiter"}}
  100. // WaitUntil{{ .Name }} uses the {{ .Operation.API.NiceName }} API operation
  101. // {{ .OperationName }} to wait for a condition to be met before returning.
  102. // If the condition is not meet within the max attempt window an error will
  103. // be returned.
  104. func (c *{{ .Operation.API.StructName }}) WaitUntil{{ .Name }}(input {{ .Operation.InputRef.GoType }}) error {
  105. return c.WaitUntil{{ .Name }}WithContext(aws.BackgroundContext(), input)
  106. }
  107. // WaitUntil{{ .Name }}WithContext is an extended version of WaitUntil{{ .Name }}.
  108. // With the support for passing in a context and options to configure the
  109. // Waiter and the underlying request options.
  110. //
  111. // The context must be non-nil and will be used for request cancellation. If
  112. // the context is nil a panic will occur. In the future the SDK may create
  113. // sub-contexts for http.Requests. See https://golang.org/pkg/context/
  114. // for more information on using Contexts.
  115. func (c *{{ .Operation.API.StructName }}) WaitUntil{{ .Name }}WithContext(` +
  116. `ctx aws.Context, input {{ .Operation.InputRef.GoType }}, opts ...request.WaiterOption) error {
  117. w := request.Waiter{
  118. Name: "WaitUntil{{ .Name }}",
  119. MaxAttempts: {{ .MaxAttempts }},
  120. Delay: request.ConstantWaiterDelay({{ .Delay }} * time.Second),
  121. Acceptors: []request.WaiterAcceptor{
  122. {{ range $_, $a := .Acceptors }}{
  123. State: request.{{ titleCase .State }}WaiterState,
  124. Matcher: request.{{ titleCase .Matcher }}WaiterMatch,
  125. {{- if .Argument }}Argument: "{{ .Argument }}",{{ end }}
  126. Expected: {{ .ExpectedString }},
  127. },
  128. {{ end }}
  129. },
  130. Logger: c.Config.Logger,
  131. NewRequest: func(opts []request.Option) (*request.Request, error) {
  132. var inCpy {{ .Operation.InputRef.GoType }}
  133. if input != nil {
  134. tmp := *input
  135. inCpy = &tmp
  136. }
  137. req, _ := c.{{ .OperationName }}Request(inCpy)
  138. req.SetContext(ctx)
  139. req.ApplyOptions(opts...)
  140. return req, nil
  141. },
  142. }
  143. w.ApplyOptions(opts...)
  144. return w.WaitWithContext(ctx)
  145. }
  146. {{- end }}
  147. {{ define "waiter interface" }}
  148. WaitUntil{{ .Name }}({{ .Operation.InputRef.GoTypeWithPkgName }}) error
  149. WaitUntil{{ .Name }}WithContext(aws.Context, {{ .Operation.InputRef.GoTypeWithPkgName }}, ...request.WaiterOption) error
  150. {{- end }}
  151. `))
  152. // InterfaceSignature returns a string representing the Waiter's interface
  153. // function signature.
  154. func (w *Waiter) InterfaceSignature() string {
  155. var buf bytes.Buffer
  156. if err := waiterTmpls.ExecuteTemplate(&buf, "waiter interface", w); err != nil {
  157. panic(err)
  158. }
  159. return strings.TrimSpace(buf.String())
  160. }
  161. // GoCode returns the generated Go code for an individual waiter.
  162. func (w *Waiter) GoCode() string {
  163. var buf bytes.Buffer
  164. if err := waiterTmpls.ExecuteTemplate(&buf, "waiter", w); err != nil {
  165. panic(err)
  166. }
  167. return buf.String()
  168. }