File: template.go

package info (click to toggle)
golang-github-rackspace-gophercloud 1.0.0%2Bgit20161013.1012.e00690e8-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 5,148 kB
  • ctags: 6,414
  • sloc: sh: 16; makefile: 6
file content (139 lines) | stat: -rw-r--r-- 4,213 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package stacks

import (
	"fmt"
	"github.com/rackspace/gophercloud"
	"reflect"
	"strings"
)

// Template is a structure that represents OpenStack Heat templates
type Template struct {
	TE
}

// TemplateFormatVersions is a map containing allowed variations of the template format version
// Note that this contains the permitted variations of the _keys_ not the values.
var TemplateFormatVersions = map[string]bool{
	"HeatTemplateFormatVersion": true,
	"heat_template_version":     true,
	"AWSTemplateFormatVersion":  true,
}

// Validate validates the contents of the Template
func (t *Template) Validate() error {
	if t.Parsed == nil {
		if err := t.Parse(); err != nil {
			return err
		}
	}
	for key := range t.Parsed {
		if _, ok := TemplateFormatVersions[key]; ok {
			return nil
		}
	}
	return fmt.Errorf("Template format version not found.")
}

// GetFileContents recursively parses a template to search for urls. These urls
// are assumed to point to other templates (known in OpenStack Heat as child
// templates). The contents of these urls are fetched and stored in the `Files`
// parameter of the template structure. This is the only way that a user can
// use child templates that are located in their filesystem; urls located on the
// web (e.g. on github or swift) can be fetched directly by Heat engine.
func (t *Template) getFileContents(te interface{}, ignoreIf igFunc, recurse bool) error {
	// initialize template if empty
	if t.Files == nil {
		t.Files = make(map[string]string)
	}
	if t.fileMaps == nil {
		t.fileMaps = make(map[string]string)
	}
	switch te.(type) {
	// if te is a map
	case map[string]interface{}, map[interface{}]interface{}:
		teMap, err := toStringKeys(te)
		if err != nil {
			return err
		}
		for k, v := range teMap {
			value, ok := v.(string)
			if !ok {
				// if the value is not a string, recursively parse that value
				if err := t.getFileContents(v, ignoreIf, recurse); err != nil {
					return err
				}
			} else if !ignoreIf(k, value) {
				// at this point, the k, v pair has a reference to an external template.
				// The assumption of heatclient is that value v is a reference
				// to a file in the users environment

				// create a new child template
				childTemplate := new(Template)

				// initialize child template

				// get the base location of the child template
				baseURL, err := gophercloud.NormalizePathURL(t.baseURL, value)
				if err != nil {
					return err
				}
				childTemplate.baseURL = baseURL
				childTemplate.client = t.client

				// fetch the contents of the child template
				if err := childTemplate.Parse(); err != nil {
					return err
				}

				// process child template recursively if required. This is
				// required if the child template itself contains references to
				// other templates
				if recurse {
					if err := childTemplate.getFileContents(childTemplate.Parsed, ignoreIf, recurse); err != nil {
						return err
					}
				}
				// update parent template with current child templates' content.
				// At this point, the child template has been parsed recursively.
				t.fileMaps[value] = childTemplate.URL
				t.Files[childTemplate.URL] = string(childTemplate.Bin)

			}
		}
		return nil
	// if te is a slice, call the function on each element of the slice.
	case []interface{}:
		teSlice := te.([]interface{})
		for i := range teSlice {
			if err := t.getFileContents(teSlice[i], ignoreIf, recurse); err != nil {
				return err
			}
		}
	// if te is anything else, return
	case string, bool, float64, nil, int:
		return nil
	default:
		return fmt.Errorf("%v: Unrecognized type", reflect.TypeOf(te))

	}
	return nil
}

// function to choose keys whose values are other template files
func ignoreIfTemplate(key string, value interface{}) bool {
	// key must be either `get_file` or `type` for value to be a URL
	if key != "get_file" && key != "type" {
		return true
	}
	// value must be a string
	valueString, ok := value.(string)
	if !ok {
		return true
	}
	// `.template` and `.yaml` are allowed suffixes for template URLs when referred to by `type`
	if key == "type" && !(strings.HasSuffix(valueString, ".template") || strings.HasSuffix(valueString, ".yaml")) {
		return true
	}
	return false
}