blob: 81b4ea0fd9df617090ec69f0158cce8ec203bf1b [file] [log] [blame]
Jon Perritt35e27e42014-12-05 11:10:46 -07001package stacks
2
3import (
4 "errors"
5
6 "github.com/racker/perigee"
7 "github.com/rackspace/gophercloud"
8 "github.com/rackspace/gophercloud/pagination"
9)
10
Jon Perrittb20ba0b2015-02-03 13:13:42 -070011type Rollback *bool
12
13var (
14 disable = true
15 Disable Rollback = &disable
16 enable = false
17 Enable Rollback = &enable
18)
19
Jon Perritt35e27e42014-12-05 11:10:46 -070020// CreateOptsBuilder is the interface options structs have to satisfy in order
21// to be used in the main Create operation in this package. Since many
22// extensions decorate or modify the common logic, it is useful for them to
23// satisfy a basic interface in order for them to be used.
24type CreateOptsBuilder interface {
25 ToStackCreateMap() (map[string]interface{}, error)
26}
27
28// CreateOpts is the common options struct used in this package's Create
29// operation.
30type CreateOpts struct {
Jon Perritt952f3e12015-02-03 12:13:24 -070031 // (REQUIRED) The name of the stack. It must start with an alphabetic character.
32 Name string
33 // (REQUIRED) The timeout for stack creation in minutes.
34 Timeout int
35 // (OPTIONAL; REQUIRED IF Template IS EMPTY) The URL of the template to instantiate.
36 // This value is ignored if Template is supplied inline.
37 TemplateURL string
38 // (OPTIONAL; REQUIRED IF TemplateURL IS EMPTY) A template to instantiate. The value
39 // is a stringified version of the JSON/YAML template. Since the template will likely
40 // be located in a file, one way to set this variable is by using ioutil.ReadFile:
41 // import "io/ioutil"
42 // var opts stacks.CreateOpts
43 // b, err := ioutil.ReadFile("path/to/you/template/file.json")
44 // if err != nil {
45 // // handle error...
46 // }
47 // opts.Template = string(b)
48 Template string
49 // (OPTIONAL) Enables or disables deletion of all stack resources when a stack
50 // creation fails. Default is true, meaning all resources are not deleted when
51 // stack creation fails.
Jon Perritt37f97742015-02-04 18:55:05 -070052 DisableRollback Rollback
Jon Perritt952f3e12015-02-03 12:13:24 -070053 // (OPTIONAL) A stringified JSON environment for the stack.
54 Environment string
55 // (OPTIONAL) A map that maps file names to file contents. It can also be used
56 // to pass provider template contents. Example:
57 // Files: `{"myfile": "#!/bin/bash\necho 'Hello world' > /root/testfile.txt"}`
58 Files map[string]interface{}
59 // (OPTIONAL) User-defined parameters to pass to the template.
60 Parameters map[string]string
Jon Perritt35e27e42014-12-05 11:10:46 -070061}
62
63// ToStackCreateMap casts a CreateOpts struct to a map.
64func (opts CreateOpts) ToStackCreateMap() (map[string]interface{}, error) {
65 s := make(map[string]interface{})
66
67 if opts.Name == "" {
68 return s, errors.New("Required field 'Name' not provided.")
69 }
70 s["stack_name"] = opts.Name
71
72 if opts.Template != "" {
73 s["template"] = opts.Template
74 } else if opts.TemplateURL != "" {
75 s["template_url"] = opts.TemplateURL
76 } else {
77 return s, errors.New("Either Template or TemplateURL must be provided.")
78 }
79
80 if opts.DisableRollback != nil {
81 s["disable_rollback"] = &opts.DisableRollback
82 }
83
84 if opts.Environment != "" {
85 s["environment"] = opts.Environment
86 }
87 if opts.Files != nil {
88 s["files"] = opts.Files
89 }
90 if opts.Parameters != nil {
91 s["parameters"] = opts.Parameters
92 }
93
Jon Perrittb20ba0b2015-02-03 13:13:42 -070094 if opts.Timeout == 0 {
95 return nil, errors.New("Required field 'Timeout' not provided.")
Jon Perritt35e27e42014-12-05 11:10:46 -070096 }
Jon Perrittb20ba0b2015-02-03 13:13:42 -070097 s["timeout_mins"] = opts.Timeout
Jon Perritt35e27e42014-12-05 11:10:46 -070098
99 return s, nil
100}
101
102// Create accepts a CreateOpts struct and creates a new stack using the values
103// provided.
104func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
105 var res CreateResult
106
107 reqBody, err := opts.ToStackCreateMap()
108 if err != nil {
109 res.Err = err
110 return res
111 }
112
113 // Send request to API
114 _, res.Err = perigee.Request("POST", createURL(c), perigee.Options{
115 MoreHeaders: c.AuthenticatedHeaders(),
116 ReqBody: &reqBody,
117 Results: &res.Body,
118 OkCodes: []int{201},
119 })
120 return res
121}
122
123// AdoptOptsBuilder is the interface options structs have to satisfy in order
124// to be used in the Adopt function in this package. Since many
125// extensions decorate or modify the common logic, it is useful for them to
126// satisfy a basic interface in order for them to be used.
127type AdoptOptsBuilder interface {
128 ToStackAdoptMap() (map[string]interface{}, error)
129}
130
131// AdoptOpts is the common options struct used in this package's Adopt
132// operation.
133type AdoptOpts struct {
Jon Perritt9741dd92015-02-04 12:05:47 -0700134 // (REQUIRED) Existing resources data represented as a string to add to the
135 // new stack. Data returned by Abandon could be provided as AdoptsStackData.
136 AdoptStackData string
137 // (REQUIRED) The name of the stack. It must start with an alphabetic character.
138 Name string
139 // (REQUIRED) The timeout for stack creation in minutes.
140 Timeout int
141 // (OPTIONAL; REQUIRED IF Template IS EMPTY) The URL of the template to instantiate.
142 // This value is ignored if Template is supplied inline.
143 TemplateURL string
144 // (OPTIONAL; REQUIRED IF TemplateURL IS EMPTY) A template to instantiate. The value
145 // is a stringified version of the JSON/YAML template. Since the template will likely
146 // be located in a file, one way to set this variable is by using ioutil.ReadFile:
147 // import "io/ioutil"
148 // var opts stacks.CreateOpts
149 // b, err := ioutil.ReadFile("path/to/you/template/file.json")
150 // if err != nil {
151 // // handle error...
152 // }
153 // opts.Template = string(b)
154 Template string
155 // (OPTIONAL) Enables or disables deletion of all stack resources when a stack
156 // creation fails. Default is true, meaning all resources are not deleted when
157 // stack creation fails.
Jon Perritt37f97742015-02-04 18:55:05 -0700158 DisableRollback Rollback
Jon Perritt9741dd92015-02-04 12:05:47 -0700159 // (OPTIONAL) A stringified JSON environment for the stack.
160 Environment string
161 // (OPTIONAL) A map that maps file names to file contents. It can also be used
162 // to pass provider template contents. Example:
163 // Files: `{"myfile": "#!/bin/bash\necho 'Hello world' > /root/testfile.txt"}`
164 Files map[string]interface{}
165 // (OPTIONAL) User-defined parameters to pass to the template.
166 Parameters map[string]string
Jon Perritt35e27e42014-12-05 11:10:46 -0700167}
168
169// ToStackAdoptMap casts a CreateOpts struct to a map.
170func (opts AdoptOpts) ToStackAdoptMap() (map[string]interface{}, error) {
171 s := make(map[string]interface{})
172
173 if opts.Name == "" {
174 return s, errors.New("Required field 'Name' not provided.")
175 }
176 s["stack_name"] = opts.Name
177
178 if opts.Template != "" {
179 s["template"] = opts.Template
180 } else if opts.TemplateURL != "" {
181 s["template_url"] = opts.TemplateURL
182 } else {
183 return s, errors.New("Either Template or TemplateURL must be provided.")
184 }
185
186 if opts.AdoptStackData == "" {
187 return s, errors.New("Required field 'AdoptStackData' not provided.")
188 }
189 s["adopt_stack_data"] = opts.AdoptStackData
190
191 if opts.DisableRollback != nil {
192 s["disable_rollback"] = &opts.DisableRollback
193 }
194
195 if opts.Environment != "" {
196 s["environment"] = opts.Environment
197 }
198 if opts.Files != nil {
199 s["files"] = opts.Files
200 }
201 if opts.Parameters != nil {
202 s["parameters"] = opts.Parameters
203 }
204
Jon Perritt9741dd92015-02-04 12:05:47 -0700205 if opts.Timeout == 0 {
206 return nil, errors.New("Required field 'Timeout' not provided.")
Jon Perritt35e27e42014-12-05 11:10:46 -0700207 }
Jon Perritt9741dd92015-02-04 12:05:47 -0700208 s["timeout_mins"] = opts.Timeout
Jon Perritt35e27e42014-12-05 11:10:46 -0700209
210 return map[string]interface{}{"stack": s}, nil
211}
212
213// Adopt accepts an AdoptOpts struct and creates a new stack using the resources
214// from another stack.
Jon Perritt9741dd92015-02-04 12:05:47 -0700215func Adopt(c *gophercloud.ServiceClient, opts AdoptOptsBuilder) AdoptResult {
216 var res AdoptResult
Jon Perritt35e27e42014-12-05 11:10:46 -0700217
218 reqBody, err := opts.ToStackAdoptMap()
219 if err != nil {
220 res.Err = err
221 return res
222 }
223
224 // Send request to API
225 _, res.Err = perigee.Request("POST", adoptURL(c), perigee.Options{
226 MoreHeaders: c.AuthenticatedHeaders(),
227 ReqBody: &reqBody,
228 Results: &res.Body,
229 OkCodes: []int{201},
230 })
231 return res
232}
233
234// SortDir is a type for specifying in which direction to sort a list of stacks.
235type SortDir string
236
237// SortKey is a type for specifying by which key to sort a list of stacks.
238type SortKey string
239
240var (
241 // SortAsc is used to sort a list of stacks in ascending order.
242 SortAsc SortDir = "asc"
243 // SortDesc is used to sort a list of stacks in descending order.
244 SortDesc SortDir = "desc"
245 // SortName is used to sort a list of stacks by name.
246 SortName SortKey = "name"
247 // SortStatus is used to sort a list of stacks by status.
248 SortStatus SortKey = "status"
249 // SortCreatedAt is used to sort a list of stacks by date created.
250 SortCreatedAt SortKey = "created_at"
251 // SortUpdatedAt is used to sort a list of stacks by date updated.
252 SortUpdatedAt SortKey = "updated_at"
253)
254
255// ListOptsBuilder allows extensions to add additional parameters to the
256// List request.
257type ListOptsBuilder interface {
258 ToStackListQuery() (string, error)
259}
260
261// ListOpts allows the filtering and sorting of paginated collections through
262// the API. Filtering is achieved by passing in struct field values that map to
263// the network attributes you want to see returned. SortKey allows you to sort
264// by a particular network attribute. SortDir sets the direction, and is either
265// `asc' or `desc'. Marker and Limit are used for pagination.
266type ListOpts struct {
267 Status string `q:"status"`
268 Name string `q:"name"`
269 Marker string `q:"marker"`
270 Limit int `q:"limit"`
271 SortKey SortKey `q:"sort_keys"`
272 SortDir SortDir `q:"sort_dir"`
273}
274
275// ToStackListQuery formats a ListOpts into a query string.
276func (opts ListOpts) ToStackListQuery() (string, error) {
277 q, err := gophercloud.BuildQueryString(opts)
278 if err != nil {
279 return "", err
280 }
281 return q.String(), nil
282}
283
284// List returns a Pager which allows you to iterate over a collection of
285// stacks. It accepts a ListOpts struct, which allows you to filter and sort
286// the returned collection for greater efficiency.
287func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
288 url := listURL(c)
289 if opts != nil {
290 query, err := opts.ToStackListQuery()
291 if err != nil {
292 return pagination.Pager{Err: err}
293 }
294 url += query
295 }
296
297 createPage := func(r pagination.PageResult) pagination.Page {
298 return StackPage{pagination.SinglePageBase(r)}
299 }
300 return pagination.NewPager(c, url, createPage)
301}
302
303// Get retreives a stack based on the stack name and stack ID.
304func Get(c *gophercloud.ServiceClient, stackName, stackID string) GetResult {
305 var res GetResult
306
307 // Send request to API
308 _, res.Err = perigee.Request("GET", getURL(c, stackName, stackID), perigee.Options{
309 MoreHeaders: c.AuthenticatedHeaders(),
310 Results: &res.Body,
311 OkCodes: []int{200},
312 })
313 return res
314}
315
316// UpdateOptsBuilder is the interface options structs have to satisfy in order
317// to be used in the Update operation in this package.
318type UpdateOptsBuilder interface {
319 ToStackUpdateMap() (map[string]interface{}, error)
320}
321
322// UpdateOpts contains the common options struct used in this package's Update
323// operation.
324type UpdateOpts struct {
Jon Perritt37f97742015-02-04 18:55:05 -0700325 // (REQUIRED) The timeout for stack creation in minutes.
326 Timeout int
327 // (OPTIONAL; REQUIRED IF Template IS EMPTY) The URL of the template to instantiate.
328 // This value is ignored if Template is supplied inline.
Jon Perritt35e27e42014-12-05 11:10:46 -0700329 TemplateURL string
Jon Perritt37f97742015-02-04 18:55:05 -0700330 // (OPTIONAL; REQUIRED IF TemplateURL IS EMPTY) A template to instantiate. The value
331 // is a stringified version of the JSON/YAML template. Since the template will likely
332 // be located in a file, one way to set this variable is by using ioutil.ReadFile:
333 // import "io/ioutil"
334 // var opts stacks.CreateOpts
335 // b, err := ioutil.ReadFile("path/to/you/template/file.json")
336 // if err != nil {
337 // // handle error...
338 // }
339 // opts.Template = string(b)
340 Template string
341 // (OPTIONAL) A stringified JSON environment for the stack.
342 Environment string
343 // (OPTIONAL) A map that maps file names to file contents. It can also be used
344 // to pass provider template contents. Example:
345 // Files: `{"myfile": "#!/bin/bash\necho 'Hello world' > /root/testfile.txt"}`
346 Files map[string]interface{}
347 // (OPTIONAL) User-defined parameters to pass to the template.
348 Parameters map[string]string
Jon Perritt35e27e42014-12-05 11:10:46 -0700349}
350
351// ToStackUpdateMap casts a CreateOpts struct to a map.
352func (opts UpdateOpts) ToStackUpdateMap() (map[string]interface{}, error) {
353 s := make(map[string]interface{})
354
355 if opts.Template != "" {
356 s["template"] = opts.Template
357 } else if opts.TemplateURL != "" {
358 s["template_url"] = opts.TemplateURL
359 } else {
360 return s, errors.New("Either Template or TemplateURL must be provided.")
361 }
362
363 if opts.Environment != "" {
364 s["environment"] = opts.Environment
365 }
366
367 if opts.Files != nil {
368 s["files"] = opts.Files
369 }
370
371 if opts.Parameters != nil {
372 s["parameters"] = opts.Parameters
373 }
374
375 if opts.Timeout != 0 {
376 s["timeout_mins"] = opts.Timeout
377 }
378
379 return s, nil
380}
381
382// Update accepts an UpdateOpts struct and updates an existing stack using the values
383// provided.
384func Update(c *gophercloud.ServiceClient, stackName, stackID string, opts UpdateOptsBuilder) UpdateResult {
385 var res UpdateResult
386
387 reqBody, err := opts.ToStackUpdateMap()
388 if err != nil {
389 res.Err = err
390 return res
391 }
392
393 // Send request to API
394 _, res.Err = perigee.Request("PUT", updateURL(c, stackName, stackID), perigee.Options{
395 MoreHeaders: c.AuthenticatedHeaders(),
396 ReqBody: &reqBody,
397 OkCodes: []int{202},
398 })
399 return res
400}
401
402// Delete deletes a stack based on the stack name and stack ID.
403func Delete(c *gophercloud.ServiceClient, stackName, stackID string) DeleteResult {
404 var res DeleteResult
405
406 // Send request to API
407 _, res.Err = perigee.Request("DELETE", deleteURL(c, stackName, stackID), perigee.Options{
408 MoreHeaders: c.AuthenticatedHeaders(),
409 OkCodes: []int{204},
410 })
411 return res
412}
413
414// PreviewOptsBuilder is the interface options structs have to satisfy in order
415// to be used in the Preview operation in this package.
416type PreviewOptsBuilder interface {
417 ToStackPreviewMap() (map[string]interface{}, error)
418}
419
420// PreviewOpts contains the common options struct used in this package's Preview
421// operation.
422type PreviewOpts struct {
Jon Perritt37f97742015-02-04 18:55:05 -0700423 // (REQUIRED) The name of the stack. It must start with an alphabetic character.
424 Name string
425 // (REQUIRED) The timeout for stack creation in minutes.
426 Timeout int
427 // (OPTIONAL; REQUIRED IF Template IS EMPTY) The URL of the template to instantiate.
428 // This value is ignored if Template is supplied inline.
429 TemplateURL string
430 // (OPTIONAL; REQUIRED IF TemplateURL IS EMPTY) A template to instantiate. The value
431 // is a stringified version of the JSON/YAML template. Since the template will likely
432 // be located in a file, one way to set this variable is by using ioutil.ReadFile:
433 // import "io/ioutil"
434 // var opts stacks.CreateOpts
435 // b, err := ioutil.ReadFile("path/to/you/template/file.json")
436 // if err != nil {
437 // // handle error...
438 // }
439 // opts.Template = string(b)
440 Template string
441 // (OPTIONAL) Enables or disables deletion of all stack resources when a stack
442 // creation fails. Default is true, meaning all resources are not deleted when
443 // stack creation fails.
444 DisableRollback Rollback
445 // (OPTIONAL) A stringified JSON environment for the stack.
446 Environment string
447 // (OPTIONAL) A map that maps file names to file contents. It can also be used
448 // to pass provider template contents. Example:
449 // Files: `{"myfile": "#!/bin/bash\necho 'Hello world' > /root/testfile.txt"}`
450 Files map[string]interface{}
451 // (OPTIONAL) User-defined parameters to pass to the template.
452 Parameters map[string]string
Jon Perritt35e27e42014-12-05 11:10:46 -0700453}
454
455// ToStackPreviewMap casts a PreviewOpts struct to a map.
456func (opts PreviewOpts) ToStackPreviewMap() (map[string]interface{}, error) {
457 s := make(map[string]interface{})
458
459 if opts.Name == "" {
460 return s, errors.New("Required field 'Name' not provided.")
461 }
462 s["stack_name"] = opts.Name
463
464 if opts.Template != "" {
465 s["template"] = opts.Template
466 } else if opts.TemplateURL != "" {
467 s["template_url"] = opts.TemplateURL
468 } else {
469 return s, errors.New("Either Template or TemplateURL must be provided.")
470 }
471
472 if opts.DisableRollback != nil {
473 s["disable_rollback"] = &opts.DisableRollback
474 }
475
476 if opts.Environment != "" {
477 s["environment"] = opts.Environment
478 }
479 if opts.Files != nil {
480 s["files"] = opts.Files
481 }
482 if opts.Parameters != nil {
483 s["parameters"] = opts.Parameters
484 }
485
486 if opts.Timeout != 0 {
487 s["timeout_mins"] = opts.Timeout
488 }
489
490 return s, nil
491}
492
493// Preview accepts a PreviewOptsBuilder interface and creates a preview of a stack using the values
494// provided.
495func Preview(c *gophercloud.ServiceClient, opts PreviewOptsBuilder) PreviewResult {
496 var res PreviewResult
497
498 reqBody, err := opts.ToStackPreviewMap()
499 if err != nil {
500 res.Err = err
501 return res
502 }
503
504 // Send request to API
505 _, res.Err = perigee.Request("POST", previewURL(c), perigee.Options{
506 MoreHeaders: c.AuthenticatedHeaders(),
507 ReqBody: &reqBody,
508 Results: &res.Body,
509 OkCodes: []int{200},
510 })
511 return res
512}
513
514// Abandon deletes the stack with the provided stackName and stackID, but leaves its
515// resources intact, and returns data describing the stack and its resources.
516func Abandon(c *gophercloud.ServiceClient, stackName, stackID string) AbandonResult {
517 var res AbandonResult
518
519 // Send request to API
Jon Perritt6e844732015-01-29 14:49:34 -0700520 _, res.Err = perigee.Request("POST", abandonURL(c, stackName, stackID), perigee.Options{
Jon Perritt35e27e42014-12-05 11:10:46 -0700521 MoreHeaders: c.AuthenticatedHeaders(),
522 Results: &res.Body,
523 OkCodes: []int{200},
524 })
525 return res
526}