blob: 88a366a288b9b24b8ac4952a76cfad52667973e2 [file] [log] [blame] [view]
jrperritted2f96c2017-01-20 13:02:54 -06001# Tips
2
3## Implementing default logging and re-authentication attempts
4
5You can implement custom logging and/or limit re-auth attempts by creating a custom HTTP client
6like the following and setting it as the provider client's HTTP Client (via the
7`gophercloud.ProviderClient.HTTPClient` field):
8
9```go
10//...
11
12// LogRoundTripper satisfies the http.RoundTripper interface and is used to
13// customize the default Gophercloud RoundTripper to allow for logging.
14type LogRoundTripper struct {
15 rt http.RoundTripper
16 numReauthAttempts int
17}
18
19// newHTTPClient return a custom HTTP client that allows for logging relevant
20// information before and after the HTTP request.
21func newHTTPClient() http.Client {
22 return http.Client{
23 Transport: &LogRoundTripper{
24 rt: http.DefaultTransport,
25 },
26 }
27}
28
29// RoundTrip performs a round-trip HTTP request and logs relevant information about it.
30func (lrt *LogRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
31 glog.Infof("Request URL: %s\n", request.URL)
32
33 response, err := lrt.rt.RoundTrip(request)
34 if response == nil {
35 return nil, err
36 }
37
38 if response.StatusCode == http.StatusUnauthorized {
39 if lrt.numReauthAttempts == 3 {
40 return response, fmt.Errorf("Tried to re-authenticate 3 times with no success.")
41 }
42 lrt.numReauthAttempts++
43 }
44
45 glog.Debugf("Response Status: %s\n", response.Status)
46
47 return response, nil
48}
49
50endpoint := "https://127.0.0.1/auth"
51pc := openstack.NewClient(endpoint)
52pc.HTTPClient = newHTTPClient()
53
54//...
55```
56
57
58## Implementing custom objects
59
60OpenStack request/response objects may differ among variable names or types.
61
62### Custom request objects
63
64To pass custom options to a request, implement the desired `<ACTION>OptsBuilder` interface. For
65example, to pass in
66
67```go
68type MyCreateServerOpts struct {
69 Name string
70 Size int
71}
72```
73
74to `servers.Create`, simply implement the `servers.CreateOptsBuilder` interface:
75
76```go
77func (o MyCreateServeropts) ToServerCreateMap() (map[string]interface{}, error) {
78 return map[string]interface{}{
79 "name": o.Name,
80 "size": o.Size,
81 }, nil
82}
83```
84
85create an instance of your custom options object, and pass it to `servers.Create`:
86
87```go
88// ...
89myOpts := MyCreateServerOpts{
90 Name: "s1",
91 Size: "100",
92}
93server, err := servers.Create(computeClient, myOpts).Extract()
94// ...
95```
96
97### Custom response objects
98
99Some OpenStack services have extensions. Extensions that are supported in Gophercloud can be
100combined to create a custom object:
101
102```go
103// ...
104type MyVolume struct {
105 volumes.Volume
106 tenantattr.VolumeExt
107}
108
109var v struct {
110 MyVolume `json:"volume"`
111}
112
113err := volumes.Get(client, volID).ExtractInto(&v)
114// ...
115```
116
117## Overriding default `UnmarshalJSON` method
118
119For some response objects, a field may be a custom type or may be allowed to take on
120different types. In these cases, overriding the default `UnmarshalJSON` method may be
121necessary. To do this, declare the JSON `struct` field tag as "-" and create an `UnmarshalJSON`
122method on the type:
123
124```go
125// ...
126type MyVolume struct {
127 ID string `json: "id"`
128 TimeCreated time.Time `json: "-"`
129}
130
131func (r *MyVolume) UnmarshalJSON(b []byte) error {
132 type tmp MyVolume
133 var s struct {
134 tmp
135 TimeCreated gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
136 }
137 err := json.Unmarshal(b, &s)
138 if err != nil {
139 return err
140 }
141 *r = Volume(s.tmp)
142
143 r.TimeCreated = time.Time(s.CreatedAt)
144
145 return err
146}
147// ...
148```