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