github.com/cbroglie/terraform@v0.7.0-rc3.0.20170410193827-735dfc416d46/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.logRequestBody(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.logResponseBody(response.Body, response.Header)
    62  	}
    63  
    64  	return response, err
    65  }
    66  
    67  // logRequestBody will log the HTTP Request body.
    68  // If the body is JSON, it will attempt to be pretty-formatted.
    69  func (lrt *LogRoundTripper) logRequestBody(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  	contentType := headers.Get("Content-Type")
    79  	if strings.HasPrefix(contentType, "application/json") {
    80  		debugInfo := lrt.formatJSON(bs.Bytes())
    81  		log.Printf("[DEBUG] OpenStack Request Options: %s", debugInfo)
    82  	} else {
    83  		log.Printf("[DEBUG] OpenStack Request Options: %s", bs.String())
    84  	}
    85  
    86  	return ioutil.NopCloser(strings.NewReader(bs.String())), nil
    87  }
    88  
    89  // logResponseBody will log the HTTP Response body.
    90  // If the body is JSON, it will attempt to be pretty-formatted.
    91  func (lrt *LogRoundTripper) logResponseBody(original io.ReadCloser, headers http.Header) (io.ReadCloser, error) {
    92  	contentType := headers.Get("Content-Type")
    93  	if strings.HasPrefix(contentType, "application/json") {
    94  		var bs bytes.Buffer
    95  		defer original.Close()
    96  		_, err := io.Copy(&bs, original)
    97  		if err != nil {
    98  			return nil, err
    99  		}
   100  		debugInfo := lrt.formatJSON(bs.Bytes())
   101  		if debugInfo != "" {
   102  			log.Printf("[DEBUG] OpenStack Response Body: %s", debugInfo)
   103  		}
   104  		return ioutil.NopCloser(strings.NewReader(bs.String())), nil
   105  	}
   106  
   107  	log.Printf("[DEBUG] Not logging because OpenStack response body isn't JSON")
   108  	return original, nil
   109  }
   110  
   111  // formatJSON will try to pretty-format a JSON body.
   112  // It will also mask known fields which contain sensitive information.
   113  func (lrt *LogRoundTripper) formatJSON(raw []byte) string {
   114  	var data map[string]interface{}
   115  
   116  	err := json.Unmarshal(raw, &data)
   117  	if err != nil {
   118  		log.Printf("[DEBUG] Unable to parse OpenStack JSON: %s", err)
   119  		return string(raw)
   120  	}
   121  
   122  	// Mask known password fields
   123  	if v, ok := data["auth"].(map[string]interface{}); ok {
   124  		if v, ok := v["identity"].(map[string]interface{}); ok {
   125  			if v, ok := v["password"].(map[string]interface{}); ok {
   126  				if v, ok := v["user"].(map[string]interface{}); ok {
   127  					v["password"] = "***"
   128  				}
   129  			}
   130  		}
   131  	}
   132  
   133  	// Ignore the catalog
   134  	if v, ok := data["token"].(map[string]interface{}); ok {
   135  		if _, ok := v["catalog"]; ok {
   136  			return ""
   137  		}
   138  	}
   139  
   140  	pretty, err := json.MarshalIndent(data, "", "  ")
   141  	if err != nil {
   142  		log.Printf("[DEBUG] Unable to re-marshal OpenStack JSON: %s", err)
   143  		return string(raw)
   144  	}
   145  
   146  	return string(pretty)
   147  }
   148  
   149  // FirewallCreateOpts represents the attributes used when creating a new firewall.
   150  type FirewallCreateOpts struct {
   151  	firewalls.CreateOpts
   152  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   153  }
   154  
   155  // ToFirewallCreateMap casts a CreateOpts struct to a map.
   156  // It overrides firewalls.ToFirewallCreateMap to add the ValueSpecs field.
   157  func (opts FirewallCreateOpts) ToFirewallCreateMap() (map[string]interface{}, error) {
   158  	return BuildRequest(opts, "firewall")
   159  }
   160  
   161  // FloatingIPCreateOpts represents the attributes used when creating a new floating ip.
   162  type FloatingIPCreateOpts struct {
   163  	floatingips.CreateOpts
   164  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   165  }
   166  
   167  // ToFloatingIPCreateMap casts a CreateOpts struct to a map.
   168  // It overrides floatingips.ToFloatingIPCreateMap to add the ValueSpecs field.
   169  func (opts FloatingIPCreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
   170  	return BuildRequest(opts, "floatingip")
   171  }
   172  
   173  // KeyPairCreateOpts represents the attributes used when creating a new keypair.
   174  type KeyPairCreateOpts struct {
   175  	keypairs.CreateOpts
   176  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   177  }
   178  
   179  // ToKeyPairCreateMap casts a CreateOpts struct to a map.
   180  // It overrides keypairs.ToKeyPairCreateMap to add the ValueSpecs field.
   181  func (opts KeyPairCreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) {
   182  	return BuildRequest(opts, "keypair")
   183  }
   184  
   185  // NetworkCreateOpts represents the attributes used when creating a new network.
   186  type NetworkCreateOpts struct {
   187  	networks.CreateOpts
   188  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   189  }
   190  
   191  // ToNetworkCreateMap casts a CreateOpts struct to a map.
   192  // It overrides networks.ToNetworkCreateMap to add the ValueSpecs field.
   193  func (opts NetworkCreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) {
   194  	return BuildRequest(opts, "network")
   195  }
   196  
   197  // PolicyCreateOpts represents the attributes used when creating a new firewall policy.
   198  type PolicyCreateOpts struct {
   199  	policies.CreateOpts
   200  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   201  }
   202  
   203  // ToPolicyCreateMap casts a CreateOpts struct to a map.
   204  // It overrides policies.ToFirewallPolicyCreateMap to add the ValueSpecs field.
   205  func (opts PolicyCreateOpts) ToFirewallPolicyCreateMap() (map[string]interface{}, error) {
   206  	return BuildRequest(opts, "firewall_policy")
   207  }
   208  
   209  // PortCreateOpts represents the attributes used when creating a new port.
   210  type PortCreateOpts struct {
   211  	ports.CreateOpts
   212  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   213  }
   214  
   215  // ToPortCreateMap casts a CreateOpts struct to a map.
   216  // It overrides ports.ToPortCreateMap to add the ValueSpecs field.
   217  func (opts PortCreateOpts) ToPortCreateMap() (map[string]interface{}, error) {
   218  	return BuildRequest(opts, "port")
   219  }
   220  
   221  // RouterCreateOpts represents the attributes used when creating a new router.
   222  type RouterCreateOpts struct {
   223  	routers.CreateOpts
   224  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   225  }
   226  
   227  // ToRouterCreateMap casts a CreateOpts struct to a map.
   228  // It overrides routers.ToRouterCreateMap to add the ValueSpecs field.
   229  func (opts RouterCreateOpts) ToRouterCreateMap() (map[string]interface{}, error) {
   230  	return BuildRequest(opts, "router")
   231  }
   232  
   233  // RuleCreateOpts represents the attributes used when creating a new firewall rule.
   234  type RuleCreateOpts struct {
   235  	rules.CreateOpts
   236  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   237  }
   238  
   239  // ToRuleCreateMap casts a CreateOpts struct to a map.
   240  // It overrides rules.ToRuleCreateMap to add the ValueSpecs field.
   241  func (opts RuleCreateOpts) ToRuleCreateMap() (map[string]interface{}, error) {
   242  	b, err := BuildRequest(opts, "firewall_rule")
   243  	if err != nil {
   244  		return nil, err
   245  	}
   246  
   247  	if m := b["firewall_rule"].(map[string]interface{}); m["protocol"] == "any" {
   248  		m["protocol"] = nil
   249  	}
   250  
   251  	return b, nil
   252  }
   253  
   254  // ServerGroupCreateOpts represents the attributes used when creating a new router.
   255  type ServerGroupCreateOpts struct {
   256  	servergroups.CreateOpts
   257  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   258  }
   259  
   260  // ToServerGroupCreateMap casts a CreateOpts struct to a map.
   261  // It overrides routers.ToServerGroupCreateMap to add the ValueSpecs field.
   262  func (opts ServerGroupCreateOpts) ToServerGroupCreateMap() (map[string]interface{}, error) {
   263  	return BuildRequest(opts, "server_group")
   264  }
   265  
   266  // SubnetCreateOpts represents the attributes used when creating a new subnet.
   267  type SubnetCreateOpts struct {
   268  	subnets.CreateOpts
   269  	ValueSpecs map[string]string `json:"value_specs,omitempty"`
   270  }
   271  
   272  // ToSubnetCreateMap casts a CreateOpts struct to a map.
   273  // It overrides subnets.ToSubnetCreateMap to add the ValueSpecs field.
   274  func (opts SubnetCreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) {
   275  	b, err := BuildRequest(opts, "subnet")
   276  	if err != nil {
   277  		return nil, err
   278  	}
   279  
   280  	if m := b["subnet"].(map[string]interface{}); m["gateway_ip"] == "" {
   281  		m["gateway_ip"] = nil
   282  	}
   283  
   284  	return b, nil
   285  }