Improve documentation of functions and methods
diff --git a/openstack/orchestration/v1/stacks/environment.go b/openstack/orchestration/v1/stacks/environment.go
index 378eeaf..1fc01e0 100644
--- a/openstack/orchestration/v1/stacks/environment.go
+++ b/openstack/orchestration/v1/stacks/environment.go
@@ -32,29 +32,41 @@
return nil
}
-// Parse environment file to resolve the urls of the resources
+// Parse environment file to resolve the URL's of the resources. This is done by
+// reading from the `Resource Registry` section, which is why the function is
+// named GetRRFileContents.
func GetRRFileContents(e *Environment, ignoreIf igFunc) error {
+ // initialize environment if empty
if e.Files == nil {
e.Files = make(map[string]string)
}
if e.fileMaps == nil {
e.fileMaps = make(map[string]string)
}
+
+ // get the resource registry
rr := e.Parsed["resource_registry"]
+
// search the resource registry for URLs
switch rr.(type) {
+ // process further only if the resource registry is a map
case map[string]interface{}, map[interface{}]interface{}:
rr_map, err := toStringKeys(rr)
if err != nil {
return err
}
+ // the resource registry might contain a base URL for the resource. If
+ // such a field is present, use it. Otherwise, use the default base URL.
var baseURL string
if val, ok := rr_map["base_url"]; ok {
baseURL = val.(string)
} else {
baseURL = e.baseURL
}
- // use a fake template to fetch contents from URLs
+
+ // The contents of the resource may be located in a remote file, which
+ // will be a template. Instantiate a temporary template to manage the
+ // contents.
tempTemplate := new(Template)
tempTemplate.baseURL = baseURL
tempTemplate.client = e.client
@@ -65,6 +77,7 @@
// check the `resources` section (if it exists) for more URLs
if val, ok := rr_map["resources"]; ok {
switch val.(type) {
+ // process further only if the contents are a map
case map[string]interface{}, map[interface{}]interface{}:
resources_map, err := toStringKeys(val)
if err != nil {
@@ -89,11 +102,11 @@
return err
}
}
-
}
-
}
}
+ // if the resource registry contained any URL's, store them. This can
+ // then be passed as parameter to api calls to Heat api.
e.Files = tempTemplate.Files
return nil
default:
diff --git a/openstack/orchestration/v1/stacks/template.go b/openstack/orchestration/v1/stacks/template.go
index 500d46c..912546b 100644
--- a/openstack/orchestration/v1/stacks/template.go
+++ b/openstack/orchestration/v1/stacks/template.go
@@ -32,7 +32,14 @@
return errors.New(fmt.Sprintf("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 GetFileContents(t *Template, te interface{}, ignoreIf igFunc, recurse bool) error {
+ // initialize template if empty
if t.Files == nil {
t.Files = make(map[string]string)
}
@@ -40,6 +47,7 @@
t.fileMaps = make(map[string]string)
}
switch te.(type) {
+ // if te is a map
case map[string]interface{}, map[interface{}]interface{}:
te_map, err := toStringKeys(te)
if err != nil {
@@ -48,36 +56,50 @@
for k, v := range te_map {
value, ok := v.(string)
if !ok {
+ // if the value is not a string, recursively parse that value
if err := GetFileContents(t, 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 relative reference
+ // 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
+
+ // process child template recursively if required. This is
+ // required if the child template itself contains references to
+ // other templates
if recurse {
if err := GetFileContents(childTemplate, childTemplate.Parsed, ignoreIf, recurse); err != nil {
return err
}
}
- // update parent template with current child templates' content
+ // 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{}:
te_slice := te.([]interface{})
for i := range te_slice {
@@ -85,6 +107,7 @@
return err
}
}
+ // if te is anything else, return
case string, bool, float64, nil, int:
return nil
default:
diff --git a/openstack/orchestration/v1/stacks/utils.go b/openstack/orchestration/v1/stacks/utils.go
index 2b80c6a..299cbc0 100644
--- a/openstack/orchestration/v1/stacks/utils.go
+++ b/openstack/orchestration/v1/stacks/utils.go
@@ -14,10 +14,14 @@
"gopkg.in/yaml.v2"
)
+// Client is an interface that expects a Get method similar to http.Get. This
+// is needed for unit testing, since we can mock an http client. Thus, the
+// client will usually be an http.Client EXCEPT in unit tests.
type Client interface {
Get(string) (*http.Response, error)
}
+// Base structure for both Template and Environment
type TE struct {
// Bin stores the contents of the template or environment.
Bin []byte
@@ -38,8 +42,10 @@
client Client
}
+// Fetch fetches the contents of a TE from its URL. Once a TE structure has a
+// URL, call the fetch method to fetch the contents.
func (t *TE) Fetch() error {
- // get baseURL if not already defined
+ // if the baseURL is not provided, use the current directors as the base URL
if t.baseURL == "" {
u, err := getBasePath()
if err != nil {
@@ -47,19 +53,26 @@
}
t.baseURL = u
}
+
+ // if the contents are already present, do nothing.
if t.Bin != nil {
- // already have contents
return nil
}
+
+ // get a fqdn from the URL using the baseURL of the template. For local files,
+ // the URL's will have the `file` scheme.
u, err := gophercloud.NormalizePathURL(t.baseURL, t.URL)
if err != nil {
return err
}
t.URL = u
+
// get an HTTP client if none present
if t.client == nil {
t.client = getHTTPClient()
}
+
+ // use the client to fetch the contents of the template
resp, err := t.client.Get(t.URL)
if err != nil {
return err
@@ -73,7 +86,7 @@
return nil
}
-// get the basepath of the template
+// get the basepath of the template.
func getBasePath() (string, error) {
basePath, err := filepath.Abs(".")
if err != nil {
@@ -86,14 +99,15 @@
return u, nil
}
-// get a an HTTP client to retrieve URLs
+// get a an HTTP client to retrieve URL's. This client allows the use of `file`
+// scheme since we may need to fetch templates from users filesystem
func getHTTPClient() Client {
transport := &http.Transport{}
transport.RegisterProtocol("file", http.NewFileTransport(http.Dir("/")))
return &http.Client{Transport: transport}
}
-// parse the contents and validate
+// Parse will parse the contents and then validate. The contents MUST be either JSON or YAML.
func (t *TE) Parse() error {
if err := t.Fetch(); err != nil {
return err
@@ -106,11 +120,13 @@
return t.Validate()
}
-// base Validate method, always returns true
+// base Validate method, always returns nil
func (t *TE) Validate() error {
return nil
}
+// igfunc is a parameter used by GetFileContents and GetRRFileContents to check
+// for valid template URL's.
type igFunc func(string, interface{}) bool
// convert map[interface{}]interface{} to map[string]interface{}
@@ -132,7 +148,8 @@
}
}
-// fix the template reference to files
+// fix the template reference to files by replacing relative URL's by absolute
+// URL's
func (t *TE) FixFileRefs() {
t_str := string(t.Bin)
if t.fileMaps == nil {