Merge lp:~cox-katherine-e/goamz/lp1319475.v4.signature.support into lp:goamz
- lp1319475.v4.signature.support
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Martin Packman | ||||
Approved revision: | 51 | ||||
Merged at revision: | 49 | ||||
Proposed branch: | lp:~cox-katherine-e/goamz/lp1319475.v4.signature.support | ||||
Merge into: | lp:goamz | ||||
Diff against target: |
1347 lines (+668/-177) 31 files modified
aws/attempt_test.go (+1/-1) aws/aws.go (+10/-0) aws/aws_test.go (+1/-1) aws/sign.go (+366/-0) aws/sign_test.go (+227/-0) ec2/ec2.go (+19/-15) ec2/ec2_test.go (+2/-18) ec2/ec2i_test.go (+1/-1) ec2/ec2t_test.go (+2/-2) ec2/export_test.go (+0/-5) ec2/networkinterfaces_test.go (+1/-1) ec2/privateips_test.go (+1/-1) ec2/sign.go (+0/-42) ec2/sign_test.go (+0/-68) ec2/subnets_test.go (+1/-1) ec2/vpc_test.go (+1/-1) exp/mturk/mturk_test.go (+1/-1) exp/mturk/sign_test.go (+1/-1) exp/sdb/sdb_test.go (+2/-2) exp/sdb/sign_test.go (+1/-1) exp/sns/sns_test.go (+2/-2) iam/iam_test.go (+2/-2) iam/iami_test.go (+1/-1) iam/iamt_test.go (+2/-2) s3/multi_test.go (+1/-1) s3/s3_test.go (+2/-2) s3/s3i_test.go (+1/-1) s3/s3t_test.go (+2/-1) s3/sign_test.go (+1/-1) testutil/http.go (+15/-1) testutil/suite.go (+1/-1) |
||||
To merge this branch: | bzr merge lp:~cox-katherine-e/goamz/lp1319475.v4.signature.support | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Packman (community) | Approve | ||
Review via email: mp+230401@code.launchpad.net |
Commit message
Description of the change
In order to keep the change as least impactful as possible, all regions will continue utilizing the v2 signing process except for China. China will utilize the new v4 signing methodology. Attaching the signature to the region also keeps the public APIs the same.
Although not tested on a live China instance, tests were performed on the sample requests provided by Amazon, of which the significant ones are passing (significant being the tests which test the signing process and not whether we can parse an HTTP request). The new version was also tested on a live EC2 instance with the v2 signing process to ensure there were no regressions. This gives us a high degree of confidence that the v4 signatures are passing.
Note many files have changes simply from switching over to gpkg.in for gocheck.
Also note that the v2 signature has been moved to aws/sign.go which accounts for a large amount of the diff.
Preview Diff
1 | === modified file 'aws/attempt_test.go' |
2 | --- aws/attempt_test.go 2013-02-01 19:38:47 +0000 |
3 | +++ aws/attempt_test.go 2014-08-12 16:51:05 +0000 |
4 | @@ -2,7 +2,7 @@ |
5 | |
6 | import ( |
7 | "launchpad.net/goamz/aws" |
8 | - . "launchpad.net/gocheck" |
9 | + . "gopkg.in/check.v1" |
10 | "time" |
11 | ) |
12 | |
13 | |
14 | === modified file 'aws/aws.go' |
15 | --- aws/aws.go 2014-07-08 15:42:26 +0000 |
16 | +++ aws/aws.go 2014-08-12 16:51:05 +0000 |
17 | @@ -27,6 +27,7 @@ |
18 | SNSEndpoint string |
19 | SQSEndpoint string |
20 | IAMEndpoint string |
21 | + Sign Signer // Method which will be used to sign requests. |
22 | } |
23 | |
24 | var USEast = Region{ |
25 | @@ -40,6 +41,7 @@ |
26 | "https://sns.us-east-1.amazonaws.com", |
27 | "https://sqs.us-east-1.amazonaws.com", |
28 | "https://iam.amazonaws.com", |
29 | + SignV2, |
30 | } |
31 | |
32 | var USWest = Region{ |
33 | @@ -53,6 +55,7 @@ |
34 | "https://sns.us-west-1.amazonaws.com", |
35 | "https://sqs.us-west-1.amazonaws.com", |
36 | "https://iam.amazonaws.com", |
37 | + SignV2, |
38 | } |
39 | |
40 | var USWest2 = Region{ |
41 | @@ -66,6 +69,7 @@ |
42 | "https://sns.us-west-2.amazonaws.com", |
43 | "https://sqs.us-west-2.amazonaws.com", |
44 | "https://iam.amazonaws.com", |
45 | + SignV2, |
46 | } |
47 | |
48 | var EUWest = Region{ |
49 | @@ -79,6 +83,7 @@ |
50 | "https://sns.eu-west-1.amazonaws.com", |
51 | "https://sqs.eu-west-1.amazonaws.com", |
52 | "https://iam.amazonaws.com", |
53 | + SignV2, |
54 | } |
55 | |
56 | var APSoutheast = Region{ |
57 | @@ -92,6 +97,7 @@ |
58 | "https://sns.ap-southeast-1.amazonaws.com", |
59 | "https://sqs.ap-southeast-1.amazonaws.com", |
60 | "https://iam.amazonaws.com", |
61 | + SignV2, |
62 | } |
63 | |
64 | var APSoutheast2 = Region{ |
65 | @@ -105,6 +111,7 @@ |
66 | "https://sns.ap-southeast-2.amazonaws.com", |
67 | "https://sqs.ap-southeast-2.amazonaws.com", |
68 | "https://iam.amazonaws.com", |
69 | + SignV2, |
70 | } |
71 | |
72 | var APNortheast = Region{ |
73 | @@ -118,6 +125,7 @@ |
74 | "https://sns.ap-northeast-1.amazonaws.com", |
75 | "https://sqs.ap-northeast-1.amazonaws.com", |
76 | "https://iam.amazonaws.com", |
77 | + SignV2, |
78 | } |
79 | |
80 | var SAEast = Region{ |
81 | @@ -131,6 +139,7 @@ |
82 | "https://sns.sa-east-1.amazonaws.com", |
83 | "https://sqs.sa-east-1.amazonaws.com", |
84 | "https://iam.amazonaws.com", |
85 | + SignV2, |
86 | } |
87 | |
88 | var CNNorth = Region{ |
89 | @@ -144,6 +153,7 @@ |
90 | "https://sns.cn-north-1.amazonaws.com.cn", |
91 | "https://sqs.cn-north-1.amazonaws.com.cn", |
92 | "https://iam.amazonaws.com.cn", |
93 | + SignV4Factory("cn-north-1"), |
94 | } |
95 | |
96 | var Regions = map[string]Region{ |
97 | |
98 | === modified file 'aws/aws_test.go' |
99 | --- aws/aws_test.go 2013-05-23 01:06:45 +0000 |
100 | +++ aws/aws_test.go 2014-08-12 16:51:05 +0000 |
101 | @@ -2,7 +2,7 @@ |
102 | |
103 | import ( |
104 | "launchpad.net/goamz/aws" |
105 | - . "launchpad.net/gocheck" |
106 | + . "gopkg.in/check.v1" |
107 | "os" |
108 | "strings" |
109 | "testing" |
110 | |
111 | === added file 'aws/sign.go' |
112 | --- aws/sign.go 1970-01-01 00:00:00 +0000 |
113 | +++ aws/sign.go 2014-08-12 16:51:05 +0000 |
114 | @@ -0,0 +1,366 @@ |
115 | +package aws |
116 | + |
117 | +import ( |
118 | + "bytes" |
119 | + "crypto/hmac" |
120 | + "crypto/sha256" |
121 | + "encoding/base64" |
122 | + "fmt" |
123 | + "io" |
124 | + "io/ioutil" |
125 | + "net/http" |
126 | + "net/url" |
127 | + "sort" |
128 | + "strings" |
129 | + "time" |
130 | +) |
131 | + |
132 | +type Signer func(*http.Request, Auth) error |
133 | + |
134 | +// Ensure our signers meet the interface |
135 | +var _ Signer = SignV2 |
136 | +var _ Signer = SignV4Factory("") |
137 | + |
138 | +type hasher func([]byte) string |
139 | + |
140 | +const ( |
141 | + ISO8601BasicFormat = "20060102T150405Z" |
142 | + ISO8601BasicFormatShort = "20060102" |
143 | +) |
144 | + |
145 | +// SignV2 signs an HTTP request utilizing version 2 of the AWS |
146 | +// signature, and the given credentials. |
147 | +func SignV2(req *http.Request, auth Auth) (err error) { |
148 | + |
149 | + queryVals := req.URL.Query() |
150 | + queryVals.Set("AWSAccessKeyId", auth.AccessKey) |
151 | + queryVals.Set("SignatureVersion", "2") |
152 | + queryVals.Set("SignatureMethod", "HmacSHA256") |
153 | + |
154 | + queryStr, err := canonicalQueryString(queryVals) |
155 | + if err != nil { |
156 | + return err |
157 | + } |
158 | + |
159 | + // The algorithm states that if the path is empty, to just use a "/". |
160 | + path := req.URL.Path |
161 | + if path == "" { |
162 | + path = "/" |
163 | + } |
164 | + |
165 | + payload := new(bytes.Buffer) |
166 | + if err := errorCollector( |
167 | + fprintfWrapper(payload, "%s\n", requestMethodVerb(req.Method)), |
168 | + fprintfWrapper(payload, "%s\n", req.Host), |
169 | + fprintfWrapper(payload, "%s\n", path), |
170 | + fprintfWrapper(payload, "%s", queryStr), |
171 | + ); err != nil { |
172 | + return err |
173 | + } |
174 | + |
175 | + hash := hmac.New(sha256.New, []byte(auth.SecretKey)) |
176 | + hash.Write(payload.Bytes()) |
177 | + signature := make([]byte, base64.StdEncoding.EncodedLen(hash.Size())) |
178 | + base64.StdEncoding.Encode(signature, hash.Sum(nil)) |
179 | + |
180 | + queryVals.Set("Signature", string(signature)) |
181 | + req.URL.RawQuery = queryVals.Encode() |
182 | + |
183 | + return nil |
184 | +} |
185 | + |
186 | +// SignV4Factory returns a version 4 Signer which will utilize the |
187 | +// given region name. |
188 | +func SignV4Factory(regionName string) Signer { |
189 | + return func(req *http.Request, auth Auth) error { |
190 | + return SignV4(req, auth, regionName) |
191 | + } |
192 | +} |
193 | + |
194 | +// SignV4 signs an HTTP request utilizing version 4 of the AWS |
195 | +// signature, and the given credentials. |
196 | +func SignV4(req *http.Request, auth Auth, regionName string) (err error) { |
197 | + |
198 | + var reqTime time.Time |
199 | + if reqTime, err = requestTime(req); err != nil { |
200 | + return err |
201 | + } |
202 | + |
203 | + svcName := inferServiceName(req.URL) |
204 | + credScope := credentialScope(reqTime, regionName, svcName) |
205 | + |
206 | + // There are several places in the algorithm that call for |
207 | + // processing the headers sorted by name. |
208 | + sortedHdrNames := sortHeaderNames(req.Header) |
209 | + |
210 | + var canonReqHash string |
211 | + if _, canonReqHash, err = canonicalRequest(req, sortedHdrNames, sha256Hasher); err != nil { |
212 | + return err |
213 | + } |
214 | + |
215 | + var strToSign string |
216 | + if strToSign, err = stringToSign(reqTime, canonReqHash, credScope); err != nil { |
217 | + return err |
218 | + } |
219 | + |
220 | + key := signingKey(reqTime, auth.SecretKey, regionName, svcName) |
221 | + signature := fmt.Sprintf("%x", hmacHasher(key, strToSign)) |
222 | + |
223 | + var authHdrVal string |
224 | + if authHdrVal, err = authHeaderString( |
225 | + req.Header, |
226 | + auth.AccessKey, |
227 | + signature, |
228 | + credScope, |
229 | + sortedHdrNames, |
230 | + ); err != nil { |
231 | + return err |
232 | + } |
233 | + |
234 | + req.Header.Set("Authorization", authHdrVal) |
235 | + |
236 | + return nil |
237 | +} |
238 | + |
239 | +// Task 1: Create a Canonical Request. |
240 | +// Returns the canonical request, and its hash. |
241 | +func canonicalRequest( |
242 | + req *http.Request, |
243 | + sortedHdrNames []string, |
244 | + hasher hasher, |
245 | +) (canReq, canReqHash string, err error) { |
246 | + |
247 | + var canHdr string |
248 | + if canHdr, err = canonicalHeaders(sortedHdrNames, req.Header); err != nil { |
249 | + return |
250 | + } |
251 | + |
252 | + var payHash string |
253 | + if payHash, err = payloadHash(req, hasher); err != nil { |
254 | + return |
255 | + } |
256 | + |
257 | + var queryStr string |
258 | + if queryStr, err = canonicalQueryString(req.URL.Query()); err != nil { |
259 | + return |
260 | + } |
261 | + |
262 | + c := new(bytes.Buffer) |
263 | + if err := errorCollector( |
264 | + fprintfWrapper(c, "%s\n", requestMethodVerb(req.Method)), |
265 | + fprintfWrapper(c, "%s\n", req.URL.RequestURI()), |
266 | + fprintfWrapper(c, "%s\n", queryStr), |
267 | + fprintfWrapper(c, "%s\n", canHdr), |
268 | + fprintfWrapper(c, "%s\n", strings.Join(sortedHdrNames, ";")), |
269 | + fprintfWrapper(c, "%s", payHash), |
270 | + ); err != nil { |
271 | + return "", "", err |
272 | + } |
273 | + |
274 | + canReq = c.String() |
275 | + return canReq, hasher([]byte(canReq)), nil |
276 | +} |
277 | + |
278 | +// Task 2: Create a string to Sign |
279 | +// Returns a string in the defined format to sign for the authorization header. |
280 | +func stringToSign( |
281 | + t time.Time, |
282 | + canonReqHash string, |
283 | + credScope string, |
284 | +) (string, error) { |
285 | + w := new(bytes.Buffer) |
286 | + if err := errorCollector( |
287 | + fprintfWrapper(w, "AWS4-HMAC-SHA256\n"), |
288 | + fprintfWrapper(w, "%s\n", t.Format(ISO8601BasicFormat)), |
289 | + fprintfWrapper(w, "%s\n", credScope), |
290 | + fprintfWrapper(w, "%s", canonReqHash), |
291 | + ); err != nil { |
292 | + return "", err |
293 | + } |
294 | + |
295 | + return w.String(), nil |
296 | +} |
297 | + |
298 | +// Task 3: Calculate the Signature |
299 | +// Returns a derived signing key. |
300 | +func signingKey(t time.Time, secretKey, regionName, svcName string) []byte { |
301 | + |
302 | + kSecret := secretKey |
303 | + kDate := hmacHasher([]byte("AWS4"+kSecret), t.Format(ISO8601BasicFormatShort)) |
304 | + kRegion := hmacHasher(kDate, regionName) |
305 | + kService := hmacHasher(kRegion, svcName) |
306 | + kSigning := hmacHasher(kService, "aws4_request") |
307 | + |
308 | + return kSigning |
309 | +} |
310 | + |
311 | +// Task 4: Add the Signing Information to the Request |
312 | +// Returns a string to be placed in the Authorization header for the request. |
313 | +func authHeaderString( |
314 | + header http.Header, |
315 | + accessKey, |
316 | + signature string, |
317 | + credScope string, |
318 | + sortedHeaderNames []string, |
319 | +) (string, error) { |
320 | + w := new(bytes.Buffer) |
321 | + if err := errorCollector( |
322 | + fprintfWrapper(w, "AWS4-HMAC-SHA256 "), |
323 | + fprintfWrapper(w, "Credential=%s/%s, ", accessKey, credScope), |
324 | + fprintfWrapper(w, "SignedHeaders=%s, ", strings.Join(sortedHeaderNames, ";")), |
325 | + fprintfWrapper(w, "Signature=%s", signature), |
326 | + ); err != nil { |
327 | + return "", err |
328 | + } |
329 | + |
330 | + return w.String(), nil |
331 | +} |
332 | + |
333 | +func canonicalQueryString(queryVals url.Values) (string, error) { |
334 | + |
335 | + // AWS dictates that we use %20 for encoding spaces rather than +. |
336 | + // All significant +s should already be encoded into their |
337 | + // hexadecimal equivalents before doing the string replace. |
338 | + return strings.Replace(queryVals.Encode(), "+", "%20", -1), nil |
339 | +} |
340 | + |
341 | +func canonicalHeaders(sortedHeaderNames []string, hdr http.Header) (string, error) { |
342 | + buffer := new(bytes.Buffer) |
343 | + |
344 | + for _, hName := range sortedHeaderNames { |
345 | + canonHdrKey := http.CanonicalHeaderKey(hName) |
346 | + sortedHdrVals := hdr[canonHdrKey] |
347 | + sort.Strings(sortedHdrVals) |
348 | + hdrVals := strings.Join(sortedHdrVals, ",") |
349 | + if _, err := fmt.Fprintf(buffer, "%s:%s\n", hName, hdrVals); err != nil { |
350 | + return "", err |
351 | + } |
352 | + } |
353 | + |
354 | + return buffer.String(), nil |
355 | +} |
356 | + |
357 | +// Returns a SHA256 checksum of the request body. Represented as a |
358 | +// lowercase hexadecimal string. |
359 | +func payloadHash(req *http.Request, hasher hasher) (string, error) { |
360 | + if b, err := ioutil.ReadAll(req.Body); err != nil { |
361 | + return "", err |
362 | + } else { |
363 | + req.Body = ioutil.NopCloser(bytes.NewBuffer(b)) |
364 | + return hasher(b), nil |
365 | + } |
366 | +} |
367 | + |
368 | +// Retrieve the header names, lower-case them, and sort them. |
369 | +func sortHeaderNames(header http.Header) []string { |
370 | + |
371 | + var sortedNames []string |
372 | + for hName, _ := range header { |
373 | + sortedNames = append(sortedNames, strings.ToLower(hName)) |
374 | + } |
375 | + |
376 | + sort.Strings(sortedNames) |
377 | + |
378 | + return sortedNames |
379 | +} |
380 | + |
381 | +func hmacHasher(key []byte, value string) []byte { |
382 | + h := hmac.New(sha256.New, key) |
383 | + h.Write([]byte(value)) |
384 | + return h.Sum(nil) |
385 | +} |
386 | + |
387 | +func inferServiceName(url *url.URL) string { |
388 | + return strings.Split(url.Host, ".")[0] |
389 | +} |
390 | + |
391 | +func sha256Hasher(payload []byte) string { |
392 | + return fmt.Sprintf("%x", sha256.Sum256(payload)) |
393 | +} |
394 | + |
395 | +func credentialScope(t time.Time, regionName, svcName string) string { |
396 | + return fmt.Sprintf( |
397 | + "%s/%s/%s/aws4_request", |
398 | + t.Format(ISO8601BasicFormatShort), |
399 | + regionName, |
400 | + svcName, |
401 | + ) |
402 | +} |
403 | + |
404 | +// We do a lot of fmt.Fprintfs in this package. Create a higher-order |
405 | +// function to elide the bytes written return value so we can submit |
406 | +// these calls to an error collector. |
407 | +func fprintfWrapper(w io.Writer, format string, vals ...interface{}) func() error { |
408 | + return func() error { |
409 | + _, err := fmt.Fprintf(w, format, vals...) |
410 | + return err |
411 | + } |
412 | +} |
413 | + |
414 | +// Poor man's maybe monad. |
415 | +func errorCollector(writers ...func() error) error { |
416 | + for _, writer := range writers { |
417 | + if err := writer(); err != nil { |
418 | + return err |
419 | + } |
420 | + } |
421 | + |
422 | + return nil |
423 | +} |
424 | + |
425 | +// Time formats to try. We want to do everything we can to accept all |
426 | +// time formats, but ultimately we may fail. In the package scope so |
427 | +// it doesn't get initialized for every request. |
428 | +var timeFormats = []string{ |
429 | + time.RFC822, |
430 | + ISO8601BasicFormat, |
431 | + time.RFC1123, |
432 | + time.ANSIC, |
433 | + time.UnixDate, |
434 | + time.RubyDate, |
435 | + time.RFC822Z, |
436 | + time.RFC850, |
437 | + time.RFC1123Z, |
438 | + time.RFC3339, |
439 | + time.RFC3339Nano, |
440 | + time.Kitchen, |
441 | +} |
442 | + |
443 | +// Retrieve the request time from the request. We will attempt to |
444 | +// parse whatever we find, but we will not make up a request date for |
445 | +// the user (i.e.: Magic!). |
446 | +func requestTime(req *http.Request) (time.Time, error) { |
447 | + |
448 | + // Get a date header. |
449 | + var date string |
450 | + if date = req.Header.Get("x-amz-date"); date == "" { |
451 | + if date = req.Header.Get("date"); date == "" { |
452 | + return time.Time{}, fmt.Errorf(`Could not retrieve a request date. Please provide one in either "x-amz-date", or "date".`) |
453 | + } |
454 | + } |
455 | + |
456 | + // Start attempting to parse |
457 | + for _, format := range timeFormats { |
458 | + if parsedTime, err := time.Parse(format, date); err == nil { |
459 | + return parsedTime, nil |
460 | + } |
461 | + } |
462 | + |
463 | + return time.Time{}, fmt.Errorf( |
464 | + "Could not parse the given date. Please utilize on of the following formats: %s", |
465 | + strings.Join(timeFormats, ","), |
466 | + ) |
467 | +} |
468 | + |
469 | +// http.Request's Method member returns the entire method. Derive the |
470 | +// verb. |
471 | +func requestMethodVerb(rawMethod string) (verb string) { |
472 | + verbPlus := strings.SplitN(rawMethod, " ", 2) |
473 | + switch { |
474 | + case len(verbPlus) == 0: // Per docs, Method will be empty if it's GET. |
475 | + verb = "GET" |
476 | + default: |
477 | + verb = verbPlus[0] |
478 | + } |
479 | + return verb |
480 | +} |
481 | |
482 | === added file 'aws/sign_test.go' |
483 | --- aws/sign_test.go 1970-01-01 00:00:00 +0000 |
484 | +++ aws/sign_test.go 2014-08-12 16:51:05 +0000 |
485 | @@ -0,0 +1,227 @@ |
486 | +package aws |
487 | + |
488 | +import ( |
489 | + "bytes" |
490 | + "fmt" |
491 | + . "gopkg.in/check.v1" |
492 | + "net/http" |
493 | + "time" |
494 | +) |
495 | + |
496 | +var _ = Suite(&SigningSuite{}) |
497 | + |
498 | +type SigningSuite struct{} |
499 | + |
500 | +// EC2 ReST authentication docs: http://goo.gl/fQmAN |
501 | +var testAuth = Auth{"user", "secret"} |
502 | + |
503 | +func (s *SigningSuite) TestV4StringToSign(c *C) { |
504 | + |
505 | + mockTime, err := time.Parse(time.RFC3339, "2011-09-09T23:36:00Z") |
506 | + c.Assert(err, IsNil) |
507 | + stringToSign, err := stringToSign( |
508 | + mockTime, |
509 | + "3511de7e95d28ecd39e9513b642aee07e54f4941150d8df8bf94b328ef7e55e2", |
510 | + "20110909/us-east-1/iam/aws4_request", |
511 | + ) |
512 | + c.Assert(err, IsNil) |
513 | + |
514 | + const expected = `AWS4-HMAC-SHA256 |
515 | +20110909T233600Z |
516 | +20110909/us-east-1/iam/aws4_request |
517 | +3511de7e95d28ecd39e9513b642aee07e54f4941150d8df8bf94b328ef7e55e2` |
518 | + c.Assert(stringToSign, Equals, expected) |
519 | +} |
520 | + |
521 | +func (s *SigningSuite) TestV4CanonicalRequest(c *C) { |
522 | + |
523 | + body := new(bytes.Buffer) |
524 | + _, err := fmt.Fprint(body, "Action=ListUsers&Version=2010-05-08") |
525 | + c.Assert(err, IsNil) |
526 | + |
527 | + req, err := http.NewRequest("POST", "https://iam.amazonaws.com", body) |
528 | + c.Assert(err, IsNil) |
529 | + |
530 | + req.Header.Add("content-type", "application/x-www-form-urlencoded; charset=utf-8") |
531 | + req.Header.Add("host", req.URL.Host) |
532 | + req.Header.Add("x-amz-date", "20110909T233600Z") |
533 | + |
534 | + canonReq, canonReqHash, err := canonicalRequest( |
535 | + req, |
536 | + []string{"content-type", "host", "x-amz-date"}, |
537 | + sha256Hasher, |
538 | + ) |
539 | + c.Assert(err, IsNil) |
540 | + |
541 | + const expected = `POST |
542 | +/ |
543 | + |
544 | +content-type:application/x-www-form-urlencoded; charset=utf-8 |
545 | +host:iam.amazonaws.com |
546 | +x-amz-date:20110909T233600Z |
547 | + |
548 | +content-type;host;x-amz-date |
549 | +b6359072c78d70ebee1e81adcbab4f01bf2c23245fa365ef83fe8f1f955085e2` |
550 | + |
551 | + c.Assert(canonReq, Equals, expected) |
552 | + c.Assert(canonReqHash, Equals, "3511de7e95d28ecd39e9513b642aee07e54f4941150d8df8bf94b328ef7e55e2") |
553 | +} |
554 | + |
555 | +func (s *SigningSuite) TestV4SigningKey(c *C) { |
556 | + mockTime, err := time.Parse(time.RFC3339, "2011-09-09T23:36:00Z") |
557 | + c.Assert(err, IsNil) |
558 | + c.Assert( |
559 | + fmt.Sprintf("%v", signingKey(mockTime, testAuth.SecretKey, USEast.Name, "iam")), |
560 | + Equals, |
561 | + "[152 241 216 137 254 196 244 66 26 220 82 43 171 12 225 248 46 105 41 194 98 237 21 229 169 76 144 239 209 227 176 231]") |
562 | +} |
563 | + |
564 | +func (s *SigningSuite) TestV4BasicSignatureV4(c *C) { |
565 | + |
566 | + body := new(bytes.Buffer) |
567 | + |
568 | + req, err := http.NewRequest("POST / http/1.1", "https://host.foo.com", body) |
569 | + c.Assert(err, IsNil) |
570 | + |
571 | + req.Header.Add("Host", req.URL.Host) |
572 | + req.Header.Add("Date", "Mon, 09 Sep 2011 23:36:00 GMT") |
573 | + |
574 | + testAuth = Auth{ |
575 | + AccessKey: "AKIDEXAMPLE", |
576 | + SecretKey: "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", |
577 | + } |
578 | + err = SignV4(req, testAuth, USEast.Name) |
579 | + c.Assert(err, IsNil) |
580 | + |
581 | + c.Assert(req.Header.Get("Authorization"), Equals, `AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=22902d79e148b64e7571c3565769328423fe276eae4b26f83afceda9e767f726`) |
582 | +} |
583 | + |
584 | +// |
585 | +// v2 Tests |
586 | +// |
587 | + |
588 | +func (s *SigningSuite) TestV2BasicSignature(c *C) { |
589 | + req, err := http.NewRequest("GET", "http://localhost/path", nil) |
590 | + c.Assert(err, IsNil) |
591 | + |
592 | + SignV2(req, testAuth) |
593 | + |
594 | + query := req.URL.Query() |
595 | + |
596 | + c.Assert(query.Get("SignatureVersion"), Equals, "2") |
597 | + c.Assert(query.Get("SignatureMethod"), Equals, "HmacSHA256") |
598 | + expected := "6lSe5QyXum0jMVc7cOUz32/52ZnL7N5RyKRk/09yiK4=" |
599 | + c.Assert(query.Get("Signature"), Equals, expected) |
600 | +} |
601 | + |
602 | +func (s *SigningSuite) TestV2ParamSignature(c *C) { |
603 | + |
604 | + req, err := http.NewRequest("GET", "http://localhost/path", nil) |
605 | + c.Assert(err, IsNil) |
606 | + |
607 | + query := req.URL.Query() |
608 | + for i := 1; i <= 3; i++ { |
609 | + query.Add(fmt.Sprintf("param%d", i), fmt.Sprintf("value%d", i)) |
610 | + } |
611 | + req.URL.RawQuery = query.Encode() |
612 | + |
613 | + SignV2(req, testAuth) |
614 | + |
615 | + expected := "XWOR4+0lmK8bD8CGDGZ4kfuSPbb2JibLJiCl/OPu1oU=" |
616 | + c.Assert(req.URL.Query().Get("Signature"), Equals, expected) |
617 | +} |
618 | + |
619 | +func (s *SigningSuite) TestV2ManyParams(c *C) { |
620 | + |
621 | + req, err := http.NewRequest("GET", "http://localhost/path", nil) |
622 | + c.Assert(err, IsNil) |
623 | + |
624 | + query := req.URL.Query() |
625 | + orderedVals := []int{10, 2, 3, 4, 5, 6, 7, 8, 9, 1} |
626 | + for i, val := range orderedVals { |
627 | + query.Add(fmt.Sprintf("param%d", i+1), fmt.Sprintf("value%d", val)) |
628 | + } |
629 | + req.URL.RawQuery = query.Encode() |
630 | + |
631 | + SignV2(req, testAuth) |
632 | + |
633 | + expected := "di0sjxIvezUgQ1SIL6i+C/H8lL+U0CQ9frLIak8jkVg=" |
634 | + c.Assert(req.URL.Query().Get("Signature"), Equals, expected) |
635 | +} |
636 | + |
637 | +func (s *SigningSuite) TestV2Escaping(c *C) { |
638 | + |
639 | + req, err := http.NewRequest("GET", "http://localhost/path", nil) |
640 | + c.Assert(err, IsNil) |
641 | + |
642 | + query := req.URL.Query() |
643 | + query.Add("Nonce", "+ +") |
644 | + req.URL.RawQuery = query.Encode() |
645 | + |
646 | + err = SignV2(req, testAuth) |
647 | + c.Assert(err, IsNil) |
648 | + |
649 | + query = req.URL.Query() |
650 | + c.Assert(query.Get("Nonce"), Equals, "+ +") |
651 | + |
652 | + expected := "bqffDELReIqwjg/W0DnsnVUmfLK4wXVLO4/LuG+1VFA=" |
653 | + c.Assert(query.Get("Signature"), Equals, expected) |
654 | +} |
655 | + |
656 | +func (s *SigningSuite) TestV2SignatureExample1(c *C) { |
657 | + |
658 | + req, err := http.NewRequest("GET", "http://sdb.amazonaws.com/", nil) |
659 | + c.Assert(err, IsNil) |
660 | + |
661 | + query := req.URL.Query() |
662 | + query.Add("Timestamp", "2009-02-01T12:53:20+00:00") |
663 | + query.Add("Version", "2007-11-07") |
664 | + query.Add("Action", "ListDomains") |
665 | + req.URL.RawQuery = query.Encode() |
666 | + |
667 | + SignV2(req, Auth{"access", "secret"}) |
668 | + |
669 | + expected := "okj96/5ucWBSc1uR2zXVfm6mDHtgfNv657rRtt/aunQ=" |
670 | + c.Assert(req.URL.Query().Get("Signature"), Equals, expected) |
671 | +} |
672 | + |
673 | +// Tests example from: |
674 | +// http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html |
675 | +// Specifically, good for testing case when URL does not contain a / |
676 | +func (s *SigningSuite) TestV2SignatureTutorialExample(c *C) { |
677 | + |
678 | + req, err := http.NewRequest("GET", "https://elasticmapreduce.amazonaws.com/", nil) |
679 | + c.Assert(err, IsNil) |
680 | + |
681 | + query := req.URL.Query() |
682 | + query.Add("Timestamp", "2011-10-03T15:19:30") |
683 | + query.Add("Version", "2009-03-31") |
684 | + query.Add("Action", "DescribeJobFlows") |
685 | + req.URL.RawQuery = query.Encode() |
686 | + |
687 | + testAuth := Auth{"AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"} |
688 | + err = SignV2(req, testAuth) |
689 | + c.Assert(err, IsNil) |
690 | + c.Assert(req.URL.Query().Get("Signature"), Equals, "i91nKc4PWAt0JJIdXwz9HxZCJDdiy6cf/Mj6vPxyYIs=") |
691 | +} |
692 | + |
693 | +// https://bugs.launchpad.net/goamz/+bug/1022749 |
694 | +func (s *SigningSuite) TestSignatureWithEndpointPath(c *C) { |
695 | + |
696 | + req, err := http.NewRequest("GET", "http://localhost:4444/services/Cloud", nil) |
697 | + c.Assert(err, IsNil) |
698 | + |
699 | + queryStr := req.URL.Query() |
700 | + queryStr.Add("Action", "RebootInstances") |
701 | + queryStr.Add("Version", "2011-12-15") |
702 | + queryStr.Add("InstanceId.1", "i-10a64379") |
703 | + queryStr.Add("Timestamp", time.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC).In(time.UTC).Format(time.RFC3339)) |
704 | + req.URL.RawQuery = queryStr.Encode() |
705 | + |
706 | + err = SignV2(req, Auth{"abc", "123"}) |
707 | + c.Assert(err, IsNil) |
708 | + c.Assert(req.URL.Query().Get("Signature"), Equals, "gdG/vEm+c6ehhhfkrJy3+wuVzw/rzKR42TYelMwti7M=") |
709 | + err = req.ParseForm() |
710 | + c.Assert(err, IsNil) |
711 | + c.Assert(req.Form["Signature"], DeepEquals, []string{"gdG/vEm+c6ehhhfkrJy3+wuVzw/rzKR42TYelMwti7M="}) |
712 | +} |
713 | |
714 | === modified file 'ec2/ec2.go' |
715 | --- ec2/ec2.go 2014-05-30 09:43:41 +0000 |
716 | +++ ec2/ec2.go 2014-08-12 16:51:05 +0000 |
717 | @@ -21,6 +21,7 @@ |
718 | "strconv" |
719 | "time" |
720 | |
721 | + "encoding/base64" |
722 | "launchpad.net/goamz/aws" |
723 | ) |
724 | |
725 | @@ -126,21 +127,25 @@ |
726 | |
727 | var timeNow = time.Now |
728 | |
729 | +// resp = response structure that will get inflated by XML unmarshaling. |
730 | func (ec2 *EC2) query(params map[string]string, resp interface{}) error { |
731 | - params["Timestamp"] = timeNow().In(time.UTC).Format(time.RFC3339) |
732 | - endpoint, err := url.Parse(ec2.Region.EC2Endpoint) |
733 | + |
734 | + req, err := http.NewRequest("GET", ec2.Region.EC2Endpoint, nil) |
735 | if err != nil { |
736 | return err |
737 | } |
738 | - if endpoint.Path == "" { |
739 | - endpoint.Path = "/" |
740 | - } |
741 | - sign(ec2.Auth, "GET", endpoint.Path, params, endpoint.Host) |
742 | - endpoint.RawQuery = multimap(params).Encode() |
743 | - if debug { |
744 | - log.Printf("get { %v } -> {\n", endpoint.String()) |
745 | - } |
746 | - r, err := http.Get(endpoint.String()) |
747 | + |
748 | + // Add the params passed in to the query string |
749 | + query := req.URL.Query() |
750 | + for varName, varVal := range params { |
751 | + query.Add(varName, varVal) |
752 | + } |
753 | + query.Add("Timestamp", timeNow().In(time.UTC).Format(time.RFC3339)) |
754 | + req.URL.RawQuery = query.Encode() |
755 | + |
756 | + ec2.Region.Sign(req, ec2.Auth) |
757 | + |
758 | + r, err := http.DefaultClient.Do(req) |
759 | if err != nil { |
760 | return err |
761 | } |
762 | @@ -154,8 +159,7 @@ |
763 | if r.StatusCode != 200 { |
764 | return buildError(r) |
765 | } |
766 | - err = xml.NewDecoder(r.Body).Decode(resp) |
767 | - return err |
768 | + return xml.NewDecoder(r.Body).Decode(resp) |
769 | } |
770 | |
771 | func multimap(p map[string]string) url.Values { |
772 | @@ -349,8 +353,8 @@ |
773 | params["RamdiskId"] = options.RamdiskId |
774 | } |
775 | if options.UserData != nil { |
776 | - userData := make([]byte, b64.EncodedLen(len(options.UserData))) |
777 | - b64.Encode(userData, options.UserData) |
778 | + userData := make([]byte, base64.StdEncoding.EncodedLen(len(options.UserData))) |
779 | + base64.StdEncoding.Encode(userData, options.UserData) |
780 | params["UserData"] = string(userData) |
781 | } |
782 | if options.AvailZone != "" { |
783 | |
784 | === modified file 'ec2/ec2_test.go' |
785 | --- ec2/ec2_test.go 2014-05-30 09:43:41 +0000 |
786 | +++ ec2/ec2_test.go 2014-08-12 16:51:05 +0000 |
787 | @@ -6,7 +6,7 @@ |
788 | "launchpad.net/goamz/aws" |
789 | "launchpad.net/goamz/ec2" |
790 | "launchpad.net/goamz/testutil" |
791 | - . "launchpad.net/gocheck" |
792 | + . "gopkg.in/check.v1" |
793 | ) |
794 | |
795 | func Test(t *testing.T) { |
796 | @@ -24,7 +24,7 @@ |
797 | func (s *S) SetUpSuite(c *C) { |
798 | testServer.Start() |
799 | auth := aws.Auth{"abc", "123"} |
800 | - s.ec2 = ec2.New(auth, aws.Region{EC2Endpoint: testServer.URL}) |
801 | + s.ec2 = ec2.New(auth, aws.Region{EC2Endpoint: testServer.URL, Sign: aws.SignV2}) |
802 | } |
803 | |
804 | func (s *S) TearDownSuite(c *C) { |
805 | @@ -791,22 +791,6 @@ |
806 | c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") |
807 | } |
808 | |
809 | -func (s *S) TestSignatureWithEndpointPath(c *C) { |
810 | - ec2.FakeTime(true) |
811 | - defer ec2.FakeTime(false) |
812 | - |
813 | - testServer.Response(200, nil, RebootInstancesExample) |
814 | - |
815 | - // https://bugs.launchpad.net/goamz/+bug/1022749 |
816 | - ec2 := ec2.New(s.ec2.Auth, aws.Region{EC2Endpoint: testServer.URL + "/services/Cloud"}) |
817 | - |
818 | - _, err := ec2.RebootInstances("i-10a64379") |
819 | - c.Assert(err, IsNil) |
820 | - |
821 | - req := testServer.WaitRequest() |
822 | - c.Assert(req.Form["Signature"], DeepEquals, []string{"gdG/vEm+c6ehhhfkrJy3+wuVzw/rzKR42TYelMwti7M="}) |
823 | -} |
824 | - |
825 | func (s *S) TestAvailabilityZonesExample1(c *C) { |
826 | testServer.Response(200, nil, DescribeAvailabilityZonesExample1) |
827 | |
828 | |
829 | === modified file 'ec2/ec2i_test.go' |
830 | --- ec2/ec2i_test.go 2014-05-21 13:52:28 +0000 |
831 | +++ ec2/ec2i_test.go 2014-08-12 16:51:05 +0000 |
832 | @@ -7,7 +7,7 @@ |
833 | "launchpad.net/goamz/aws" |
834 | "launchpad.net/goamz/ec2" |
835 | "launchpad.net/goamz/testutil" |
836 | - . "launchpad.net/gocheck" |
837 | + . "gopkg.in/check.v1" |
838 | ) |
839 | |
840 | // AmazonServer represents an Amazon EC2 server. |
841 | |
842 | === modified file 'ec2/ec2t_test.go' |
843 | --- ec2/ec2t_test.go 2014-05-30 09:43:41 +0000 |
844 | +++ ec2/ec2t_test.go 2014-08-12 16:51:05 +0000 |
845 | @@ -11,7 +11,7 @@ |
846 | "launchpad.net/goamz/ec2" |
847 | "launchpad.net/goamz/ec2/ec2test" |
848 | "launchpad.net/goamz/testutil" |
849 | - . "launchpad.net/gocheck" |
850 | + . "gopkg.in/check.v1" |
851 | ) |
852 | |
853 | // LocalServer represents a local ec2test fake server. |
854 | @@ -33,7 +33,7 @@ |
855 | }) |
856 | |
857 | s.srv = srv |
858 | - s.region = aws.Region{EC2Endpoint: srv.URL()} |
859 | + s.region = aws.Region{EC2Endpoint: srv.URL(), Sign: aws.SignV2} |
860 | } |
861 | |
862 | // LocalServerSuite defines tests that will run |
863 | |
864 | === modified file 'ec2/export_test.go' |
865 | --- ec2/export_test.go 2014-02-12 17:04:23 +0000 |
866 | +++ ec2/export_test.go 2014-08-12 16:51:05 +0000 |
867 | @@ -1,14 +1,9 @@ |
868 | package ec2 |
869 | |
870 | import ( |
871 | - "launchpad.net/goamz/aws" |
872 | "time" |
873 | ) |
874 | |
875 | -func Sign(auth aws.Auth, method, path string, params map[string]string, host string) { |
876 | - sign(auth, method, path, params, host) |
877 | -} |
878 | - |
879 | func fixedTime() time.Time { |
880 | return time.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC) |
881 | } |
882 | |
883 | === modified file 'ec2/networkinterfaces_test.go' |
884 | --- ec2/networkinterfaces_test.go 2014-02-12 17:29:13 +0000 |
885 | +++ ec2/networkinterfaces_test.go 2014-08-12 16:51:05 +0000 |
886 | @@ -11,7 +11,7 @@ |
887 | import ( |
888 | "launchpad.net/goamz/aws" |
889 | "launchpad.net/goamz/ec2" |
890 | - . "launchpad.net/gocheck" |
891 | + . "gopkg.in/check.v1" |
892 | "time" |
893 | ) |
894 | |
895 | |
896 | === modified file 'ec2/privateips_test.go' |
897 | --- ec2/privateips_test.go 2014-05-27 11:43:56 +0000 |
898 | +++ ec2/privateips_test.go 2014-08-12 16:51:05 +0000 |
899 | @@ -14,7 +14,7 @@ |
900 | |
901 | "launchpad.net/goamz/aws" |
902 | "launchpad.net/goamz/ec2" |
903 | - . "launchpad.net/gocheck" |
904 | + . "gopkg.in/check.v1" |
905 | ) |
906 | |
907 | // Private IP tests with example responses |
908 | |
909 | === removed file 'ec2/sign.go' |
910 | --- ec2/sign.go 2013-05-23 01:06:45 +0000 |
911 | +++ ec2/sign.go 1970-01-01 00:00:00 +0000 |
912 | @@ -1,42 +0,0 @@ |
913 | -package ec2 |
914 | - |
915 | -import ( |
916 | - "crypto/hmac" |
917 | - "crypto/sha256" |
918 | - "encoding/base64" |
919 | - "launchpad.net/goamz/aws" |
920 | - "sort" |
921 | - "strings" |
922 | -) |
923 | - |
924 | -// ---------------------------------------------------------------------------- |
925 | -// EC2 signing (http://goo.gl/fQmAN) |
926 | - |
927 | -var b64 = base64.StdEncoding |
928 | - |
929 | -func sign(auth aws.Auth, method, path string, params map[string]string, host string) { |
930 | - params["AWSAccessKeyId"] = auth.AccessKey |
931 | - params["SignatureVersion"] = "2" |
932 | - params["SignatureMethod"] = "HmacSHA256" |
933 | - |
934 | - // AWS specifies that the parameters in a signed request must |
935 | - // be provided in the natural order of the keys. This is distinct |
936 | - // from the natural order of the encoded value of key=value. |
937 | - // Percent and equals affect the sorting order. |
938 | - var keys, sarray []string |
939 | - for k, _ := range params { |
940 | - keys = append(keys, k) |
941 | - } |
942 | - sort.Strings(keys) |
943 | - for _, k := range keys { |
944 | - sarray = append(sarray, aws.Encode(k)+"="+aws.Encode(params[k])) |
945 | - } |
946 | - joined := strings.Join(sarray, "&") |
947 | - payload := method + "\n" + host + "\n" + path + "\n" + joined |
948 | - hash := hmac.New(sha256.New, []byte(auth.SecretKey)) |
949 | - hash.Write([]byte(payload)) |
950 | - signature := make([]byte, b64.EncodedLen(hash.Size())) |
951 | - b64.Encode(signature, hash.Sum(nil)) |
952 | - |
953 | - params["Signature"] = string(signature) |
954 | -} |
955 | |
956 | === removed file 'ec2/sign_test.go' |
957 | --- ec2/sign_test.go 2012-10-18 01:02:48 +0000 |
958 | +++ ec2/sign_test.go 1970-01-01 00:00:00 +0000 |
959 | @@ -1,68 +0,0 @@ |
960 | -package ec2_test |
961 | - |
962 | -import ( |
963 | - "launchpad.net/goamz/aws" |
964 | - "launchpad.net/goamz/ec2" |
965 | - . "launchpad.net/gocheck" |
966 | -) |
967 | - |
968 | -// EC2 ReST authentication docs: http://goo.gl/fQmAN |
969 | - |
970 | -var testAuth = aws.Auth{"user", "secret"} |
971 | - |
972 | -func (s *S) TestBasicSignature(c *C) { |
973 | - params := map[string]string{} |
974 | - ec2.Sign(testAuth, "GET", "/path", params, "localhost") |
975 | - c.Assert(params["SignatureVersion"], Equals, "2") |
976 | - c.Assert(params["SignatureMethod"], Equals, "HmacSHA256") |
977 | - expected := "6lSe5QyXum0jMVc7cOUz32/52ZnL7N5RyKRk/09yiK4=" |
978 | - c.Assert(params["Signature"], Equals, expected) |
979 | -} |
980 | - |
981 | -func (s *S) TestParamSignature(c *C) { |
982 | - params := map[string]string{ |
983 | - "param1": "value1", |
984 | - "param2": "value2", |
985 | - "param3": "value3", |
986 | - } |
987 | - ec2.Sign(testAuth, "GET", "/path", params, "localhost") |
988 | - expected := "XWOR4+0lmK8bD8CGDGZ4kfuSPbb2JibLJiCl/OPu1oU=" |
989 | - c.Assert(params["Signature"], Equals, expected) |
990 | -} |
991 | - |
992 | -func (s *S) TestManyParams(c *C) { |
993 | - params := map[string]string{ |
994 | - "param1": "value10", |
995 | - "param2": "value2", |
996 | - "param3": "value3", |
997 | - "param4": "value4", |
998 | - "param5": "value5", |
999 | - "param6": "value6", |
1000 | - "param7": "value7", |
1001 | - "param8": "value8", |
1002 | - "param9": "value9", |
1003 | - "param10": "value1", |
1004 | - } |
1005 | - ec2.Sign(testAuth, "GET", "/path", params, "localhost") |
1006 | - expected := "di0sjxIvezUgQ1SIL6i+C/H8lL+U0CQ9frLIak8jkVg=" |
1007 | - c.Assert(params["Signature"], Equals, expected) |
1008 | -} |
1009 | - |
1010 | -func (s *S) TestEscaping(c *C) { |
1011 | - params := map[string]string{"Nonce": "+ +"} |
1012 | - ec2.Sign(testAuth, "GET", "/path", params, "localhost") |
1013 | - c.Assert(params["Nonce"], Equals, "+ +") |
1014 | - expected := "bqffDELReIqwjg/W0DnsnVUmfLK4wXVLO4/LuG+1VFA=" |
1015 | - c.Assert(params["Signature"], Equals, expected) |
1016 | -} |
1017 | - |
1018 | -func (s *S) TestSignatureExample1(c *C) { |
1019 | - params := map[string]string{ |
1020 | - "Timestamp": "2009-02-01T12:53:20+00:00", |
1021 | - "Version": "2007-11-07", |
1022 | - "Action": "ListDomains", |
1023 | - } |
1024 | - ec2.Sign(aws.Auth{"access", "secret"}, "GET", "/", params, "sdb.amazonaws.com") |
1025 | - expected := "okj96/5ucWBSc1uR2zXVfm6mDHtgfNv657rRtt/aunQ=" |
1026 | - c.Assert(params["Signature"], Equals, expected) |
1027 | -} |
1028 | |
1029 | === modified file 'ec2/subnets_test.go' |
1030 | --- ec2/subnets_test.go 2014-02-12 16:38:47 +0000 |
1031 | +++ ec2/subnets_test.go 2014-08-12 16:51:05 +0000 |
1032 | @@ -11,7 +11,7 @@ |
1033 | import ( |
1034 | "launchpad.net/goamz/aws" |
1035 | "launchpad.net/goamz/ec2" |
1036 | - . "launchpad.net/gocheck" |
1037 | + . "gopkg.in/check.v1" |
1038 | "time" |
1039 | ) |
1040 | |
1041 | |
1042 | === modified file 'ec2/vpc_test.go' |
1043 | --- ec2/vpc_test.go 2014-02-12 13:58:45 +0000 |
1044 | +++ ec2/vpc_test.go 2014-08-12 16:51:05 +0000 |
1045 | @@ -11,7 +11,7 @@ |
1046 | import ( |
1047 | "launchpad.net/goamz/aws" |
1048 | "launchpad.net/goamz/ec2" |
1049 | - . "launchpad.net/gocheck" |
1050 | + . "gopkg.in/check.v1" |
1051 | "time" |
1052 | ) |
1053 | |
1054 | |
1055 | === modified file 'exp/mturk/mturk_test.go' |
1056 | --- exp/mturk/mturk_test.go 2013-05-23 02:56:41 +0000 |
1057 | +++ exp/mturk/mturk_test.go 2014-08-12 16:51:05 +0000 |
1058 | @@ -4,7 +4,7 @@ |
1059 | "launchpad.net/goamz/aws" |
1060 | "launchpad.net/goamz/exp/mturk" |
1061 | "launchpad.net/goamz/testutil" |
1062 | - . "launchpad.net/gocheck" |
1063 | + . "gopkg.in/check.v1" |
1064 | "net/url" |
1065 | "testing" |
1066 | ) |
1067 | |
1068 | === modified file 'exp/mturk/sign_test.go' |
1069 | --- exp/mturk/sign_test.go 2012-05-03 17:12:28 +0000 |
1070 | +++ exp/mturk/sign_test.go 2014-08-12 16:51:05 +0000 |
1071 | @@ -3,7 +3,7 @@ |
1072 | import ( |
1073 | "launchpad.net/goamz/aws" |
1074 | "launchpad.net/goamz/exp/mturk" |
1075 | - . "launchpad.net/gocheck" |
1076 | + . "gopkg.in/check.v1" |
1077 | ) |
1078 | |
1079 | // Mechanical Turk REST authentication docs: http://goo.gl/wrzfn |
1080 | |
1081 | === modified file 'exp/sdb/sdb_test.go' |
1082 | --- exp/sdb/sdb_test.go 2013-05-23 02:56:41 +0000 |
1083 | +++ exp/sdb/sdb_test.go 2014-08-12 16:51:05 +0000 |
1084 | @@ -4,7 +4,7 @@ |
1085 | "launchpad.net/goamz/aws" |
1086 | "launchpad.net/goamz/exp/sdb" |
1087 | "launchpad.net/goamz/testutil" |
1088 | - . "launchpad.net/gocheck" |
1089 | + . "gopkg.in/check.v1" |
1090 | "testing" |
1091 | ) |
1092 | |
1093 | @@ -23,7 +23,7 @@ |
1094 | func (s *S) SetUpSuite(c *C) { |
1095 | testServer.Start() |
1096 | auth := aws.Auth{"abc", "123"} |
1097 | - s.sdb = sdb.New(auth, aws.Region{SDBEndpoint: testServer.URL}) |
1098 | + s.sdb = sdb.New(auth, aws.Region{SDBEndpoint: testServer.URL, Sign: aws.SignV2}) |
1099 | } |
1100 | |
1101 | func (s *S) TearDownSuite(c *C) { |
1102 | |
1103 | === modified file 'exp/sdb/sign_test.go' |
1104 | --- exp/sdb/sign_test.go 2012-03-09 15:34:56 +0000 |
1105 | +++ exp/sdb/sign_test.go 2014-08-12 16:51:05 +0000 |
1106 | @@ -3,7 +3,7 @@ |
1107 | import ( |
1108 | "launchpad.net/goamz/aws" |
1109 | "launchpad.net/goamz/exp/sdb" |
1110 | - . "launchpad.net/gocheck" |
1111 | + . "gopkg.in/check.v1" |
1112 | ) |
1113 | |
1114 | // SimpleDB ReST authentication docs: http://goo.gl/CaY81 |
1115 | |
1116 | === modified file 'exp/sns/sns_test.go' |
1117 | --- exp/sns/sns_test.go 2013-05-23 02:56:41 +0000 |
1118 | +++ exp/sns/sns_test.go 2014-08-12 16:51:05 +0000 |
1119 | @@ -4,7 +4,7 @@ |
1120 | "launchpad.net/goamz/aws" |
1121 | "launchpad.net/goamz/exp/sns" |
1122 | "launchpad.net/goamz/testutil" |
1123 | - . "launchpad.net/gocheck" |
1124 | + . "gopkg.in/check.v1" |
1125 | "testing" |
1126 | ) |
1127 | |
1128 | @@ -23,7 +23,7 @@ |
1129 | func (s *S) SetUpSuite(c *C) { |
1130 | testServer.Start() |
1131 | auth := aws.Auth{"abc", "123"} |
1132 | - s.sns = sns.New(auth, aws.Region{SNSEndpoint: testServer.URL}) |
1133 | + s.sns = sns.New(auth, aws.Region{SNSEndpoint: testServer.URL, Sign: aws.SignV2}) |
1134 | } |
1135 | |
1136 | func (s *S) TearDownSuite(c *C) { |
1137 | |
1138 | === modified file 'iam/iam_test.go' |
1139 | --- iam/iam_test.go 2013-05-23 02:56:41 +0000 |
1140 | +++ iam/iam_test.go 2014-08-12 16:51:05 +0000 |
1141 | @@ -4,7 +4,7 @@ |
1142 | "launchpad.net/goamz/aws" |
1143 | "launchpad.net/goamz/iam" |
1144 | "launchpad.net/goamz/testutil" |
1145 | - . "launchpad.net/gocheck" |
1146 | + . "gopkg.in/check.v1" |
1147 | "strings" |
1148 | "testing" |
1149 | ) |
1150 | @@ -24,7 +24,7 @@ |
1151 | func (s *S) SetUpSuite(c *C) { |
1152 | testServer.Start() |
1153 | auth := aws.Auth{"abc", "123"} |
1154 | - s.iam = iam.New(auth, aws.Region{IAMEndpoint: testServer.URL}) |
1155 | + s.iam = iam.New(auth, aws.Region{IAMEndpoint: testServer.URL, Sign: aws.SignV2}) |
1156 | } |
1157 | |
1158 | func (s *S) TearDownSuite(c *C) { |
1159 | |
1160 | === modified file 'iam/iami_test.go' |
1161 | --- iam/iami_test.go 2013-03-02 02:11:38 +0000 |
1162 | +++ iam/iami_test.go 2014-08-12 16:51:05 +0000 |
1163 | @@ -4,7 +4,7 @@ |
1164 | "launchpad.net/goamz/aws" |
1165 | "launchpad.net/goamz/iam" |
1166 | "launchpad.net/goamz/testutil" |
1167 | - . "launchpad.net/gocheck" |
1168 | + . "gopkg.in/check.v1" |
1169 | "net/url" |
1170 | ) |
1171 | |
1172 | |
1173 | === modified file 'iam/iamt_test.go' |
1174 | --- iam/iamt_test.go 2012-10-10 21:18:36 +0000 |
1175 | +++ iam/iamt_test.go 2014-08-12 16:51:05 +0000 |
1176 | @@ -4,7 +4,7 @@ |
1177 | "launchpad.net/goamz/aws" |
1178 | "launchpad.net/goamz/iam" |
1179 | "launchpad.net/goamz/iam/iamtest" |
1180 | - . "launchpad.net/gocheck" |
1181 | + . "gopkg.in/check.v1" |
1182 | ) |
1183 | |
1184 | // LocalServer represents a local ec2test fake server. |
1185 | @@ -20,7 +20,7 @@ |
1186 | c.Assert(srv, NotNil) |
1187 | |
1188 | s.srv = srv |
1189 | - s.region = aws.Region{IAMEndpoint: srv.URL()} |
1190 | + s.region = aws.Region{IAMEndpoint: srv.URL(), Sign: aws.SignV2} |
1191 | } |
1192 | |
1193 | // LocalServerSuite defines tests that will run |
1194 | |
1195 | === modified file 's3/multi_test.go' |
1196 | --- s3/multi_test.go 2013-08-15 13:18:02 +0000 |
1197 | +++ s3/multi_test.go 2014-08-12 16:51:05 +0000 |
1198 | @@ -5,7 +5,7 @@ |
1199 | "io" |
1200 | "io/ioutil" |
1201 | "launchpad.net/goamz/s3" |
1202 | - . "launchpad.net/gocheck" |
1203 | + . "gopkg.in/check.v1" |
1204 | "strings" |
1205 | ) |
1206 | |
1207 | |
1208 | === modified file 's3/s3_test.go' |
1209 | --- s3/s3_test.go 2013-08-15 13:18:02 +0000 |
1210 | +++ s3/s3_test.go 2014-08-12 16:51:05 +0000 |
1211 | @@ -9,7 +9,7 @@ |
1212 | "launchpad.net/goamz/aws" |
1213 | "launchpad.net/goamz/s3" |
1214 | "launchpad.net/goamz/testutil" |
1215 | - . "launchpad.net/gocheck" |
1216 | + . "gopkg.in/check.v1" |
1217 | "time" |
1218 | ) |
1219 | |
1220 | @@ -28,7 +28,7 @@ |
1221 | func (s *S) SetUpSuite(c *C) { |
1222 | testServer.Start() |
1223 | auth := aws.Auth{"abc", "123"} |
1224 | - s.s3 = s3.New(auth, aws.Region{Name: "faux-region-1", S3Endpoint: testServer.URL}) |
1225 | + s.s3 = s3.New(auth, aws.Region{Name: "faux-region-1", S3Endpoint: testServer.URL, Sign: aws.SignV2}) |
1226 | } |
1227 | |
1228 | func (s *S) TearDownSuite(c *C) { |
1229 | |
1230 | === modified file 's3/s3i_test.go' |
1231 | --- s3/s3i_test.go 2013-02-13 12:20:38 +0000 |
1232 | +++ s3/s3i_test.go 2014-08-12 16:51:05 +0000 |
1233 | @@ -11,7 +11,7 @@ |
1234 | "launchpad.net/goamz/aws" |
1235 | "launchpad.net/goamz/s3" |
1236 | "launchpad.net/goamz/testutil" |
1237 | - . "launchpad.net/gocheck" |
1238 | + . "gopkg.in/check.v1" |
1239 | "net" |
1240 | "sort" |
1241 | "time" |
1242 | |
1243 | === modified file 's3/s3t_test.go' |
1244 | --- s3/s3t_test.go 2013-02-01 05:55:19 +0000 |
1245 | +++ s3/s3t_test.go 2014-08-12 16:51:05 +0000 |
1246 | @@ -4,7 +4,7 @@ |
1247 | "launchpad.net/goamz/aws" |
1248 | "launchpad.net/goamz/s3" |
1249 | "launchpad.net/goamz/s3/s3test" |
1250 | - . "launchpad.net/gocheck" |
1251 | + . "gopkg.in/check.v1" |
1252 | ) |
1253 | |
1254 | type LocalServer struct { |
1255 | @@ -24,6 +24,7 @@ |
1256 | Name: "faux-region-1", |
1257 | S3Endpoint: srv.URL(), |
1258 | S3LocationConstraint: true, // s3test server requires a LocationConstraint |
1259 | + Sign: aws.SignV2, |
1260 | } |
1261 | } |
1262 | |
1263 | |
1264 | === modified file 's3/sign_test.go' |
1265 | --- s3/sign_test.go 2012-07-16 00:36:17 +0000 |
1266 | +++ s3/sign_test.go 2014-08-12 16:51:05 +0000 |
1267 | @@ -3,7 +3,7 @@ |
1268 | import ( |
1269 | "launchpad.net/goamz/aws" |
1270 | "launchpad.net/goamz/s3" |
1271 | - . "launchpad.net/gocheck" |
1272 | + . "gopkg.in/check.v1" |
1273 | ) |
1274 | |
1275 | // S3 ReST authentication docs: http://goo.gl/G1LrK |
1276 | |
1277 | === modified file 'testutil/http.go' |
1278 | --- testutil/http.go 2013-05-23 03:13:02 +0000 |
1279 | +++ testutil/http.go 2014-08-12 16:51:05 +0000 |
1280 | @@ -4,6 +4,7 @@ |
1281 | "bytes" |
1282 | "fmt" |
1283 | "io/ioutil" |
1284 | + "log" |
1285 | "net" |
1286 | "net/http" |
1287 | "net/url" |
1288 | @@ -27,7 +28,12 @@ |
1289 | } |
1290 | |
1291 | func NewHTTPServer() *HTTPServer { |
1292 | - return &HTTPServer{URL: "http://localhost:4444", Timeout: 5 * time.Second} |
1293 | + s := &HTTPServer{ |
1294 | + URL: "http://localhost:0", |
1295 | + Timeout: 5 * time.Second, |
1296 | + } |
1297 | + |
1298 | + return s |
1299 | } |
1300 | |
1301 | type ResponseFunc func(path string) Response |
1302 | @@ -43,10 +49,16 @@ |
1303 | if err != nil { |
1304 | panic(err) |
1305 | } |
1306 | + |
1307 | s.listener, err = net.Listen("tcp", u.Host) |
1308 | if err != nil { |
1309 | panic(err) |
1310 | } |
1311 | + |
1312 | + // The OS has found an open port; reset the URL to reflect the |
1313 | + // actual port. |
1314 | + s.URL = fmt.Sprintf("http://%s", s.listener.Addr().String()) |
1315 | + log.Printf("Listening on %v\n", s.URL) |
1316 | go http.Serve(s.listener, s) |
1317 | |
1318 | s.Response(203, nil, "") |
1319 | @@ -56,6 +68,7 @@ |
1320 | if err == nil && resp.StatusCode == 203 { |
1321 | break |
1322 | } |
1323 | + log.Printf("Waiting on addr: %s\n", s.URL) |
1324 | time.Sleep(1e8) |
1325 | } |
1326 | s.WaitRequest() // Consume dummy request. |
1327 | @@ -70,6 +83,7 @@ |
1328 | panic(err) |
1329 | } |
1330 | s.listener = nil |
1331 | + s.started = false |
1332 | } |
1333 | |
1334 | // Flush discards all pending requests and responses. |
1335 | |
1336 | === modified file 'testutil/suite.go' |
1337 | --- testutil/suite.go 2013-01-31 14:52:05 +0000 |
1338 | +++ testutil/suite.go 2014-08-12 16:51:05 +0000 |
1339 | @@ -3,7 +3,7 @@ |
1340 | import ( |
1341 | "flag" |
1342 | "launchpad.net/goamz/aws" |
1343 | - . "launchpad.net/gocheck" |
1344 | + . "gopkg.in/check.v1" |
1345 | ) |
1346 | |
1347 | // Amazon must be used by all tested packages to determine whether to |
Looks good to me.