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  }