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