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