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  }