github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/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  		log.Printf("[DEBUG] OpenStack Response Body: %s", debugInfo)
   101  		return ioutil.NopCloser(strings.NewReader(bs.String())), nil
   102  	}
   103  
   104  	log.Printf("[DEBUG] Not logging because OpenStack response body isn't JSON")
   105  	return original, nil
   106  }
   107  
   108  // formatJSON will try to pretty-format a JSON body.
   109  // It will also mask known fields which contain sensitive information.
   110  func (lrt *LogRoundTripper) formatJSON(raw []byte) string {
   111  	var data map[string]interface{}
   112  
   113  	err := json.Unmarshal(raw, &data)
   114  	if err != nil {
   115  		log.Printf("[DEBUG] Unable to parse OpenStack JSON: %s", err)
   116  		return string(raw)
   117  	}
   118  
   119  	// Mask known password fields
   120  	if v, ok := data["auth"].(map[string]interface{}); ok {
   121  		if v, ok := v["identity"].(map[string]interface{}); ok {
   122  			if v, ok := v["password"].(map[string]interface{}); ok {
   123  				if v, ok := v["user"].(map[string]interface{}); ok {
   124  					v["password"] = "***"
   125  				}
   126  			}
   127  		}
   128  	}
   129  
   130  	pretty, err := json.MarshalIndent(data, "", "  ")
   131  	if err != nil {
   132  		log.Printf("[DEBUG] Unable to re-marshal OpenStack JSON: %s", err)
   133  		return string(raw)
   134  	}
   135  
   136  	return string(pretty)
   137  }
   138  
   139  // FirewallCreateOpts represents the attributes used when creating a new firewall.
   140  type FirewallCreateOpts struct {
   141  	firewalls.CreateOpts
   142  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   143  }
   144  
   145  // ToFirewallCreateMap casts a CreateOpts struct to a map.
   146  // It overrides firewalls.ToFirewallCreateMap to add the ValueSpecs field.
   147  func (opts FirewallCreateOpts) ToFirewallCreateMap() (map[string]interface{}, error) {
   148  	return BuildRequest(opts, "firewall")
   149  }
   150  
   151  // FloatingIPCreateOpts represents the attributes used when creating a new floating ip.
   152  type FloatingIPCreateOpts struct {
   153  	floatingips.CreateOpts
   154  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   155  }
   156  
   157  // ToFloatingIPCreateMap casts a CreateOpts struct to a map.
   158  // It overrides floatingips.ToFloatingIPCreateMap to add the ValueSpecs field.
   159  func (opts FloatingIPCreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
   160  	return BuildRequest(opts, "floatingip")
   161  }
   162  
   163  // KeyPairCreateOpts represents the attributes used when creating a new keypair.
   164  type KeyPairCreateOpts struct {
   165  	keypairs.CreateOpts
   166  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   167  }
   168  
   169  // ToKeyPairCreateMap casts a CreateOpts struct to a map.
   170  // It overrides keypairs.ToKeyPairCreateMap to add the ValueSpecs field.
   171  func (opts KeyPairCreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) {
   172  	return BuildRequest(opts, "keypair")
   173  }
   174  
   175  // NetworkCreateOpts represents the attributes used when creating a new network.
   176  type NetworkCreateOpts struct {
   177  	networks.CreateOpts
   178  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   179  }
   180  
   181  // ToNetworkCreateMap casts a CreateOpts struct to a map.
   182  // It overrides networks.ToNetworkCreateMap to add the ValueSpecs field.
   183  func (opts NetworkCreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) {
   184  	return BuildRequest(opts, "network")
   185  }
   186  
   187  // PolicyCreateOpts represents the attributes used when creating a new firewall policy.
   188  type PolicyCreateOpts struct {
   189  	policies.CreateOpts
   190  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   191  }
   192  
   193  // ToPolicyCreateMap casts a CreateOpts struct to a map.
   194  // It overrides policies.ToFirewallPolicyCreateMap to add the ValueSpecs field.
   195  func (opts PolicyCreateOpts) ToFirewallPolicyCreateMap() (map[string]interface{}, error) {
   196  	return BuildRequest(opts, "firewall_policy")
   197  }
   198  
   199  // PortCreateOpts represents the attributes used when creating a new port.
   200  type PortCreateOpts struct {
   201  	ports.CreateOpts
   202  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   203  }
   204  
   205  // ToPortCreateMap casts a CreateOpts struct to a map.
   206  // It overrides ports.ToPortCreateMap to add the ValueSpecs field.
   207  func (opts PortCreateOpts) ToPortCreateMap() (map[string]interface{}, error) {
   208  	return BuildRequest(opts, "port")
   209  }
   210  
   211  // RouterCreateOpts represents the attributes used when creating a new router.
   212  type RouterCreateOpts struct {
   213  	routers.CreateOpts
   214  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   215  }
   216  
   217  // ToRouterCreateMap casts a CreateOpts struct to a map.
   218  // It overrides routers.ToRouterCreateMap to add the ValueSpecs field.
   219  func (opts RouterCreateOpts) ToRouterCreateMap() (map[string]interface{}, error) {
   220  	return BuildRequest(opts, "router")
   221  }
   222  
   223  // RuleCreateOpts represents the attributes used when creating a new firewall rule.
   224  type RuleCreateOpts struct {
   225  	rules.CreateOpts
   226  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   227  }
   228  
   229  // ToRuleCreateMap casts a CreateOpts struct to a map.
   230  // It overrides rules.ToRuleCreateMap to add the ValueSpecs field.
   231  func (opts RuleCreateOpts) ToRuleCreateMap() (map[string]interface{}, error) {
   232  	b, err := BuildRequest(opts, "firewall_rule")
   233  	if err != nil {
   234  		return nil, err
   235  	}
   236  
   237  	if m := b["firewall_rule"].(map[string]interface{}); m["protocol"] == "any" {
   238  		m["protocol"] = nil
   239  	}
   240  
   241  	return b, nil
   242  }
   243  
   244  // SubnetCreateOpts represents the attributes used when creating a new subnet.
   245  type SubnetCreateOpts struct {
   246  	subnets.CreateOpts
   247  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   248  }
   249  
   250  // ToSubnetCreateMap casts a CreateOpts struct to a map.
   251  // It overrides subnets.ToSubnetCreateMap to add the ValueSpecs field.
   252  func (opts SubnetCreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) {
   253  	b, err := BuildRequest(opts, "subnet")
   254  	if err != nil {
   255  		return nil, err
   256  	}
   257  
   258  	if m := b["subnet"].(map[string]interface{}); m["gateway_ip"] == "" {
   259  		m["gateway_ip"] = nil
   260  	}
   261  
   262  	return b, nil
   263  }