| # Tips |
| |
| ## Implementing default logging and re-authentication attempts |
| |
| You can implement custom logging and/or limit re-auth attempts by creating a custom HTTP client |
| like the following and setting it as the provider client's HTTP Client (via the |
| `gophercloud.ProviderClient.HTTPClient` field): |
| |
| ```go |
| //... |
| |
| // LogRoundTripper satisfies the http.RoundTripper interface and is used to |
| // customize the default Gophercloud RoundTripper to allow for logging. |
| type LogRoundTripper struct { |
| rt http.RoundTripper |
| numReauthAttempts int |
| } |
| |
| // newHTTPClient return a custom HTTP client that allows for logging relevant |
| // information before and after the HTTP request. |
| func newHTTPClient() http.Client { |
| return http.Client{ |
| Transport: &LogRoundTripper{ |
| rt: http.DefaultTransport, |
| }, |
| } |
| } |
| |
| // RoundTrip performs a round-trip HTTP request and logs relevant information about it. |
| func (lrt *LogRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) { |
| glog.Infof("Request URL: %s\n", request.URL) |
| |
| response, err := lrt.rt.RoundTrip(request) |
| if response == nil { |
| return nil, err |
| } |
| |
| if response.StatusCode == http.StatusUnauthorized { |
| if lrt.numReauthAttempts == 3 { |
| return response, fmt.Errorf("Tried to re-authenticate 3 times with no success.") |
| } |
| lrt.numReauthAttempts++ |
| } |
| |
| glog.Debugf("Response Status: %s\n", response.Status) |
| |
| return response, nil |
| } |
| |
| endpoint := "https://127.0.0.1/auth" |
| pc := openstack.NewClient(endpoint) |
| pc.HTTPClient = newHTTPClient() |
| |
| //... |
| ``` |
| |
| |
| ## Implementing custom objects |
| |
| OpenStack request/response objects may differ among variable names or types. |
| |
| ### Custom request objects |
| |
| To pass custom options to a request, implement the desired `<ACTION>OptsBuilder` interface. For |
| example, to pass in |
| |
| ```go |
| type MyCreateServerOpts struct { |
| Name string |
| Size int |
| } |
| ``` |
| |
| to `servers.Create`, simply implement the `servers.CreateOptsBuilder` interface: |
| |
| ```go |
| func (o MyCreateServeropts) ToServerCreateMap() (map[string]interface{}, error) { |
| return map[string]interface{}{ |
| "name": o.Name, |
| "size": o.Size, |
| }, nil |
| } |
| ``` |
| |
| create an instance of your custom options object, and pass it to `servers.Create`: |
| |
| ```go |
| // ... |
| myOpts := MyCreateServerOpts{ |
| Name: "s1", |
| Size: "100", |
| } |
| server, err := servers.Create(computeClient, myOpts).Extract() |
| // ... |
| ``` |
| |
| ### Custom response objects |
| |
| Some OpenStack services have extensions. Extensions that are supported in Gophercloud can be |
| combined to create a custom object: |
| |
| ```go |
| // ... |
| type MyVolume struct { |
| volumes.Volume |
| tenantattr.VolumeExt |
| } |
| |
| var v struct { |
| MyVolume `json:"volume"` |
| } |
| |
| err := volumes.Get(client, volID).ExtractInto(&v) |
| // ... |
| ``` |
| |
| ## Overriding default `UnmarshalJSON` method |
| |
| For some response objects, a field may be a custom type or may be allowed to take on |
| different types. In these cases, overriding the default `UnmarshalJSON` method may be |
| necessary. To do this, declare the JSON `struct` field tag as "-" and create an `UnmarshalJSON` |
| method on the type: |
| |
| ```go |
| // ... |
| type MyVolume struct { |
| ID string `json: "id"` |
| TimeCreated time.Time `json: "-"` |
| } |
| |
| func (r *MyVolume) UnmarshalJSON(b []byte) error { |
| type tmp MyVolume |
| var s struct { |
| tmp |
| TimeCreated gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` |
| } |
| err := json.Unmarshal(b, &s) |
| if err != nil { |
| return err |
| } |
| *r = Volume(s.tmp) |
| |
| r.TimeCreated = time.Time(s.CreatedAt) |
| |
| return err |
| } |
| // ... |
| ``` |