blob: 8d27e7b4703027857a6cce5cbda5b19f4c3835de [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 Perritt35e27e42014-12-05 11:10:46 -070052 DisableRollback *bool
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 Perritt35e27e42014-12-05 11:10:46 -0700158 DisableRollback *bool
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 {
325 Environment string
326 Files map[string]interface{}
327 Parameters map[string]string
328 Template string
329 TemplateURL string
330 Timeout int
331}
332
333// ToStackUpdateMap casts a CreateOpts struct to a map.
334func (opts UpdateOpts) ToStackUpdateMap() (map[string]interface{}, error) {
335 s := make(map[string]interface{})
336
337 if opts.Template != "" {
338 s["template"] = opts.Template
339 } else if opts.TemplateURL != "" {
340 s["template_url"] = opts.TemplateURL
341 } else {
342 return s, errors.New("Either Template or TemplateURL must be provided.")
343 }
344
345 if opts.Environment != "" {
346 s["environment"] = opts.Environment
347 }
348
349 if opts.Files != nil {
350 s["files"] = opts.Files
351 }
352
353 if opts.Parameters != nil {
354 s["parameters"] = opts.Parameters
355 }
356
357 if opts.Timeout != 0 {
358 s["timeout_mins"] = opts.Timeout
359 }
360
361 return s, nil
362}
363
364// Update accepts an UpdateOpts struct and updates an existing stack using the values
365// provided.
366func Update(c *gophercloud.ServiceClient, stackName, stackID string, opts UpdateOptsBuilder) UpdateResult {
367 var res UpdateResult
368
369 reqBody, err := opts.ToStackUpdateMap()
370 if err != nil {
371 res.Err = err
372 return res
373 }
374
375 // Send request to API
376 _, res.Err = perigee.Request("PUT", updateURL(c, stackName, stackID), perigee.Options{
377 MoreHeaders: c.AuthenticatedHeaders(),
378 ReqBody: &reqBody,
379 OkCodes: []int{202},
380 })
381 return res
382}
383
384// Delete deletes a stack based on the stack name and stack ID.
385func Delete(c *gophercloud.ServiceClient, stackName, stackID string) DeleteResult {
386 var res DeleteResult
387
388 // Send request to API
389 _, res.Err = perigee.Request("DELETE", deleteURL(c, stackName, stackID), perigee.Options{
390 MoreHeaders: c.AuthenticatedHeaders(),
391 OkCodes: []int{204},
392 })
393 return res
394}
395
396// PreviewOptsBuilder is the interface options structs have to satisfy in order
397// to be used in the Preview operation in this package.
398type PreviewOptsBuilder interface {
399 ToStackPreviewMap() (map[string]interface{}, error)
400}
401
402// PreviewOpts contains the common options struct used in this package's Preview
403// operation.
404type PreviewOpts struct {
405 DisableRollback *bool
406 Environment string
407 Files map[string]interface{}
408 Name string
409 Parameters map[string]string
410 Template string
411 TemplateURL string
412 Timeout int
413}
414
415// ToStackPreviewMap casts a PreviewOpts struct to a map.
416func (opts PreviewOpts) ToStackPreviewMap() (map[string]interface{}, error) {
417 s := make(map[string]interface{})
418
419 if opts.Name == "" {
420 return s, errors.New("Required field 'Name' not provided.")
421 }
422 s["stack_name"] = opts.Name
423
424 if opts.Template != "" {
425 s["template"] = opts.Template
426 } else if opts.TemplateURL != "" {
427 s["template_url"] = opts.TemplateURL
428 } else {
429 return s, errors.New("Either Template or TemplateURL must be provided.")
430 }
431
432 if opts.DisableRollback != nil {
433 s["disable_rollback"] = &opts.DisableRollback
434 }
435
436 if opts.Environment != "" {
437 s["environment"] = opts.Environment
438 }
439 if opts.Files != nil {
440 s["files"] = opts.Files
441 }
442 if opts.Parameters != nil {
443 s["parameters"] = opts.Parameters
444 }
445
446 if opts.Timeout != 0 {
447 s["timeout_mins"] = opts.Timeout
448 }
449
450 return s, nil
451}
452
453// Preview accepts a PreviewOptsBuilder interface and creates a preview of a stack using the values
454// provided.
455func Preview(c *gophercloud.ServiceClient, opts PreviewOptsBuilder) PreviewResult {
456 var res PreviewResult
457
458 reqBody, err := opts.ToStackPreviewMap()
459 if err != nil {
460 res.Err = err
461 return res
462 }
463
464 // Send request to API
465 _, res.Err = perigee.Request("POST", previewURL(c), perigee.Options{
466 MoreHeaders: c.AuthenticatedHeaders(),
467 ReqBody: &reqBody,
468 Results: &res.Body,
469 OkCodes: []int{200},
470 })
471 return res
472}
473
474// Abandon deletes the stack with the provided stackName and stackID, but leaves its
475// resources intact, and returns data describing the stack and its resources.
476func Abandon(c *gophercloud.ServiceClient, stackName, stackID string) AbandonResult {
477 var res AbandonResult
478
479 // Send request to API
Jon Perritt6e844732015-01-29 14:49:34 -0700480 _, res.Err = perigee.Request("POST", abandonURL(c, stackName, stackID), perigee.Options{
Jon Perritt35e27e42014-12-05 11:10:46 -0700481 MoreHeaders: c.AuthenticatedHeaders(),
482 Results: &res.Body,
483 OkCodes: []int{200},
484 })
485 return res
486}