github.com/huaweicloud/golangsdk@v0.0.0-20210831081626-d823fe11ceba/openstack/rts/v1/stacks/template.go (about) 1 package stacks 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 7 8 "github.com/huaweicloud/golangsdk" 9 ) 10 11 // Template is a structure that represents OpenStack Heat templates 12 type Template struct { 13 TE 14 } 15 16 // TemplateFormatVersions is a map containing allowed variations of the template format version 17 // Note that this contains the permitted variations of the _keys_ not the values. 18 var TemplateFormatVersions = map[string]bool{ 19 "HeatTemplateFormatVersion": true, 20 "heat_template_version": true, 21 "AWSTemplateFormatVersion": true, 22 } 23 24 // Validate validates the contents of the Template 25 func (t *Template) Validate() error { 26 if t.Parsed == nil { 27 if err := t.Parse(); err != nil { 28 return err 29 } 30 } 31 var invalid string 32 for key := range t.Parsed { 33 if _, ok := TemplateFormatVersions[key]; ok { 34 return nil 35 } 36 invalid = key 37 } 38 return ErrInvalidTemplateFormatVersion{Version: invalid} 39 } 40 41 // GetFileContents recursively parses a template to search for urls. These urls 42 // are assumed to point to other templates (known in OpenStack Heat as child 43 // templates). The contents of these urls are fetched and stored in the `Files` 44 // parameter of the template structure. This is the only way that a user can 45 // use child templates that are located in their filesystem; urls located on the 46 // web (e.g. on github or swift) can be fetched directly by Heat engine. 47 func (t *Template) getFileContents(te interface{}, ignoreIf igFunc, recurse bool) error { 48 // initialize template if empty 49 if t.Files == nil { 50 t.Files = make(map[string]string) 51 } 52 if t.fileMaps == nil { 53 t.fileMaps = make(map[string]string) 54 } 55 switch te.(type) { 56 // if te is a map 57 case map[string]interface{}, map[interface{}]interface{}: 58 teMap, err := toStringKeys(te) 59 if err != nil { 60 return err 61 } 62 for k, v := range teMap { 63 value, ok := v.(string) 64 if !ok { 65 // if the value is not a string, recursively parse that value 66 if err := t.getFileContents(v, ignoreIf, recurse); err != nil { 67 return err 68 } 69 } else if !ignoreIf(k, value) { 70 // at this point, the k, v pair has a reference to an external template. 71 // The assumption of heatclient is that value v is a reference 72 // to a file in the users environment 73 74 // create a new child template 75 childTemplate := new(Template) 76 77 // initialize child template 78 79 // get the base location of the child template 80 baseURL, err := golangsdk.NormalizePathURL(t.baseURL, value) 81 if err != nil { 82 return err 83 } 84 childTemplate.baseURL = baseURL 85 childTemplate.client = t.client 86 87 // fetch the contents of the child template 88 if err := childTemplate.Parse(); err != nil { 89 return err 90 } 91 92 // process child template recursively if required. This is 93 // required if the child template itself contains references to 94 // other templates 95 if recurse { 96 if err := childTemplate.getFileContents(childTemplate.Parsed, ignoreIf, recurse); err != nil { 97 return err 98 } 99 } 100 // update parent template with current child templates' content. 101 // At this point, the child template has been parsed recursively. 102 t.fileMaps[value] = childTemplate.URL 103 t.Files[childTemplate.URL] = string(childTemplate.Bin) 104 105 } 106 } 107 return nil 108 // if te is a slice, call the function on each element of the slice. 109 case []interface{}: 110 teSlice := te.([]interface{}) 111 for i := range teSlice { 112 if err := t.getFileContents(teSlice[i], ignoreIf, recurse); err != nil { 113 return err 114 } 115 } 116 // if te is anything else, return 117 case string, bool, float64, nil, int: 118 return nil 119 default: 120 return golangsdk.ErrUnexpectedType{Actual: fmt.Sprintf("%v", reflect.TypeOf(te))} 121 } 122 return nil 123 } 124 125 // function to choose keys whose values are other template files 126 func ignoreIfTemplate(key string, value interface{}) bool { 127 // key must be either `get_file` or `type` for value to be a URL 128 if key != "get_file" && key != "type" { 129 return true 130 } 131 // value must be a string 132 valueString, ok := value.(string) 133 if !ok { 134 return true 135 } 136 // `.template` and `.yaml` are allowed suffixes for template URLs when referred to by `type` 137 if key == "type" && !(strings.HasSuffix(valueString, ".template") || strings.HasSuffix(valueString, ".yaml")) { 138 return true 139 } 140 return false 141 }