1
1
package radosgwadmin
2
2
3
3
import (
4
- "bytes"
5
4
"context"
6
- "crypto/tls"
7
- "crypto/x509"
8
- "encoding/json"
9
- "fmt"
10
- "io"
11
- "io/ioutil"
12
- "net"
13
5
"net/http"
14
6
"net/url"
15
- "reflect"
16
- "regexp"
17
7
"strings"
18
8
"time"
19
9
20
- "github.com/google/go-querystring/query "
10
+ "github.com/myENA/restclient "
21
11
"github.com/smartystreets/go-aws-auth"
22
- "gopkg.in/go-playground/validator.v9"
23
12
)
24
13
25
14
var tz * time.Location
26
15
27
16
var falsch = false
28
17
29
- var validate * validator.Validate
30
-
31
- var altMatch = regexp .MustCompile (`eq=([^=\|]+)` )
32
-
33
18
func init () {
34
19
tz , _ = time .LoadLocation ("Local" ) // Defaults to local
35
- validate = validator .New ()
36
20
}
37
21
38
22
// SetTimeZone - override time zone. Not thread-safe, do
@@ -51,14 +35,9 @@ var FalseRef = &falsch
51
35
52
36
// AdminAPI - admin api struct
53
37
type AdminAPI struct {
54
- c * http.Client
55
- u * url.URL
56
- creds * awsauth.Credentials
57
- rawValidatorErrors bool
58
- }
59
-
60
- type customDecoder interface {
61
- decode (data io.Reader ) error
38
+ * restclient.Client
39
+ u * url.URL
40
+ creds * awsauth.Credentials
62
41
}
63
42
64
43
// NewAdminAPI - AdminAPI factory method.
@@ -71,49 +50,12 @@ func NewAdminAPI(cfg *Config) (*AdminAPI, error) {
71
50
if err != nil {
72
51
return nil , err
73
52
}
74
- // Lifted from http package DefaultTransort.
75
- t := & http.Transport {
76
- Proxy : http .ProxyFromEnvironment ,
77
- DialContext : (& net.Dialer {
78
- Timeout : 30 * time .Second ,
79
- KeepAlive : 30 * time .Second ,
80
- DualStack : true ,
81
- }).DialContext ,
82
- MaxIdleConns : 100 ,
83
- IdleConnTimeout : 90 * time .Second ,
84
- TLSHandshakeTimeout : 10 * time .Second ,
85
- ExpectContinueTimeout : 1 * time .Second ,
86
- }
87
-
88
- tlsc := new (tls.Config )
89
- tlsc .InsecureSkipVerify = cfg .InsecureSkipVerify
90
-
91
- var cacerts []byte
92
- if len (cfg .CACertBundle ) > 0 {
93
- cacerts = cfg .CACertBundle
94
- } else if cfg .CACertBundlePath != "" {
95
- cacerts , err = ioutil .ReadFile (cfg .CACertBundlePath )
96
- if err != nil {
97
- return nil , fmt .Errorf ("Cannot open ca cert bundle %s: %s" , cfg .CACertBundlePath , err )
98
- }
99
- }
100
53
101
- if len (cacerts ) > 0 {
102
- bundle := x509 .NewCertPool ()
103
- ok := bundle .AppendCertsFromPEM (cacerts )
104
- if ! ok {
105
- return nil , fmt .Errorf ("Invalid cert bundle" )
106
- }
107
- tlsc .RootCAs = bundle
108
- tlsc .BuildNameToCertificate ()
109
- }
110
-
111
- t .TLSClientConfig = tlsc
112
-
113
- aa .c = & http.Client {
114
- Timeout : time .Duration (cfg .ClientTimeout ),
115
- Transport : t ,
54
+ aa .Client , err = restclient .NewClient (& (cfg .ClientConfig ), nil )
55
+ if err != nil {
56
+ return nil , err
116
57
}
58
+ aa .Client .FixupCallback = aa .fixupCallback
117
59
118
60
aa .creds = & awsauth.Credentials {
119
61
AccessKeyID : cfg .AccessKeyID ,
@@ -134,143 +76,20 @@ func NewAdminAPI(cfg *Config) (*AdminAPI, error) {
134
76
}
135
77
136
78
func (aa * AdminAPI ) get (ctx context.Context , path string , queryStruct interface {}, responseBody interface {}) error {
137
- return aa .req (ctx , "GET" , path , queryStruct , nil , responseBody )
79
+ return aa .Client . Get (ctx , aa . u , path , queryStruct , responseBody )
138
80
}
139
81
140
82
func (aa * AdminAPI ) delete (ctx context.Context , path string , queryStruct interface {}, responseBody interface {}) error {
141
- return aa .req (ctx , "DELETE" , path , queryStruct , nil , responseBody )
83
+ return aa .Client . Delete (ctx , aa . u , path , queryStruct , responseBody )
142
84
}
143
85
144
86
func (aa * AdminAPI ) post (ctx context.Context , path string , queryStruct , requestBody interface {}, responseBody interface {}) error {
145
87
146
- return aa .req (ctx , "POST" , path , queryStruct , requestBody , responseBody )
88
+ return aa .Client . Post (ctx , aa . u , path , queryStruct , requestBody , responseBody )
147
89
}
148
90
149
91
func (aa * AdminAPI ) put (ctx context.Context , path string , queryStruct , requestBody interface {}, responseBody interface {}) error {
150
- return aa .req (ctx , "PUT" , path , queryStruct , requestBody , responseBody )
151
- }
152
-
153
- func isNil (i interface {}) bool {
154
- if i == nil {
155
- return true
156
- }
157
- v := reflect .ValueOf (i )
158
- switch v .Kind () {
159
- case reflect .Ptr :
160
- return v .IsNil ()
161
-
162
- default :
163
- panic ("Invalid interface type: " + v .Kind ().String ())
164
- }
165
- }
166
-
167
- func (aa * AdminAPI ) req (ctx context.Context , verb , path string , queryStruct , requestBody , responseBody interface {}) error {
168
- path = strings .TrimLeft (path , "/" )
169
- url := aa .u .String () + "/" + path
170
- if ! isNil (queryStruct ) {
171
- err := aa .validate (queryStruct )
172
- if err != nil {
173
- return err
174
- }
175
- v , err := query .Values (queryStruct )
176
- if err != nil {
177
- return err
178
- }
179
-
180
- qs := v .Encode ()
181
-
182
- if qs != "" {
183
- if strings .Contains (url , "?" ) {
184
- url = url + "&" + qs
185
- } else {
186
- url = url + "?" + qs
187
- }
188
- }
189
- }
190
-
191
- var bodyReader io.Reader
192
- if ! isNil (requestBody ) {
193
- err := aa .validate (requestBody )
194
- if err != nil {
195
- return err
196
- }
197
- bjson , err := json .Marshal (requestBody )
198
- if err != nil {
199
- return err
200
- }
201
- bodyReader = bytes .NewReader (bjson )
202
- }
203
- req , err := http .NewRequest (verb , url , bodyReader )
204
- if err != nil {
205
- return err
206
- }
207
-
208
- req = req .WithContext (ctx )
209
- req .URL .Query ().Set ("format" , "json" )
210
-
211
- _ = awsauth .SignS3 (req , * aa .creds )
212
-
213
- // This is to appease AWS signature algorithm. spaces must
214
- // be %20, go defaults to +
215
- req .URL .RawQuery = strings .Replace (req .URL .RawQuery , "+" , "%20" , - 1 )
216
-
217
- resp , err := aa .c .Do (req )
218
- if err != nil {
219
- return err
220
- }
221
-
222
- defer func () {
223
- _ = resp .Body .Close ()
224
- }()
225
- if resp .StatusCode != 200 {
226
- body , _ := ioutil .ReadAll (resp .Body )
227
- return fmt .Errorf ("Invalid status code %d : %s : body: %s" , resp .StatusCode , resp .Status , string (body ))
228
- }
229
- if isNil (responseBody ) {
230
- return nil
231
- }
232
-
233
- if cd , ok := responseBody .(customDecoder ); ok {
234
- return cd .decode (resp .Body )
235
- }
236
-
237
- return json .NewDecoder (resp .Body ).Decode (responseBody )
238
- }
239
-
240
- // make sense of the validator error types
241
- func (aa * AdminAPI ) validate (i interface {}) error {
242
- err := validate .Struct (i )
243
- if err != nil {
244
- if aa .rawValidatorErrors {
245
- return err
246
- }
247
- if verr , ok := err .(validator.ValidationErrors ); ok {
248
- var errs []string
249
- for _ , ferr := range verr {
250
- if ferr .ActualTag () == "required" {
251
- errs = append (errs ,
252
- fmt .Sprintf ("Required field %s is missing or empty" ,
253
- ferr .StructField (),
254
- ),
255
- )
256
- } else if matches := altMatch .FindAllStringSubmatch (ferr .ActualTag (), - 1 ); len (matches ) > 0 {
257
- valids := make ([]string , len (matches ))
258
- for i := 0 ; i < len (matches ); i ++ {
259
- valids [i ] = "\" " + matches [i ][1 ] + "\" "
260
- }
261
- errs = append (errs ,
262
- fmt .Sprintf ("Field '%s' invalid value: '%s', valid values are: %s" ,
263
- ferr .StructNamespace (),
264
- ferr .Value (), // for now all are string - revise this if other types are needed
265
- strings .Join (valids , "," )),
266
- )
267
- }
268
- }
269
-
270
- return fmt .Errorf ("Validation error: %s" , strings .Join (errs , " ; " ))
271
- }
272
- }
273
- return err
92
+ return aa .Put (ctx , aa .u , path , queryStruct , requestBody , responseBody )
274
93
}
275
94
276
95
// Config - this configures an AdminAPI.
@@ -279,47 +98,24 @@ func (aa *AdminAPI) validate(i interface{}) error {
279
98
// Specify CACertBundle if you want embed the cacert bundle in PEM format.
280
99
// Specify one or the other. If both are specified, CACertBundle is honored.
281
100
type Config struct {
282
- ClientTimeout Duration
283
- ServerURL string
284
- AdminPath string
285
- CACertBundlePath string
286
- CACertBundle [] byte
287
- InsecureSkipVerify bool
288
- ZoneName string
289
- AccessKeyID string
290
- SecretAccessKey string
291
- SecurityToken string
292
- Expiration time. Time
293
- RawValidatorErrors bool // If true, then no attempt to interpret validator errors will be made.
294
- }
101
+ restclient. ClientConfig
102
+ ServerURL string
103
+ AdminPath string
104
+ CACertBundlePath string
105
+ ZoneName string
106
+ AccessKeyID string
107
+ SecretAccessKey string
108
+ SecurityToken string
109
+ Expiration time. Time
110
+ }
111
+
112
+ func ( aa * AdminAPI ) fixupCallback ( req * http. Request ) error {
113
+ req . URL . Query (). Set ( "format" , "json" )
295
114
296
- // Duration - this allows us to use a text representation of a duration and
297
- // have it parse correctly. The go standard library time.Duration does not
298
- // implement the TextUnmarshaller interface, so we have to do this workaround
299
- // in order for json.Unmarshal or external parsers like toml.Decode to work
300
- // with human friendly input.
301
- type Duration time.Duration
115
+ _ = awsauth .SignS3 (req , * aa .creds )
302
116
303
- // UnmarshalText - this implements the TextUnmarshaler interface
304
- func (d * Duration ) UnmarshalText (text []byte ) error {
305
- if len (text ) == 0 {
306
- return nil
307
- }
308
- dur , err := time .ParseDuration (string (text ))
309
- if err != nil {
310
- return err
311
- }
312
- * d = Duration (dur )
117
+ // This is to appease AWS signature algorithm. spaces must
118
+ // be %20, go defaults to +
119
+ req .URL .RawQuery = strings .Replace (req .URL .RawQuery , "+" , "%20" , - 1 )
313
120
return nil
314
121
}
315
-
316
- // MarshalText - this implements TextMarshaler
317
- func (d Duration ) MarshalText () ([]byte , error ) {
318
- return []byte (time .Duration (d ).String ()), nil
319
- }
320
-
321
- // HTTPClient return the underlying http.Client. You can use this to fine tune
322
- // the http.Transport settings, for example.
323
- func (aa * AdminAPI ) HTTPClient () * http.Client {
324
- return aa .c
325
- }
0 commit comments