11package openstack
22
33import (
4+ "bytes"
5+ "encoding/json"
6+ "io"
7+ "io/ioutil"
8+ "log"
9+ "net/http"
10+ "strings"
11+
412 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
513 "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
614 "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
@@ -12,6 +20,122 @@ import (
1220 "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
1321)
1422
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+
15139// FirewallCreateOpts represents the attributes used when creating a new firewall.
16140type FirewallCreateOpts struct {
17141 firewalls.CreateOpts
0 commit comments