blob: 500d46c09df22fb69e19937aa18a2f1a2c1b4b1d [file] [log] [blame]
Pratik Mallya5fddb2a2015-09-14 14:04:49 -05001package stacks
2
3import (
4 "errors"
5 "fmt"
6 "github.com/rackspace/gophercloud"
7 "reflect"
8 "strings"
9)
10
11type Template struct {
12 TE
13}
14
15var TemplateFormatVersions = map[string]bool{
16 "HeatTemplateFormatVersion": true,
17 "heat_template_version": true,
18 "AWSTemplateFormatVersion": true,
19}
20
21func (t *Template) Validate() error {
22 if t.Parsed == nil {
23 if err := t.Parse(); err != nil {
24 return err
25 }
26 }
27 for key, _ := range t.Parsed {
28 if _, ok := TemplateFormatVersions[key]; ok {
29 return nil
30 }
31 }
32 return errors.New(fmt.Sprintf("Template format version not found."))
33}
34
35func GetFileContents(t *Template, te interface{}, ignoreIf igFunc, recurse bool) error {
36 if t.Files == nil {
37 t.Files = make(map[string]string)
38 }
39 if t.fileMaps == nil {
40 t.fileMaps = make(map[string]string)
41 }
42 switch te.(type) {
43 case map[string]interface{}, map[interface{}]interface{}:
44 te_map, err := toStringKeys(te)
45 if err != nil {
46 return err
47 }
48 for k, v := range te_map {
49 value, ok := v.(string)
50 if !ok {
51 if err := GetFileContents(t, v, ignoreIf, recurse); err != nil {
52 return err
53 }
54 } else if !ignoreIf(k, value) {
55 // at this point, the k, v pair has a reference to an external template.
56 // The assumption of heatclient is that value v is a relative reference
57 // to a file in the users environment
58 childTemplate := new(Template)
59 baseURL, err := gophercloud.NormalizePathURL(t.baseURL, value)
60 if err != nil {
61 return err
62 }
63 childTemplate.baseURL = baseURL
64 childTemplate.client = t.client
65 if err := childTemplate.Parse(); err != nil {
66 return err
67 }
68 // process child template recursively if required
69 if recurse {
70 if err := GetFileContents(childTemplate, childTemplate.Parsed, ignoreIf, recurse); err != nil {
71 return err
72 }
73 }
74 // update parent template with current child templates' content
75 t.fileMaps[value] = childTemplate.URL
76 t.Files[childTemplate.URL] = string(childTemplate.Bin)
77
78 }
79 }
80 return nil
81 case []interface{}:
82 te_slice := te.([]interface{})
83 for i := range te_slice {
84 if err := GetFileContents(t, te_slice[i], ignoreIf, recurse); err != nil {
85 return err
86 }
87 }
88 case string, bool, float64, nil, int:
89 return nil
90 default:
91 return errors.New(fmt.Sprintf("%v: Unrecognized type", reflect.TypeOf(te)))
92
93 }
94 return nil
95}
96
97// function to choose keys whose values are other template files
98func ignoreIfTemplate(key string, value interface{}) bool {
99 // key must be either `get_file` or `type` for value to be a URL
100 if key != "get_file" && key != "type" {
101 return true
102 }
103 // value must be a string
104 valueString, ok := value.(string)
105 if !ok {
106 return true
107 }
108 // `.template` and `.yaml` are allowed suffixes for template URLs when referred to by `type`
109 if key == "type" && !(strings.HasSuffix(valueString, ".template") || strings.HasSuffix(valueString, ".yaml")) {
110 return true
111 }
112 return false
113}