github.com/vnpaycloud-console/gophercloud/v2@v2.0.5/openstack/orchestration/v1/stacks/environment.go (about)

     1  package stacks
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	yaml "gopkg.in/yaml.v2"
     8  )
     9  
    10  // Environment is a structure that represents stack environments
    11  type Environment struct {
    12  	TE
    13  }
    14  
    15  // EnvironmentSections is a map containing allowed sections in a stack environment file
    16  var EnvironmentSections = map[string]bool{
    17  	"parameters":         true,
    18  	"parameter_defaults": true,
    19  	"resource_registry":  true,
    20  }
    21  
    22  // Validate validates the contents of the Environment
    23  func (e *Environment) Validate() error {
    24  	if e.Parsed == nil {
    25  		if err := e.Parse(); err != nil {
    26  			return err
    27  		}
    28  	}
    29  	for key := range e.Parsed {
    30  		if _, ok := EnvironmentSections[key]; !ok {
    31  			return ErrInvalidEnvironment{Section: key}
    32  		}
    33  	}
    34  	return nil
    35  }
    36  
    37  // Parse environment file to resolve the URL's of the resources. This is done by
    38  // reading from the `Resource Registry` section, which is why the function is
    39  // named GetRRFileContents.
    40  func (e *Environment) getRRFileContents(ignoreIf igFunc) error {
    41  	// initialize environment if empty
    42  	if e.Files == nil {
    43  		e.Files = make(map[string]string)
    44  	}
    45  	if e.fileMaps == nil {
    46  		e.fileMaps = make(map[string]string)
    47  	}
    48  
    49  	// get the resource registry
    50  	rr := e.Parsed["resource_registry"]
    51  
    52  	// search the resource registry for URLs
    53  	switch rr.(type) {
    54  	// process further only if the resource registry is a map
    55  	case map[string]any, map[any]any:
    56  		rrMap, err := toStringKeys(rr)
    57  		if err != nil {
    58  			return err
    59  		}
    60  		// the resource registry might contain a base URL for the resource. If
    61  		// such a field is present, use it. Otherwise, use the default base URL.
    62  		var baseURL string
    63  		if val, ok := rrMap["base_url"]; ok {
    64  			baseURL = val.(string)
    65  		} else {
    66  			baseURL = e.baseURL
    67  		}
    68  
    69  		// The contents of the resource may be located in a remote file, which
    70  		// will be a template. Instantiate a temporary template to manage the
    71  		// contents.
    72  		tempTemplate := new(Template)
    73  		tempTemplate.baseURL = baseURL
    74  		tempTemplate.client = e.client
    75  
    76  		// Fetch the contents of remote resource URL's
    77  		if err = tempTemplate.getFileContents(rr, ignoreIf, false); err != nil {
    78  			return err
    79  		}
    80  		// check the `resources` section (if it exists) for more URL's. Note that
    81  		// the previous call to GetFileContents was (deliberately) not recursive
    82  		// as we want more control over where to look for URL's
    83  		if val, ok := rrMap["resources"]; ok {
    84  			switch val.(type) {
    85  			// process further only if the contents are a map
    86  			case map[string]any, map[any]any:
    87  				resourcesMap, err := toStringKeys(val)
    88  				if err != nil {
    89  					return err
    90  				}
    91  				for _, v := range resourcesMap {
    92  					switch v.(type) {
    93  					case map[string]any, map[any]any:
    94  						resourceMap, err := toStringKeys(v)
    95  						if err != nil {
    96  							return err
    97  						}
    98  						var resourceBaseURL string
    99  						// if base_url for the resource type is defined, use it
   100  						if val, ok := resourceMap["base_url"]; ok {
   101  							resourceBaseURL = val.(string)
   102  						} else {
   103  							resourceBaseURL = baseURL
   104  						}
   105  						tempTemplate.baseURL = resourceBaseURL
   106  						if err := tempTemplate.getFileContents(v, ignoreIf, false); err != nil {
   107  							return err
   108  						}
   109  					}
   110  				}
   111  			}
   112  		}
   113  		// if the resource registry contained any URL's, store them. This can
   114  		// then be passed as parameter to api calls to Heat api.
   115  		e.Files = tempTemplate.Files
   116  
   117  		// In case some element was updated, regenerate the string representation
   118  		if len(e.Files) > 0 {
   119  			var err error
   120  			e.Bin, err = yaml.Marshal(&e.Parsed)
   121  			if err != nil {
   122  				return fmt.Errorf("failed to marshal updated environment: %w", err)
   123  			}
   124  		}
   125  
   126  		return nil
   127  	default:
   128  		return nil
   129  	}
   130  }
   131  
   132  // function to choose keys whose values are other environment files
   133  func ignoreIfEnvironment(key string, value any) bool {
   134  	// base_url and hooks refer to components which cannot have urls
   135  	if key == "base_url" || key == "hooks" {
   136  		return true
   137  	}
   138  	// if value is not string, it cannot be a URL
   139  	valueString, ok := value.(string)
   140  	if !ok {
   141  		return true
   142  	}
   143  	// if value contains `::`, it must be a reference to another resource type
   144  	// e.g. OS::Nova::Server : Rackspace::Cloud::Server
   145  	if strings.Contains(valueString, "::") {
   146  		return true
   147  	}
   148  	return false
   149  }