github.com/huaweicloud/golangsdk@v0.0.0-20210831081626-d823fe11ceba/openstack/rts/v1/stacks/utils.go (about) 1 package stacks 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 "path/filepath" 9 "reflect" 10 "strings" 11 12 "github.com/huaweicloud/golangsdk" 13 yaml "gopkg.in/yaml.v2" 14 ) 15 16 // Client is an interface that expects a Get method similar to http.Get. This 17 // is needed for unit testing, since we can mock an http client. Thus, the 18 // client will usually be an http.Client EXCEPT in unit tests. 19 type Client interface { 20 Get(string) (*http.Response, error) 21 } 22 23 // TE is a base structure for both Template and Environment 24 type TE struct { 25 // Bin stores the contents of the template or environment. 26 Bin []byte 27 // URL stores the URL of the template. This is allowed to be a 'file://' 28 // for local files. 29 URL string 30 // Parsed contains a parsed version of Bin. Since there are 2 different 31 // fields referring to the same value, you must be careful when accessing 32 // this filed. 33 Parsed map[string]interface{} 34 // Files contains a mapping between the urls in templates to their contents. 35 Files map[string]string 36 // fileMaps is a map used internally when determining Files. 37 fileMaps map[string]string 38 // baseURL represents the location of the template or environment file. 39 baseURL string 40 // client is an interface which allows TE to fetch contents from URLS 41 client Client 42 } 43 44 // Fetch fetches the contents of a TE from its URL. Once a TE structure has a 45 // URL, call the fetch method to fetch the contents. 46 func (t *TE) Fetch() error { 47 // if the baseURL is not provided, use the current directors as the base URL 48 if t.baseURL == "" { 49 u, err := getBasePath() 50 if err != nil { 51 return err 52 } 53 t.baseURL = u 54 } 55 56 // if the contents are already present, do nothing. 57 if t.Bin != nil { 58 return nil 59 } 60 61 // get a fqdn from the URL using the baseURL of the TE. For local files, 62 // the URL's will have the `file` scheme. 63 u, err := golangsdk.NormalizePathURL(t.baseURL, t.URL) 64 if err != nil { 65 return err 66 } 67 t.URL = u 68 69 // get an HTTP client if none present 70 if t.client == nil { 71 t.client = getHTTPClient() 72 } 73 74 // use the client to fetch the contents of the TE 75 resp, err := t.client.Get(t.URL) 76 if err != nil { 77 return err 78 } 79 defer resp.Body.Close() 80 body, err := ioutil.ReadAll(resp.Body) 81 if err != nil { 82 return err 83 } 84 t.Bin = body 85 return nil 86 } 87 88 // get the basepath of the TE 89 func getBasePath() (string, error) { 90 basePath, err := filepath.Abs(".") 91 if err != nil { 92 return "", err 93 } 94 u, err := golangsdk.NormalizePathURL("", basePath) 95 if err != nil { 96 return "", err 97 } 98 return u, nil 99 } 100 101 // get a an HTTP client to retrieve URL's. This client allows the use of `file` 102 // scheme since we may need to fetch files from users filesystem 103 func getHTTPClient() Client { 104 transport := &http.Transport{} 105 transport.RegisterProtocol("file", http.NewFileTransport(http.Dir("/"))) 106 return &http.Client{Transport: transport} 107 } 108 109 // Parse will parse the contents and then validate. The contents MUST be either JSON or YAML. 110 func (t *TE) Parse() error { 111 if err := t.Fetch(); err != nil { 112 return err 113 } 114 if jerr := json.Unmarshal(t.Bin, &t.Parsed); jerr != nil { 115 if yerr := yaml.Unmarshal(t.Bin, &t.Parsed); yerr != nil { 116 return ErrInvalidDataFormat{} 117 } 118 } 119 return t.Validate() 120 } 121 122 // Validate validates the contents of TE 123 func (t *TE) Validate() error { 124 return nil 125 } 126 127 // igfunc is a parameter used by GetFileContents and GetRRFileContents to check 128 // for valid URL's. 129 type igFunc func(string, interface{}) bool 130 131 // convert map[interface{}]interface{} to map[string]interface{} 132 func toStringKeys(m interface{}) (map[string]interface{}, error) { 133 switch m.(type) { 134 case map[string]interface{}, map[interface{}]interface{}: 135 typedMap := make(map[string]interface{}) 136 if _, ok := m.(map[interface{}]interface{}); ok { 137 for k, v := range m.(map[interface{}]interface{}) { 138 typedMap[k.(string)] = v 139 } 140 } else { 141 typedMap = m.(map[string]interface{}) 142 } 143 return typedMap, nil 144 default: 145 return nil, golangsdk.ErrUnexpectedType{Expected: "map[string]interface{}/map[interface{}]interface{}", Actual: fmt.Sprintf("%v", reflect.TypeOf(m))} 146 } 147 } 148 149 // fix the reference to files by replacing relative URL's by absolute 150 // URL's 151 func (t *TE) fixFileRefs() { 152 tStr := string(t.Bin) 153 if t.fileMaps == nil { 154 return 155 } 156 for k, v := range t.fileMaps { 157 tStr = strings.Replace(tStr, k, v, -1) 158 } 159 t.Bin = []byte(tStr) 160 }