provider/openstack: Enable HTTP Logging (#12089)
This commit adds the ability to log all requests and responses between Terraform and the OpenStack cloud. To enable, set the OS_DEBUG environment variable to 1.
This commit is contained in:
parent
6d4fc8d21a
commit
1b5694b7f4
|
@ -5,6 +5,7 @@ import (
|
|||
"crypto/x509"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/openstack"
|
||||
|
@ -103,8 +104,19 @@ func (c *Config) loadAndValidate() error {
|
|||
config.BuildNameToCertificate()
|
||||
}
|
||||
|
||||
// if OS_DEBUG is set, log the requests and responses
|
||||
var osDebug bool
|
||||
if os.Getenv("OS_DEBUG") != "" {
|
||||
osDebug = true
|
||||
}
|
||||
|
||||
transport := &http.Transport{Proxy: http.ProxyFromEnvironment, TLSClientConfig: config}
|
||||
client.HTTPClient.Transport = transport
|
||||
client.HTTPClient = http.Client{
|
||||
Transport: &LogRoundTripper{
|
||||
rt: transport,
|
||||
osDebug: osDebug,
|
||||
},
|
||||
}
|
||||
|
||||
// If using Swift Authentication, there's no need to validate authentication normally.
|
||||
if !c.Swauth {
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
|
||||
|
@ -12,6 +20,122 @@ import (
|
|||
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
|
||||
)
|
||||
|
||||
// LogRoundTripper satisfies the http.RoundTripper interface and is used to
|
||||
// customize the default http client RoundTripper to allow for logging.
|
||||
type LogRoundTripper struct {
|
||||
rt http.RoundTripper
|
||||
osDebug bool
|
||||
}
|
||||
|
||||
// RoundTrip performs a round-trip HTTP request and logs relevant information about it.
|
||||
func (lrt *LogRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
|
||||
defer func() {
|
||||
if request.Body != nil {
|
||||
request.Body.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
// for future reference, this is how to access the Transport struct:
|
||||
//tlsconfig := lrt.rt.(*http.Transport).TLSClientConfig
|
||||
|
||||
var err error
|
||||
|
||||
if lrt.osDebug {
|
||||
log.Printf("[DEBUG] OpenStack Request URL: %s %s", request.Method, request.URL)
|
||||
|
||||
if request.Body != nil {
|
||||
request.Body, err = lrt.logRequestBody(request.Body, request.Header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response, err := lrt.rt.RoundTrip(request)
|
||||
if response == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if lrt.osDebug {
|
||||
response.Body, err = lrt.logResponseBody(response.Body, response.Header)
|
||||
}
|
||||
|
||||
return response, err
|
||||
}
|
||||
|
||||
// logRequestBody will log the HTTP Request body.
|
||||
// If the body is JSON, it will attempt to be pretty-formatted.
|
||||
func (lrt *LogRoundTripper) logRequestBody(original io.ReadCloser, headers http.Header) (io.ReadCloser, error) {
|
||||
defer original.Close()
|
||||
|
||||
var bs bytes.Buffer
|
||||
_, err := io.Copy(&bs, original)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contentType := headers.Get("Content-Type")
|
||||
if strings.HasPrefix(contentType, "application/json") {
|
||||
debugInfo := lrt.formatJSON(bs.Bytes())
|
||||
log.Printf("[DEBUG] OpenStack Request Options: %s", debugInfo)
|
||||
} else {
|
||||
log.Printf("[DEBUG] OpenStack Request Options: %s", bs.String())
|
||||
}
|
||||
|
||||
return ioutil.NopCloser(strings.NewReader(bs.String())), nil
|
||||
}
|
||||
|
||||
// logResponseBody will log the HTTP Response body.
|
||||
// If the body is JSON, it will attempt to be pretty-formatted.
|
||||
func (lrt *LogRoundTripper) logResponseBody(original io.ReadCloser, headers http.Header) (io.ReadCloser, error) {
|
||||
contentType := headers.Get("Content-Type")
|
||||
if strings.HasPrefix(contentType, "application/json") {
|
||||
var bs bytes.Buffer
|
||||
defer original.Close()
|
||||
_, err := io.Copy(&bs, original)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
debugInfo := lrt.formatJSON(bs.Bytes())
|
||||
log.Printf("[DEBUG] OpenStack Response Body: %s", debugInfo)
|
||||
return ioutil.NopCloser(strings.NewReader(bs.String())), nil
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Not logging because OpenStack response body isn't JSON")
|
||||
return original, nil
|
||||
}
|
||||
|
||||
// formatJSON will try to pretty-format a JSON body.
|
||||
// It will also mask known fields which contain sensitive information.
|
||||
func (lrt *LogRoundTripper) formatJSON(raw []byte) string {
|
||||
var data map[string]interface{}
|
||||
|
||||
err := json.Unmarshal(raw, &data)
|
||||
if err != nil {
|
||||
log.Printf("[DEBUG] Unable to parse OpenStack JSON: %s", err)
|
||||
return string(raw)
|
||||
}
|
||||
|
||||
// Mask known password fields
|
||||
if v, ok := data["auth"].(map[string]interface{}); ok {
|
||||
if v, ok := v["identity"].(map[string]interface{}); ok {
|
||||
if v, ok := v["password"].(map[string]interface{}); ok {
|
||||
if v, ok := v["user"].(map[string]interface{}); ok {
|
||||
v["password"] = "***"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pretty, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
log.Printf("[DEBUG] Unable to re-marshal OpenStack JSON: %s", err)
|
||||
return string(raw)
|
||||
}
|
||||
|
||||
return string(pretty)
|
||||
}
|
||||
|
||||
// FirewallCreateOpts represents the attributes used when creating a new firewall.
|
||||
type FirewallCreateOpts struct {
|
||||
firewalls.CreateOpts
|
||||
|
|
|
@ -96,6 +96,22 @@ The following arguments are supported:
|
|||
Finally, set `auth_url` as the location of the Swift service. Note that this
|
||||
will only work when used with the OpenStack Object Storage resources.
|
||||
|
||||
## Additional Logging
|
||||
|
||||
This provider has the ability to log all HTTP requests and responses between
|
||||
Terraform and the OpenStack cloud which is useful for troubleshooting and
|
||||
debugging.
|
||||
|
||||
To enable these logs, set the `OS_DEBUG` environment variable to `1` along
|
||||
with the usual `TF_LOG=DEBUG` environment variable:
|
||||
|
||||
```shell
|
||||
$ OS_DEBUG=1 TF_LOG=DEBUG terraform apply
|
||||
```
|
||||
|
||||
If you submit these logs with a bug report, please ensure any sensitive
|
||||
information has been scrubbed first!
|
||||
|
||||
## Rackspace Compatibility
|
||||
|
||||
Using this OpenStack provider with Rackspace is not supported and not
|
||||
|
|
Loading…
Reference in New Issue