github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/openstack/types.go (about)

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