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