Vendor update github.com/pkg/errors

This commit is contained in:
Andrey Smirnov
2019-01-20 00:28:36 +03:00
parent 50f8cfbc15
commit 4b6c159e3a
20 changed files with 727 additions and 731 deletions
Generated
+124 -10
View File
@@ -2,18 +2,23 @@
[[projects]] [[projects]]
digest = "1:e8777c437e157121465a08c05304d5847ae70b5683ef4afeb723b3c9e5e3bc67"
name = "github.com/AlekSi/pointer" name = "github.com/AlekSi/pointer"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "08a25bac605b3fcb6cc27f3917b2c2c87451963d" revision = "08a25bac605b3fcb6cc27f3917b2c2c87451963d"
version = "v1.0.0" version = "v1.0.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:218a66570875d243938fff08f17ec03e79b18c138d0845b2428bcb4929ffa4fe"
name = "github.com/DisposaBoy/JsonConfigReader" name = "github.com/DisposaBoy/JsonConfigReader"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "33a99fdf1d5ee1f79b5077e9c06f955ad356d5f4" revision = "33a99fdf1d5ee1f79b5077e9c06f955ad356d5f4"
[[projects]] [[projects]]
digest = "1:658ab137074ef3d1216e01cbe166eb171a1bd77daeae0b686ab1ae184a9a5ec9"
name = "github.com/awalterschulze/gographviz" name = "github.com/awalterschulze/gographviz"
packages = [ packages = [
".", ".",
@@ -22,10 +27,12 @@
"scanner", "scanner",
"token" "token"
] ]
pruneopts = ""
revision = "761fd5fbb34e4c2c138c280395b65b48e4ff5a53" revision = "761fd5fbb34e4c2c138c280395b65b48e4ff5a53"
version = "v1.0" version = "v1.0"
[[projects]] [[projects]]
digest = "1:a72e35a51b628d148241720897de644fd6d3cea45d8489dae9d373ced5dfc302"
name = "github.com/aws/aws-sdk-go" name = "github.com/aws/aws-sdk-go"
packages = [ packages = [
"aws", "aws",
@@ -56,155 +63,203 @@
"service/s3", "service/s3",
"service/sts" "service/sts"
] ]
pruneopts = ""
revision = "a72204b9bf8d48230ee0fe8995613b394c66f2da" revision = "a72204b9bf8d48230ee0fe8995613b394c66f2da"
version = "v1.13.31" version = "v1.13.31"
[[projects]] [[projects]]
digest = "1:b9922c7da8a89c973758c03bde497017567f785b49bd253402a3587abec1e53d"
name = "github.com/cheggaaa/pb" name = "github.com/cheggaaa/pb"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "cdf719fac0dd208251aa828e687c2d5802053b51" revision = "cdf719fac0dd208251aa828e687c2d5802053b51"
version = "v1.0.10" version = "v1.0.10"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:1120f960f5c334f0f94bad29eefaf73d52d226893369693686148f66c1993f15"
name = "github.com/gin-contrib/sse" name = "github.com/gin-contrib/sse"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "22d885f9ecc78bf4ee5d72b937e4bbcdc58e8cae" revision = "22d885f9ecc78bf4ee5d72b937e4bbcdc58e8cae"
[[projects]] [[projects]]
digest = "1:348ceb76f2ac958e541e4ba3190484b68df28c38ac9720ed4ef8d36af69ce52e"
name = "github.com/gin-gonic/gin" name = "github.com/gin-gonic/gin"
packages = [ packages = [
".", ".",
"binding", "binding",
"render" "render"
] ]
pruneopts = ""
revision = "d459835d2b077e44f7c9b453505ee29881d5d12d" revision = "d459835d2b077e44f7c9b453505ee29881d5d12d"
version = "v1.2" version = "v1.2"
[[projects]] [[projects]]
digest = "1:fbed9ba4076145ae05ef7deeb14d49565e112d6c283a8c4304fa3b7be785fa5e"
name = "github.com/go-ini/ini" name = "github.com/go-ini/ini"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "1730955e3146956d6a087861380f9b4667ed5071" revision = "1730955e3146956d6a087861380f9b4667ed5071"
version = "v1.26.0" version = "v1.26.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:27854310d59099f8dcc61dd8af4a69f0a3597f001154b2fb4d1c41baf2e31ec1"
name = "github.com/golang/protobuf" name = "github.com/golang/protobuf"
packages = ["proto"] packages = ["proto"]
pruneopts = ""
revision = "130e6b02ab059e7b717a096f397c5b60111cae74" revision = "130e6b02ab059e7b717a096f397c5b60111cae74"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:09307dfb1aa3f49a2bf869dcfa4c6c06ecd3c207221bd1c1a1141f0e51f209eb"
name = "github.com/golang/snappy" name = "github.com/golang/snappy"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "553a641470496b2327abcac10b36396bd98e45c9" revision = "553a641470496b2327abcac10b36396bd98e45c9"
[[projects]] [[projects]]
digest = "1:6ee50e0ace655f26787c120dbce51b3185c470f475c9e57d0dcada8ebb115004"
name = "github.com/h2non/filetype" name = "github.com/h2non/filetype"
packages = ["matchers"] packages = ["matchers"]
pruneopts = ""
revision = "cc14fdc9ca0e4c2bafad7458f6ff79fd3947cfbb" revision = "cc14fdc9ca0e4c2bafad7458f6ff79fd3947cfbb"
version = "v1.0.5" version = "v1.0.5"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:64b78d98b8956492576911baf6a1e3499816d4575e485d12792e4abe7d8b6c46"
name = "github.com/jlaffaye/ftp" name = "github.com/jlaffaye/ftp"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "2403248fa8cc9f7909862627aa7337f13f8e0bf1" revision = "2403248fa8cc9f7909862627aa7337f13f8e0bf1"
[[projects]] [[projects]]
digest = "1:6f49eae0c1e5dab1dafafee34b207aeb7a42303105960944828c2079b92fc88e"
name = "github.com/jmespath/go-jmespath" name = "github.com/jmespath/go-jmespath"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "0b12d6b5" revision = "0b12d6b5"
[[projects]] [[projects]]
branch = "master"
digest = "1:c728183dd470c8bb2d1775edc641a81ec25f5e3804b8b0536e99f71df17a13a6"
name = "github.com/kjk/lzma"
packages = ["."]
pruneopts = ""
revision = "3fd93898850d5252457e48c1b3d5e1510597280b"
[[projects]]
digest = "1:78229b46ddb7434f881390029bd1af7661294af31f6802e0e1bedaad4ab0af3c"
name = "github.com/mattn/go-isatty" name = "github.com/mattn/go-isatty"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39" revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3" version = "v0.0.3"
[[projects]] [[projects]]
digest = "1:81e673df85e765593a863f67cba4544cf40e8919590f04d67664940786c2b61a"
name = "github.com/mattn/go-runewidth" name = "github.com/mattn/go-runewidth"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "9e777a8366cce605130a531d2cd6363d07ad7317" revision = "9e777a8366cce605130a531d2cd6363d07ad7317"
version = "v0.0.2" version = "v0.0.2"
[[projects]] [[projects]]
digest = "1:fb8502ed69803c4cd97b1def119db73869ec2f7e2b408eb9c1b8276df6f05b3e"
name = "github.com/mattn/go-shellwords" name = "github.com/mattn/go-shellwords"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "005a0944d84452842197c2108bd9168ced206f78" revision = "005a0944d84452842197c2108bd9168ced206f78"
version = "v1.0.2" version = "v1.0.2"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:3ba28ef4fbbf8d099c6227b698a6ceae28f8a071e12f06839c12c4c0003b7554"
name = "github.com/mkrautz/goar" name = "github.com/mkrautz/goar"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "282caa8bd9daba480b51f1d5a988714913b97aad" revision = "282caa8bd9daba480b51f1d5a988714913b97aad"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:d33ce379780d7c43405b9251289493cabada82f6bf9ab35eea6915d04f6ac8e0"
name = "github.com/mxk/go-flowrate" name = "github.com/mxk/go-flowrate"
packages = ["flowrate"] packages = ["flowrate"]
pruneopts = ""
revision = "cca7078d478f8520f85629ad7c68962d31ed7682" revision = "cca7078d478f8520f85629ad7c68962d31ed7682"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:428d8af27f534ed06d783b03d477124796de06aa86402777cd2b494c64278da5"
name = "github.com/ncw/swift" name = "github.com/ncw/swift"
packages = [ packages = [
".", ".",
"swifttest" "swifttest"
] ]
pruneopts = ""
revision = "8e9b10220613abdbc2896808ee6b43e411a4fa6c" revision = "8e9b10220613abdbc2896808ee6b43e411a4fa6c"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:beeb9206cc21cfeb113066c3dcf4bbb0ba304d73dd441f3244721566f51f44e6"
name = "github.com/pborman/uuid" name = "github.com/pborman/uuid"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "c65b2f87fee37d1c7854c9164a450713c28d50cd" revision = "c65b2f87fee37d1c7854c9164a450713c28d50cd"
[[projects]] [[projects]]
digest = "1:1d7e1867c49a6dd9856598ef7c3123604ea3daabf5b83f303ff457bcbc410b1d"
name = "github.com/pkg/errors" name = "github.com/pkg/errors"
packages = ["."] packages = ["."]
revision = "645ef00459ed84a119197bfb8d8205042c6df63d" pruneopts = ""
version = "v0.8.0" revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4"
version = "v0.8.1"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:c6e71685a14409c3a1fdbecef044ee6447c3887de7c2bb025ee12f9a8a368960"
name = "github.com/smira/commander" name = "github.com/smira/commander"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "f408b00e68d5d6e21b9f18bd310978dafc604e47" revision = "f408b00e68d5d6e21b9f18bd310978dafc604e47"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:a75eb2e6e718988505f704d651eeb4734f0f0b580f761aa58b4a5b0afe585091"
name = "github.com/smira/flag" name = "github.com/smira/flag"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "695ea5e84e76dea7c8656e43c384e54b32aa1b2a" revision = "695ea5e84e76dea7c8656e43c384e54b32aa1b2a"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:cd3b8057abbb453404cbef569951a7359986eedfc0a887f9bc5efff8d02e1760"
name = "github.com/smira/go-aws-auth" name = "github.com/smira/go-aws-auth"
packages = ["."] packages = ["."]
revision = "0070896e9d7f4f9f2d558532b2d896ce2239992a" pruneopts = ""
revision = "8b73995fd8d1d82519c7037ee243a497552cd54d"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:7dd59b3536a07651baf394466da3b8a20bd3ed9ce1984c7583fdf2412fdcfea9"
name = "github.com/smira/go-ftp-protocol" name = "github.com/smira/go-ftp-protocol"
packages = ["protocol"] packages = ["protocol"]
pruneopts = ""
revision = "066b75c2b70dca7ae10b1b88b47534a3c31ccfaa" revision = "066b75c2b70dca7ae10b1b88b47534a3c31ccfaa"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:55fe1086100b305a5562e9f1e6825ef97c49e82331cb0f055b2069f7c7ebd469"
name = "github.com/smira/go-xz" name = "github.com/smira/go-xz"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "0c531f070014e218b21f3cfca801cc992d52726d" revision = "0c531f070014e218b21f3cfca801cc992d52726d"
[[projects]] [[projects]]
branch = "master" branch = "master"
name = "github.com/kjk/lzma" digest = "1:175bc8d87d54849b9b9fc6cd473c5830e090fbf3c2f0ca71c7642a697e5d9237"
packages = ["."]
revision = "3fd93898850d5252457e48c1b3d5e1510597280b"
[[projects]]
branch = "master"
name = "github.com/syndtr/goleveldb" name = "github.com/syndtr/goleveldb"
packages = [ packages = [
"leveldb", "leveldb",
@@ -220,21 +275,27 @@
"leveldb/table", "leveldb/table",
"leveldb/util" "leveldb/util"
] ]
pruneopts = ""
revision = "714f901b98fdb3aa954b4193d8cbd64a28d80cad" revision = "714f901b98fdb3aa954b4193d8cbd64a28d80cad"
[[projects]] [[projects]]
digest = "1:b9bca27d5fbe4ad1a1802706629580ce923bb14255c51375480dff1bebcbb8b2"
name = "github.com/ugorji/go" name = "github.com/ugorji/go"
packages = ["codec"] packages = ["codec"]
pruneopts = ""
revision = "71c2886f5a673a35f909803f38ece5810165097b" revision = "71c2886f5a673a35f909803f38ece5810165097b"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:2d8078b329a80bf4b71537dcd30853ff89fcafb611c94b2dfdb3d43ef1f23a1a"
name = "github.com/wsxiaoys/terminal" name = "github.com/wsxiaoys/terminal"
packages = ["color"] packages = ["color"]
pruneopts = ""
revision = "0940f3fc43a0ed42d04916b1c04578462c650b09" revision = "0940f3fc43a0ed42d04916b1c04578462c650b09"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:51aff2b69272cb95713b74675c0817e0a0caadc20337a8d9115bc68bf1105280"
name = "golang.org/x/crypto" name = "golang.org/x/crypto"
packages = [ packages = [
"cast5", "cast5",
@@ -247,44 +308,97 @@
"openpgp/s2k", "openpgp/s2k",
"ssh/terminal" "ssh/terminal"
] ]
pruneopts = ""
revision = "b2aa35443fbc700ab74c586ae79b81c171851023" revision = "b2aa35443fbc700ab74c586ae79b81c171851023"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:267aa6124260d0e48d1ebf8bb84dce97caa133406b3caabc076cfc7a9e85ffe4"
name = "golang.org/x/sys" name = "golang.org/x/sys"
packages = [ packages = [
"unix", "unix",
"windows" "windows"
] ]
pruneopts = ""
revision = "1d206c9fa8975fb4cf00df1dc8bf3283dc24ba0e" revision = "1d206c9fa8975fb4cf00df1dc8bf3283dc24ba0e"
[[projects]] [[projects]]
branch = "v1" branch = "v1"
digest = "1:e75566abfb876e81f00290ec153ff994c33bf8886134c1a38a9a9df5c15a2045"
name = "gopkg.in/check.v1" name = "gopkg.in/check.v1"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "20d25e2804050c1cd24a7eea1e7a6447dd0e74ec" revision = "20d25e2804050c1cd24a7eea1e7a6447dd0e74ec"
[[projects]] [[projects]]
digest = "1:dd549e360e5a8f982a28c2bcbe667307ceffe538ed9afc7c965524f1ac285b3f"
name = "gopkg.in/go-playground/validator.v8" name = "gopkg.in/go-playground/validator.v8"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "5f1438d3fca68893a817e4a66806cea46a9e4ebf" revision = "5f1438d3fca68893a817e4a66806cea46a9e4ebf"
version = "v8.18.2" version = "v8.18.2"
[[projects]] [[projects]]
digest = "1:0e886d5a5845f58255fb57bcb4a6aae7e1f9dd7c53defe7b87d57dd4cd127545"
name = "gopkg.in/h2non/filetype.v1" name = "gopkg.in/h2non/filetype.v1"
packages = ["types"] packages = ["types"]
pruneopts = ""
revision = "3093b8ebec6efb56ac813238b8beab4ed4eaac6a" revision = "3093b8ebec6efb56ac813238b8beab4ed4eaac6a"
version = "v1.0.1" version = "v1.0.1"
[[projects]] [[projects]]
branch = "v2" branch = "v2"
digest = "1:81314a486195626940617e43740b4fa073f265b0715c9f54ce2027fee1cb5f61"
name = "gopkg.in/yaml.v2" name = "gopkg.in/yaml.v2"
packages = ["."] packages = ["."]
pruneopts = ""
revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f" revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f"
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "3ae43715f5d857c1686ad4b4310a5537afc8d0ab50dc658d11db7c2b36f17fee" input-imports = [
"github.com/AlekSi/pointer",
"github.com/DisposaBoy/JsonConfigReader",
"github.com/awalterschulze/gographviz",
"github.com/aws/aws-sdk-go/aws",
"github.com/aws/aws-sdk-go/aws/awserr",
"github.com/aws/aws-sdk-go/aws/corehandlers",
"github.com/aws/aws-sdk-go/aws/credentials",
"github.com/aws/aws-sdk-go/aws/request",
"github.com/aws/aws-sdk-go/aws/session",
"github.com/aws/aws-sdk-go/service/s3",
"github.com/cheggaaa/pb",
"github.com/gin-gonic/gin",
"github.com/h2non/filetype/matchers",
"github.com/kjk/lzma",
"github.com/mattn/go-shellwords",
"github.com/mkrautz/goar",
"github.com/mxk/go-flowrate/flowrate",
"github.com/ncw/swift",
"github.com/ncw/swift/swifttest",
"github.com/pborman/uuid",
"github.com/pkg/errors",
"github.com/smira/commander",
"github.com/smira/flag",
"github.com/smira/go-aws-auth",
"github.com/smira/go-ftp-protocol/protocol",
"github.com/smira/go-xz",
"github.com/syndtr/goleveldb/leveldb",
"github.com/syndtr/goleveldb/leveldb/filter",
"github.com/syndtr/goleveldb/leveldb/opt",
"github.com/syndtr/goleveldb/leveldb/storage",
"github.com/syndtr/goleveldb/leveldb/util",
"github.com/ugorji/go/codec",
"github.com/wsxiaoys/terminal/color",
"golang.org/x/crypto/openpgp",
"golang.org/x/crypto/openpgp/armor",
"golang.org/x/crypto/openpgp/clearsign",
"golang.org/x/crypto/openpgp/errors",
"golang.org/x/crypto/openpgp/packet",
"golang.org/x/crypto/ssh/terminal",
"golang.org/x/sys/unix",
"gopkg.in/check.v1"
]
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1
+8 -4
View File
@@ -1,10 +1,14 @@
language: go language: go
go_import_path: github.com/pkg/errors go_import_path: github.com/pkg/errors
go: go:
- 1.4.3 - 1.4.x
- 1.5.4 - 1.5.x
- 1.6.2 - 1.6.x
- 1.7.1 - 1.7.x
- 1.8.x
- 1.9.x
- 1.10.x
- 1.11.x
- tip - tip
script: script:
+2 -2
View File
@@ -1,4 +1,4 @@
# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) # errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge)
Package errors provides simple error handling primitives. Package errors provides simple error handling primitives.
@@ -47,6 +47,6 @@ We welcome pull requests, bug fixes and issue reports. With that said, the bar f
Before proposing a change, please discuss your change by raising an issue. Before proposing a change, please discuss your change by raising an issue.
## Licence ## License
BSD-2-Clause BSD-2-Clause
+6 -2
View File
@@ -15,6 +15,7 @@ func noErrors(at, depth int) error {
} }
return noErrors(at+1, depth) return noErrors(at+1, depth)
} }
func yesErrors(at, depth int) error { func yesErrors(at, depth int) error {
if at >= depth { if at >= depth {
return New("ye error") return New("ye error")
@@ -22,8 +23,11 @@ func yesErrors(at, depth int) error {
return yesErrors(at+1, depth) return yesErrors(at+1, depth)
} }
// GlobalE is an exported global to store the result of benchmark results,
// preventing the compiler from optimising the benchmark functions away.
var GlobalE error
func BenchmarkErrors(b *testing.B) { func BenchmarkErrors(b *testing.B) {
var toperr error
type run struct { type run struct {
stack int stack int
std bool std bool
@@ -53,7 +57,7 @@ func BenchmarkErrors(b *testing.B) {
err = f(0, r.stack) err = f(0, r.stack)
} }
b.StopTimer() b.StopTimer()
toperr = err GlobalE = err
}) })
} }
} }
+28 -15
View File
@@ -6,7 +6,7 @@
// return err // return err
// } // }
// //
// which applied recursively up the call stack results in error reports // which when applied recursively up the call stack results in error reports
// without context or debugging information. The errors package allows // without context or debugging information. The errors package allows
// programmers to add context to the failure path in their code in a way // programmers to add context to the failure path in their code in a way
// that does not destroy the original value of the error. // that does not destroy the original value of the error.
@@ -15,16 +15,17 @@
// //
// The errors.Wrap function returns a new error that adds context to the // The errors.Wrap function returns a new error that adds context to the
// original error by recording a stack trace at the point Wrap is called, // original error by recording a stack trace at the point Wrap is called,
// and the supplied message. For example // together with the supplied message. For example
// //
// _, err := ioutil.ReadAll(r) // _, err := ioutil.ReadAll(r)
// if err != nil { // if err != nil {
// return errors.Wrap(err, "read failed") // return errors.Wrap(err, "read failed")
// } // }
// //
// If additional control is required the errors.WithStack and errors.WithMessage // If additional control is required, the errors.WithStack and
// functions destructure errors.Wrap into its component operations of annotating // errors.WithMessage functions destructure errors.Wrap into its component
// an error with a stack trace and an a message, respectively. // operations: annotating an error with a stack trace and with a message,
// respectively.
// //
// Retrieving the cause of an error // Retrieving the cause of an error
// //
@@ -38,7 +39,7 @@
// } // }
// //
// can be inspected by errors.Cause. errors.Cause will recursively retrieve // can be inspected by errors.Cause. errors.Cause will recursively retrieve
// the topmost error which does not implement causer, which is assumed to be // the topmost error that does not implement causer, which is assumed to be
// the original cause. For example: // the original cause. For example:
// //
// switch err := errors.Cause(err).(type) { // switch err := errors.Cause(err).(type) {
@@ -48,16 +49,16 @@
// // unknown error // // unknown error
// } // }
// //
// causer interface is not exported by this package, but is considered a part // Although the causer interface is not exported by this package, it is
// of stable public API. // considered a part of its stable public interface.
// //
// Formatted printing of errors // Formatted printing of errors
// //
// All error values returned from this package implement fmt.Formatter and can // All error values returned from this package implement fmt.Formatter and can
// be formatted by the fmt package. The following verbs are supported // be formatted by the fmt package. The following verbs are supported:
// //
// %s print the error. If the error has a Cause it will be // %s print the error. If the error has a Cause it will be
// printed recursively // printed recursively.
// %v see %s // %v see %s
// %+v extended format. Each Frame of the error's StackTrace will // %+v extended format. Each Frame of the error's StackTrace will
// be printed in detail. // be printed in detail.
@@ -65,13 +66,13 @@
// Retrieving the stack trace of an error or wrapper // Retrieving the stack trace of an error or wrapper
// //
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are // New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
// invoked. This information can be retrieved with the following interface. // invoked. This information can be retrieved with the following interface:
// //
// type stackTracer interface { // type stackTracer interface {
// StackTrace() errors.StackTrace // StackTrace() errors.StackTrace
// } // }
// //
// Where errors.StackTrace is defined as // The returned errors.StackTrace type is defined as
// //
// type StackTrace []Frame // type StackTrace []Frame
// //
@@ -85,8 +86,8 @@
// } // }
// } // }
// //
// stackTracer interface is not exported by this package, but is considered a part // Although the stackTracer interface is not exported by this package, it is
// of stable public API. // considered a part of its stable public interface.
// //
// See the documentation for Frame.Format for more details. // See the documentation for Frame.Format for more details.
package errors package errors
@@ -192,7 +193,7 @@ func Wrap(err error, message string) error {
} }
// Wrapf returns an error annotating err with a stack trace // Wrapf returns an error annotating err with a stack trace
// at the point Wrapf is call, and the format specifier. // at the point Wrapf is called, and the format specifier.
// If err is nil, Wrapf returns nil. // If err is nil, Wrapf returns nil.
func Wrapf(err error, format string, args ...interface{}) error { func Wrapf(err error, format string, args ...interface{}) error {
if err == nil { if err == nil {
@@ -220,6 +221,18 @@ func WithMessage(err error, message string) error {
} }
} }
// WithMessagef annotates err with the format specifier.
// If err is nil, WithMessagef returns nil.
func WithMessagef(err error, format string, args ...interface{}) error {
if err == nil {
return nil
}
return &withMessage{
cause: err,
msg: fmt.Sprintf(format, args...),
}
}
type withMessage struct { type withMessage struct {
cause error cause error
msg string msg string
+25
View File
@@ -196,7 +196,32 @@ func TestWithMessage(t *testing.T) {
t.Errorf("WithMessage(%v, %q): got: %q, want %q", tt.err, tt.message, got, tt.want) t.Errorf("WithMessage(%v, %q): got: %q, want %q", tt.err, tt.message, got, tt.want)
} }
} }
}
func TestWithMessagefNil(t *testing.T) {
got := WithMessagef(nil, "no error")
if got != nil {
t.Errorf("WithMessage(nil, \"no error\"): got %#v, expected nil", got)
}
}
func TestWithMessagef(t *testing.T) {
tests := []struct {
err error
message string
want string
}{
{io.EOF, "read error", "read error: EOF"},
{WithMessagef(io.EOF, "read error without format specifier"), "client error", "client error: read error without format specifier: EOF"},
{WithMessagef(io.EOF, "read error with %d format specifier", 1), "client error", "client error: read error with 1 format specifier: EOF"},
}
for _, tt := range tests {
got := WithMessagef(tt.err, tt.message).Error()
if got != tt.want {
t.Errorf("WithMessage(%v, %q): got: %q, want %q", tt.err, tt.message, got, tt.want)
}
}
} }
// errors.New, etc values are not expected to be compared by value // errors.New, etc values are not expected to be compared by value
+1 -1
View File
@@ -491,7 +491,7 @@ type wrapper struct {
want []string want []string
} }
func prettyBlocks(blocks []string, prefix ...string) string { func prettyBlocks(blocks []string) string {
var out []string var out []string
for _, b := range blocks { for _, b := range blocks {
+10 -41
View File
@@ -46,7 +46,8 @@ func (f Frame) line() int {
// //
// Format accepts flags that alter the printing of some verbs, as follows: // Format accepts flags that alter the printing of some verbs, as follows:
// //
// %+s path of source file relative to the compile time GOPATH // %+s function name and path of source file relative to the compile time
// GOPATH separated by \n\t (<funcname>\n\t<path>)
// %+v equivalent to %+s:%d // %+v equivalent to %+s:%d
func (f Frame) Format(s fmt.State, verb rune) { func (f Frame) Format(s fmt.State, verb rune) {
switch verb { switch verb {
@@ -79,6 +80,14 @@ func (f Frame) Format(s fmt.State, verb rune) {
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest). // StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
type StackTrace []Frame type StackTrace []Frame
// Format formats the stack of Frames according to the fmt.Formatter interface.
//
// %s lists source files for each Frame in the stack
// %v lists the source file and line number for each Frame in the stack
//
// Format accepts flags that alter the printing of some verbs, as follows:
//
// %+v Prints filename, function, and line number for each Frame in the stack.
func (st StackTrace) Format(s fmt.State, verb rune) { func (st StackTrace) Format(s fmt.State, verb rune) {
switch verb { switch verb {
case 'v': case 'v':
@@ -136,43 +145,3 @@ func funcname(name string) string {
i = strings.Index(name, ".") i = strings.Index(name, ".")
return name[i+1:] return name[i+1:]
} }
func trimGOPATH(name, file string) string {
// Here we want to get the source file path relative to the compile time
// GOPATH. As of Go 1.6.x there is no direct way to know the compiled
// GOPATH at runtime, but we can infer the number of path segments in the
// GOPATH. We note that fn.Name() returns the function name qualified by
// the import path, which does not include the GOPATH. Thus we can trim
// segments from the beginning of the file path until the number of path
// separators remaining is one more than the number of path separators in
// the function name. For example, given:
//
// GOPATH /home/user
// file /home/user/src/pkg/sub/file.go
// fn.Name() pkg/sub.Type.Method
//
// We want to produce:
//
// pkg/sub/file.go
//
// From this we can easily see that fn.Name() has one less path separator
// than our desired output. We count separators from the end of the file
// path until it finds two more than in the function name and then move
// one character forward to preserve the initial path segment without a
// leading separator.
const sep = "/"
goal := strings.Count(name, sep) + 2
i := len(file)
for n := 0; n < goal; n++ {
i = strings.LastIndex(file[:i], sep)
if i == -1 {
// not enough separators found, set i so that the slice expression
// below leaves file unmodified
i = -len(sep)
break
}
}
// get back to 0 or trim the leading separator
file = file[i+len(sep):]
return file
}
+12 -30
View File
@@ -146,24 +146,6 @@ func TestFuncname(t *testing.T) {
} }
} }
func TestTrimGOPATH(t *testing.T) {
var tests = []struct {
Frame
want string
}{{
Frame(initpc),
"github.com/pkg/errors/stack_test.go",
}}
for i, tt := range tests {
pc := tt.Frame.pc()
fn := runtime.FuncForPC(pc)
file, _ := fn.FileLine(pc)
got := trimGOPATH(fn.Name(), file)
testFormatRegexp(t, i, got, "%s", tt.want)
}
}
func TestStackTrace(t *testing.T) { func TestStackTrace(t *testing.T) {
tests := []struct { tests := []struct {
err error err error
@@ -171,24 +153,24 @@ func TestStackTrace(t *testing.T) {
}{{ }{{
New("ooh"), []string{ New("ooh"), []string{
"github.com/pkg/errors.TestStackTrace\n" + "github.com/pkg/errors.TestStackTrace\n" +
"\t.+/github.com/pkg/errors/stack_test.go:172", "\t.+/github.com/pkg/errors/stack_test.go:154",
}, },
}, { }, {
Wrap(New("ooh"), "ahh"), []string{ Wrap(New("ooh"), "ahh"), []string{
"github.com/pkg/errors.TestStackTrace\n" + "github.com/pkg/errors.TestStackTrace\n" +
"\t.+/github.com/pkg/errors/stack_test.go:177", // this is the stack of Wrap, not New "\t.+/github.com/pkg/errors/stack_test.go:159", // this is the stack of Wrap, not New
}, },
}, { }, {
Cause(Wrap(New("ooh"), "ahh")), []string{ Cause(Wrap(New("ooh"), "ahh")), []string{
"github.com/pkg/errors.TestStackTrace\n" + "github.com/pkg/errors.TestStackTrace\n" +
"\t.+/github.com/pkg/errors/stack_test.go:182", // this is the stack of New "\t.+/github.com/pkg/errors/stack_test.go:164", // this is the stack of New
}, },
}, { }, {
func() error { return New("ooh") }(), []string{ func() error { return New("ooh") }(), []string{
`github.com/pkg/errors.(func·009|TestStackTrace.func1)` + `github.com/pkg/errors.(func·009|TestStackTrace.func1)` +
"\n\t.+/github.com/pkg/errors/stack_test.go:187", // this is the stack of New "\n\t.+/github.com/pkg/errors/stack_test.go:169", // this is the stack of New
"github.com/pkg/errors.TestStackTrace\n" + "github.com/pkg/errors.TestStackTrace\n" +
"\t.+/github.com/pkg/errors/stack_test.go:187", // this is the stack of New's caller "\t.+/github.com/pkg/errors/stack_test.go:169", // this is the stack of New's caller
}, },
}, { }, {
Cause(func() error { Cause(func() error {
@@ -197,11 +179,11 @@ func TestStackTrace(t *testing.T) {
}() }()
}()), []string{ }()), []string{
`github.com/pkg/errors.(func·010|TestStackTrace.func2.1)` + `github.com/pkg/errors.(func·010|TestStackTrace.func2.1)` +
"\n\t.+/github.com/pkg/errors/stack_test.go:196", // this is the stack of Errorf "\n\t.+/github.com/pkg/errors/stack_test.go:178", // this is the stack of Errorf
`github.com/pkg/errors.(func·011|TestStackTrace.func2)` + `github.com/pkg/errors.(func·011|TestStackTrace.func2)` +
"\n\t.+/github.com/pkg/errors/stack_test.go:197", // this is the stack of Errorf's caller "\n\t.+/github.com/pkg/errors/stack_test.go:179", // this is the stack of Errorf's caller
"github.com/pkg/errors.TestStackTrace\n" + "github.com/pkg/errors.TestStackTrace\n" +
"\t.+/github.com/pkg/errors/stack_test.go:198", // this is the stack of Errorf's caller's caller "\t.+/github.com/pkg/errors/stack_test.go:180", // this is the stack of Errorf's caller's caller
}, },
}} }}
for i, tt := range tests { for i, tt := range tests {
@@ -271,19 +253,19 @@ func TestStackTraceFormat(t *testing.T) {
}, { }, {
stackTrace()[:2], stackTrace()[:2],
"%v", "%v",
`\[stack_test.go:225 stack_test.go:272\]`, `\[stack_test.go:207 stack_test.go:254\]`,
}, { }, {
stackTrace()[:2], stackTrace()[:2],
"%+v", "%+v",
"\n" + "\n" +
"github.com/pkg/errors.stackTrace\n" + "github.com/pkg/errors.stackTrace\n" +
"\t.+/github.com/pkg/errors/stack_test.go:225\n" + "\t.+/github.com/pkg/errors/stack_test.go:207\n" +
"github.com/pkg/errors.TestStackTraceFormat\n" + "github.com/pkg/errors.TestStackTraceFormat\n" +
"\t.+/github.com/pkg/errors/stack_test.go:276", "\t.+/github.com/pkg/errors/stack_test.go:258",
}, { }, {
stackTrace()[:2], stackTrace()[:2],
"%#v", "%#v",
`\[\]errors.Frame{stack_test.go:225, stack_test.go:284}`, `\[\]errors.Frame{stack_test.go:207, stack_test.go:266}`,
}} }}
for i, tt := range tests { for i, tt := range tests {
+1
View File
@@ -0,0 +1 @@
/.idea
+12
View File
@@ -0,0 +1,12 @@
# Contributing
In general, the code posted to the [SmartyStreets github organization](https://github.com/smartystreets) is created to solve specific problems at SmartyStreets that are ancillary to our core products in the address verification industry and may or may not be useful to other organizations or developers. Our reason for posting said code isn't necessarily to solicit feedback or contributions from the community but more as a showcase of some of the approaches to solving problems we have adopted.
Having stated that, we do consider issues raised by other githubbers as well as contributions submitted via pull requests. When submitting such a pull request, please follow these guidelines:
- _Look before you leap:_ If the changes you plan to make are significant, it's in everyone's best interest for you to discuss them with a SmartyStreets team member prior to opening a pull request.
- _License and ownership:_ If modifying the `LICENSE.md` file, limit your changes to fixing typographical mistakes. Do NOT modify the actual terms in the license or the copyright by **SmartyStreets, LLC**. Code submitted to SmartyStreets projects becomes property of SmartyStreets and must be compatible with the associated license.
- _Testing:_ If the code you are submitting resides in packages/modules covered by automated tests, be sure to add passing tests that cover your changes and assert expected behavior and state. Submit the additional test cases as part of your change set.
- _Style:_ Match your approach to **naming** and **formatting** with the surrounding code. Basically, the code you submit shouldn't stand out.
- "Naming" refers to such constructs as variables, methods, functions, classes, structs, interfaces, packages, modules, directories, files, etc...
- "Formatting" refers to such constructs as whitespace, horizontal line length, vertical function length, vertical file length, indentation, curly braces, etc...
-20
View File
@@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2013 SmartyStreets
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+23
View File
@@ -0,0 +1,23 @@
Copyright (c) 2016 SmartyStreets
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
NOTE: Various optional and subordinate components carry their own licensing
requirements and restrictions. Use of those components is subject to the terms
and conditions outlined the respective license of each component.
+7 -5
View File
@@ -192,15 +192,17 @@ const (
var ( var (
awsSignVersion = map[string]int{ awsSignVersion = map[string]int{
"autoscaling": 4, "autoscaling": 4,
"ce": 4,
"cloudfront": 4, "cloudfront": 4,
"cloudformation": 4, "cloudformation": 4,
"cloudsearch": 4, "cloudsearch": 4,
"monitoring": 4, "monitoring": 4,
"dynamodb": 4, "dynamodb": 4,
"ec2": 2, "ec2": 4,
"elasticmapreduce": 4, "elasticmapreduce": 4,
"elastictranscoder": 4, "elastictranscoder": 4,
"elasticache": 2, "elasticache": 4,
"es": 4,
"glacier": 4, "glacier": 4,
"kinesis": 4, "kinesis": 4,
"redshift": 4, "redshift": 4,
@@ -210,10 +212,10 @@ var (
"sqs": 4, "sqs": 4,
"s3": 4, "s3": 4,
"elasticbeanstalk": 4, "elasticbeanstalk": 4,
"importexport": 2, "importexport": 4,
"iam": 4, "iam": 4,
"route53": 3, "route53": 4,
"elasticloadbalancing": 4, "elasticloadbalancing": 4,
"email": 3, "email": 4,
} }
) )
+142 -216
View File
@@ -9,246 +9,172 @@ import (
"testing" "testing"
"time" "time"
. "github.com/smartystreets/goconvey/convey" "github.com/smartystreets/assertions"
"github.com/smartystreets/assertions/should"
"github.com/smartystreets/gunit"
) )
func TestIntegration(t *testing.T) { func TestIntegrationFixture(t *testing.T) {
if testing.Short() { if !credentialsSet() {
t.Skip("Skipping long-running integration test.") t.Skip("Required credentials absent from environment.")
} }
Convey("Given real credentials from environment variables", t, func() { gunit.RunSequential(new(IntegrationFixture), t)
Convey("A request (with out-of-order query string) with to IAM should succeed (assuming Administrator Access policy)", func() {
request := newRequest("GET", "https://iam.amazonaws.com/?Version=2010-05-08&Action=ListRoles", nil)
if !credentialsSet() {
SkipSo(http.StatusOK, ShouldEqual, http.StatusOK)
} else {
response := sign4AndDo(request)
if response.StatusCode != http.StatusOK {
message, _ := ioutil.ReadAll(response.Body)
t.Error(string(message))
}
So(response.StatusCode, ShouldEqual, http.StatusOK)
}
})
Convey("A request to S3 should succeed", func() {
request, _ := http.NewRequest("GET", "https://s3.amazonaws.com", nil)
if !credentialsSet() {
SkipSo(http.StatusOK, ShouldEqual, http.StatusOK)
} else {
response := sign4AndDo(request)
if response.StatusCode != http.StatusOK {
message, _ := ioutil.ReadAll(response.Body)
t.Error(string(message))
}
So(response.StatusCode, ShouldEqual, http.StatusOK)
}
})
Convey("A request to EC2 should succeed", func() {
request := newRequest("GET", "https://ec2.amazonaws.com/?Version=2013-10-15&Action=DescribeInstances", nil)
if !credentialsSet() {
SkipSo(http.StatusOK, ShouldEqual, http.StatusOK)
} else {
response := sign2AndDo(request)
if response.StatusCode != http.StatusOK {
message, _ := ioutil.ReadAll(response.Body)
t.Error(string(message))
}
So(response.StatusCode, ShouldEqual, http.StatusOK)
}
})
Convey("A request to SQS should succeed", func() {
request := newRequest("POST", "https://sqs.us-west-2.amazonaws.com", url.Values{
"Action": []string{"ListQueues"},
})
if !credentialsSet() {
SkipSo(http.StatusOK, ShouldEqual, http.StatusOK)
} else {
response := sign4AndDo(request)
if response.StatusCode != http.StatusOK {
message, _ := ioutil.ReadAll(response.Body)
t.Error(string(message))
}
So(response.StatusCode, ShouldEqual, http.StatusOK)
}
})
Convey("A request to SES should succeed", func() {
request := newRequest("GET", "https://email.us-east-1.amazonaws.com/?Action=GetSendStatistics", nil)
if !credentialsSet() {
SkipSo(http.StatusOK, ShouldEqual, http.StatusOK)
} else {
response := sign3AndDo(request)
if response.StatusCode != http.StatusOK {
message, _ := ioutil.ReadAll(response.Body)
t.Error(string(message))
}
So(response.StatusCode, ShouldEqual, http.StatusOK)
}
})
Convey("A request to Route 53 should succeed", func() {
request := newRequest("GET", "https://route53.amazonaws.com/2013-04-01/hostedzone?maxitems=1", nil)
if !credentialsSet() {
SkipSo(http.StatusOK, ShouldEqual, http.StatusOK)
} else {
response := sign3AndDo(request)
if response.StatusCode != http.StatusOK {
message, _ := ioutil.ReadAll(response.Body)
t.Error(string(message))
}
So(response.StatusCode, ShouldEqual, http.StatusOK)
}
})
Convey("A request to SimpleDB should succeed", func() {
request := newRequest("GET", "https://sdb.amazonaws.com/?Action=ListDomains&Version=2009-04-15", nil)
if !credentialsSet() {
SkipSo(http.StatusOK, ShouldEqual, http.StatusOK)
} else {
response := sign2AndDo(request)
if response.StatusCode != http.StatusOK {
message, _ := ioutil.ReadAll(response.Body)
t.Error(string(message))
}
So(response.StatusCode, ShouldEqual, http.StatusOK)
}
})
Convey("If S3Resource env variable is set", func() {
s3res := os.Getenv("S3Resource")
Convey("A URL-signed request to that S3 resource should succeed", func() {
request, _ := http.NewRequest("GET", s3res, nil)
if !credentialsSet() || s3res == "" {
SkipSo(http.StatusOK, ShouldEqual, http.StatusOK)
} else {
response := signS3UrlAndDo(request)
if response.StatusCode != http.StatusOK {
message, _ := ioutil.ReadAll(response.Body)
t.Error(string(message))
}
So(response.StatusCode, ShouldEqual, http.StatusOK)
}
})
})
})
} }
func TestSign(t *testing.T) { type IntegrationFixture struct {
Convey("Requests to services using Version 2 should be signed accordingly", t, func() { *gunit.Fixture
reqs := []*http.Request{ }
newRequest("GET", "https://ec2.amazonaws.com", url.Values{}),
newRequest("GET", "https://elasticache.amazonaws.com/", url.Values{}),
}
for _, request := range reqs {
signedReq := Sign(request)
So(signedReq.URL.Query().Get("SignatureVersion"), ShouldEqual, "2")
}
})
Convey("Requests to services using Version 3 should be signed accordingly", t, func() { func (this *IntegrationFixture) assertOK(response *http.Response) {
reqs := []*http.Request{ if !this.So(response.StatusCode, should.Equal, http.StatusOK) {
newRequest("GET", "https://route53.amazonaws.com", url.Values{}), message, _ := ioutil.ReadAll(response.Body)
newRequest("GET", "https://email.us-east-1.amazonaws.com/", url.Values{}), this.Error(string(message))
} }
for _, request := range reqs { }
signedReq := Sign(request)
So(signedReq.Header.Get("X-Amzn-Authorization"), ShouldNotBeBlank)
}
})
Convey("Requests to services using Version 4 should be signed accordingly", t, func() { func (this *IntegrationFixture) LongTestSign4_IAM_OutOfOrderQueryString() {
reqs := []*http.Request{ request := newRequest("GET", "https://iam.amazonaws.com/?Version=2010-05-08&Action=ListRoles", nil)
newRequest("POST", "https://sqs.amazonaws.com/", url.Values{}), response := sign4AndDo(request)
newRequest("GET", "https://iam.amazonaws.com", url.Values{}), this.assertOK(response)
newRequest("GET", "https://s3.amazonaws.com", url.Values{}), }
}
for _, request := range reqs {
signedReq := Sign(request)
So(signedReq.Header.Get("Authorization"), ShouldContainSubstring, ", Signature=")
}
})
var keys Credentials func (this *IntegrationFixture) LongTestSign4_S3() {
keys = newKeys() request, _ := http.NewRequest("GET", "https://s3.amazonaws.com", nil)
Convey("Requests to services using existing credentials Version 2 should be signed accordingly", t, func() { response := sign4AndDo(request)
reqs := []*http.Request{ this.assertOK(response)
newRequest("GET", "https://ec2.amazonaws.com", url.Values{}), }
newRequest("GET", "https://elasticache.amazonaws.com/", url.Values{}),
}
for _, request := range reqs {
signedReq := Sign(request, keys)
So(signedReq.URL.Query().Get("SignatureVersion"), ShouldEqual, "2")
}
})
Convey("Requests to services using existing credentials Version 3 should be signed accordingly", t, func() { func (this *IntegrationFixture) LongTestSign2_EC2() {
reqs := []*http.Request{ request := newRequest("GET", "https://ec2.amazonaws.com/?Version=2013-10-15&Action=DescribeInstances", nil)
newRequest("GET", "https://route53.amazonaws.com", url.Values{}), response := sign2AndDo(request)
newRequest("GET", "https://email.us-east-1.amazonaws.com/", url.Values{}), this.assertOK(response)
} }
for _, request := range reqs { func (this *IntegrationFixture) LongTestSign4_SQS() {
signedReq := Sign(request, keys) request := newRequest("POST", "https://sqs.us-west-2.amazonaws.com", url.Values{"Action": []string{"ListQueues"}})
So(signedReq.Header.Get("X-Amzn-Authorization"), ShouldNotBeBlank) response := sign4AndDo(request)
} this.assertOK(response)
}) }
Convey("Requests to services using existing credentials Version 4 should be signed accordingly", t, func() { func (this *IntegrationFixture) LongTestSign3_SES() {
reqs := []*http.Request{ request := newRequest("GET", "https://email.us-east-1.amazonaws.com/?Action=GetSendStatistics", nil)
newRequest("POST", "https://sqs.amazonaws.com/", url.Values{}), response := sign3AndDo(request)
newRequest("GET", "https://iam.amazonaws.com", url.Values{}), this.assertOK(response)
newRequest("GET", "https://s3.amazonaws.com", url.Values{}), }
}
for _, request := range reqs { func (this *IntegrationFixture) LongTestSign3_Route53() {
signedReq := Sign(request, keys) request := newRequest("GET", "https://route53.amazonaws.com/2013-04-01/hostedzone?maxitems=1", nil)
So(signedReq.Header.Get("Authorization"), ShouldContainSubstring, ", Signature=") response := sign3AndDo(request)
} this.assertOK(response)
}) }
func (this *IntegrationFixture) LongTestSign2_SimpleDB() {
request := newRequest("GET", "https://sdb.amazonaws.com/?Action=ListDomains&Version=2009-04-15", nil)
response := sign2AndDo(request)
this.assertOK(response)
}
func (this *IntegrationFixture) LongTestSignS3Url() {
s3res := os.Getenv("S3Resource")
if s3res == "" {
return
}
request, _ := http.NewRequest("GET", s3res, nil)
response := signS3UrlAndDo(request)
this.assertOK(response)
}
func TestSign_Version2(t *testing.T) {
requests := []*http.Request{
newRequest("GET", "https://ec2.amazonaws.com", url.Values{}),
newRequest("GET", "https://elasticache.amazonaws.com/", url.Values{}),
}
for _, request := range requests {
signed := Sign(request)
assertions.New(t).So(signed.URL.Query().Get("SignatureVersion"), should.Equal, "2")
}
}
func TestSign_Version3(t *testing.T) {
requests := []*http.Request{
newRequest("GET", "https://route53.amazonaws.com", url.Values{}),
newRequest("GET", "https://email.us-east-1.amazonaws.com/", url.Values{}),
}
for _, request := range requests {
signed := Sign(request)
assertions.New(t).So(signed.Header.Get("X-Amzn-Authorization"), should.NotBeBlank)
}
}
func TestSign_Version4(t *testing.T) {
requests := []*http.Request{
newRequest("POST", "https://sqs.amazonaws.com/", url.Values{}),
newRequest("GET", "https://iam.amazonaws.com", url.Values{}),
newRequest("GET", "https://s3.amazonaws.com", url.Values{}),
}
for _, request := range requests {
signed := Sign(request)
assertions.New(t).So(signed.Header.Get("Authorization"), should.ContainSubstring, ", Signature=")
}
}
func TestSign_ExistingCredentials_Version2(t *testing.T) {
requests := []*http.Request{
newRequest("GET", "https://ec2.amazonaws.com", url.Values{}),
newRequest("GET", "https://elasticache.amazonaws.com/", url.Values{}),
}
for _, request := range requests {
signed := Sign(request, newKeys())
assertions.New(t).So(signed.URL.Query().Get("SignatureVersion"), should.Equal, "2")
}
}
func TestSign_ExistingCredentials_Version3(t *testing.T) {
requests := []*http.Request{
newRequest("GET", "https://route53.amazonaws.com", url.Values{}),
newRequest("GET", "https://email.us-east-1.amazonaws.com/", url.Values{}),
}
for _, request := range requests {
signed := Sign(request, newKeys())
assertions.New(t).So(signed.Header.Get("X-Amzn-Authorization"), should.NotBeBlank)
}
}
func TestSign_ExistingCredentials_Version4(t *testing.T) {
requests := []*http.Request{
newRequest("POST", "https://sqs.amazonaws.com/", url.Values{}),
newRequest("GET", "https://iam.amazonaws.com", url.Values{}),
newRequest("GET", "https://s3.amazonaws.com", url.Values{}),
}
for _, request := range requests {
signed := Sign(request, newKeys())
assertions.New(t).So(signed.Header.Get("Authorization"), should.ContainSubstring, ", Signature=")
}
} }
func TestExpiration(t *testing.T) { func TestExpiration(t *testing.T) {
assert := assertions.New(t)
var credentials = &Credentials{} var credentials = &Credentials{}
Convey("Credentials without an expiration can't expire", t, func() { // Credentials without an expiration can't expire
So(credentials.expired(), ShouldBeFalse) assert.So(credentials.expired(), should.BeFalse)
})
Convey("Credentials that expire in 5 minutes aren't expired", t, func() { // Credentials that expire in 5 minutes aren't expired
credentials.Expiration = time.Now().Add(5 * time.Minute) credentials.Expiration = time.Now().Add(5 * time.Minute)
So(credentials.expired(), ShouldBeFalse) assert.So(credentials.expired(), should.BeFalse)
})
Convey("Credentials that expire in 1 minute are expired", t, func() { // Credentials that expire in 1 minute are expired
credentials.Expiration = time.Now().Add(1 * time.Minute) credentials.Expiration = time.Now().Add(1 * time.Minute)
So(credentials.expired(), ShouldBeTrue) assert.So(credentials.expired(), should.BeTrue)
})
Convey("Credentials that expired 2 hours ago are expired", t, func() { // Credentials that expired 2 hours ago are expired
credentials.Expiration = time.Now().Add(-2 * time.Hour) credentials.Expiration = time.Now().Add(-2 * time.Hour)
So(credentials.expired(), ShouldBeTrue) assert.So(credentials.expired(), should.BeTrue)
})
} }
func credentialsSet() bool { func credentialsSet() bool {
var keys Credentials var keys Credentials
keys = newKeys() keys = newKeys()
if keys.AccessKeyID == "" { return keys.AccessKeyID != ""
return false
} else {
return true
}
} }
func newRequest(method string, url string, v url.Values) *http.Request { func newRequest(method string, url string, v url.Values) *http.Request {
+60 -80
View File
@@ -4,86 +4,66 @@ import (
"net/url" "net/url"
"testing" "testing"
. "github.com/smartystreets/goconvey/convey" "github.com/smartystreets/assertions/should"
"github.com/smartystreets/gunit"
) )
func TestCommonFunctions(t *testing.T) { func TestCommonFixture(t *testing.T) {
Convey("Service and region should be properly extracted from host strings", t, func() { gunit.Run(new(CommonFixture), t)
service, region := serviceAndRegion("sqs.us-west-2.amazonaws.com") }
So(service, ShouldEqual, "sqs")
So(region, ShouldEqual, "us-west-2") type CommonFixture struct {
*gunit.Fixture
service, region = serviceAndRegion("iam.amazonaws.com") }
So(service, ShouldEqual, "iam")
So(region, ShouldEqual, "us-east-1") func (this *CommonFixture) serviceAndRegion(id string) []string {
service, region := serviceAndRegion(id)
service, region = serviceAndRegion("sns.us-west-2.amazonaws.com") return []string{service, region}
So(service, ShouldEqual, "sns") }
So(region, ShouldEqual, "us-west-2") func (this *CommonFixture) TestServiceAndRegion() {
this.So(this.serviceAndRegion("sqs.us-west-2.amazonaws.com"), should.Resemble, []string{"sqs", "us-west-2"})
service, region = serviceAndRegion("bucketname.s3.amazonaws.com") this.So(this.serviceAndRegion("iam.amazonaws.com"), should.Resemble, []string{"iam", "us-east-1"})
So(service, ShouldEqual, "s3") this.So(this.serviceAndRegion("sns.us-west-2.amazonaws.com"), should.Resemble, []string{"sns", "us-west-2"})
So(region, ShouldEqual, "us-east-1") this.So(this.serviceAndRegion("bucketname.s3.amazonaws.com"), should.Resemble, []string{"s3", "us-east-1"})
this.So(this.serviceAndRegion("s3.amazonaws.com"), should.Resemble, []string{"s3", "us-east-1"})
service, region = serviceAndRegion("s3.amazonaws.com") this.So(this.serviceAndRegion("s3-us-west-1.amazonaws.com"), should.Resemble, []string{"s3", "us-west-1"})
So(service, ShouldEqual, "s3") this.So(this.serviceAndRegion("s3-external-1.amazonaws.com"), should.Resemble, []string{"s3", "us-east-1"})
So(region, ShouldEqual, "us-east-1") }
service, region = serviceAndRegion("s3-us-west-1.amazonaws.com") func (this *CommonFixture) TestHashFunctions() {
So(service, ShouldEqual, "s3") this.So(hashMD5([]byte("Pretend this is a REALLY long byte array...")), should.Equal, "KbVTY8Vl6VccnzQf1AGOFw==")
So(region, ShouldEqual, "us-west-1") this.So(hashSHA256([]byte("This is... Sparta!!")), should.Equal,
"5c81a4ef1172e89b1a9d575f4cd82f4ed20ea9137e61aa7f1ab936291d24e79a")
service, region = serviceAndRegion("s3-external-1.amazonaws.com")
So(service, ShouldEqual, "s3") key := []byte("asdf1234")
So(region, ShouldEqual, "us-east-1") contents := "SmartyStreets was here"
})
expectedHMAC_SHA256 := []byte{
Convey("MD5 hashes should be properly computed and base-64 encoded", t, func() { 65, 46, 186, 78, 2, 155, 71, 104, 49, 37, 5, 66, 195, 129, 159, 227,
input := []byte("Pretend this is a REALLY long byte array...") 239, 53, 240, 107, 83, 21, 235, 198, 238, 216, 108, 149, 143, 222, 144, 94}
actual := hashMD5(input) this.So(hmacSHA256(key, contents), should.Resemble, expectedHMAC_SHA256)
So(actual, ShouldEqual, "KbVTY8Vl6VccnzQf1AGOFw==") expectedHMAC_SHA1 := []byte{
}) 164, 77, 252, 0, 87, 109, 207, 110, 163, 75, 228, 122, 83, 255, 233, 237, 125, 206, 85, 70}
this.So(hmacSHA1(key, contents), should.Resemble, expectedHMAC_SHA1)
Convey("SHA-256 hashes should be properly hex-encoded (base 16)", t, func() { }
input := []byte("This is... Sparta!!")
actual := hashSHA256(input) func (this *CommonFixture) TestConcat() {
this.So(concat("\n", "Test1", "Test2"), should.Equal, "Test1\nTest2")
So(actual, ShouldEqual, "5c81a4ef1172e89b1a9d575f4cd82f4ed20ea9137e61aa7f1ab936291d24e79a") this.So(concat(".", "Test1"), should.Equal, "Test1")
}) this.So(concat("\t", "1", "2", "3", "4"), should.Equal, "1\t2\t3\t4")
}
Convey("Given a key and contents", t, func() {
key := []byte("asdf1234") func (this *CommonFixture) TestURINormalization() {
contents := "SmartyStreets was here" this.So(
normuri("/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), should.Equal,
Convey("HMAC-SHA256 should be properly computed", func() { "/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
expected := []byte{65, 46, 186, 78, 2, 155, 71, 104, 49, 37, 5, 66, 195, 129, 159, 227, 239, 53, 240, 107, 83, 21, 235, 198, 238, 216, 108, 149, 143, 222, 144, 94}
actual := hmacSHA256(key, contents) this.So(normuri("/ /foo"), should.Equal, "/%20/foo")
this.So(normuri("/(foo)"), should.Equal, "/%28foo%29")
So(actual, ShouldResemble, expected)
}) this.So(
normquery(url.Values{"p": []string{" +&;-=._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"}}),
Convey("HMAC-SHA1 should be properly computed", func() { should.Equal,
expected := []byte{164, 77, 252, 0, 87, 109, 207, 110, 163, 75, 228, 122, 83, 255, 233, 237, 125, 206, 85, 70} "p=%20%2B%26%3B-%3D._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
actual := hmacSHA1(key, contents)
So(actual, ShouldResemble, expected)
})
})
Convey("Strings should be properly concatenated with a delimiter", t, func() {
So(concat("\n", "Test1", "Test2"), ShouldEqual, "Test1\nTest2")
So(concat(".", "Test1"), ShouldEqual, "Test1")
So(concat("\t", "1", "2", "3", "4"), ShouldEqual, "1\t2\t3\t4")
})
Convey("URI components should be properly encoded", t, func() {
So(normuri("/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), ShouldEqual, "/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
So(normuri("/ /foo"), ShouldEqual, "/%20/foo")
So(normuri("/(foo)"), ShouldEqual, "/%28foo%29")
})
Convey("URI query strings should be properly encoded", t, func() {
So(normquery(url.Values{"p": []string{" +&;-=._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"}}), ShouldEqual, "p=%20%2B%26%3B-%3D._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
})
} }
+69 -70
View File
@@ -7,99 +7,98 @@ import (
"testing" "testing"
"time" "time"
. "github.com/smartystreets/goconvey/convey" "net/http/httptest"
"github.com/smartystreets/assertions"
"github.com/smartystreets/assertions/should"
"github.com/smartystreets/gunit"
) )
func TestSignatureS3(t *testing.T) { // http://docs.aws.amazon.com/AmazonS3/2006-03-01/dev/RESTAuthentication.html
// http://docs.aws.amazon.com/AmazonS3/2006-03-01/dev/RESTAuthentication.html // Note: S3 now supports signed signature version 4
// Note: S3 now supports signed signature version 4 // (but signed URL requests still utilize a lot of the same functionality)
// (but signed URL requests still utilize a lot of the same functionality)
Convey("Given a GET request to Amazon S3", t, func() { func TestSignatureS3Fixture(t *testing.T) {
keys := *testCredS3 gunit.RunSequential(new(SignatureS3Fixture), t)
request := test_plainRequestS3() }
// Mock time type SignatureS3Fixture struct {
now = func() time.Time { *gunit.Fixture
parsed, _ := time.Parse(timeFormatS3, exampleReqTsS3)
return parsed
}
Convey("The request should be prepared with a Date header", func() { keys Credentials
prepareRequestS3(request) request *http.Request
So(request.Header.Get("Date"), ShouldEqual, exampleReqTsS3) }
})
Convey("The CanonicalizedAmzHeaders should be built properly", func() { func (this *SignatureS3Fixture) Setup() {
req2 := test_headerRequestS3() this.keys = *testCredS3
actual := canonicalAmzHeadersS3(req2) this.request = test_plainRequestS3()
So(actual, ShouldEqual, expectedCanonAmzHeadersS3)
})
Convey("The CanonicalizedResource should be built properly", func() { now = func() time.Time {
actual := canonicalResourceS3(request) parsed, _ := time.Parse(timeFormatS3, exampleReqTsS3)
So(actual, ShouldEqual, expectedCanonResourceS3) return parsed
}) }
}
Convey("The string to sign should be correct", func() { func (this *SignatureS3Fixture) TestRequestShouldHaveADateHeader() {
actual := stringToSignS3(request) prepareRequestS3(this.request)
So(actual, ShouldEqual, expectedStringToSignS3) this.So(this.request.Header.Get("Date"), should.Equal, exampleReqTsS3)
}) }
Convey("The final signature string should be exactly correct", func() { func (this *SignatureS3Fixture) TestRequestShouldHaveCanonicalizedAmzHeaders() {
actual := signatureS3(stringToSignS3(request), keys) req2 := test_headerRequestS3()
So(actual, ShouldEqual, "bWq2s1WEIj+Ydj0vQ697zp+IXMU=") actual := canonicalAmzHeadersS3(req2)
}) this.So(actual, should.Equal, expectedCanonAmzHeadersS3)
}) }
Convey("Given a GET request for a resource on S3 for query string authentication", t, func() { func (this *SignatureS3Fixture) TestCanonicalizedResourceBuiltProperly() {
keys := *testCredS3 actual := canonicalResourceS3(this.request)
request, _ := http.NewRequest("GET", "https://johnsmith.s3.amazonaws.com/johnsmith/photos/puppy.jpg", nil) this.So(actual, should.Equal, expectedCanonResourceS3)
}
now = func() time.Time { func (this *SignatureS3Fixture) TestStringToSignShouldBeCorrect() {
parsed, _ := time.Parse(timeFormatS3, exampleReqTsS3) actual := stringToSignS3(this.request)
return parsed this.So(actual, should.Equal, expectedStringToSignS3)
} }
Convey("The string to sign should be correct", func() { func (this *SignatureS3Fixture) TestFinalSignatureShouldBeExactlyCorrect() {
actual := stringToSignS3Url("GET", now(), request.URL.Path) actual := signatureS3(stringToSignS3(this.request), this.keys)
So(actual, ShouldEqual, expectedStringToSignS3Url) this.So(actual, should.Equal, "bWq2s1WEIj+Ydj0vQ697zp+IXMU=")
}) }
Convey("The signature of string to sign should be correct", func() { func (this *SignatureS3Fixture) TestQueryStringAuthentication() {
actual := signatureS3(expectedStringToSignS3Url, keys) this.request = httptest.NewRequest("GET", "https://johnsmith.s3.amazonaws.com/johnsmith/photos/puppy.jpg", nil)
So(actual, ShouldEqual, "R2K/+9bbnBIbVDCs7dqlz3XFtBQ=")
})
Convey("The finished signed URL should be correct", func() { // The string to sign should be correct
expiry := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) actual := stringToSignS3Url("GET", now(), this.request.URL.Path)
So(SignS3Url(request, expiry, keys).URL.String(), ShouldEqual, expectedSignedS3Url) this.So(actual, should.Equal, expectedStringToSignS3Url)
})
}) // The signature of string to sign should be correct
actualSignature := signatureS3(expectedStringToSignS3Url, this.keys)
this.So(actualSignature, should.Equal, "R2K/+9bbnBIbVDCs7dqlz3XFtBQ=")
// The finished signed URL should be correct
expiry := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
this.So(SignS3Url(this.request, expiry, this.keys).URL.String(), should.Equal, expectedSignedS3Url)
} }
func TestS3STSRequestPreparer(t *testing.T) { func TestS3STSRequestPreparer(t *testing.T) {
Convey("Given a plain request with no custom headers", t, func() { // Given a plain request with no custom headers
request := test_plainRequestS3() request := test_plainRequestS3()
Convey("And a set of credentials with an STS token", func() { // And a set of credentials with an STS token
keys := *testCredS3WithSTS keys := *testCredS3WithSTS
Convey("It should include an X-Amz-Security-Token when the request is signed", func() { // It should include an X-Amz-Security-Token when the request is signed
actualSigned := SignS3(request, keys) actualSigned := SignS3(request, keys)
actual := actualSigned.Header.Get("X-Amz-Security-Token") actual := actualSigned.Header.Get("X-Amz-Security-Token")
So(actual, ShouldNotBeBlank) assert := assertions.New(t)
So(actual, ShouldEqual, testCredS3WithSTS.SecurityToken) assert.So(actual, should.NotBeBlank)
assert.So(actual, should.Equal, testCredS3WithSTS.SecurityToken)
})
})
})
} }
func test_plainRequestS3() *http.Request { func test_plainRequestS3() *http.Request {
request, _ := http.NewRequest("GET", "https://johnsmith.s3.amazonaws.com/photos/puppy.jpg", nil) return httptest.NewRequest("GET", "https://johnsmith.s3.amazonaws.com/photos/puppy.jpg", nil)
return request
} }
func test_headerRequestS3() *http.Request { func test_headerRequestS3() *http.Request {
+47 -61
View File
@@ -6,82 +6,68 @@ import (
"testing" "testing"
"time" "time"
. "github.com/smartystreets/goconvey/convey" "github.com/smartystreets/assertions"
"github.com/smartystreets/assertions/should"
"github.com/smartystreets/gunit"
) )
func TestSignature2(t *testing.T) { // http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html
// http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html
Convey("Given bogus credentials", t, func() { func TestSignature2Fixture(t *testing.T) {
keys := *testCredV2 gunit.RunSequential(new(Signature2Fixture), t)
}
// Mock time type Signature2Fixture struct {
now = func() time.Time { *gunit.Fixture
parsed, _ := time.Parse(timeFormatV2, exampleReqTsV2)
return parsed
}
Convey("Given a plain request that is unprepared", func() { keys Credentials
request := test_plainRequestV2() }
Convey("The request should be prepared to be signed", func() { func (this *Signature2Fixture) Setup() {
expectedUnsigned := test_unsignedRequestV2() this.keys = *testCredV2
prepareRequestV2(request, keys)
So(request, ShouldResemble, expectedUnsigned)
})
})
Convey("Given a prepared, but unsigned, request", func() { // Mock time
request := test_unsignedRequestV2() now = func() time.Time {
parsed, _ := time.Parse(timeFormatV2, exampleReqTsV2)
return parsed
}
}
Convey("The canonical query string should be correct", func() { func (this *Signature2Fixture) TestSignUnpreparedPlanRequest() {
actual := canonicalQueryStringV2(request) request := test_plainRequestV2()
expected := canonicalQsV2 prepareRequestV2(request, this.keys)
So(actual, ShouldEqual, expected) this.So(request, should.Resemble, test_unsignedRequestV2())
}) }
Convey("The absolute path should be extracted correctly", func() { func (this *Signature2Fixture) TestSignPreparedUnsignedRequest() {
So(request.URL.Path, ShouldEqual, "/") request := test_unsignedRequestV2()
}) actual := canonicalQueryStringV2(request)
expected := canonicalQsV2
this.So(actual, should.Equal, expected)
this.So(request.URL.Path, should.Equal, "/")
Convey("The string to sign should be well-formed", func() { this.So(stringToSignV2(request), should.Equal, expectedStringToSignV2)
actual := stringToSignV2(request) this.So(signatureV2(stringToSignV2(request), this.keys), should.Equal, "i91nKc4PWAt0JJIdXwz9HxZCJDdiy6cf/Mj6vPxyYIs=")
So(actual, ShouldEqual, expectedStringToSignV2)
})
Convey("The resulting signature should be correct", func() { Sign2(request, this.keys)
actual := signatureV2(stringToSignV2(request), keys) this.So(request.URL.String(), should.Equal, expectedFinalUrlV2)
So(actual, ShouldEqual, "i91nKc4PWAt0JJIdXwz9HxZCJDdiy6cf/Mj6vPxyYIs=")
})
Convey("The final signed request should be correctly formed", func() {
Sign2(request, keys)
actual := request.URL.String()
So(actual, ShouldResemble, expectedFinalUrlV2)
})
})
})
} }
func TestVersion2STSRequestPreparer(t *testing.T) { func TestVersion2STSRequestPreparer(t *testing.T) {
Convey("Given a plain request ", t, func() { // Given a plain request
request := test_plainRequestV2() request := test_plainRequestV2()
Convey("And a set of credentials with an STS token", func() { // And a set of credentials with an STS token
var keys Credentials var keys Credentials
keys = *testCredV2WithSTS keys = *testCredV2WithSTS
Convey("It should include the SecurityToken parameter when the request is signed", func() { // It should include the SecurityToken parameter when the request is signed
actualSigned := Sign2(request, keys) actualSigned := Sign2(request, keys)
actual := actualSigned.URL.Query()["SecurityToken"][0] actual := actualSigned.URL.Query()["SecurityToken"][0]
So(actual, ShouldNotBeBlank)
So(actual, ShouldEqual, testCredV2WithSTS.SecurityToken)
})
})
})
assert := assertions.New(t)
assert.So(actual, should.NotBeBlank)
assert.So(actual, should.Equal, testCredV2WithSTS.SecurityToken)
} }
func test_plainRequestV2() *http.Request { func test_plainRequestV2() *http.Request {
@@ -89,9 +75,9 @@ func test_plainRequestV2() *http.Request {
values.Set("Action", "DescribeJobFlows") values.Set("Action", "DescribeJobFlows")
values.Set("Version", "2009-03-31") values.Set("Version", "2009-03-31")
url := baseUrlV2 + "?" + values.Encode() address := baseUrlV2 + "?" + values.Encode()
request, err := http.NewRequest("GET", url, nil) request, err := http.NewRequest("GET", address, nil)
if err != nil { if err != nil {
panic(err) panic(err)
} }
+41 -52
View File
@@ -6,56 +6,48 @@ import (
"testing" "testing"
"time" "time"
. "github.com/smartystreets/goconvey/convey" "github.com/smartystreets/assertions"
"github.com/smartystreets/assertions/should"
) )
func TestSignature3(t *testing.T) { func TestSignature3(t *testing.T) {
// http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/RESTAuthentication.html // http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/RESTAuthentication.html
// http://docs.aws.amazon.com/ses/latest/DeveloperGuide/query-interface-authentication.html // http://docs.aws.amazon.com/ses/latest/DeveloperGuide/query-interface-authentication.html
Convey("Given bogus credentials", t, func() { assert := assertions.New(t)
keys := *testCredV3
// Mock time // Given bogus credentials
now = func() time.Time { keys := *testCredV3
parsed, _ := time.Parse(timeFormatV3, exampleReqTsV3)
return parsed
}
Convey("Given a plain request that is unprepared", func() { // Mock time
request := test_plainRequestV3() now = func() time.Time {
parsed, _ := time.Parse(timeFormatV3, exampleReqTsV3)
return parsed
}
Convey("The request should be prepared to be signed", func() { // Given a plain request that is unprepared
expectedUnsigned := test_unsignedRequestV3() request := test_plainRequestV3()
prepareRequestV3(request)
So(request, ShouldResemble, expectedUnsigned)
})
})
Convey("Given a prepared, but unsigned, request", func() { // The request should be prepared to be signed
request := test_unsignedRequestV3() expectedUnsigned := test_unsignedRequestV3()
prepareRequestV3(request)
assert.So(request, should.Resemble, expectedUnsigned)
Convey("The absolute path should be extracted correctly", func() { // Given a prepared, but unsigned, request
So(request.URL.Path, ShouldEqual, "/") request = test_unsignedRequestV3()
})
Convey("The string to sign should be well-formed", func() { // The absolute path should be extracted correctly
actual := stringToSignV3(request) assert.So(request.URL.Path, should.Equal, "/")
So(actual, ShouldEqual, expectedStringToSignV3)
})
Convey("The resulting signature should be correct", func() { // The string to sign should be well-formed
actual := signatureV3(stringToSignV3(request), keys) assert.So(stringToSignV3(request), should.Equal, expectedStringToSignV3)
So(actual, ShouldEqual, "PjAJ6buiV6l4WyzmmuwtKE59NJXVg5Dr3Sn4PCMZ0Yk=")
})
Convey("The final signed request should be correctly formed", func() { // The resulting signature should be correct
Sign3(request, keys) assert.So(signatureV3(stringToSignV3(request), keys), should.Equal, "PjAJ6buiV6l4WyzmmuwtKE59NJXVg5Dr3Sn4PCMZ0Yk=")
actual := request.Header.Get("X-Amzn-Authorization")
So(actual, ShouldResemble, expectedAuthHeaderV3) // The final signed request should be correctly formed
}) Sign3(request, keys)
}) assert.So(request.Header.Get("X-Amzn-Authorization"), should.Resemble, expectedAuthHeaderV3)
})
} }
func test_plainRequestV3() *http.Request { func test_plainRequestV3() *http.Request {
@@ -63,9 +55,9 @@ func test_plainRequestV3() *http.Request {
values.Set("Action", "GetSendStatistics") values.Set("Action", "GetSendStatistics")
values.Set("Version", "2010-12-01") values.Set("Version", "2010-12-01")
url := baseUrlV3 + "/?" + values.Encode() address := baseUrlV3 + "/?" + values.Encode()
request, err := http.NewRequest("GET", url, nil) request, err := http.NewRequest("GET", address, nil)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -83,23 +75,20 @@ func test_unsignedRequestV3() *http.Request {
} }
func TestVersion3STSRequestPreparer(t *testing.T) { func TestVersion3STSRequestPreparer(t *testing.T) {
Convey("Given a plain request with no custom headers", t, func() { // Given a plain request with no custom headers
request := test_plainRequestV3() request := test_plainRequestV3()
Convey("And a set of credentials with an STS token", func() { // And a set of credentials with an STS token
var keys Credentials var keys Credentials
keys = *testCredV3WithSTS keys = *testCredV3WithSTS
Convey("It should include an X-Amz-Security-Token when the request is signed", func() { // It should include an X-Amz-Security-Token when the request is signed
actualSigned := Sign3(request, keys) actualSigned := Sign3(request, keys)
actual := actualSigned.Header.Get("X-Amz-Security-Token") actual := actualSigned.Header.Get("X-Amz-Security-Token")
So(actual, ShouldNotBeBlank) assert := assertions.New(t)
So(actual, ShouldEqual, testCredV4WithSTS.SecurityToken) assert.So(actual, should.NotBeBlank)
assert.So(actual, should.Equal, testCredV4WithSTS.SecurityToken)
})
})
})
} }
+109 -122
View File
@@ -2,173 +2,155 @@ package awsauth
import ( import (
"net/http" "net/http"
"net/http/httputil"
"net/url" "net/url"
"strings" "strings"
"testing" "testing"
. "github.com/smartystreets/goconvey/convey" "github.com/smartystreets/assertions"
"github.com/smartystreets/assertions/should"
) )
func TestVersion4RequestPreparer(t *testing.T) { func TestVersion4RequestPreparer_1(t *testing.T) {
Convey("Given a plain request with no custom headers", t, func() { // Given a plain request with no custom headers
request := test_plainRequestV4(false) request := test_plainRequestV4(false)
prepareRequestV4(request)
expectedUnsigned := test_unsignedRequestV4(true, false) expectedUnsigned := test_unsignedRequestV4(true, false)
expectedUnsigned.Header.Set("X-Amz-Date", timestampV4()) expectedUnsigned.Header.Set("X-Amz-Date", timestampV4())
Convey("The necessary, default headers should be appended", func() { assert := assertions.New(t)
prepareRequestV4(request)
So(request, ShouldResemble, expectedUnsigned)
})
Convey("Forward-slash should be appended to URI if not present", func() { // The necessary, default headers should be appended
prepareRequestV4(request) assert.So(dumpRequest(request), should.Equal, dumpRequest(expectedUnsigned))
So(request.URL.Path, ShouldEqual, "/")
})
Convey("And a set of credentials", func() { // Forward-slash should be appended to URI if not present
var keys Credentials assert.So(request.URL.Path, should.Equal, "/")
keys = *testCredV4 }
Convey("It should be signed with an Authorization header", func() { func TestVersion4RequestPreparer_2(t *testing.T) {
actualSigned := Sign4(request, keys) // And a set of credentials
actual := actualSigned.Header.Get("Authorization") // It should be signed with an Authorization header
request := test_plainRequestV4(false)
actualSigned := Sign4(request, *testCredV4)
actual := actualSigned.Header.Get("Authorization")
So(actual, ShouldNotBeBlank) assert := assertions.New(t)
So(actual, ShouldContainSubstring, "Credential="+testCredV4.AccessKeyID) assert.So(actual, should.NotBeBlank)
So(actual, ShouldContainSubstring, "SignedHeaders=") assert.So(actual, should.ContainSubstring, "Credential="+testCredV4.AccessKeyID)
So(actual, ShouldContainSubstring, "Signature=") assert.So(actual, should.ContainSubstring, "SignedHeaders=")
So(actual, ShouldContainSubstring, "AWS4") assert.So(actual, should.ContainSubstring, "Signature=")
}) assert.So(actual, should.ContainSubstring, "AWS4")
}) }
})
Convey("Given a request with custom, necessary headers", t, func() { func TestVersion4RequestPreparer_3(t *testing.T) {
Convey("The custom, necessary headers must not be changed", func() { // Given a request with custom, necessary headers
request := test_unsignedRequestV4(true, false) // The custom, necessary headers must not be changed
prepareRequestV4(request) request := test_unsignedRequestV4(true, false)
So(request, ShouldResemble, test_unsignedRequestV4(true, false)) prepareRequestV4(request)
}) assertions.New(t).So(dumpRequest(request), should.Equal, dumpRequest(test_unsignedRequestV4(true, false)))
})
} }
func TestVersion4STSRequestPreparer(t *testing.T) { func TestVersion4STSRequestPreparer(t *testing.T) {
Convey("Given a plain request with no custom headers", t, func() { // Given a plain request with no custom headers
request := test_plainRequestV4(false) request := test_plainRequestV4(false)
Convey("And a set of credentials with an STS token", func() { // And a set of credentials with an STS token
var keys Credentials var keys Credentials
keys = *testCredV4WithSTS keys = *testCredV4WithSTS
Convey("It should include an X-Amz-Security-Token when the request is signed", func() { // It should include an X-Amz-Security-Token when the request is signed
actualSigned := Sign4(request, keys) actualSigned := Sign4(request, keys)
actual := actualSigned.Header.Get("X-Amz-Security-Token") actual := actualSigned.Header.Get("X-Amz-Security-Token")
So(actual, ShouldNotBeBlank)
So(actual, ShouldEqual, testCredV4WithSTS.SecurityToken)
})
})
})
assert := assertions.New(t)
assert.So(actual, should.NotBeBlank)
assert.So(actual, should.Equal, testCredV4WithSTS.SecurityToken)
} }
func TestVersion4SigningTasks(t *testing.T) { func TestVersion4SigningTasks(t *testing.T) {
// http://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html // http://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html
Convey("Given a bogus request and credentials from AWS documentation with an additional meta tag", t, func() { // Given a bogus request and credentials from AWS documentation with an additional meta tag
request := test_unsignedRequestV4(true, true) request := test_unsignedRequestV4(true, true)
meta := new(metadata) meta := new(metadata)
assert := assertions.New(t)
Convey("(Task 1) The canonical request should be built correctly", func() { // (Task 1) The canonical request should be built correctly
hashedCanonReq := hashedCanonicalRequestV4(request, meta) hashedCanonReq := hashedCanonicalRequestV4(request, meta)
assert.So(hashedCanonReq, should.Equal, expectingV4["CanonicalHash"])
So(hashedCanonReq, ShouldEqual, expectingV4["CanonicalHash"]) // (Task 2) The string to sign should be built correctly
}) stringToSign := stringToSignV4(request, hashedCanonReq, meta)
assert.So(stringToSign, should.Equal, expectingV4["StringToSign"])
Convey("(Task 2) The string to sign should be built correctly", func() { // (Task 3) The version 4 signed signature should be correct
hashedCanonReq := hashedCanonicalRequestV4(request, meta) signature := signatureV4(test_signingKeyV4(), stringToSign)
stringToSign := stringToSignV4(request, hashedCanonReq, meta) assert.So(signature, should.Equal, expectingV4["SignatureV4"])
So(stringToSign, ShouldEqual, expectingV4["StringToSign"])
})
Convey("(Task 3) The version 4 signed signature should be correct", func() {
hashedCanonReq := hashedCanonicalRequestV4(request, meta)
stringToSign := stringToSignV4(request, hashedCanonReq, meta)
signature := signatureV4(test_signingKeyV4(), stringToSign)
So(signature, ShouldEqual, expectingV4["SignatureV4"])
})
})
} }
func TestSignature4Helpers(t *testing.T) { func TestSignature4Helpers(t *testing.T) {
// The signing key should be properly generated
expected := []byte{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}
actual := test_signingKeyV4()
keys := *testCredV4 assertions.New(t).So(actual, should.Resemble, expected)
}
func TestSignature4Helpers_1(t *testing.T) {
// Authorization headers should be built properly
meta := &metadata{
algorithm: "AWS4-HMAC-SHA256",
credentialScope: "20110909/us-east-1/iam/aws4_request",
signedHeaders: "content-type;host;x-amz-date",
}
expected := expectingV4["AuthHeader"] + expectingV4["SignatureV4"]
actual := buildAuthHeaderV4(expectingV4["SignatureV4"], meta, *testCredV4)
Convey("The signing key should be properly generated", t, func() { assertions.New(t).So(actual, should.Equal, expected)
expected := []byte{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} }
actual := test_signingKeyV4() func TestSignature4Helpers_2(t *testing.T) {
// Timestamps should be in the correct format, in UTC time
actual := timestampV4()
So(actual, ShouldResemble, expected) assert := assertions.New(t)
}) assert.So(len(actual), should.Equal, 16)
assert.So(actual, should.NotContainSubstring, ":")
assert.So(actual, should.NotContainSubstring, "-")
assert.So(actual, should.NotContainSubstring, " ")
assert.So(actual, should.EndWith, "Z")
assert.So(actual, should.ContainSubstring, "T")
}
func TestSignature4Helpers_3(t *testing.T) {
// Given an Version 4 AWS-formatted timestamp
ts := "20110909T233600Z"
Convey("Authorization headers should be built properly", t, func() { // The date string should be extracted properly
meta := &metadata{ assertions.New(t).So(tsDateV4(ts), should.Equal, "20110909")
algorithm: "AWS4-HMAC-SHA256", }
credentialScope: "20110909/us-east-1/iam/aws4_request", func TestSignature4Helpers_4(t *testing.T) {
signedHeaders: "content-type;host;x-amz-date", // Given any request with a body
} request := test_plainRequestV4(false)
expected := expectingV4["AuthHeader"] + expectingV4["SignatureV4"]
actual := buildAuthHeaderV4(expectingV4["SignatureV4"], meta, keys)
So(actual, ShouldEqual, expected) // Its body should be read and replaced without differences
}) expected := []byte(requestValuesV4.Encode())
assert := assertions.New(t)
Convey("Timestamps should be in the correct format, in UTC time", t, func() { actual1 := readAndReplaceBody(request)
actual := timestampV4() assert.So(actual1, should.Resemble, expected)
So(len(actual), ShouldEqual, 16) actual2 := readAndReplaceBody(request)
So(actual, ShouldNotContainSubstring, ":") assert.So(actual2, should.Resemble, expected)
So(actual, ShouldNotContainSubstring, "-")
So(actual, ShouldNotContainSubstring, " ")
So(actual, ShouldEndWith, "Z")
So(actual, ShouldContainSubstring, "T")
})
Convey("Given an Version 4 AWS-formatted timestamp", t, func() {
ts := "20110909T233600Z"
Convey("The date string should be extracted properly", func() {
So(tsDateV4(ts), ShouldEqual, "20110909")
})
})
Convey("Given any request with a body", t, func() {
request := test_plainRequestV4(false)
Convey("Its body should be read and replaced without differences", func() {
expected := []byte(requestValuesV4.Encode())
actual1 := readAndReplaceBody(request)
So(actual1, ShouldResemble, expected)
actual2 := readAndReplaceBody(request)
So(actual2, ShouldResemble, expected)
})
})
} }
func test_plainRequestV4(trailingSlash bool) *http.Request { func test_plainRequestV4(trailingSlash bool) *http.Request {
url := "http://iam.amazonaws.com" address := "http://iam.amazonaws.com"
body := strings.NewReader(requestValuesV4.Encode()) body := strings.NewReader(requestValuesV4.Encode())
if trailingSlash { if trailingSlash {
url += "/" address += "/"
} }
request, err := http.NewRequest("POST", url, body) request, err := http.NewRequest("POST", address, body)
if err != nil { if err != nil {
panic(err) panic(err)
@@ -191,6 +173,11 @@ func test_signingKeyV4() []byte {
return signingKeyV4(testCredV4.SecretAccessKey, "20110909", "us-east-1", "iam") return signingKeyV4(testCredV4.SecretAccessKey, "20110909", "us-east-1", "iam")
} }
func dumpRequest(request *http.Request) string {
dump, _ := httputil.DumpRequestOut(request, true)
return string(dump)
}
var ( var (
testCredV4 = &Credentials{ testCredV4 = &Credentials{
AccessKeyID: "AKIDEXAMPLE", AccessKeyID: "AKIDEXAMPLE",