github.com/jrperritt/terraform@v0.1.1-0.20170525065507-96f391dafc38/builtin/providers/openstack/types.go (about) 1 package openstack 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "log" 10 "net/http" 11 "strings" 12 13 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" 14 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups" 15 "github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets" 16 "github.com/gophercloud/gophercloud/openstack/dns/v2/zones" 17 "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls" 18 "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies" 19 "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules" 20 "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" 21 "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" 22 "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" 23 "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" 24 "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" 25 ) 26 27 // LogRoundTripper satisfies the http.RoundTripper interface and is used to 28 // customize the default http client RoundTripper to allow for logging. 29 type LogRoundTripper struct { 30 Rt http.RoundTripper 31 OsDebug bool 32 } 33 34 // RoundTrip performs a round-trip HTTP request and logs relevant information about it. 35 func (lrt *LogRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) { 36 defer func() { 37 if request.Body != nil { 38 request.Body.Close() 39 } 40 }() 41 42 // for future reference, this is how to access the Transport struct: 43 //tlsconfig := lrt.Rt.(*http.Transport).TLSClientConfig 44 45 var err error 46 47 if lrt.OsDebug { 48 log.Printf("[DEBUG] OpenStack Request URL: %s %s", request.Method, request.URL) 49 50 if request.Body != nil { 51 request.Body, err = lrt.logRequest(request.Body, request.Header) 52 if err != nil { 53 return nil, err 54 } 55 } 56 } 57 58 response, err := lrt.Rt.RoundTrip(request) 59 if response == nil { 60 return nil, err 61 } 62 63 if lrt.OsDebug { 64 response.Body, err = lrt.logResponse(response.Body, response.Header) 65 } 66 67 return response, err 68 } 69 70 // logRequest will log the HTTP Request details. 71 // If the body is JSON, it will attempt to be pretty-formatted. 72 func (lrt *LogRoundTripper) logRequest(original io.ReadCloser, headers http.Header) (io.ReadCloser, error) { 73 defer original.Close() 74 75 var bs bytes.Buffer 76 _, err := io.Copy(&bs, original) 77 if err != nil { 78 return nil, err 79 } 80 81 log.Printf("[DEBUG] Openstack Request headers:\n%s", strings.Join(RedactHeaders(headers), "\n")) 82 83 // Handle request contentType 84 contentType := headers.Get("Content-Type") 85 if strings.HasPrefix(contentType, "application/json") { 86 debugInfo := lrt.formatJSON(bs.Bytes()) 87 log.Printf("[DEBUG] OpenStack Request Body: %s", debugInfo) 88 } else { 89 log.Printf("[DEBUG] OpenStack Request Body: %s", bs.String()) 90 } 91 92 return ioutil.NopCloser(strings.NewReader(bs.String())), nil 93 } 94 95 // logResponse will log the HTTP Response details. 96 // If the body is JSON, it will attempt to be pretty-formatted. 97 func (lrt *LogRoundTripper) logResponse(original io.ReadCloser, headers http.Header) (io.ReadCloser, error) { 98 log.Printf("[DEBUG] Openstack Response headers:\n%s", strings.Join(RedactHeaders(headers), "\n")) 99 100 contentType := headers.Get("Content-Type") 101 if strings.HasPrefix(contentType, "application/json") { 102 var bs bytes.Buffer 103 defer original.Close() 104 _, err := io.Copy(&bs, original) 105 if err != nil { 106 return nil, err 107 } 108 debugInfo := lrt.formatJSON(bs.Bytes()) 109 if debugInfo != "" { 110 log.Printf("[DEBUG] OpenStack Response Body: %s", debugInfo) 111 } 112 return ioutil.NopCloser(strings.NewReader(bs.String())), nil 113 } 114 115 log.Printf("[DEBUG] Not logging because OpenStack response body isn't JSON") 116 return original, nil 117 } 118 119 // formatJSON will try to pretty-format a JSON body. 120 // It will also mask known fields which contain sensitive information. 121 func (lrt *LogRoundTripper) formatJSON(raw []byte) string { 122 var data map[string]interface{} 123 124 err := json.Unmarshal(raw, &data) 125 if err != nil { 126 log.Printf("[DEBUG] Unable to parse OpenStack JSON: %s", err) 127 return string(raw) 128 } 129 130 // Mask known password fields 131 if v, ok := data["auth"].(map[string]interface{}); ok { 132 if v, ok := v["identity"].(map[string]interface{}); ok { 133 if v, ok := v["password"].(map[string]interface{}); ok { 134 if v, ok := v["user"].(map[string]interface{}); ok { 135 v["password"] = "***" 136 } 137 } 138 } 139 } 140 141 // Ignore the catalog 142 if v, ok := data["token"].(map[string]interface{}); ok { 143 if _, ok := v["catalog"]; ok { 144 return "" 145 } 146 } 147 148 pretty, err := json.MarshalIndent(data, "", " ") 149 if err != nil { 150 log.Printf("[DEBUG] Unable to re-marshal OpenStack JSON: %s", err) 151 return string(raw) 152 } 153 154 return string(pretty) 155 } 156 157 // FirewallCreateOpts represents the attributes used when creating a new firewall. 158 type FirewallCreateOpts struct { 159 firewalls.CreateOpts 160 ValueSpecs map[string]string `json:"value_specs,omitempty"` 161 } 162 163 // ToFirewallCreateMap casts a CreateOpts struct to a map. 164 // It overrides firewalls.ToFirewallCreateMap to add the ValueSpecs field. 165 func (opts FirewallCreateOpts) ToFirewallCreateMap() (map[string]interface{}, error) { 166 return BuildRequest(opts, "firewall") 167 } 168 169 // FloatingIPCreateOpts represents the attributes used when creating a new floating ip. 170 type FloatingIPCreateOpts struct { 171 floatingips.CreateOpts 172 ValueSpecs map[string]string `json:"value_specs,omitempty"` 173 } 174 175 // ToFloatingIPCreateMap casts a CreateOpts struct to a map. 176 // It overrides floatingips.ToFloatingIPCreateMap to add the ValueSpecs field. 177 func (opts FloatingIPCreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) { 178 return BuildRequest(opts, "floatingip") 179 } 180 181 // KeyPairCreateOpts represents the attributes used when creating a new keypair. 182 type KeyPairCreateOpts struct { 183 keypairs.CreateOpts 184 ValueSpecs map[string]string `json:"value_specs,omitempty"` 185 } 186 187 // ToKeyPairCreateMap casts a CreateOpts struct to a map. 188 // It overrides keypairs.ToKeyPairCreateMap to add the ValueSpecs field. 189 func (opts KeyPairCreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) { 190 return BuildRequest(opts, "keypair") 191 } 192 193 // NetworkCreateOpts represents the attributes used when creating a new network. 194 type NetworkCreateOpts struct { 195 networks.CreateOpts 196 ValueSpecs map[string]string `json:"value_specs,omitempty"` 197 } 198 199 // ToNetworkCreateMap casts a CreateOpts struct to a map. 200 // It overrides networks.ToNetworkCreateMap to add the ValueSpecs field. 201 func (opts NetworkCreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) { 202 return BuildRequest(opts, "network") 203 } 204 205 // PolicyCreateOpts represents the attributes used when creating a new firewall policy. 206 type PolicyCreateOpts struct { 207 policies.CreateOpts 208 ValueSpecs map[string]string `json:"value_specs,omitempty"` 209 } 210 211 // ToPolicyCreateMap casts a CreateOpts struct to a map. 212 // It overrides policies.ToFirewallPolicyCreateMap to add the ValueSpecs field. 213 func (opts PolicyCreateOpts) ToFirewallPolicyCreateMap() (map[string]interface{}, error) { 214 return BuildRequest(opts, "firewall_policy") 215 } 216 217 // PortCreateOpts represents the attributes used when creating a new port. 218 type PortCreateOpts struct { 219 ports.CreateOpts 220 ValueSpecs map[string]string `json:"value_specs,omitempty"` 221 } 222 223 // ToPortCreateMap casts a CreateOpts struct to a map. 224 // It overrides ports.ToPortCreateMap to add the ValueSpecs field. 225 func (opts PortCreateOpts) ToPortCreateMap() (map[string]interface{}, error) { 226 return BuildRequest(opts, "port") 227 } 228 229 // RecordSetCreateOpts represents the attributes used when creating a new DNS record set. 230 type RecordSetCreateOpts struct { 231 recordsets.CreateOpts 232 ValueSpecs map[string]string `json:"value_specs,omitempty"` 233 } 234 235 // ToRecordSetCreateMap casts a CreateOpts struct to a map. 236 // It overrides recordsets.ToRecordSetCreateMap to add the ValueSpecs field. 237 func (opts RecordSetCreateOpts) ToRecordSetCreateMap() (map[string]interface{}, error) { 238 b, err := BuildRequest(opts, "") 239 if err != nil { 240 return nil, err 241 } 242 243 if m, ok := b[""].(map[string]interface{}); ok { 244 return m, nil 245 } 246 247 return nil, fmt.Errorf("Expected map but got %T", b[""]) 248 } 249 250 // RouterCreateOpts represents the attributes used when creating a new router. 251 type RouterCreateOpts struct { 252 routers.CreateOpts 253 ValueSpecs map[string]string `json:"value_specs,omitempty"` 254 } 255 256 // ToRouterCreateMap casts a CreateOpts struct to a map. 257 // It overrides routers.ToRouterCreateMap to add the ValueSpecs field. 258 func (opts RouterCreateOpts) ToRouterCreateMap() (map[string]interface{}, error) { 259 return BuildRequest(opts, "router") 260 } 261 262 // RuleCreateOpts represents the attributes used when creating a new firewall rule. 263 type RuleCreateOpts struct { 264 rules.CreateOpts 265 ValueSpecs map[string]string `json:"value_specs,omitempty"` 266 } 267 268 // ToRuleCreateMap casts a CreateOpts struct to a map. 269 // It overrides rules.ToRuleCreateMap to add the ValueSpecs field. 270 func (opts RuleCreateOpts) ToRuleCreateMap() (map[string]interface{}, error) { 271 b, err := BuildRequest(opts, "firewall_rule") 272 if err != nil { 273 return nil, err 274 } 275 276 if m := b["firewall_rule"].(map[string]interface{}); m["protocol"] == "any" { 277 m["protocol"] = nil 278 } 279 280 return b, nil 281 } 282 283 // ServerGroupCreateOpts represents the attributes used when creating a new router. 284 type ServerGroupCreateOpts struct { 285 servergroups.CreateOpts 286 ValueSpecs map[string]string `json:"value_specs,omitempty"` 287 } 288 289 // ToServerGroupCreateMap casts a CreateOpts struct to a map. 290 // It overrides routers.ToServerGroupCreateMap to add the ValueSpecs field. 291 func (opts ServerGroupCreateOpts) ToServerGroupCreateMap() (map[string]interface{}, error) { 292 return BuildRequest(opts, "server_group") 293 } 294 295 // SubnetCreateOpts represents the attributes used when creating a new subnet. 296 type SubnetCreateOpts struct { 297 subnets.CreateOpts 298 ValueSpecs map[string]string `json:"value_specs,omitempty"` 299 } 300 301 // ToSubnetCreateMap casts a CreateOpts struct to a map. 302 // It overrides subnets.ToSubnetCreateMap to add the ValueSpecs field. 303 func (opts SubnetCreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) { 304 b, err := BuildRequest(opts, "subnet") 305 if err != nil { 306 return nil, err 307 } 308 309 if m := b["subnet"].(map[string]interface{}); m["gateway_ip"] == "" { 310 m["gateway_ip"] = nil 311 } 312 313 return b, nil 314 } 315 316 // ZoneCreateOpts represents the attributes used when creating a new DNS zone. 317 type ZoneCreateOpts struct { 318 zones.CreateOpts 319 ValueSpecs map[string]string `json:"value_specs,omitempty"` 320 } 321 322 // ToZoneCreateMap casts a CreateOpts struct to a map. 323 // It overrides zones.ToZoneCreateMap to add the ValueSpecs field. 324 func (opts ZoneCreateOpts) ToZoneCreateMap() (map[string]interface{}, error) { 325 b, err := BuildRequest(opts, "") 326 if err != nil { 327 return nil, err 328 } 329 330 if m, ok := b[""].(map[string]interface{}); ok { 331 if opts.TTL > 0 { 332 m["ttl"] = opts.TTL 333 } 334 335 return m, nil 336 } 337 338 return nil, fmt.Errorf("Expected map but got %T", b[""]) 339 }