mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-22 10:57:42 +08:00
Merge pull request #2493 from ipfs/feat/deps-cleanup
clean up dependencies
This commit is contained in:
commit
78ac786c7c
89
Godeps/Godeps.json
generated
89
Godeps/Godeps.json
generated
@ -14,39 +14,10 @@
|
||||
"Comment": "null-5",
|
||||
"Rev": "75cd24fc2f2c2a2088577d12123ddee5f54e0675"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/alecthomas/kingpin",
|
||||
"Comment": "v2.1.0-2-gaedd543",
|
||||
"Rev": "aedd5430ecd39ba1396fee0c00308b494c552b1e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/alecthomas/template",
|
||||
"Rev": "b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/alecthomas/units",
|
||||
"Rev": "6b4e7dc5e3143b85ea77909c72caf89416fc2915"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/anacrolix/jitter",
|
||||
"Rev": "2ea5c18645100745b24e9f5cfc9b3f6f7eac51ef"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/anacrolix/missinggo",
|
||||
"Rev": "4e1ca5963308863b56c31863f60c394a7365ec29"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/anacrolix/utp",
|
||||
"Rev": "0bb24de92c268452fb9106ca4fb9302442ca0dee"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/beorn7/perks/quantile",
|
||||
"Rev": "b965b613227fddccbfffe13eae360ed3fa822f8d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bradfitz/iter",
|
||||
"Rev": "454541ec3da2a73fc34fd049b19ee5777bf19345"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bren2010/proquint",
|
||||
"Rev": "5958552242606512f714d2e93513b380f43f9991"
|
||||
@ -75,18 +46,6 @@
|
||||
"ImportPath": "github.com/codahale/metrics",
|
||||
"Rev": "7c37910bc765e705301b159683480bdd44555c91"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/go-semver/semver",
|
||||
"Rev": "568e959cd89871e61434c1143528d9162da89ef2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/crowdmob/goamz/aws",
|
||||
"Rev": "82345796204222aa56be89cf930c316b1297f906"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/crowdmob/goamz/s3",
|
||||
"Rev": "82345796204222aa56be89cf930c316b1297f906"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cryptix/mdns",
|
||||
"Rev": "04ff72a32679d57d009c0ac0fc5c4cda10350bad"
|
||||
@ -107,10 +66,6 @@
|
||||
"ImportPath": "github.com/facebookgo/atomicfile",
|
||||
"Rev": "6f117f2e7f224fb03eb5e5fba370eade6e2b90c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/fd/go-nat",
|
||||
"Rev": "50e7633d5f27d81490026a13e5b92d2e42d8c6bb"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gogo/protobuf/io",
|
||||
"Rev": "0ac967c269268f1af7d9bcc7927ccc9a589b2b36"
|
||||
@ -127,22 +82,10 @@
|
||||
"ImportPath": "github.com/hashicorp/golang-lru",
|
||||
"Rev": "253b2dc1ca8bae42c3b5b6e53dd2eab1a7551116"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/hashicorp/yamux",
|
||||
"Rev": "9feabe6854fadca1abec9cd3bd2a613fe9a34000"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/huin/goupnp",
|
||||
"Rev": "223008361153d7d434c1f0ac990cd3fcae6931f5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ipfs/go-datastore",
|
||||
"Rev": "e63957b6da369d986ef3e7a3f249779ba3f56c7e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/jackpal/go-nat-pmp",
|
||||
"Rev": "a45aa3d54aef73b504e15eb71bea0e5565b5e6e1"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/jbenet/go-base58",
|
||||
"Rev": "6237cf65f3a6f7111cd8a42be3590df99a66bc7d"
|
||||
@ -201,34 +144,10 @@
|
||||
"ImportPath": "github.com/jbenet/go-random-files",
|
||||
"Rev": "737479700b40b4b50e914e963ce8d9d44603e3c8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/jbenet/go-reuseport",
|
||||
"Rev": "48959f1fad204b6cf2c0e8d086ef69f03f2de961"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/jbenet/go-sockaddr/net",
|
||||
"Rev": "da304f94eea1af8ba8d1faf184623e1f9d9777dc"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/jbenet/go-stream-muxer",
|
||||
"Rev": "e2e261765847234749629e0190fef193a4548303"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/jbenet/go-temp-err-catcher",
|
||||
"Rev": "aac704a3f4f27190b4ccc05f303a4931fd1241ff"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/jbenet/goprocess",
|
||||
"Rev": "64a8220330a485070813201cc05b0c6777f6a516"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/matttproud/golang_protobuf_extensions/pbutil",
|
||||
"Rev": "fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/miekg/dns",
|
||||
"Rev": "82ffc45b1f84ff71bd1cebed8b210118ce3d181e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mitchellh/go-homedir",
|
||||
"Rev": "1f6da4a72e57d4e7edd4a7295a585e0a3999a2d4"
|
||||
@ -302,14 +221,6 @@
|
||||
"ImportPath": "github.com/whyrusleeping/go-metrics",
|
||||
"Rev": "1cd8009604ec2238b5a71305a0ecd974066e0e16"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/whyrusleeping/go-multiplex",
|
||||
"Rev": "474b9aebeb391746f304ddf7c764a5da12319857"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/whyrusleeping/go-multistream",
|
||||
"Rev": "31bb014803a6eba2261bda5593e42c016a5f33bb"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/whyrusleeping/go-sysinfo",
|
||||
"Rev": "769b7c0b50e8030895abc74ba8107ac715e3162a"
|
||||
|
||||
12
Godeps/_workspace/src/github.com/anacrolix/jitter/jitter.go
generated
vendored
12
Godeps/_workspace/src/github.com/anacrolix/jitter/jitter.go
generated
vendored
@ -1,12 +0,0 @@
|
||||
package jitter
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Duration(average, plusMinus time.Duration) (ret time.Duration) {
|
||||
ret = average - plusMinus
|
||||
ret += time.Duration(rand.Int63n(2*int64(plusMinus) + 1))
|
||||
return
|
||||
}
|
||||
72
Godeps/_workspace/src/github.com/anacrolix/missinggo/addr.go
generated
vendored
72
Godeps/_workspace/src/github.com/anacrolix/missinggo/addr.go
generated
vendored
@ -1,72 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type HostMaybePort struct {
|
||||
Host string
|
||||
Port int
|
||||
NoPort bool
|
||||
}
|
||||
|
||||
func (me HostMaybePort) String() string {
|
||||
if me.NoPort {
|
||||
return me.Host
|
||||
}
|
||||
return net.JoinHostPort(me.Host, strconv.FormatInt(int64(me.Port), 10))
|
||||
}
|
||||
|
||||
func SplitHostPort(hostport string) (ret HostMaybePort) {
|
||||
host, port, err := net.SplitHostPort(hostport)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "missing port") {
|
||||
ret.Host = hostport
|
||||
ret.NoPort = true
|
||||
return
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
i64, err := strconv.ParseInt(port, 0, 0)
|
||||
ret.Host = host
|
||||
ret.Port = int(i64)
|
||||
if err != nil {
|
||||
ret.NoPort = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Extracts the port as an integer from an address string.
|
||||
func AddrPort(addr net.Addr) int {
|
||||
switch raw := addr.(type) {
|
||||
case *net.UDPAddr:
|
||||
return raw.Port
|
||||
default:
|
||||
_, port, err := net.SplitHostPort(addr.String())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
i64, err := strconv.ParseInt(port, 0, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return int(i64)
|
||||
}
|
||||
}
|
||||
|
||||
func AddrIP(addr net.Addr) net.IP {
|
||||
switch raw := addr.(type) {
|
||||
case *net.UDPAddr:
|
||||
return raw.IP
|
||||
case *net.TCPAddr:
|
||||
return raw.IP
|
||||
default:
|
||||
host, _, err := net.SplitHostPort(addr.String())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return net.ParseIP(host)
|
||||
}
|
||||
}
|
||||
17
Godeps/_workspace/src/github.com/anacrolix/missinggo/addr_test.go
generated
vendored
17
Godeps/_workspace/src/github.com/anacrolix/missinggo/addr_test.go
generated
vendored
@ -1,17 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gx/ipfs/QmZwjfAKWe7vWZ8f48u7AGA1xYfzR1iCD9A2XSCYFRBWot/testify/assert"
|
||||
)
|
||||
|
||||
func TestSplitHostPort(t *testing.T) {
|
||||
assert.EqualValues(t, HostMaybePort{"a", 1, false}, SplitHostPort("a:1"))
|
||||
assert.EqualValues(t, HostMaybePort{"a", 0, true}, SplitHostPort("a"))
|
||||
}
|
||||
|
||||
func TestHostMaybePortString(t *testing.T) {
|
||||
assert.EqualValues(t, "a:1", (HostMaybePort{"a", 1, false}).String())
|
||||
assert.EqualValues(t, "a", (HostMaybePort{"a", 0, true}).String())
|
||||
}
|
||||
15
Godeps/_workspace/src/github.com/anacrolix/missinggo/args/args.go
generated
vendored
15
Godeps/_workspace/src/github.com/anacrolix/missinggo/args/args.go
generated
vendored
@ -1,15 +0,0 @@
|
||||
package args
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func Parse() {
|
||||
flag.Parse()
|
||||
if flag.NArg() != 0 {
|
||||
fmt.Fprintf(os.Stderr, "unexpected positional arguments\n")
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
11
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime.go
generated
vendored
11
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime.go
generated
vendored
@ -1,11 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Extracts the access time from the FileInfo internals.
|
||||
func FileInfoAccessTime(fi os.FileInfo) time.Time {
|
||||
return fileInfoAccessTime(fi)
|
||||
}
|
||||
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_darwin.go
generated
vendored
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_darwin.go
generated
vendored
@ -1,12 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func fileInfoAccessTime(fi os.FileInfo) time.Time {
|
||||
ts := fi.Sys().(*syscall.Stat_t).Atimespec
|
||||
return time.Unix(int64(ts.Sec), int64(ts.Nsec))
|
||||
}
|
||||
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_freebsd.go
generated
vendored
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_freebsd.go
generated
vendored
@ -1,12 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func fileInfoAccessTime(fi os.FileInfo) time.Time {
|
||||
ts := fi.Sys().(*syscall.Stat_t).Atimespec
|
||||
return time.Unix(int64(ts.Sec), int64(ts.Nsec))
|
||||
}
|
||||
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_linux.go
generated
vendored
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_linux.go
generated
vendored
@ -1,12 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func fileInfoAccessTime(fi os.FileInfo) time.Time {
|
||||
ts := fi.Sys().(*syscall.Stat_t).Atim
|
||||
return time.Unix(int64(ts.Sec), int64(ts.Nsec))
|
||||
}
|
||||
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_windows.go
generated
vendored
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_windows.go
generated
vendored
@ -1,12 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func fileInfoAccessTime(fi os.FileInfo) time.Time {
|
||||
ts := fi.Sys().(syscall.Win32FileAttributeData).LastAccessTime
|
||||
return time.Unix(0, int64(ts.Nanoseconds()))
|
||||
}
|
||||
17
Godeps/_workspace/src/github.com/anacrolix/missinggo/castslice.go
generated
vendored
17
Godeps/_workspace/src/github.com/anacrolix/missinggo/castslice.go
generated
vendored
@ -1,17 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/bradfitz/iter"
|
||||
)
|
||||
|
||||
func ConvertToSliceOfEmptyInterface(slice interface{}) (ret []interface{}) {
|
||||
v := reflect.ValueOf(slice)
|
||||
l := v.Len()
|
||||
ret = make([]interface{}, 0, l)
|
||||
for i := range iter.N(v.Len()) {
|
||||
ret = append(ret, v.Index(i).Interface())
|
||||
}
|
||||
return
|
||||
}
|
||||
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/go-env/main.go
generated
vendored
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/go-env/main.go
generated
vendored
@ -1,12 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for _, v := range os.Environ() {
|
||||
fmt.Printf("%s\n", v)
|
||||
}
|
||||
}
|
||||
3
Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/nop/main.go
generated
vendored
3
Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/nop/main.go
generated
vendored
@ -1,3 +0,0 @@
|
||||
package main
|
||||
|
||||
func main() {}
|
||||
11
Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/query-escape/main.go
generated
vendored
11
Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/query-escape/main.go
generated
vendored
@ -1,11 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(url.QueryEscape(os.Args[1]))
|
||||
}
|
||||
11
Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/query-unescape/main.go
generated
vendored
11
Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/query-unescape/main.go
generated
vendored
@ -1,11 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(url.QueryUnescape(os.Args[1]))
|
||||
}
|
||||
32
Godeps/_workspace/src/github.com/anacrolix/missinggo/copy.go
generated
vendored
32
Godeps/_workspace/src/github.com/anacrolix/missinggo/copy.go
generated
vendored
@ -1,32 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func CopyExact(dest interface{}, src interface{}) {
|
||||
dV := reflect.ValueOf(dest)
|
||||
sV := reflect.ValueOf(src)
|
||||
if dV.Kind() == reflect.Ptr {
|
||||
dV = dV.Elem()
|
||||
}
|
||||
if dV.Kind() == reflect.Array && !dV.CanAddr() {
|
||||
panic(fmt.Sprintf("dest not addressable: %T", dest))
|
||||
}
|
||||
if sV.Kind() == reflect.Ptr {
|
||||
sV = sV.Elem()
|
||||
}
|
||||
if sV.Kind() == reflect.String {
|
||||
sV = sV.Convert(reflect.SliceOf(dV.Type().Elem()))
|
||||
}
|
||||
if !sV.IsValid() {
|
||||
panic("invalid source, probably nil")
|
||||
}
|
||||
if dV.Len() != sV.Len() {
|
||||
panic(fmt.Sprintf("dest len (%d) != src len (%d)", dV.Len(), sV.Len()))
|
||||
}
|
||||
if dV.Len() != reflect.Copy(dV, sV) {
|
||||
panic("dammit")
|
||||
}
|
||||
}
|
||||
89
Godeps/_workspace/src/github.com/anacrolix/missinggo/copy_test.go
generated
vendored
89
Godeps/_workspace/src/github.com/anacrolix/missinggo/copy_test.go
generated
vendored
@ -1,89 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCopyToArray(t *testing.T) {
|
||||
var arr [3]byte
|
||||
bb := []byte{1, 2, 3}
|
||||
CopyExact(&arr, bb)
|
||||
if !bytes.Equal(arr[:], bb) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyToSlicedArray(t *testing.T) {
|
||||
var arr [5]byte
|
||||
CopyExact(arr[:], "hello")
|
||||
if !bytes.Equal(arr[:], []byte("hello")) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyDestNotAddr(t *testing.T) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r == nil {
|
||||
t.FailNow()
|
||||
}
|
||||
t.Log(r)
|
||||
}()
|
||||
var arr [3]byte
|
||||
CopyExact(arr, "nope")
|
||||
}
|
||||
|
||||
func TestCopyLenMismatch(t *testing.T) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r == nil {
|
||||
t.FailNow()
|
||||
}
|
||||
t.Log(r)
|
||||
}()
|
||||
CopyExact(make([]byte, 2), "abc")
|
||||
}
|
||||
|
||||
func TestCopySrcString(t *testing.T) {
|
||||
dest := make([]byte, 3)
|
||||
CopyExact(dest, "lol")
|
||||
if string(dest) != "lol" {
|
||||
t.FailNow()
|
||||
}
|
||||
func() {
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r == nil {
|
||||
t.FailNow()
|
||||
}
|
||||
}()
|
||||
CopyExact(dest, "rofl")
|
||||
}()
|
||||
var arr [5]byte
|
||||
CopyExact(&arr, interface{}("hello"))
|
||||
if string(arr[:]) != "hello" {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopySrcNilInterface(t *testing.T) {
|
||||
var arr [3]byte
|
||||
defer func() {
|
||||
r := recover().(string)
|
||||
if !strings.Contains(r, "invalid source") {
|
||||
t.FailNow()
|
||||
}
|
||||
}()
|
||||
CopyExact(&arr, nil)
|
||||
}
|
||||
|
||||
func TestCopySrcPtr(t *testing.T) {
|
||||
var bigDst [1024]byte
|
||||
var bigSrc [1024]byte = [1024]byte{'h', 'i'}
|
||||
CopyExact(&bigDst, &bigSrc)
|
||||
if !bytes.Equal(bigDst[:], bigSrc[:]) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
18
Godeps/_workspace/src/github.com/anacrolix/missinggo/croak.go
generated
vendored
18
Godeps/_workspace/src/github.com/anacrolix/missinggo/croak.go
generated
vendored
@ -1,18 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func Unchomp(s string) string {
|
||||
if len(s) > 0 && s[len(s)-1] == '\n' {
|
||||
return s
|
||||
}
|
||||
return s + "\n"
|
||||
}
|
||||
|
||||
func Fatal(msg interface{}) {
|
||||
os.Stderr.WriteString(Unchomp(fmt.Sprint(msg)))
|
||||
os.Exit(1)
|
||||
}
|
||||
3
Godeps/_workspace/src/github.com/anacrolix/missinggo/doc.go
generated
vendored
3
Godeps/_workspace/src/github.com/anacrolix/missinggo/doc.go
generated
vendored
@ -1,3 +0,0 @@
|
||||
// Package missinggo contains miscellaneous helpers used in many of anacrolix'
|
||||
// projects.
|
||||
package missinggo
|
||||
35
Godeps/_workspace/src/github.com/anacrolix/missinggo/expvarIndentMap.go
generated
vendored
35
Godeps/_workspace/src/github.com/anacrolix/missinggo/expvarIndentMap.go
generated
vendored
@ -1,35 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"expvar"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type IndentMap struct {
|
||||
expvar.Map
|
||||
}
|
||||
|
||||
var _ expvar.Var = &IndentMap{}
|
||||
|
||||
func NewExpvarIndentMap(name string) *IndentMap {
|
||||
v := new(IndentMap)
|
||||
v.Init()
|
||||
expvar.Publish(name, v)
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *IndentMap) String() string {
|
||||
var b bytes.Buffer
|
||||
fmt.Fprintf(&b, "{")
|
||||
first := true
|
||||
v.Do(func(kv expvar.KeyValue) {
|
||||
if !first {
|
||||
fmt.Fprintf(&b, ",")
|
||||
}
|
||||
fmt.Fprintf(&b, "\n\t%q: %v", kv.Key, kv.Value)
|
||||
first = false
|
||||
})
|
||||
fmt.Fprintf(&b, "}")
|
||||
return b.String()
|
||||
}
|
||||
269
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/cache.go
generated
vendored
269
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/cache.go
generated
vendored
@ -1,269 +0,0 @@
|
||||
package filecache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/anacrolix/missinggo"
|
||||
)
|
||||
|
||||
type Cache struct {
|
||||
mu sync.Mutex
|
||||
capacity int64
|
||||
filled int64
|
||||
items *lruItems
|
||||
paths map[string]ItemInfo
|
||||
root string
|
||||
}
|
||||
|
||||
type CacheInfo struct {
|
||||
Capacity int64
|
||||
Filled int64
|
||||
NumItems int
|
||||
}
|
||||
|
||||
type ItemInfo struct {
|
||||
Accessed time.Time
|
||||
Size int64
|
||||
Path string
|
||||
}
|
||||
|
||||
// Calls the function for every item known to the cache. The ItemInfo should
|
||||
// not be modified.
|
||||
func (me *Cache) WalkItems(cb func(ItemInfo)) {
|
||||
me.mu.Lock()
|
||||
defer me.mu.Unlock()
|
||||
for e := me.items.Front(); e != nil; e = e.Next() {
|
||||
cb(e.Value().(ItemInfo))
|
||||
}
|
||||
}
|
||||
|
||||
func (me *Cache) Info() (ret CacheInfo) {
|
||||
me.mu.Lock()
|
||||
defer me.mu.Unlock()
|
||||
ret.Capacity = me.capacity
|
||||
ret.Filled = me.filled
|
||||
ret.NumItems = len(me.paths)
|
||||
return
|
||||
}
|
||||
|
||||
func (me *Cache) SetCapacity(capacity int64) {
|
||||
me.mu.Lock()
|
||||
defer me.mu.Unlock()
|
||||
me.capacity = capacity
|
||||
}
|
||||
|
||||
func NewCache(root string) (ret *Cache, err error) {
|
||||
if !filepath.IsAbs(root) {
|
||||
err = errors.New("root is not an absolute filepath")
|
||||
return
|
||||
}
|
||||
ret = &Cache{
|
||||
root: root,
|
||||
capacity: -1, // unlimited
|
||||
}
|
||||
ret.mu.Lock()
|
||||
go func() {
|
||||
defer ret.mu.Unlock()
|
||||
ret.rescan()
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
// An empty return path is an error.
|
||||
func sanitizePath(p string) (ret string) {
|
||||
if p == "" {
|
||||
return
|
||||
}
|
||||
ret = path.Clean("/" + p)
|
||||
if ret[0] == '/' {
|
||||
ret = ret[1:]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Leaf is a descendent of root.
|
||||
func pruneEmptyDirs(root string, leaf string) (err error) {
|
||||
rootInfo, err := os.Stat(root)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for {
|
||||
var leafInfo os.FileInfo
|
||||
leafInfo, err = os.Stat(leaf)
|
||||
if os.IsNotExist(err) {
|
||||
goto parent
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !leafInfo.IsDir() {
|
||||
return
|
||||
}
|
||||
if os.SameFile(rootInfo, leafInfo) {
|
||||
return
|
||||
}
|
||||
if os.Remove(leaf) != nil {
|
||||
return
|
||||
}
|
||||
parent:
|
||||
leaf = filepath.Dir(leaf)
|
||||
}
|
||||
}
|
||||
|
||||
func (me *Cache) Remove(path string) (err error) {
|
||||
path = sanitizePath(path)
|
||||
me.mu.Lock()
|
||||
defer me.mu.Unlock()
|
||||
err = me.remove(path)
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
ErrBadPath = errors.New("bad path")
|
||||
ErrIsDir = errors.New("is directory")
|
||||
)
|
||||
|
||||
func (me *Cache) OpenFile(path string, flag int) (ret *File, err error) {
|
||||
path = sanitizePath(path)
|
||||
if path == "" {
|
||||
err = ErrIsDir
|
||||
return
|
||||
}
|
||||
f, err := os.OpenFile(me.realpath(path), flag, 0644)
|
||||
if flag&os.O_CREATE != 0 && os.IsNotExist(err) {
|
||||
os.MkdirAll(me.root, 0755)
|
||||
os.MkdirAll(filepath.Dir(me.realpath(path)), 0755)
|
||||
f, err = os.OpenFile(me.realpath(path), flag, 0644)
|
||||
if err != nil {
|
||||
me.pruneEmptyDirs(path)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ret = &File{
|
||||
c: me,
|
||||
path: path,
|
||||
f: f,
|
||||
}
|
||||
me.mu.Lock()
|
||||
go func() {
|
||||
defer me.mu.Unlock()
|
||||
me.statItem(path, time.Now())
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
func (me *Cache) rescan() {
|
||||
me.filled = 0
|
||||
me.items = newLRUItems()
|
||||
me.paths = make(map[string]ItemInfo)
|
||||
err := filepath.Walk(me.root, func(path string, info os.FileInfo, err error) error {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
path, err = filepath.Rel(me.root, path)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
me.statItem(path, time.Time{})
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (me *Cache) insertItem(i ItemInfo) {
|
||||
me.items.Insert(i)
|
||||
}
|
||||
|
||||
func (me *Cache) removeInfo(path string) (ret ItemInfo, ok bool) {
|
||||
ret, ok = me.paths[path]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if !me.items.Remove(ret) {
|
||||
panic(ret)
|
||||
}
|
||||
me.filled -= ret.Size
|
||||
delete(me.paths, path)
|
||||
return
|
||||
}
|
||||
|
||||
// Triggers the item for path to be updated. If access is non-zero, set the
|
||||
// item's access time to that value, otherwise deduce it appropriately.
|
||||
func (me *Cache) statItem(path string, access time.Time) {
|
||||
info, ok := me.removeInfo(path)
|
||||
fi, err := os.Stat(me.realpath(path))
|
||||
if os.IsNotExist(err) {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if !ok {
|
||||
info.Path = path
|
||||
}
|
||||
if !access.IsZero() {
|
||||
info.Accessed = access
|
||||
}
|
||||
if info.Accessed.IsZero() {
|
||||
info.Accessed = missinggo.FileInfoAccessTime(fi)
|
||||
}
|
||||
info.Size = fi.Size()
|
||||
me.filled += info.Size
|
||||
me.insertItem(info)
|
||||
me.paths[path] = info
|
||||
}
|
||||
|
||||
func (me *Cache) realpath(path string) string {
|
||||
return filepath.Join(me.root, filepath.FromSlash(path))
|
||||
}
|
||||
|
||||
func (me *Cache) TrimToCapacity() {
|
||||
me.mu.Lock()
|
||||
defer me.mu.Unlock()
|
||||
me.trimToCapacity()
|
||||
}
|
||||
|
||||
func (me *Cache) pruneEmptyDirs(path string) {
|
||||
pruneEmptyDirs(me.root, me.realpath(path))
|
||||
}
|
||||
|
||||
func (me *Cache) remove(path string) (err error) {
|
||||
err = os.Remove(me.realpath(path))
|
||||
if os.IsNotExist(err) {
|
||||
err = nil
|
||||
}
|
||||
me.pruneEmptyDirs(path)
|
||||
me.removeInfo(path)
|
||||
return
|
||||
}
|
||||
|
||||
func (me *Cache) trimToCapacity() {
|
||||
if me.capacity < 0 {
|
||||
return
|
||||
}
|
||||
for me.filled > me.capacity {
|
||||
item := me.items.LRU()
|
||||
me.remove(item.Path)
|
||||
}
|
||||
}
|
||||
|
||||
func (me *Cache) pathInfo(p string) ItemInfo {
|
||||
return me.paths[p]
|
||||
}
|
||||
84
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/cache_test.go
generated
vendored
84
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/cache_test.go
generated
vendored
@ -1,84 +0,0 @@
|
||||
package filecache
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"gx/ipfs/QmZwjfAKWe7vWZ8f48u7AGA1xYfzR1iCD9A2XSCYFRBWot/testify/assert"
|
||||
"gx/ipfs/QmZwjfAKWe7vWZ8f48u7AGA1xYfzR1iCD9A2XSCYFRBWot/testify/require"
|
||||
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/anacrolix/missinggo"
|
||||
)
|
||||
|
||||
func TestCache(t *testing.T) {
|
||||
td, err := ioutil.TempDir("", "gotest")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
c, err := NewCache(filepath.Join(td, "cache"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.EqualValues(t, 0, c.Info().Filled)
|
||||
c.WalkItems(func(i ItemInfo) {})
|
||||
_, err = c.OpenFile("/", os.O_CREATE)
|
||||
assert.NotNil(t, err)
|
||||
_, err = c.OpenFile("", os.O_CREATE)
|
||||
assert.NotNil(t, err)
|
||||
c.WalkItems(func(i ItemInfo) {})
|
||||
require.Equal(t, 0, c.Info().NumItems)
|
||||
_, err = c.OpenFile("notexist", 0)
|
||||
assert.True(t, os.IsNotExist(err), err)
|
||||
_, err = c.OpenFile("/notexist", 0)
|
||||
assert.True(t, os.IsNotExist(err), err)
|
||||
_, err = c.OpenFile("/dir/notexist", 0)
|
||||
assert.True(t, os.IsNotExist(err), err)
|
||||
f, err := c.OpenFile("dir/blah", os.O_CREATE)
|
||||
require.NoError(t, err)
|
||||
defer f.Close()
|
||||
c.WalkItems(func(i ItemInfo) {})
|
||||
assert.True(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/blah"))))
|
||||
assert.True(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/"))))
|
||||
assert.Equal(t, 1, c.Info().NumItems)
|
||||
f.Remove()
|
||||
assert.False(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("dir/blah"))))
|
||||
assert.False(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("dir/"))))
|
||||
_, err = f.Read(nil)
|
||||
assert.NotEqual(t, io.EOF, err)
|
||||
a, err := c.OpenFile("/a", os.O_CREATE|os.O_WRONLY)
|
||||
defer a.Close()
|
||||
require.Nil(t, err)
|
||||
b, err := c.OpenFile("b", os.O_CREATE|os.O_WRONLY)
|
||||
defer b.Close()
|
||||
require.Nil(t, err)
|
||||
c.mu.Lock()
|
||||
assert.True(t, c.pathInfo("a").Accessed.Before(c.pathInfo("b").Accessed))
|
||||
c.mu.Unlock()
|
||||
n, err := a.Write([]byte("hello"))
|
||||
assert.Nil(t, err)
|
||||
assert.EqualValues(t, 5, n)
|
||||
assert.EqualValues(t, 5, c.Info().Filled)
|
||||
assert.True(t, c.pathInfo("b").Accessed.Before(c.pathInfo("a").Accessed))
|
||||
c.SetCapacity(5)
|
||||
n, err = a.Write([]byte(" world"))
|
||||
assert.NotNil(t, err)
|
||||
_, err = b.Write([]byte("boom!"))
|
||||
// "a" and "b" have been evicted.
|
||||
assert.NotNil(t, err)
|
||||
assert.EqualValues(t, 0, c.Info().Filled)
|
||||
assert.EqualValues(t, 0, c.Info().NumItems)
|
||||
_, err = a.Seek(0, os.SEEK_SET)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestSanitizePath(t *testing.T) {
|
||||
assert.Equal(t, "", sanitizePath("////"))
|
||||
assert.Equal(t, "", sanitizePath("/../.."))
|
||||
assert.Equal(t, "a", sanitizePath("/a//b/.."))
|
||||
assert.Equal(t, "a", sanitizePath("../a"))
|
||||
assert.Equal(t, "a", sanitizePath("./a"))
|
||||
}
|
||||
117
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/file.go
generated
vendored
117
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/file.go
generated
vendored
@ -1,117 +0,0 @@
|
||||
package filecache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
mu sync.Mutex
|
||||
c *Cache
|
||||
path string
|
||||
f *os.File
|
||||
gone bool
|
||||
}
|
||||
|
||||
func (me *File) Remove() (err error) {
|
||||
return me.c.Remove(me.path)
|
||||
}
|
||||
|
||||
func (me *File) Seek(offset int64, whence int) (ret int64, err error) {
|
||||
ret, err = me.f.Seek(offset, whence)
|
||||
return
|
||||
}
|
||||
|
||||
func (me *File) maxWrite() (max int64, err error) {
|
||||
if me.c.capacity < 0 {
|
||||
max = math.MaxInt64
|
||||
return
|
||||
}
|
||||
pos, err := me.Seek(0, os.SEEK_CUR)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
max = me.c.capacity - pos
|
||||
if max < 0 {
|
||||
max = 0
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
ErrFileTooLarge = errors.New("file too large for cache")
|
||||
ErrFileDisappeared = errors.New("file disappeared")
|
||||
)
|
||||
|
||||
func (me *File) checkGone() {
|
||||
if me.gone {
|
||||
return
|
||||
}
|
||||
ffi, _ := me.Stat()
|
||||
fsfi, _ := os.Stat(me.c.realpath(me.path))
|
||||
me.gone = !os.SameFile(ffi, fsfi)
|
||||
}
|
||||
|
||||
func (me *File) goneErr() error {
|
||||
me.mu.Lock()
|
||||
defer me.mu.Unlock()
|
||||
me.checkGone()
|
||||
if me.gone {
|
||||
me.f.Close()
|
||||
return ErrFileDisappeared
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (me *File) Write(b []byte) (n int, err error) {
|
||||
err = me.goneErr()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n, err = me.f.Write(b)
|
||||
me.c.mu.Lock()
|
||||
me.c.statItem(me.path, time.Now())
|
||||
me.c.trimToCapacity()
|
||||
me.c.mu.Unlock()
|
||||
if err == nil {
|
||||
err = me.goneErr()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (me *File) Close() error {
|
||||
return me.f.Close()
|
||||
}
|
||||
|
||||
func (me *File) Stat() (os.FileInfo, error) {
|
||||
return me.f.Stat()
|
||||
}
|
||||
|
||||
func (me *File) Read(b []byte) (n int, err error) {
|
||||
err = me.goneErr()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
me.c.mu.Lock()
|
||||
defer me.c.mu.Unlock()
|
||||
me.c.statItem(me.path, time.Now())
|
||||
}()
|
||||
return me.f.Read(b)
|
||||
}
|
||||
|
||||
func (me *File) ReadAt(b []byte, off int64) (n int, err error) {
|
||||
err = me.goneErr()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
me.c.mu.Lock()
|
||||
defer me.c.mu.Unlock()
|
||||
me.c.statItem(me.path, time.Now())
|
||||
}()
|
||||
return me.f.ReadAt(b, off)
|
||||
}
|
||||
94
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/lruitems.go
generated
vendored
94
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/lruitems.go
generated
vendored
@ -1,94 +0,0 @@
|
||||
package filecache
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"io"
|
||||
|
||||
"gx/ipfs/QmVgtwPh2NNoZTSyYkr4Y3epaYACBKf26r8hV6EFA7xS6c/b"
|
||||
)
|
||||
|
||||
type Iterator interface {
|
||||
Next() Iterator
|
||||
Value() interface{}
|
||||
}
|
||||
|
||||
type listElementIterator struct {
|
||||
le *list.Element
|
||||
}
|
||||
|
||||
func (me listElementIterator) Next() Iterator {
|
||||
e := me.le.Next()
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return listElementIterator{e}
|
||||
}
|
||||
|
||||
func (me listElementIterator) Value() interface{} {
|
||||
return me.le.Value
|
||||
}
|
||||
|
||||
func newLRUItems() *lruItems {
|
||||
return &lruItems{b.TreeNew(func(_a, _b interface{}) int {
|
||||
a := _a.(ItemInfo)
|
||||
b := _b.(ItemInfo)
|
||||
if a.Accessed != b.Accessed {
|
||||
if a.Accessed.Before(b.Accessed) {
|
||||
return -1
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
if a.Path == b.Path {
|
||||
return 0
|
||||
}
|
||||
if a.Path < b.Path {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
})}
|
||||
}
|
||||
|
||||
type lruItems struct {
|
||||
tree *b.Tree
|
||||
}
|
||||
|
||||
type bEnumeratorIterator struct {
|
||||
e *b.Enumerator
|
||||
v ItemInfo
|
||||
}
|
||||
|
||||
func (me bEnumeratorIterator) Next() Iterator {
|
||||
_, v, err := me.e.Next()
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return bEnumeratorIterator{me.e, v.(ItemInfo)}
|
||||
}
|
||||
|
||||
func (me bEnumeratorIterator) Value() interface{} {
|
||||
return me.v
|
||||
}
|
||||
|
||||
func (me *lruItems) Front() Iterator {
|
||||
e, _ := me.tree.SeekFirst()
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return bEnumeratorIterator{
|
||||
e: e,
|
||||
}.Next()
|
||||
}
|
||||
|
||||
func (me *lruItems) LRU() ItemInfo {
|
||||
_, v := me.tree.First()
|
||||
return v.(ItemInfo)
|
||||
}
|
||||
|
||||
func (me *lruItems) Insert(ii ItemInfo) {
|
||||
me.tree.Set(ii, ii)
|
||||
}
|
||||
|
||||
func (me *lruItems) Remove(ii ItemInfo) bool {
|
||||
return me.tree.Delete(ii)
|
||||
}
|
||||
22
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/lruitems_test.go
generated
vendored
22
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/lruitems_test.go
generated
vendored
@ -1,22 +0,0 @@
|
||||
package filecache
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/bradfitz/iter"
|
||||
)
|
||||
|
||||
func BenchmarkInsert(b *testing.B) {
|
||||
for range iter.N(b.N) {
|
||||
li := newLRUItems()
|
||||
for range iter.N(10000) {
|
||||
r := rand.Int63()
|
||||
t := time.Unix(r/1e9, r%1e9)
|
||||
li.Insert(ItemInfo{
|
||||
Accessed: t,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
38
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpcontentrange.go
generated
vendored
38
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpcontentrange.go
generated
vendored
@ -1,38 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type HTTPBytesContentRange struct {
|
||||
First, Last, Length int64
|
||||
}
|
||||
|
||||
var bytesContentRangeRegexp = regexp.MustCompile(`bytes[ =](\d+)-(\d+)/(\d+|\*)`)
|
||||
|
||||
func ParseHTTPBytesContentRange(s string) (ret HTTPBytesContentRange, ok bool) {
|
||||
ss := bytesContentRangeRegexp.FindStringSubmatch(s)
|
||||
if ss == nil {
|
||||
return
|
||||
}
|
||||
var err error
|
||||
ret.First, err = strconv.ParseInt(ss[1], 10, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ret.Last, err = strconv.ParseInt(ss[2], 10, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if ss[3] == "*" {
|
||||
ret.Length = -1
|
||||
} else {
|
||||
ret.Length, err = strconv.ParseInt(ss[3], 10, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
27
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpcontentrange_test.go
generated
vendored
27
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpcontentrange_test.go
generated
vendored
@ -1,27 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gx/ipfs/QmZwjfAKWe7vWZ8f48u7AGA1xYfzR1iCD9A2XSCYFRBWot/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseHTTPContentRange(t *testing.T) {
|
||||
for _, _case := range []struct {
|
||||
h string
|
||||
cr *HTTPBytesContentRange
|
||||
}{
|
||||
{"", nil},
|
||||
{"1-2/*", nil},
|
||||
{"bytes=1-2/3", &HTTPBytesContentRange{1, 2, 3}},
|
||||
{"bytes=12-34/*", &HTTPBytesContentRange{12, 34, -1}},
|
||||
{" bytes=12-34/*", &HTTPBytesContentRange{12, 34, -1}},
|
||||
{" bytes 12-34/56", &HTTPBytesContentRange{12, 34, 56}},
|
||||
} {
|
||||
ret, ok := ParseHTTPBytesContentRange(_case.h)
|
||||
assert.Equal(t, _case.cr != nil, ok)
|
||||
if _case.cr != nil {
|
||||
assert.Equal(t, *_case.cr, ret)
|
||||
}
|
||||
}
|
||||
}
|
||||
222
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpfile/httpfile.go
generated
vendored
222
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpfile/httpfile.go
generated
vendored
@ -1,222 +0,0 @@
|
||||
package httpfile
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/anacrolix/missinggo"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
off int64
|
||||
r io.ReadCloser
|
||||
rOff int64
|
||||
length int64
|
||||
url string
|
||||
}
|
||||
|
||||
func OpenSectionReader(url string, off, n int64) (ret io.ReadCloser, err error) {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", off, off+n-1))
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
err = ErrNotFound
|
||||
resp.Body.Close()
|
||||
return
|
||||
}
|
||||
if resp.StatusCode != http.StatusPartialContent {
|
||||
err = fmt.Errorf("bad response status: %s", resp.Status)
|
||||
resp.Body.Close()
|
||||
return
|
||||
}
|
||||
ret = resp.Body
|
||||
return
|
||||
}
|
||||
|
||||
func Open(url string) *File {
|
||||
return &File{
|
||||
url: url,
|
||||
}
|
||||
}
|
||||
|
||||
func (me *File) prepareReader() (err error) {
|
||||
if me.r != nil && me.off != me.rOff {
|
||||
me.r.Close()
|
||||
me.r = nil
|
||||
}
|
||||
if me.r != nil {
|
||||
return nil
|
||||
}
|
||||
req, err := http.NewRequest("GET", me.url, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if me.off != 0 {
|
||||
req.Header.Set("Range", fmt.Sprintf("bytes=%d-", me.off))
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch resp.StatusCode {
|
||||
case http.StatusPartialContent:
|
||||
cr, ok := missinggo.ParseHTTPBytesContentRange(resp.Header.Get("Content-Range"))
|
||||
if !ok || cr.First != me.off {
|
||||
err = errors.New("bad response")
|
||||
resp.Body.Close()
|
||||
return
|
||||
}
|
||||
me.length = cr.Length
|
||||
case http.StatusOK:
|
||||
if me.off != 0 {
|
||||
err = errors.New("bad response")
|
||||
resp.Body.Close()
|
||||
return
|
||||
}
|
||||
if h := resp.Header.Get("Content-Length"); h != "" {
|
||||
var cl uint64
|
||||
cl, err = strconv.ParseUint(h, 10, 64)
|
||||
if err != nil {
|
||||
resp.Body.Close()
|
||||
return
|
||||
}
|
||||
me.length = int64(cl)
|
||||
}
|
||||
default:
|
||||
err = errors.New(resp.Status)
|
||||
resp.Body.Close()
|
||||
return
|
||||
}
|
||||
me.r = resp.Body
|
||||
me.rOff = me.off
|
||||
return
|
||||
}
|
||||
|
||||
func (me *File) Read(b []byte) (n int, err error) {
|
||||
err = me.prepareReader()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n, err = me.r.Read(b)
|
||||
me.off += int64(n)
|
||||
me.rOff += int64(n)
|
||||
return
|
||||
}
|
||||
|
||||
func instanceLength(r *http.Response) (int64, error) {
|
||||
switch r.StatusCode {
|
||||
case http.StatusOK:
|
||||
if h := r.Header.Get("Content-Length"); h != "" {
|
||||
return strconv.ParseInt(h, 10, 64)
|
||||
} else {
|
||||
return -1, nil
|
||||
}
|
||||
case http.StatusPartialContent:
|
||||
cr, ok := missinggo.ParseHTTPBytesContentRange(r.Header.Get("Content-Range"))
|
||||
if !ok {
|
||||
return -1, errors.New("bad 206 response")
|
||||
}
|
||||
return cr.Length, nil
|
||||
default:
|
||||
return -1, errors.New(r.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func (me *File) Seek(offset int64, whence int) (ret int64, err error) {
|
||||
switch whence {
|
||||
case os.SEEK_SET:
|
||||
ret = offset
|
||||
case os.SEEK_CUR:
|
||||
ret = me.off + offset
|
||||
case os.SEEK_END:
|
||||
if me.length < 0 {
|
||||
err = errors.New("length unknown")
|
||||
return
|
||||
}
|
||||
ret = me.length + offset
|
||||
default:
|
||||
err = fmt.Errorf("unhandled whence: %d", whence)
|
||||
return
|
||||
}
|
||||
me.off = ret
|
||||
return
|
||||
}
|
||||
|
||||
func (me *File) Write(b []byte) (n int, err error) {
|
||||
req, err := http.NewRequest("PATCH", me.url, bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req.Header.Set("Content-Range", fmt.Sprintf("bytes=%d-", me.off))
|
||||
req.ContentLength = int64(len(b))
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusPartialContent {
|
||||
err = errors.New(resp.Status)
|
||||
return
|
||||
}
|
||||
n = len(b)
|
||||
me.off += int64(n)
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
ErrNotFound = errors.New("not found")
|
||||
)
|
||||
|
||||
// Returns the length of the resource in bytes.
|
||||
func GetLength(url string) (ret int64, err error) {
|
||||
resp, err := http.Head(url)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
err = ErrNotFound
|
||||
return
|
||||
}
|
||||
return instanceLength(resp)
|
||||
}
|
||||
|
||||
func (me *File) Close() error {
|
||||
me.url = ""
|
||||
if me.r != nil {
|
||||
me.r.Close()
|
||||
me.r = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Delete(urlStr string) (err error) {
|
||||
req, err := http.NewRequest("DELETE", urlStr, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
err = ErrNotFound
|
||||
return
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
err = fmt.Errorf("response: %s", resp.Status)
|
||||
}
|
||||
return
|
||||
}
|
||||
47
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpgzip.go
generated
vendored
47
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpgzip.go
generated
vendored
@ -1,47 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type gzipResponseWriter struct {
|
||||
io.Writer
|
||||
http.ResponseWriter
|
||||
haveWritten bool
|
||||
}
|
||||
|
||||
func (w *gzipResponseWriter) Write(b []byte) (int, error) {
|
||||
if w.haveWritten {
|
||||
goto write
|
||||
}
|
||||
w.haveWritten = true
|
||||
if w.Header().Get("Content-Type") != "" {
|
||||
goto write
|
||||
}
|
||||
if type_ := http.DetectContentType(b); type_ != "application/octet-stream" {
|
||||
w.Header().Set("Content-Type", type_)
|
||||
}
|
||||
write:
|
||||
return w.Writer.Write(b)
|
||||
}
|
||||
|
||||
// Gzips response body if the request says it'll allow it.
|
||||
func GzipHTTPHandler(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") || w.Header().Get("Content-Encoding") != "" || w.Header().Get("Vary") != "" {
|
||||
h.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Encoding", "gzip")
|
||||
w.Header().Set("Vary", "Accept-Encoding")
|
||||
gz := gzip.NewWriter(w)
|
||||
defer gz.Close()
|
||||
h.ServeHTTP(&gzipResponseWriter{
|
||||
Writer: gz,
|
||||
ResponseWriter: w,
|
||||
}, r)
|
||||
})
|
||||
}
|
||||
60
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpresponsestatus.go
generated
vendored
60
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpresponsestatus.go
generated
vendored
@ -1,60 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// A http.ResponseWriter that tracks the status of the response. The status
|
||||
// code, and number of bytes written for example.
|
||||
type StatusResponseWriter struct {
|
||||
RW http.ResponseWriter
|
||||
Code int
|
||||
BytesWritten int64
|
||||
}
|
||||
|
||||
var _ http.ResponseWriter = &StatusResponseWriter{}
|
||||
|
||||
func (me *StatusResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
return me.RW.(http.Hijacker).Hijack()
|
||||
}
|
||||
|
||||
func (me *StatusResponseWriter) CloseNotify() <-chan bool {
|
||||
return me.RW.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
func (me *StatusResponseWriter) Flush() {
|
||||
me.RW.(http.Flusher).Flush()
|
||||
}
|
||||
|
||||
func (me *StatusResponseWriter) Header() http.Header {
|
||||
return me.RW.Header()
|
||||
}
|
||||
|
||||
func (me *StatusResponseWriter) Write(b []byte) (n int, err error) {
|
||||
if me.Code == 0 {
|
||||
me.Code = 200
|
||||
}
|
||||
n, err = me.RW.Write(b)
|
||||
me.BytesWritten += int64(n)
|
||||
return
|
||||
}
|
||||
|
||||
func (me *StatusResponseWriter) WriteHeader(code int) {
|
||||
me.RW.WriteHeader(code)
|
||||
me.Code = code
|
||||
}
|
||||
|
||||
type ReaderFromStatusResponseWriter struct {
|
||||
StatusResponseWriter
|
||||
io.ReaderFrom
|
||||
}
|
||||
|
||||
func NewReaderFromStatusResponseWriter(w http.ResponseWriter) *ReaderFromStatusResponseWriter {
|
||||
return &ReaderFromStatusResponseWriter{
|
||||
StatusResponseWriter{RW: w},
|
||||
w.(io.ReaderFrom),
|
||||
}
|
||||
}
|
||||
81
Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/groupby.go
generated
vendored
81
Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/groupby.go
generated
vendored
@ -1,81 +0,0 @@
|
||||
package itertools
|
||||
|
||||
type groupBy struct {
|
||||
curKey interface{}
|
||||
curKeyOk bool
|
||||
curValue interface{}
|
||||
keyFunc func(interface{}) interface{}
|
||||
input Iterator
|
||||
groupKey interface{}
|
||||
groupKeyOk bool
|
||||
}
|
||||
|
||||
type Group interface {
|
||||
Iterator
|
||||
Key() interface{}
|
||||
}
|
||||
|
||||
type group struct {
|
||||
gb *groupBy
|
||||
key interface{}
|
||||
first bool
|
||||
}
|
||||
|
||||
func (me *group) Next() (ok bool) {
|
||||
if me.first {
|
||||
me.first = false
|
||||
return true
|
||||
}
|
||||
me.gb.advance()
|
||||
if !me.gb.curKeyOk || me.gb.curKey != me.key {
|
||||
return
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
func (me group) Value() (ret interface{}) {
|
||||
ret = me.gb.curValue
|
||||
return
|
||||
}
|
||||
|
||||
func (me group) Key() interface{} {
|
||||
return me.key
|
||||
}
|
||||
|
||||
func (me *groupBy) advance() {
|
||||
me.curKeyOk = me.input.Next()
|
||||
if me.curKeyOk {
|
||||
me.curValue = me.input.Value()
|
||||
me.curKey = me.keyFunc(me.curValue)
|
||||
}
|
||||
}
|
||||
|
||||
func (me *groupBy) Next() (ok bool) {
|
||||
for me.curKey == me.groupKey {
|
||||
ok = me.input.Next()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
me.curValue = me.input.Value()
|
||||
me.curKey = me.keyFunc(me.curValue)
|
||||
me.curKeyOk = true
|
||||
}
|
||||
me.groupKey = me.curKey
|
||||
me.groupKeyOk = true
|
||||
return true
|
||||
}
|
||||
|
||||
func (me *groupBy) Value() (ret interface{}) {
|
||||
return &group{me, me.groupKey, true}
|
||||
}
|
||||
|
||||
func GroupBy(input Iterator, keyFunc func(interface{}) interface{}) Iterator {
|
||||
if keyFunc == nil {
|
||||
keyFunc = func(a interface{}) interface{} { return a }
|
||||
}
|
||||
return &groupBy{
|
||||
input: input,
|
||||
keyFunc: keyFunc,
|
||||
}
|
||||
}
|
||||
31
Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/groupby_test.go
generated
vendored
31
Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/groupby_test.go
generated
vendored
@ -1,31 +0,0 @@
|
||||
package itertools
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gx/ipfs/QmZwjfAKWe7vWZ8f48u7AGA1xYfzR1iCD9A2XSCYFRBWot/testify/require"
|
||||
)
|
||||
|
||||
func TestGroupByKey(t *testing.T) {
|
||||
var ks []byte
|
||||
gb := GroupBy(StringIterator("AAAABBBCCDAABBB"), nil)
|
||||
for gb.Next() {
|
||||
ks = append(ks, gb.Value().(Group).Key().(byte))
|
||||
}
|
||||
t.Log(ks)
|
||||
require.EqualValues(t, "ABCDAB", ks)
|
||||
}
|
||||
|
||||
func TestGroupByList(t *testing.T) {
|
||||
var gs []string
|
||||
gb := GroupBy(StringIterator("AAAABBBCCD"), nil)
|
||||
for gb.Next() {
|
||||
i := gb.Value().(Iterator)
|
||||
var g string
|
||||
for i.Next() {
|
||||
g += string(i.Value().(byte))
|
||||
}
|
||||
gs = append(gs, g)
|
||||
}
|
||||
t.Log(gs)
|
||||
}
|
||||
41
Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/iterator.go
generated
vendored
41
Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/iterator.go
generated
vendored
@ -1,41 +0,0 @@
|
||||
package itertools
|
||||
|
||||
import "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/anacrolix/missinggo"
|
||||
|
||||
type Iterator interface {
|
||||
Next() bool
|
||||
Value() interface{}
|
||||
}
|
||||
|
||||
type sliceIterator struct {
|
||||
slice []interface{}
|
||||
value interface{}
|
||||
ok bool
|
||||
}
|
||||
|
||||
func (me *sliceIterator) Next() bool {
|
||||
if len(me.slice) == 0 {
|
||||
return false
|
||||
}
|
||||
me.value = me.slice[0]
|
||||
me.slice = me.slice[1:]
|
||||
me.ok = true
|
||||
return true
|
||||
}
|
||||
|
||||
func (me *sliceIterator) Value() interface{} {
|
||||
if !me.ok {
|
||||
panic("no value; call Next")
|
||||
}
|
||||
return me.value
|
||||
}
|
||||
|
||||
func SliceIterator(a []interface{}) Iterator {
|
||||
return &sliceIterator{
|
||||
slice: a,
|
||||
}
|
||||
}
|
||||
|
||||
func StringIterator(a string) Iterator {
|
||||
return SliceIterator(missinggo.ConvertToSliceOfEmptyInterface(a))
|
||||
}
|
||||
17
Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/iterator_test.go
generated
vendored
17
Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/iterator_test.go
generated
vendored
@ -1,17 +0,0 @@
|
||||
package itertools
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gx/ipfs/QmZwjfAKWe7vWZ8f48u7AGA1xYfzR1iCD9A2XSCYFRBWot/testify/require"
|
||||
)
|
||||
|
||||
func TestIterator(t *testing.T) {
|
||||
const s = "AAAABBBCCDAABBB"
|
||||
si := StringIterator(s)
|
||||
for i := range s {
|
||||
require.True(t, si.Next())
|
||||
require.Equal(t, s[i], si.Value().(byte))
|
||||
}
|
||||
require.False(t, si.Next())
|
||||
}
|
||||
26
Godeps/_workspace/src/github.com/anacrolix/missinggo/net.go
generated
vendored
26
Godeps/_workspace/src/github.com/anacrolix/missinggo/net.go
generated
vendored
@ -1,26 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
type HostPort struct {
|
||||
Host string // Just the host, with no port.
|
||||
Port string // May be empty if no port was given.
|
||||
Err error // The error returned from net.SplitHostPort.
|
||||
}
|
||||
|
||||
// Parse a "hostport" string, a concept that floats around the stdlib a lot
|
||||
// and is painful to work with. If no port is present, what's usually present
|
||||
// is just the host.
|
||||
func ParseHostPort(hostPort string) (ret HostPort) {
|
||||
ret.Host, ret.Port, ret.Err = net.SplitHostPort(hostPort)
|
||||
if ret.Err != nil {
|
||||
ret.Host = hostPort
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (me *HostPort) Join() string {
|
||||
return net.JoinHostPort(me.Host, me.Port)
|
||||
}
|
||||
20
Godeps/_workspace/src/github.com/anacrolix/missinggo/path.go
generated
vendored
20
Godeps/_workspace/src/github.com/anacrolix/missinggo/path.go
generated
vendored
@ -1,20 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
// Splits the pathname p into Root and Ext, such that Root+Ext==p.
|
||||
func PathSplitExt(p string) (ret struct {
|
||||
Root, Ext string
|
||||
}) {
|
||||
ret.Ext = path.Ext(p)
|
||||
ret.Root = p[:len(p)-len(ret.Ext)]
|
||||
return
|
||||
}
|
||||
|
||||
func FilePathExists(p string) bool {
|
||||
_, err := os.Stat(p)
|
||||
return err == nil
|
||||
}
|
||||
17
Godeps/_workspace/src/github.com/anacrolix/missinggo/path_test.go
generated
vendored
17
Godeps/_workspace/src/github.com/anacrolix/missinggo/path_test.go
generated
vendored
@ -1,17 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func ExamplePathSplitExt() {
|
||||
fmt.Printf("%q\n", PathSplitExt(".cshrc"))
|
||||
fmt.Printf("%q\n", PathSplitExt("dir/a.ext"))
|
||||
fmt.Printf("%q\n", PathSplitExt("dir/.rc"))
|
||||
fmt.Printf("%q\n", PathSplitExt("home/.secret/file"))
|
||||
// Output:
|
||||
// {"" ".cshrc"}
|
||||
// {"dir/a" ".ext"}
|
||||
// {"dir/" ".rc"}
|
||||
// {"home/.secret/file" ""}
|
||||
}
|
||||
48
Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/mutex.go
generated
vendored
48
Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/mutex.go
generated
vendored
@ -1,48 +0,0 @@
|
||||
package perf
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/anacrolix/missinggo"
|
||||
)
|
||||
|
||||
type TimedLocker struct {
|
||||
L sync.Locker
|
||||
Desc string
|
||||
}
|
||||
|
||||
func (me *TimedLocker) Lock() {
|
||||
tr := NewTimer()
|
||||
me.L.Lock()
|
||||
tr.Stop(me.Desc)
|
||||
}
|
||||
|
||||
func (me *TimedLocker) Unlock() {
|
||||
me.L.Unlock()
|
||||
}
|
||||
|
||||
type TimedRWLocker struct {
|
||||
RWL missinggo.RWLocker
|
||||
WriteDesc string
|
||||
ReadDesc string
|
||||
}
|
||||
|
||||
func (me *TimedRWLocker) Lock() {
|
||||
tr := NewTimer()
|
||||
me.RWL.Lock()
|
||||
tr.Stop(me.WriteDesc)
|
||||
}
|
||||
|
||||
func (me *TimedRWLocker) Unlock() {
|
||||
me.RWL.Unlock()
|
||||
}
|
||||
|
||||
func (me *TimedRWLocker) RLock() {
|
||||
tr := NewTimer()
|
||||
me.RWL.RLock()
|
||||
tr.Stop(me.ReadDesc)
|
||||
}
|
||||
|
||||
func (me *TimedRWLocker) RUnlock() {
|
||||
me.RWL.RUnlock()
|
||||
}
|
||||
92
Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/perf.go
generated
vendored
92
Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/perf.go
generated
vendored
@ -1,92 +0,0 @@
|
||||
package perf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"expvar"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/anacrolix/missinggo"
|
||||
)
|
||||
|
||||
var (
|
||||
em = missinggo.NewExpvarIndentMap("perfBuckets")
|
||||
mu sync.RWMutex
|
||||
)
|
||||
|
||||
type Timer struct {
|
||||
started time.Time
|
||||
}
|
||||
|
||||
func NewTimer() Timer {
|
||||
return Timer{time.Now()}
|
||||
}
|
||||
|
||||
func bucketExponent(d time.Duration) int {
|
||||
e := -9
|
||||
for d != 0 {
|
||||
d /= 10
|
||||
e++
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
type buckets struct {
|
||||
mu sync.Mutex
|
||||
buckets []int64
|
||||
}
|
||||
|
||||
func (me *buckets) Add(t time.Duration) {
|
||||
e := bucketExponent(t)
|
||||
me.mu.Lock()
|
||||
for e+9 >= len(me.buckets) {
|
||||
me.buckets = append(me.buckets, 0)
|
||||
}
|
||||
me.buckets[e+9]++
|
||||
me.mu.Unlock()
|
||||
}
|
||||
|
||||
func (me *buckets) String() string {
|
||||
var b bytes.Buffer
|
||||
fmt.Fprintf(&b, "{")
|
||||
first := true
|
||||
me.mu.Lock()
|
||||
for i, count := range me.buckets {
|
||||
if first {
|
||||
if count == 0 {
|
||||
continue
|
||||
}
|
||||
first = false
|
||||
} else {
|
||||
fmt.Fprintf(&b, ", ")
|
||||
}
|
||||
key := strconv.Itoa(i - 9)
|
||||
fmt.Fprintf(&b, "%q: %d", key, count)
|
||||
}
|
||||
me.mu.Unlock()
|
||||
fmt.Fprintf(&b, "}")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
var _ expvar.Var = &buckets{}
|
||||
|
||||
func (t *Timer) Stop(desc string) time.Duration {
|
||||
d := time.Since(t.started)
|
||||
mu.RLock()
|
||||
_m := em.Get(desc)
|
||||
mu.RUnlock()
|
||||
if _m == nil {
|
||||
mu.Lock()
|
||||
_m = em.Get(desc)
|
||||
if _m == nil {
|
||||
_m = new(buckets)
|
||||
em.Set(desc, _m)
|
||||
}
|
||||
mu.Unlock()
|
||||
}
|
||||
m := _m.(*buckets)
|
||||
m.Add(d)
|
||||
return d
|
||||
}
|
||||
52
Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/perf_test.go
generated
vendored
52
Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/perf_test.go
generated
vendored
@ -1,52 +0,0 @@
|
||||
package perf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/bradfitz/iter"
|
||||
"gx/ipfs/QmZwjfAKWe7vWZ8f48u7AGA1xYfzR1iCD9A2XSCYFRBWot/testify/assert"
|
||||
)
|
||||
|
||||
func TestTimer(t *testing.T) {
|
||||
tr := NewTimer()
|
||||
tr.Stop("hiyo")
|
||||
tr.Stop("hiyo")
|
||||
t.Log(em.Get("hiyo").(*buckets))
|
||||
}
|
||||
|
||||
func BenchmarkStopWarm(b *testing.B) {
|
||||
tr := NewTimer()
|
||||
for range iter.N(b.N) {
|
||||
tr.Stop("a")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStopCold(b *testing.B) {
|
||||
tr := NewTimer()
|
||||
for i := range iter.N(b.N) {
|
||||
tr.Stop(strconv.FormatInt(int64(i), 10))
|
||||
}
|
||||
}
|
||||
|
||||
func TestExponent(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
e int
|
||||
d time.Duration
|
||||
}{
|
||||
{-1, 10 * time.Millisecond},
|
||||
{-2, 5 * time.Millisecond},
|
||||
{-2, time.Millisecond},
|
||||
{-3, 500 * time.Microsecond},
|
||||
{-3, 100 * time.Microsecond},
|
||||
} {
|
||||
tr := NewTimer()
|
||||
time.Sleep(c.d)
|
||||
assert.Equal(t, c.e, bucketExponent(tr.Stop(fmt.Sprintf("%d", c.e))), "%s", c.d)
|
||||
}
|
||||
assert.Equal(t, `{"-1": 1}`, em.Get("-1").String())
|
||||
assert.Equal(t, `{"-2": 2}`, em.Get("-2").String())
|
||||
assert.Equal(t, `{"-3": 2}`, em.Get("-3").String())
|
||||
}
|
||||
92
Godeps/_workspace/src/github.com/anacrolix/missinggo/pubsub/pubsub.go
generated
vendored
92
Godeps/_workspace/src/github.com/anacrolix/missinggo/pubsub/pubsub.go
generated
vendored
@ -1,92 +0,0 @@
|
||||
package pubsub
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type PubSub struct {
|
||||
mu sync.Mutex
|
||||
next chan item
|
||||
closed bool
|
||||
}
|
||||
|
||||
type item struct {
|
||||
value interface{}
|
||||
next chan item
|
||||
}
|
||||
|
||||
type Subscription struct {
|
||||
next chan item
|
||||
Values chan interface{}
|
||||
mu sync.Mutex
|
||||
closed chan struct{}
|
||||
}
|
||||
|
||||
func NewPubSub() (ret *PubSub) {
|
||||
return &PubSub{
|
||||
next: make(chan item, 1),
|
||||
}
|
||||
}
|
||||
|
||||
func (me *PubSub) Publish(v interface{}) {
|
||||
next := make(chan item, 1)
|
||||
i := item{v, next}
|
||||
me.mu.Lock()
|
||||
me.next <- i
|
||||
me.next = next
|
||||
me.mu.Unlock()
|
||||
}
|
||||
|
||||
func (me *Subscription) Close() {
|
||||
me.mu.Lock()
|
||||
defer me.mu.Unlock()
|
||||
select {
|
||||
case <-me.closed:
|
||||
default:
|
||||
close(me.closed)
|
||||
}
|
||||
}
|
||||
|
||||
func (me *Subscription) runner() {
|
||||
defer close(me.Values)
|
||||
for {
|
||||
select {
|
||||
case i, ok := <-me.next:
|
||||
if !ok {
|
||||
me.Close()
|
||||
return
|
||||
}
|
||||
me.next <- i
|
||||
me.next = i.next
|
||||
select {
|
||||
case me.Values <- i.value:
|
||||
case <-me.closed:
|
||||
return
|
||||
}
|
||||
case <-me.closed:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (me *PubSub) Subscribe() (ret *Subscription) {
|
||||
ret = &Subscription{
|
||||
closed: make(chan struct{}),
|
||||
Values: make(chan interface{}),
|
||||
}
|
||||
me.mu.Lock()
|
||||
ret.next = me.next
|
||||
me.mu.Unlock()
|
||||
go ret.runner()
|
||||
return
|
||||
}
|
||||
|
||||
func (me *PubSub) Close() {
|
||||
me.mu.Lock()
|
||||
defer me.mu.Unlock()
|
||||
if me.closed {
|
||||
return
|
||||
}
|
||||
close(me.next)
|
||||
me.closed = true
|
||||
}
|
||||
74
Godeps/_workspace/src/github.com/anacrolix/missinggo/pubsub/pubsub_test.go
generated
vendored
74
Godeps/_workspace/src/github.com/anacrolix/missinggo/pubsub/pubsub_test.go
generated
vendored
@ -1,74 +0,0 @@
|
||||
package pubsub
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/bradfitz/iter"
|
||||
"gx/ipfs/QmZwjfAKWe7vWZ8f48u7AGA1xYfzR1iCD9A2XSCYFRBWot/testify/assert"
|
||||
"gx/ipfs/QmZwjfAKWe7vWZ8f48u7AGA1xYfzR1iCD9A2XSCYFRBWot/testify/require"
|
||||
)
|
||||
|
||||
func TestDoubleClose(t *testing.T) {
|
||||
ps := NewPubSub()
|
||||
ps.Close()
|
||||
ps.Close()
|
||||
}
|
||||
|
||||
func testBroadcast(t testing.TB, subs, vals int) {
|
||||
ps := NewPubSub()
|
||||
var wg sync.WaitGroup
|
||||
for range iter.N(subs) {
|
||||
wg.Add(1)
|
||||
s := ps.Subscribe()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
var e int
|
||||
for i := range s.Values {
|
||||
assert.Equal(t, e, i.(int))
|
||||
e++
|
||||
}
|
||||
assert.Equal(t, vals, e)
|
||||
}()
|
||||
}
|
||||
for i := range iter.N(vals) {
|
||||
ps.Publish(i)
|
||||
}
|
||||
ps.Close()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestBroadcast(t *testing.T) {
|
||||
testBroadcast(t, 100, 10)
|
||||
}
|
||||
|
||||
func BenchmarkBroadcast(b *testing.B) {
|
||||
for range iter.N(b.N) {
|
||||
testBroadcast(b, 10, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloseSubscription(t *testing.T) {
|
||||
ps := NewPubSub()
|
||||
ps.Publish(1)
|
||||
s := ps.Subscribe()
|
||||
select {
|
||||
case <-s.Values:
|
||||
t.FailNow()
|
||||
default:
|
||||
}
|
||||
ps.Publish(2)
|
||||
s2 := ps.Subscribe()
|
||||
ps.Publish(3)
|
||||
require.Equal(t, 2, <-s.Values)
|
||||
require.EqualValues(t, 3, <-s.Values)
|
||||
s.Close()
|
||||
_, ok := <-s.Values
|
||||
require.False(t, ok)
|
||||
ps.Publish(4)
|
||||
ps.Close()
|
||||
require.Equal(t, 3, <-s2.Values)
|
||||
require.Equal(t, 4, <-s2.Values)
|
||||
require.Nil(t, <-s2.Values)
|
||||
s2.Close()
|
||||
}
|
||||
46
Godeps/_workspace/src/github.com/anacrolix/missinggo/rle.go
generated
vendored
46
Godeps/_workspace/src/github.com/anacrolix/missinggo/rle.go
generated
vendored
@ -1,46 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
// A RunLengthEncoder counts successive duplicate elements and emits the
|
||||
// element and the run length when the element changes or the encoder is
|
||||
// flushed.
|
||||
type RunLengthEncoder interface {
|
||||
// Add a series of identical elements to the stream.
|
||||
Append(element interface{}, count uint64)
|
||||
// Emit the current element and its count if non-zero without waiting for
|
||||
// the element to change.
|
||||
Flush()
|
||||
}
|
||||
|
||||
type runLengthEncoder struct {
|
||||
eachRun func(element interface{}, count uint64)
|
||||
element interface{}
|
||||
count uint64
|
||||
}
|
||||
|
||||
// Creates a new RunLengthEncoder. eachRun is called when an element and its
|
||||
// count is emitted, per the RunLengthEncoder interface.
|
||||
func NewRunLengthEncoder(eachRun func(element interface{}, count uint64)) RunLengthEncoder {
|
||||
return &runLengthEncoder{
|
||||
eachRun: eachRun,
|
||||
}
|
||||
}
|
||||
|
||||
func (me *runLengthEncoder) Append(element interface{}, count uint64) {
|
||||
if element == me.element {
|
||||
me.count += count
|
||||
return
|
||||
}
|
||||
if me.count != 0 {
|
||||
me.eachRun(me.element, me.count)
|
||||
}
|
||||
me.count = count
|
||||
me.element = element
|
||||
}
|
||||
|
||||
func (me *runLengthEncoder) Flush() {
|
||||
if me.count == 0 {
|
||||
return
|
||||
}
|
||||
me.eachRun(me.element, me.count)
|
||||
me.count = 0
|
||||
}
|
||||
20
Godeps/_workspace/src/github.com/anacrolix/missinggo/rle_test.go
generated
vendored
20
Godeps/_workspace/src/github.com/anacrolix/missinggo/rle_test.go
generated
vendored
@ -1,20 +0,0 @@
|
||||
package missinggo_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/anacrolix/missinggo"
|
||||
)
|
||||
|
||||
func ExampleNewRunLengthEncoder() {
|
||||
var s string
|
||||
rle := missinggo.NewRunLengthEncoder(func(e interface{}, count uint64) {
|
||||
s += fmt.Sprintf("%d%c", count, e)
|
||||
})
|
||||
for _, e := range "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWBWWWWWWWWWWWWWW" {
|
||||
rle.Append(e, 1)
|
||||
}
|
||||
rle.Flush()
|
||||
fmt.Println(s)
|
||||
// Output: 12W1B12W3B24W1B14W
|
||||
}
|
||||
39
Godeps/_workspace/src/github.com/anacrolix/missinggo/singleflight.go
generated
vendored
39
Godeps/_workspace/src/github.com/anacrolix/missinggo/singleflight.go
generated
vendored
@ -1,39 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import "sync"
|
||||
|
||||
type ongoing struct {
|
||||
do sync.Mutex
|
||||
users int
|
||||
}
|
||||
|
||||
type SingleFlight struct {
|
||||
mu sync.Mutex
|
||||
ongoing map[string]*ongoing
|
||||
}
|
||||
|
||||
func (me *SingleFlight) Lock(id string) {
|
||||
me.mu.Lock()
|
||||
on, ok := me.ongoing[id]
|
||||
if !ok {
|
||||
on = new(ongoing)
|
||||
if me.ongoing == nil {
|
||||
me.ongoing = make(map[string]*ongoing)
|
||||
}
|
||||
me.ongoing[id] = on
|
||||
}
|
||||
on.users++
|
||||
me.mu.Unlock()
|
||||
on.do.Lock()
|
||||
}
|
||||
|
||||
func (me *SingleFlight) Unlock(id string) {
|
||||
me.mu.Lock()
|
||||
on := me.ongoing[id]
|
||||
on.do.Unlock()
|
||||
on.users--
|
||||
if on.users == 0 {
|
||||
delete(me.ongoing, id)
|
||||
}
|
||||
me.mu.Unlock()
|
||||
}
|
||||
11
Godeps/_workspace/src/github.com/anacrolix/missinggo/sync.go
generated
vendored
11
Godeps/_workspace/src/github.com/anacrolix/missinggo/sync.go
generated
vendored
@ -1,11 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type RWLocker interface {
|
||||
sync.Locker
|
||||
RLock()
|
||||
RUnlock()
|
||||
}
|
||||
30
Godeps/_workspace/src/github.com/anacrolix/missinggo/url.go
generated
vendored
30
Godeps/_workspace/src/github.com/anacrolix/missinggo/url.go
generated
vendored
@ -1,30 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// Deep copies a URL.
|
||||
func CopyURL(u *url.URL) (ret *url.URL) {
|
||||
ret = new(url.URL)
|
||||
*ret = *u
|
||||
if u.User != nil {
|
||||
ret.User = new(url.Userinfo)
|
||||
*ret.User = *u.User
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Reconstructs the URL that would have produced the given Request.
|
||||
// Request.URLs are not fully populated in http.Server handlers.
|
||||
func RequestedURL(r *http.Request) (ret *url.URL) {
|
||||
ret = CopyURL(r.URL)
|
||||
ret.Host = r.Host
|
||||
if r.TLS != nil {
|
||||
ret.Scheme = "https"
|
||||
} else {
|
||||
ret.Scheme = "http"
|
||||
}
|
||||
return
|
||||
}
|
||||
51
Godeps/_workspace/src/github.com/anacrolix/missinggo/wolf.go
generated
vendored
51
Godeps/_workspace/src/github.com/anacrolix/missinggo/wolf.go
generated
vendored
@ -1,51 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"log"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const debug = false
|
||||
|
||||
// A Wolf represents some event that becomes less and less interesting as it
|
||||
// occurs. Call CryHeard to see if we should pay attention this time.
|
||||
type Wolf struct {
|
||||
cries uint64
|
||||
}
|
||||
|
||||
// Returns true less and less often. Convenient for exponentially decreasing
|
||||
// the amount of noise due to errors.
|
||||
func (me *Wolf) CryHeard() bool {
|
||||
n := atomic.AddUint64(&me.cries, 1)
|
||||
return n&(n-1) == 0
|
||||
}
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
wolves map[uintptr]*Wolf
|
||||
)
|
||||
|
||||
// Calls CryHeard() on a Wolf that is unique to the callers program counter.
|
||||
// i.e. every CryHeard() expression has its own Wolf.
|
||||
func CryHeard() bool {
|
||||
pc, file, line, ok := runtime.Caller(1)
|
||||
if debug {
|
||||
log.Println(pc, file, line, ok)
|
||||
}
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
mu.Lock()
|
||||
if wolves == nil {
|
||||
wolves = make(map[uintptr]*Wolf)
|
||||
}
|
||||
w, ok := wolves[pc]
|
||||
if !ok {
|
||||
w = new(Wolf)
|
||||
wolves[pc] = w
|
||||
}
|
||||
mu.Unlock()
|
||||
return w.CryHeard()
|
||||
}
|
||||
30
Godeps/_workspace/src/github.com/anacrolix/missinggo/wolf_test.go
generated
vendored
30
Godeps/_workspace/src/github.com/anacrolix/missinggo/wolf_test.go
generated
vendored
@ -1,30 +0,0 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gx/ipfs/QmZwjfAKWe7vWZ8f48u7AGA1xYfzR1iCD9A2XSCYFRBWot/testify/require"
|
||||
)
|
||||
|
||||
func cryHeard() bool {
|
||||
return CryHeard()
|
||||
}
|
||||
|
||||
func TestCrySameLocation(t *testing.T) {
|
||||
require.True(t, cryHeard())
|
||||
require.True(t, cryHeard())
|
||||
require.False(t, cryHeard())
|
||||
require.True(t, cryHeard())
|
||||
require.False(t, cryHeard())
|
||||
require.False(t, cryHeard())
|
||||
require.False(t, cryHeard())
|
||||
require.True(t, cryHeard())
|
||||
}
|
||||
|
||||
func TestCryDifferentLocations(t *testing.T) {
|
||||
require.True(t, CryHeard())
|
||||
require.True(t, CryHeard())
|
||||
require.True(t, CryHeard())
|
||||
require.True(t, CryHeard())
|
||||
require.True(t, CryHeard())
|
||||
}
|
||||
362
Godeps/_workspace/src/github.com/anacrolix/utp/LICENSE
generated
vendored
362
Godeps/_workspace/src/github.com/anacrolix/utp/LICENSE
generated
vendored
@ -1,362 +0,0 @@
|
||||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. "Contributor"
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the terms of
|
||||
a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
|
||||
means a work that combines Covered Software with other material, in a
|
||||
separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether
|
||||
at the time of the initial grant or subsequently, any and all of the
|
||||
rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the License,
|
||||
by the making, using, selling, offering for sale, having made, import,
|
||||
or transfer of either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, "control" means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights to
|
||||
grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter the
|
||||
recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||
limitations of liability) contained within the Source Code Form of the
|
||||
Covered Software, except that You may alter any license notices to the
|
||||
extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute,
|
||||
judicial order, or regulation then You must: (a) comply with the terms of
|
||||
this License to the maximum extent possible; and (b) describe the
|
||||
limitations and the code they affect. Such description must be placed in a
|
||||
text file included with all distributions of the Covered Software under
|
||||
this License. Except to the extent prohibited by statute or regulation,
|
||||
such description must be sufficiently detailed for a recipient of ordinary
|
||||
skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||
basis, if such Contributor fails to notify You of the non-compliance by
|
||||
some reasonable means prior to 60 days after You have come back into
|
||||
compliance. Moreover, Your grants from a particular Contributor are
|
||||
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||
non-compliance by some reasonable means, this is the first time You have
|
||||
received notice of non-compliance with this License from such
|
||||
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||
of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an "as is" basis,
|
||||
without warranty of any kind, either expressed, implied, or statutory,
|
||||
including, without limitation, warranties that the Covered Software is free
|
||||
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||
The entire risk as to the quality and performance of the Covered Software
|
||||
is with You. Should any Covered Software prove defective in any respect,
|
||||
You (not any Contributor) assume the cost of any necessary servicing,
|
||||
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||
part of this License. No use of any Covered Software is authorized under
|
||||
this License except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from
|
||||
such party's negligence to the extent applicable law prohibits such
|
||||
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||
incidental or consequential damages, so this exclusion and limitation may
|
||||
not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts
|
||||
of a jurisdiction where the defendant maintains its principal place of
|
||||
business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||
in this Section shall prevent a party's ability to bring cross-claims or
|
||||
counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides that
|
||||
the language of a contract shall be construed against the drafter shall not
|
||||
be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses If You choose to distribute Source Code Form that is
|
||||
Incompatible With Secondary Licenses under the terms of this version of
|
||||
the License, the notice described in Exhibit B of this License must be
|
||||
attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file,
|
||||
then You may include the notice in a location (such as a LICENSE file in a
|
||||
relevant directory) where a recipient would be likely to look for such a
|
||||
notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
This Source Code Form is "Incompatible
|
||||
With Secondary Licenses", as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
||||
19
Godeps/_workspace/src/github.com/anacrolix/utp/README.md
generated
vendored
19
Godeps/_workspace/src/github.com/anacrolix/utp/README.md
generated
vendored
@ -1,19 +0,0 @@
|
||||
# utp
|
||||
[](https://godoc.org/github.com/anacrolix/utp)
|
||||
[](https://drone.io/github.com/anacrolix/utp/latest)
|
||||
|
||||
Package utp implements uTP, the micro transport protocol as used with Bittorrent. It opts for simplicity and reliability over strict adherence to the (poor) spec.
|
||||
|
||||
## Supported
|
||||
|
||||
* Multiple uTP connections switched on a single PacketConn, including those initiated locally.
|
||||
* Raw access to the PacketConn for non-uTP purposes, like sharing the PacketConn with a DHT implementation.
|
||||
|
||||
## Implementation characteristics
|
||||
|
||||
* Receive window size is used to limit out of order packets received.
|
||||
* There is no MTU path discovery. The minimum size is always used.
|
||||
* A fixed 64 slot selective ack window is used in both sending and receiving.
|
||||
* All received non-ACK packets are ACKed in response.
|
||||
|
||||
Patches welcomed.
|
||||
66
Godeps/_workspace/src/github.com/anacrolix/utp/cmd/ucat/ucat.go
generated
vendored
66
Godeps/_workspace/src/github.com/anacrolix/utp/cmd/ucat/ucat.go
generated
vendored
@ -1,66 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"gx/ipfs/QmazECKVXFsA3J6cHAqf8HeTDUB8zARjfo75nxE6o63AAp/envpprof"
|
||||
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/anacrolix/utp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer envpprof.Stop()
|
||||
listen := flag.Bool("l", false, "listen")
|
||||
port := flag.Int("p", 0, "port to listen on")
|
||||
flag.Parse()
|
||||
var (
|
||||
conn net.Conn
|
||||
err error
|
||||
)
|
||||
if *listen {
|
||||
s, err := utp.NewSocket("udp", fmt.Sprintf(":%d", *port))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer s.Close()
|
||||
conn, err = s.Accept()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
conn, err = utp.Dial(net.JoinHostPort(flag.Arg(0), flag.Arg(1)))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
defer conn.Close()
|
||||
go func() {
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, os.Interrupt)
|
||||
<-sig
|
||||
conn.Close()
|
||||
}()
|
||||
writerDone := make(chan struct{})
|
||||
go func() {
|
||||
defer close(writerDone)
|
||||
written, err := io.Copy(conn, os.Stdin)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
log.Fatalf("error after writing %d bytes: %s", written, err)
|
||||
}
|
||||
log.Printf("wrote %d bytes", written)
|
||||
conn.Close()
|
||||
}()
|
||||
n, err := io.Copy(os.Stdout, conn)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Printf("received %d bytes", n)
|
||||
// <-writerDone
|
||||
}
|
||||
61
Godeps/_workspace/src/github.com/anacrolix/utp/pingpong
generated
vendored
61
Godeps/_workspace/src/github.com/anacrolix/utp/pingpong
generated
vendored
@ -1,61 +0,0 @@
|
||||
# This shell script uses nc-like executables to send and receive the file at
|
||||
# $1, and prints the checksums. 3 such executables are
|
||||
# github.com/h2so5/utp/ucat, invoked as h2so5-ucat, libutp-ucat, which is the
|
||||
# ucat or ucat-static generated by the C++ libutp, and lastly, ./cmd/ucat from
|
||||
# this repository. A good file in my experiments is no more than a few 100MB,
|
||||
# or you'll be waiting a while.
|
||||
|
||||
set -eu
|
||||
# set -x
|
||||
|
||||
# Passed to invocations of godo for package ./cmd/ucat.
|
||||
#GODOFLAGS=-race
|
||||
|
||||
#export GO_UTP_PACKET_DROP=0.1
|
||||
export GOPPROF=
|
||||
|
||||
# Invokes the implementation to test against. If there's an arg, then it's
|
||||
# expected to listen.
|
||||
function other_ucat() {
|
||||
if [[ $# != 0 ]]; then
|
||||
libutp-ucat -l -p 4000
|
||||
# h2so5-ucat -l :4000
|
||||
else
|
||||
libutp-ucat localhost 4000
|
||||
# h2so5-ucat localhost:4000
|
||||
fi
|
||||
}
|
||||
|
||||
# Check what the correct result is.
|
||||
md5 "$1"
|
||||
|
||||
rate() {
|
||||
pv -a -W -b
|
||||
}
|
||||
|
||||
echo 'utp->other_ucat'
|
||||
# Send from this uTP implementation to another client.
|
||||
other_ucat -l | rate | md5 &
|
||||
# sleep 1
|
||||
godo ${GODOFLAGS-} ./cmd/ucat localhost 4000 < "$1"
|
||||
wait
|
||||
|
||||
echo 'other_ucat->utp'
|
||||
# Send from the other implementation, to this one.
|
||||
GO_UTP_LOGGING=0 GOPPROF= godo ${GODOFLAGS-} ./cmd/ucat -l -p 4000 | rate | md5 &
|
||||
# Never receive from h2so5's ucat without a small sleep first. Don't know why.
|
||||
# sleep 1
|
||||
other_ucat < "$1"
|
||||
wait
|
||||
|
||||
echo 'libutp->libutp'
|
||||
libutp-ucat -l -p 4000 | rate | md5 &
|
||||
libutp-ucat localhost 4000 < "$1"
|
||||
wait
|
||||
|
||||
echo 'utp->utp'
|
||||
godo ./cmd/ucat -l -p 4000 | rate | md5 &
|
||||
godo ./cmd/ucat localhost 4000 < "$1"
|
||||
wait
|
||||
|
||||
# Now check the hashes match (yes you).
|
||||
1461
Godeps/_workspace/src/github.com/anacrolix/utp/utp.go
generated
vendored
1461
Godeps/_workspace/src/github.com/anacrolix/utp/utp.go
generated
vendored
File diff suppressed because it is too large
Load Diff
411
Godeps/_workspace/src/github.com/anacrolix/utp/utp_test.go
generated
vendored
411
Godeps/_workspace/src/github.com/anacrolix/utp/utp_test.go
generated
vendored
@ -1,411 +0,0 @@
|
||||
package utp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/anacrolix/missinggo"
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/bradfitz/iter"
|
||||
"gx/ipfs/QmZwjfAKWe7vWZ8f48u7AGA1xYfzR1iCD9A2XSCYFRBWot/testify/require"
|
||||
_ "gx/ipfs/QmazECKVXFsA3J6cHAqf8HeTDUB8zARjfo75nxE6o63AAp/envpprof"
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.Flags() | log.Lshortfile)
|
||||
}
|
||||
|
||||
func TestUTPPingPong(t *testing.T) {
|
||||
defer goroutineLeakCheck(t)()
|
||||
s, err := NewSocket("udp", "localhost:0")
|
||||
require.NoError(t, err)
|
||||
defer s.Close()
|
||||
pingerClosed := make(chan struct{})
|
||||
go func() {
|
||||
defer close(pingerClosed)
|
||||
b, err := Dial(s.Addr().String())
|
||||
require.NoError(t, err)
|
||||
defer b.Close()
|
||||
n, err := b.Write([]byte("ping"))
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 4, n)
|
||||
buf := make([]byte, 4)
|
||||
b.Read(buf)
|
||||
require.EqualValues(t, "pong", buf)
|
||||
log.Printf("got pong")
|
||||
}()
|
||||
a, err := s.Accept()
|
||||
require.NoError(t, err)
|
||||
defer a.Close()
|
||||
log.Printf("accepted %s", a)
|
||||
buf := make([]byte, 42)
|
||||
n, err := a.Read(buf)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, "ping", buf[:n])
|
||||
log.Print("got ping")
|
||||
n, err = a.Write([]byte("pong"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 4, n)
|
||||
log.Print("waiting for pinger to close")
|
||||
<-pingerClosed
|
||||
}
|
||||
|
||||
func goroutineLeakCheck(t testing.TB) func() {
|
||||
if !testing.Verbose() {
|
||||
return func() {}
|
||||
}
|
||||
numStart := runtime.NumGoroutine()
|
||||
return func() {
|
||||
var numNow int
|
||||
for range iter.N(1) {
|
||||
numNow = runtime.NumGoroutine()
|
||||
if numNow == numStart {
|
||||
return
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
// I'd print stacks, or treat this as fatal, but I think
|
||||
// runtime.NumGoroutine is including system routines for which we are
|
||||
// not provided the stacks, and are spawned unpredictably.
|
||||
t.Logf("have %d goroutines, started with %d", numNow, numStart)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialTimeout(t *testing.T) {
|
||||
defer goroutineLeakCheck(t)()
|
||||
s, _ := NewSocket("udp", "localhost:0")
|
||||
defer s.Close()
|
||||
conn, err := DialTimeout(s.Addr().String(), 10*time.Millisecond)
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
t.Fatal("expected timeout")
|
||||
}
|
||||
t.Log(err)
|
||||
}
|
||||
|
||||
func TestMinMaxHeaderType(t *testing.T) {
|
||||
require.Equal(t, stSyn, stMax)
|
||||
}
|
||||
|
||||
func TestUTPRawConn(t *testing.T) {
|
||||
l, err := NewSocket("udp", "")
|
||||
require.NoError(t, err)
|
||||
defer l.Close()
|
||||
go func() {
|
||||
for {
|
||||
_, err := l.Accept()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
// Connect a UTP peer to see if the RawConn will still work.
|
||||
log.Print("dialing")
|
||||
utpPeer := func() net.Conn {
|
||||
s, _ := NewSocket("udp", "")
|
||||
defer s.Close()
|
||||
ret, err := s.Dial(fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
|
||||
require.NoError(t, err)
|
||||
return ret
|
||||
}()
|
||||
log.Print("dial returned")
|
||||
if err != nil {
|
||||
t.Fatalf("error dialing utp listener: %s", err)
|
||||
}
|
||||
defer utpPeer.Close()
|
||||
peer, err := net.ListenPacket("udp", ":0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer peer.Close()
|
||||
|
||||
msgsReceived := 0
|
||||
const N = 5000 // How many messages to send.
|
||||
readerStopped := make(chan struct{})
|
||||
// The reader goroutine.
|
||||
go func() {
|
||||
defer close(readerStopped)
|
||||
b := make([]byte, 500)
|
||||
for i := 0; i < N; i++ {
|
||||
n, _, err := l.ReadFrom(b)
|
||||
if err != nil {
|
||||
t.Fatalf("error reading from raw conn: %s", err)
|
||||
}
|
||||
msgsReceived++
|
||||
var d int
|
||||
fmt.Sscan(string(b[:n]), &d)
|
||||
if d != i {
|
||||
log.Printf("got wrong number: expected %d, got %d", i, d)
|
||||
}
|
||||
}
|
||||
}()
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := 0; i < N; i++ {
|
||||
_, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
time.Sleep(time.Microsecond)
|
||||
}
|
||||
select {
|
||||
case <-readerStopped:
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("reader timed out")
|
||||
}
|
||||
if msgsReceived != N {
|
||||
t.Fatalf("messages received: %d", msgsReceived)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnReadDeadline(t *testing.T) {
|
||||
ls, _ := NewSocket("udp", "localhost:0")
|
||||
ds, _ := NewSocket("udp", "localhost:0")
|
||||
dcReadErr := make(chan error)
|
||||
go func() {
|
||||
c, _ := ds.Dial(ls.Addr().String())
|
||||
defer c.Close()
|
||||
_, err := c.Read(nil)
|
||||
dcReadErr <- err
|
||||
}()
|
||||
c, _ := ls.Accept()
|
||||
dl := time.Now().Add(time.Millisecond)
|
||||
c.SetReadDeadline(dl)
|
||||
_, err := c.Read(nil)
|
||||
require.Equal(t, errTimeout, err)
|
||||
// The deadline has passed.
|
||||
if !time.Now().After(dl) {
|
||||
t.FailNow()
|
||||
}
|
||||
// Returns timeout on subsequent read.
|
||||
_, err = c.Read(nil)
|
||||
require.Equal(t, errTimeout, err)
|
||||
// Disable the deadline.
|
||||
c.SetReadDeadline(time.Time{})
|
||||
readReturned := make(chan struct{})
|
||||
go func() {
|
||||
c.Read(nil)
|
||||
close(readReturned)
|
||||
}()
|
||||
select {
|
||||
case <-readReturned:
|
||||
// Read returned but shouldn't have.
|
||||
t.FailNow()
|
||||
case <-time.After(time.Millisecond):
|
||||
}
|
||||
c.Close()
|
||||
select {
|
||||
case <-readReturned:
|
||||
case <-time.After(time.Millisecond):
|
||||
t.Fatal("read should return after Conn is closed")
|
||||
}
|
||||
if err := <-dcReadErr; err != io.EOF {
|
||||
t.Fatalf("dial conn read returned %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func connectSelfLots(n int, t testing.TB) {
|
||||
defer goroutineLeakCheck(t)()
|
||||
s, err := NewSocket("udp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
go func() {
|
||||
for range iter.N(n) {
|
||||
c, err := s.Accept()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
}
|
||||
}()
|
||||
dialErr := make(chan error)
|
||||
connCh := make(chan net.Conn)
|
||||
dialSema := make(chan struct{}, backlog)
|
||||
for range iter.N(n) {
|
||||
go func() {
|
||||
dialSema <- struct{}{}
|
||||
c, err := s.Dial(s.Addr().String())
|
||||
<-dialSema
|
||||
if err != nil {
|
||||
dialErr <- err
|
||||
return
|
||||
}
|
||||
connCh <- c
|
||||
}()
|
||||
}
|
||||
conns := make([]net.Conn, 0, n)
|
||||
for range iter.N(n) {
|
||||
select {
|
||||
case c := <-connCh:
|
||||
conns = append(conns, c)
|
||||
case err := <-dialErr:
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
for _, c := range conns {
|
||||
if c != nil {
|
||||
c.Close()
|
||||
}
|
||||
}
|
||||
s.mu.Lock()
|
||||
for len(s.conns) != 0 {
|
||||
// log.Print(len(s.conns))
|
||||
s.event.Wait()
|
||||
}
|
||||
s.mu.Unlock()
|
||||
s.Close()
|
||||
}
|
||||
|
||||
// Connect to ourself heaps.
|
||||
func TestConnectSelf(t *testing.T) {
|
||||
// A rough guess says that at worst, I can only have 0x10000/3 connections
|
||||
// to the same socket, due to fragmentation in the assigned connection
|
||||
// IDs.
|
||||
connectSelfLots(0x1000, t)
|
||||
}
|
||||
|
||||
func BenchmarkConnectSelf(b *testing.B) {
|
||||
for range iter.N(b.N) {
|
||||
connectSelfLots(2, b)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNewCloseSocket(b *testing.B) {
|
||||
for range iter.N(b.N) {
|
||||
s, err := NewSocket("udp", "localhost:0")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
err = s.Close()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRejectDialBacklogFilled(t *testing.T) {
|
||||
s, err := NewSocket("udp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
errChan := make(chan error, 1)
|
||||
dial := func() {
|
||||
_, err := s.Dial(s.Addr().String())
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
}
|
||||
}
|
||||
// Fill the backlog.
|
||||
for range iter.N(backlog + 1) {
|
||||
go dial()
|
||||
}
|
||||
s.mu.Lock()
|
||||
for len(s.backlog) < backlog {
|
||||
s.event.Wait()
|
||||
}
|
||||
s.mu.Unlock()
|
||||
select {
|
||||
case <-errChan:
|
||||
t.FailNow()
|
||||
default:
|
||||
}
|
||||
// One more connection should cause a dial attempt to get reset.
|
||||
go dial()
|
||||
err = <-errChan
|
||||
if err.Error() != "peer reset" {
|
||||
t.FailNow()
|
||||
}
|
||||
s.Close()
|
||||
}
|
||||
|
||||
// Make sure that we can reset AfterFunc timers, so we don't have to create
|
||||
// brand new ones everytime they fire. Specifically for the Conn resend timer.
|
||||
func TestResetAfterFuncTimer(t *testing.T) {
|
||||
fired := make(chan struct{})
|
||||
timer := time.AfterFunc(time.Millisecond, func() {
|
||||
fired <- struct{}{}
|
||||
})
|
||||
<-fired
|
||||
if timer.Reset(time.Millisecond) {
|
||||
// The timer should have expired
|
||||
t.FailNow()
|
||||
}
|
||||
<-fired
|
||||
}
|
||||
|
||||
func connPair() (initer, accepted net.Conn) {
|
||||
s, err := NewSocket("udp", "localhost:0")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer s.Close()
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
var err error
|
||||
initer, err = Dial(s.Addr().String())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
accepted, err = s.Accept()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
wg.Wait()
|
||||
return
|
||||
}
|
||||
|
||||
// Check that peer sending FIN doesn't cause unread data to be dropped in a
|
||||
// receiver.
|
||||
func TestReadFinishedConn(t *testing.T) {
|
||||
a, b := connPair()
|
||||
defer a.Close()
|
||||
defer b.Close()
|
||||
mu.Lock()
|
||||
originalAPDC := artificialPacketDropChance
|
||||
artificialPacketDropChance = 1
|
||||
mu.Unlock()
|
||||
n, err := a.Write([]byte("hello"))
|
||||
require.Equal(t, 5, n)
|
||||
require.NoError(t, err)
|
||||
n, err = a.Write([]byte("world"))
|
||||
require.Equal(t, 5, n)
|
||||
require.NoError(t, err)
|
||||
mu.Lock()
|
||||
artificialPacketDropChance = originalAPDC
|
||||
mu.Unlock()
|
||||
a.Close()
|
||||
all, err := ioutil.ReadAll(b)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, "helloworld", all)
|
||||
}
|
||||
|
||||
func TestCloseDetachesQuickly(t *testing.T) {
|
||||
s, _ := NewSocket("udp", "localhost:0")
|
||||
defer s.Close()
|
||||
go func() {
|
||||
a, _ := s.Dial(s.Addr().String())
|
||||
log.Print("close a")
|
||||
a.Close()
|
||||
log.Print("closed a")
|
||||
}()
|
||||
b, _ := s.Accept()
|
||||
b.Close()
|
||||
s.mu.Lock()
|
||||
for len(s.conns) != 0 {
|
||||
log.Print(len(s.conns))
|
||||
s.event.Wait()
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
18
Godeps/_workspace/src/github.com/codegangsta/cli/.travis.yml
generated
vendored
Normal file
18
Godeps/_workspace/src/github.com/codegangsta/cli/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
language: go
|
||||
sudo: false
|
||||
|
||||
go:
|
||||
- 1.1.2
|
||||
- 1.2.2
|
||||
- 1.3.3
|
||||
- 1.4.2
|
||||
- 1.5.1
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
|
||||
script:
|
||||
- go vet ./...
|
||||
- go test -v ./...
|
||||
21
Godeps/_workspace/src/github.com/codegangsta/cli/LICENSE
generated
vendored
Normal file
21
Godeps/_workspace/src/github.com/codegangsta/cli/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
Copyright (C) 2013 Jeremy Saenz
|
||||
All Rights Reserved.
|
||||
|
||||
MIT LICENSE
|
||||
|
||||
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.
|
||||
394
Godeps/_workspace/src/github.com/codegangsta/cli/README.md
generated
vendored
Normal file
394
Godeps/_workspace/src/github.com/codegangsta/cli/README.md
generated
vendored
Normal file
@ -0,0 +1,394 @@
|
||||
[](http://gocover.io/github.com/codegangsta/cli)
|
||||
[](https://travis-ci.org/codegangsta/cli)
|
||||
[](https://godoc.org/github.com/codegangsta/cli)
|
||||
|
||||
# cli.go
|
||||
|
||||
`cli.go` is simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way.
|
||||
|
||||
## Overview
|
||||
|
||||
Command line apps are usually so tiny that there is absolutely no reason why your code should *not* be self-documenting. Things like generating help text and parsing command flags/options should not hinder productivity when writing a command line app.
|
||||
|
||||
**This is where `cli.go` comes into play.** `cli.go` makes command line programming fun, organized, and expressive!
|
||||
|
||||
## Installation
|
||||
|
||||
Make sure you have a working Go environment (go 1.1+ is *required*). [See the install instructions](http://golang.org/doc/install.html).
|
||||
|
||||
To install `cli.go`, simply run:
|
||||
```
|
||||
$ go get github.com/codegangsta/cli
|
||||
```
|
||||
|
||||
Make sure your `PATH` includes to the `$GOPATH/bin` directory so your commands can be easily used:
|
||||
```
|
||||
export PATH=$PATH:$GOPATH/bin
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
One of the philosophies behind `cli.go` is that an API should be playful and full of discovery. So a `cli.go` app can be as little as one line of code in `main()`.
|
||||
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cli.NewApp().Run(os.Args)
|
||||
}
|
||||
```
|
||||
|
||||
This app will run and show help text, but is not very useful. Let's give an action to execute and some help documentation:
|
||||
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "boom"
|
||||
app.Usage = "make an explosive entrance"
|
||||
app.Action = func(c *cli.Context) {
|
||||
println("boom! I say!")
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
}
|
||||
```
|
||||
|
||||
Running this already gives you a ton of functionality, plus support for things like subcommands and flags, which are covered below.
|
||||
|
||||
## Example
|
||||
|
||||
Being a programmer can be a lonely job. Thankfully by the power of automation that is not the case! Let's create a greeter app to fend off our demons of loneliness!
|
||||
|
||||
Start by creating a directory named `greet`, and within it, add a file, `greet.go` with the following code in it:
|
||||
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "greet"
|
||||
app.Usage = "fight the loneliness!"
|
||||
app.Action = func(c *cli.Context) {
|
||||
println("Hello friend!")
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
}
|
||||
```
|
||||
|
||||
Install our command to the `$GOPATH/bin` directory:
|
||||
|
||||
```
|
||||
$ go install
|
||||
```
|
||||
|
||||
Finally run our new command:
|
||||
|
||||
```
|
||||
$ greet
|
||||
Hello friend!
|
||||
```
|
||||
|
||||
`cli.go` also generates neat help text:
|
||||
|
||||
```
|
||||
$ greet help
|
||||
NAME:
|
||||
greet - fight the loneliness!
|
||||
|
||||
USAGE:
|
||||
greet [global options] command [command options] [arguments...]
|
||||
|
||||
VERSION:
|
||||
0.0.0
|
||||
|
||||
COMMANDS:
|
||||
help, h Shows a list of commands or help for one command
|
||||
|
||||
GLOBAL OPTIONS
|
||||
--version Shows version information
|
||||
```
|
||||
|
||||
### Arguments
|
||||
|
||||
You can lookup arguments by calling the `Args` function on `cli.Context`.
|
||||
|
||||
``` go
|
||||
...
|
||||
app.Action = func(c *cli.Context) {
|
||||
println("Hello", c.Args()[0])
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
### Flags
|
||||
|
||||
Setting and querying flags is simple.
|
||||
|
||||
``` go
|
||||
...
|
||||
app.Flags = []cli.Flag {
|
||||
cli.StringFlag{
|
||||
Name: "lang",
|
||||
Value: "english",
|
||||
Usage: "language for the greeting",
|
||||
},
|
||||
}
|
||||
app.Action = func(c *cli.Context) {
|
||||
name := "someone"
|
||||
if c.NArg() > 0 {
|
||||
name = c.Args()[0]
|
||||
}
|
||||
if c.String("lang") == "spanish" {
|
||||
println("Hola", name)
|
||||
} else {
|
||||
println("Hello", name)
|
||||
}
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
You can also set a destination variable for a flag, to which the content will be scanned.
|
||||
|
||||
``` go
|
||||
...
|
||||
var language string
|
||||
app.Flags = []cli.Flag {
|
||||
cli.StringFlag{
|
||||
Name: "lang",
|
||||
Value: "english",
|
||||
Usage: "language for the greeting",
|
||||
Destination: &language,
|
||||
},
|
||||
}
|
||||
app.Action = func(c *cli.Context) {
|
||||
name := "someone"
|
||||
if c.NArg() > 0 {
|
||||
name = c.Args()[0]
|
||||
}
|
||||
if language == "spanish" {
|
||||
println("Hola", name)
|
||||
} else {
|
||||
println("Hello", name)
|
||||
}
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
See full list of flags at http://godoc.org/github.com/codegangsta/cli
|
||||
|
||||
#### Alternate Names
|
||||
|
||||
You can set alternate (or short) names for flags by providing a comma-delimited list for the `Name`. e.g.
|
||||
|
||||
``` go
|
||||
app.Flags = []cli.Flag {
|
||||
cli.StringFlag{
|
||||
Name: "lang, l",
|
||||
Value: "english",
|
||||
Usage: "language for the greeting",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
That flag can then be set with `--lang spanish` or `-l spanish`. Note that giving two different forms of the same flag in the same command invocation is an error.
|
||||
|
||||
#### Values from the Environment
|
||||
|
||||
You can also have the default value set from the environment via `EnvVar`. e.g.
|
||||
|
||||
``` go
|
||||
app.Flags = []cli.Flag {
|
||||
cli.StringFlag{
|
||||
Name: "lang, l",
|
||||
Value: "english",
|
||||
Usage: "language for the greeting",
|
||||
EnvVar: "APP_LANG",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The `EnvVar` may also be given as a comma-delimited "cascade", where the first environment variable that resolves is used as the default.
|
||||
|
||||
``` go
|
||||
app.Flags = []cli.Flag {
|
||||
cli.StringFlag{
|
||||
Name: "lang, l",
|
||||
Value: "english",
|
||||
Usage: "language for the greeting",
|
||||
EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
#### Values from alternate input sources (YAML and others)
|
||||
|
||||
There is a separate package altsrc that adds support for getting flag values from other input sources like YAML.
|
||||
|
||||
In order to get values for a flag from an alternate input source the following code would be added to wrap an existing cli.Flag like below:
|
||||
|
||||
``` go
|
||||
altsrc.NewIntFlag(cli.IntFlag{Name: "test"})
|
||||
```
|
||||
|
||||
Initialization must also occur for these flags. Below is an example initializing getting data from a yaml file below.
|
||||
|
||||
``` go
|
||||
command.Before = altsrc.InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
||||
```
|
||||
|
||||
The code above will use the "load" string as a flag name to get the file name of a yaml file from the cli.Context.
|
||||
It will then use that file name to initialize the yaml input source for any flags that are defined on that command.
|
||||
As a note the "load" flag used would also have to be defined on the command flags in order for this code snipped to work.
|
||||
|
||||
Currently only YAML files are supported but developers can add support for other input sources by implementing the
|
||||
altsrc.InputSourceContext for their given sources.
|
||||
|
||||
Here is a more complete sample of a command using YAML support:
|
||||
|
||||
``` go
|
||||
command := &cli.Command{
|
||||
Name: "test-cmd",
|
||||
Aliases: []string{"tc"},
|
||||
Usage: "this is for testing",
|
||||
Description: "testing",
|
||||
Action: func(c *cli.Context) {
|
||||
// Action to run
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
NewIntFlag(cli.IntFlag{Name: "test"}),
|
||||
cli.StringFlag{Name: "load"}},
|
||||
}
|
||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
||||
err := command.Run(c)
|
||||
```
|
||||
|
||||
### Subcommands
|
||||
|
||||
Subcommands can be defined for a more git-like command line app.
|
||||
|
||||
```go
|
||||
...
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "add",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "add a task to the list",
|
||||
Action: func(c *cli.Context) {
|
||||
println("added task: ", c.Args().First())
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "complete",
|
||||
Aliases: []string{"c"},
|
||||
Usage: "complete a task on the list",
|
||||
Action: func(c *cli.Context) {
|
||||
println("completed task: ", c.Args().First())
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "template",
|
||||
Aliases: []string{"r"},
|
||||
Usage: "options for task templates",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "add",
|
||||
Usage: "add a new template",
|
||||
Action: func(c *cli.Context) {
|
||||
println("new task template: ", c.Args().First())
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "remove",
|
||||
Usage: "remove an existing template",
|
||||
Action: func(c *cli.Context) {
|
||||
println("removed task template: ", c.Args().First())
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
### Bash Completion
|
||||
|
||||
You can enable completion commands by setting the `EnableBashCompletion`
|
||||
flag on the `App` object. By default, this setting will only auto-complete to
|
||||
show an app's subcommands, but you can write your own completion methods for
|
||||
the App or its subcommands.
|
||||
|
||||
```go
|
||||
...
|
||||
var tasks = []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
|
||||
app := cli.NewApp()
|
||||
app.EnableBashCompletion = true
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "complete",
|
||||
Aliases: []string{"c"},
|
||||
Usage: "complete a task on the list",
|
||||
Action: func(c *cli.Context) {
|
||||
println("completed task: ", c.Args().First())
|
||||
},
|
||||
BashComplete: func(c *cli.Context) {
|
||||
// This will complete if no args are passed
|
||||
if c.NArg() > 0 {
|
||||
return
|
||||
}
|
||||
for _, t := range tasks {
|
||||
fmt.Println(t)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
#### To Enable
|
||||
|
||||
Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while
|
||||
setting the `PROG` variable to the name of your program:
|
||||
|
||||
`PROG=myprogram source /.../cli/autocomplete/bash_autocomplete`
|
||||
|
||||
#### To Distribute
|
||||
|
||||
Copy `autocomplete/bash_autocomplete` into `/etc/bash_completion.d/` and rename
|
||||
it to the name of the program you wish to add autocomplete support for (or
|
||||
automatically install it there if you are distributing a package). Don't forget
|
||||
to source the file to make it active in the current shell.
|
||||
|
||||
```
|
||||
sudo cp src/bash_autocomplete /etc/bash_completion.d/<myprogram>
|
||||
source /etc/bash_completion.d/<myprogram>
|
||||
```
|
||||
|
||||
Alternatively, you can just document that users should source the generic
|
||||
`autocomplete/bash_autocomplete` in their bash configuration with `$PROG` set
|
||||
to the name of their program (as above).
|
||||
|
||||
## Contribution Guidelines
|
||||
|
||||
Feel free to put up a pull request to fix a bug or maybe add a feature. I will give it a code review and make sure that it does not break backwards compatibility. If I or any other collaborators agree that it is in line with the vision of the project, we will work with you to get the code into a mergeable state and merge it into the master branch.
|
||||
|
||||
If you have contributed something significant to the project, I will most likely add you as a collaborator. As a collaborator you are given the ability to merge others pull requests. It is very important that new code does not break existing code, so be careful about what code you do choose to merge. If you have any questions feel free to link @codegangsta to the issue in question and we can review it together.
|
||||
|
||||
If you feel like you have contributed to the project but have not yet been added as a collaborator, I probably forgot to add you. Hit @codegangsta up over email and we will get it figured out.
|
||||
439
Godeps/_workspace/src/github.com/codegangsta/cli/altsrc/flag.go
generated
vendored
Normal file
439
Godeps/_workspace/src/github.com/codegangsta/cli/altsrc/flag.go
generated
vendored
Normal file
@ -0,0 +1,439 @@
|
||||
package altsrc
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
// FlagInputSourceExtension is an extension interface of cli.Flag that
|
||||
// allows a value to be set on the existing parsed flags.
|
||||
type FlagInputSourceExtension interface {
|
||||
cli.Flag
|
||||
ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error
|
||||
}
|
||||
|
||||
// ApplyInputSourceValues iterates over all provided flags and
|
||||
// executes ApplyInputSourceValue on flags implementing the
|
||||
// FlagInputSourceExtension interface to initialize these flags
|
||||
// to an alternate input source.
|
||||
func ApplyInputSourceValues(context *cli.Context, inputSourceContext InputSourceContext, flags []cli.Flag) error {
|
||||
for _, f := range flags {
|
||||
inputSourceExtendedFlag, isType := f.(FlagInputSourceExtension)
|
||||
if isType {
|
||||
err := inputSourceExtendedFlag.ApplyInputSourceValue(context, inputSourceContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitInputSource is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new
|
||||
// input source based on the func provided. If there is no error it will then apply the new input source to any flags
|
||||
// that are supported by the input source
|
||||
func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) func(context *cli.Context) error {
|
||||
return func(context *cli.Context) error {
|
||||
inputSource, err := createInputSource()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to create input source: inner error: \n'%v'", err.Error())
|
||||
}
|
||||
|
||||
return ApplyInputSourceValues(context, inputSource, flags)
|
||||
}
|
||||
}
|
||||
|
||||
// InitInputSourceWithContext is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new
|
||||
// input source based on the func provided with potentially using existing cli.Context values to initialize itself. If there is
|
||||
// no error it will then apply the new input source to any flags that are supported by the input source
|
||||
func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(context *cli.Context) (InputSourceContext, error)) func(context *cli.Context) error {
|
||||
return func(context *cli.Context) error {
|
||||
inputSource, err := createInputSource(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to create input source with context: inner error: \n'%v'", err.Error())
|
||||
}
|
||||
|
||||
return ApplyInputSourceValues(context, inputSource, flags)
|
||||
}
|
||||
}
|
||||
|
||||
// GenericFlag is the flag type that wraps cli.GenericFlag to allow
|
||||
// for other values to be specified
|
||||
type GenericFlag struct {
|
||||
cli.GenericFlag
|
||||
set *flag.FlagSet
|
||||
}
|
||||
|
||||
// NewGenericFlag creates a new GenericFlag
|
||||
func NewGenericFlag(flag cli.GenericFlag) *GenericFlag {
|
||||
return &GenericFlag{GenericFlag: flag, set: nil}
|
||||
}
|
||||
|
||||
// ApplyInputSourceValue applies a generic value to the flagSet if required
|
||||
func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
|
||||
if f.set != nil {
|
||||
if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
|
||||
value, err := isc.Generic(f.GenericFlag.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if value != nil {
|
||||
eachName(f.Name, func(name string) {
|
||||
f.set.Set(f.Name, value.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage then calls
|
||||
// the wrapped GenericFlag.Apply
|
||||
func (f *GenericFlag) Apply(set *flag.FlagSet) {
|
||||
f.set = set
|
||||
f.GenericFlag.Apply(set)
|
||||
}
|
||||
|
||||
// StringSliceFlag is the flag type that wraps cli.StringSliceFlag to allow
|
||||
// for other values to be specified
|
||||
type StringSliceFlag struct {
|
||||
cli.StringSliceFlag
|
||||
set *flag.FlagSet
|
||||
}
|
||||
|
||||
// NewStringSliceFlag creates a new StringSliceFlag
|
||||
func NewStringSliceFlag(flag cli.StringSliceFlag) *StringSliceFlag {
|
||||
return &StringSliceFlag{StringSliceFlag: flag, set: nil}
|
||||
}
|
||||
|
||||
// ApplyInputSourceValue applies a StringSlice value to the flagSet if required
|
||||
func (f *StringSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
|
||||
if f.set != nil {
|
||||
if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
|
||||
value, err := isc.StringSlice(f.StringSliceFlag.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if value != nil {
|
||||
var sliceValue cli.StringSlice = value
|
||||
eachName(f.Name, func(name string) {
|
||||
underlyingFlag := f.set.Lookup(f.Name)
|
||||
if underlyingFlag != nil {
|
||||
underlyingFlag.Value = &sliceValue
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage then calls
|
||||
// the wrapped StringSliceFlag.Apply
|
||||
func (f *StringSliceFlag) Apply(set *flag.FlagSet) {
|
||||
f.set = set
|
||||
f.StringSliceFlag.Apply(set)
|
||||
}
|
||||
|
||||
// IntSliceFlag is the flag type that wraps cli.IntSliceFlag to allow
|
||||
// for other values to be specified
|
||||
type IntSliceFlag struct {
|
||||
cli.IntSliceFlag
|
||||
set *flag.FlagSet
|
||||
}
|
||||
|
||||
// NewIntSliceFlag creates a new IntSliceFlag
|
||||
func NewIntSliceFlag(flag cli.IntSliceFlag) *IntSliceFlag {
|
||||
return &IntSliceFlag{IntSliceFlag: flag, set: nil}
|
||||
}
|
||||
|
||||
// ApplyInputSourceValue applies a IntSlice value if required
|
||||
func (f *IntSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
|
||||
if f.set != nil {
|
||||
if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
|
||||
value, err := isc.IntSlice(f.IntSliceFlag.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if value != nil {
|
||||
var sliceValue cli.IntSlice = value
|
||||
eachName(f.Name, func(name string) {
|
||||
underlyingFlag := f.set.Lookup(f.Name)
|
||||
if underlyingFlag != nil {
|
||||
underlyingFlag.Value = &sliceValue
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage then calls
|
||||
// the wrapped IntSliceFlag.Apply
|
||||
func (f *IntSliceFlag) Apply(set *flag.FlagSet) {
|
||||
f.set = set
|
||||
f.IntSliceFlag.Apply(set)
|
||||
}
|
||||
|
||||
// BoolFlag is the flag type that wraps cli.BoolFlag to allow
|
||||
// for other values to be specified
|
||||
type BoolFlag struct {
|
||||
cli.BoolFlag
|
||||
set *flag.FlagSet
|
||||
}
|
||||
|
||||
// NewBoolFlag creates a new BoolFlag
|
||||
func NewBoolFlag(flag cli.BoolFlag) *BoolFlag {
|
||||
return &BoolFlag{BoolFlag: flag, set: nil}
|
||||
}
|
||||
|
||||
// ApplyInputSourceValue applies a Bool value to the flagSet if required
|
||||
func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
|
||||
if f.set != nil {
|
||||
if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
|
||||
value, err := isc.Bool(f.BoolFlag.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if value {
|
||||
eachName(f.Name, func(name string) {
|
||||
f.set.Set(f.Name, strconv.FormatBool(value))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage then calls
|
||||
// the wrapped BoolFlag.Apply
|
||||
func (f *BoolFlag) Apply(set *flag.FlagSet) {
|
||||
f.set = set
|
||||
f.BoolFlag.Apply(set)
|
||||
}
|
||||
|
||||
// BoolTFlag is the flag type that wraps cli.BoolTFlag to allow
|
||||
// for other values to be specified
|
||||
type BoolTFlag struct {
|
||||
cli.BoolTFlag
|
||||
set *flag.FlagSet
|
||||
}
|
||||
|
||||
// NewBoolTFlag creates a new BoolTFlag
|
||||
func NewBoolTFlag(flag cli.BoolTFlag) *BoolTFlag {
|
||||
return &BoolTFlag{BoolTFlag: flag, set: nil}
|
||||
}
|
||||
|
||||
// ApplyInputSourceValue applies a BoolT value to the flagSet if required
|
||||
func (f *BoolTFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
|
||||
if f.set != nil {
|
||||
if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
|
||||
value, err := isc.BoolT(f.BoolTFlag.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !value {
|
||||
eachName(f.Name, func(name string) {
|
||||
f.set.Set(f.Name, strconv.FormatBool(value))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage then calls
|
||||
// the wrapped BoolTFlag.Apply
|
||||
func (f *BoolTFlag) Apply(set *flag.FlagSet) {
|
||||
f.set = set
|
||||
|
||||
f.BoolTFlag.Apply(set)
|
||||
}
|
||||
|
||||
// StringFlag is the flag type that wraps cli.StringFlag to allow
|
||||
// for other values to be specified
|
||||
type StringFlag struct {
|
||||
cli.StringFlag
|
||||
set *flag.FlagSet
|
||||
}
|
||||
|
||||
// NewStringFlag creates a new StringFlag
|
||||
func NewStringFlag(flag cli.StringFlag) *StringFlag {
|
||||
return &StringFlag{StringFlag: flag, set: nil}
|
||||
}
|
||||
|
||||
// ApplyInputSourceValue applies a String value to the flagSet if required
|
||||
func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
|
||||
if f.set != nil {
|
||||
if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) {
|
||||
value, err := isc.String(f.StringFlag.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if value != "" {
|
||||
eachName(f.Name, func(name string) {
|
||||
f.set.Set(f.Name, value)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage then calls
|
||||
// the wrapped StringFlag.Apply
|
||||
func (f *StringFlag) Apply(set *flag.FlagSet) {
|
||||
f.set = set
|
||||
|
||||
f.StringFlag.Apply(set)
|
||||
}
|
||||
|
||||
// IntFlag is the flag type that wraps cli.IntFlag to allow
|
||||
// for other values to be specified
|
||||
type IntFlag struct {
|
||||
cli.IntFlag
|
||||
set *flag.FlagSet
|
||||
}
|
||||
|
||||
// NewIntFlag creates a new IntFlag
|
||||
func NewIntFlag(flag cli.IntFlag) *IntFlag {
|
||||
return &IntFlag{IntFlag: flag, set: nil}
|
||||
}
|
||||
|
||||
// ApplyInputSourceValue applies a int value to the flagSet if required
|
||||
func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
|
||||
if f.set != nil {
|
||||
if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) {
|
||||
value, err := isc.Int(f.IntFlag.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if value > 0 {
|
||||
eachName(f.Name, func(name string) {
|
||||
f.set.Set(f.Name, strconv.FormatInt(int64(value), 10))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage then calls
|
||||
// the wrapped IntFlag.Apply
|
||||
func (f *IntFlag) Apply(set *flag.FlagSet) {
|
||||
f.set = set
|
||||
f.IntFlag.Apply(set)
|
||||
}
|
||||
|
||||
// DurationFlag is the flag type that wraps cli.DurationFlag to allow
|
||||
// for other values to be specified
|
||||
type DurationFlag struct {
|
||||
cli.DurationFlag
|
||||
set *flag.FlagSet
|
||||
}
|
||||
|
||||
// NewDurationFlag creates a new DurationFlag
|
||||
func NewDurationFlag(flag cli.DurationFlag) *DurationFlag {
|
||||
return &DurationFlag{DurationFlag: flag, set: nil}
|
||||
}
|
||||
|
||||
// ApplyInputSourceValue applies a Duration value to the flagSet if required
|
||||
func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
|
||||
if f.set != nil {
|
||||
if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) {
|
||||
value, err := isc.Duration(f.DurationFlag.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if value > 0 {
|
||||
eachName(f.Name, func(name string) {
|
||||
f.set.Set(f.Name, value.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage then calls
|
||||
// the wrapped DurationFlag.Apply
|
||||
func (f *DurationFlag) Apply(set *flag.FlagSet) {
|
||||
f.set = set
|
||||
|
||||
f.DurationFlag.Apply(set)
|
||||
}
|
||||
|
||||
// Float64Flag is the flag type that wraps cli.Float64Flag to allow
|
||||
// for other values to be specified
|
||||
type Float64Flag struct {
|
||||
cli.Float64Flag
|
||||
set *flag.FlagSet
|
||||
}
|
||||
|
||||
// NewFloat64Flag creates a new Float64Flag
|
||||
func NewFloat64Flag(flag cli.Float64Flag) *Float64Flag {
|
||||
return &Float64Flag{Float64Flag: flag, set: nil}
|
||||
}
|
||||
|
||||
// ApplyInputSourceValue applies a Float64 value to the flagSet if required
|
||||
func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
|
||||
if f.set != nil {
|
||||
if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) {
|
||||
value, err := isc.Float64(f.Float64Flag.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if value > 0 {
|
||||
floatStr := float64ToString(value)
|
||||
eachName(f.Name, func(name string) {
|
||||
f.set.Set(f.Name, floatStr)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage then calls
|
||||
// the wrapped Float64Flag.Apply
|
||||
func (f *Float64Flag) Apply(set *flag.FlagSet) {
|
||||
f.set = set
|
||||
|
||||
f.Float64Flag.Apply(set)
|
||||
}
|
||||
|
||||
func isEnvVarSet(envVars string) bool {
|
||||
for _, envVar := range strings.Split(envVars, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
// TODO: Can't use this for bools as
|
||||
// set means that it was true or false based on
|
||||
// Bool flag type, should work for other types
|
||||
if len(envVal) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func float64ToString(f float64) string {
|
||||
return fmt.Sprintf("%v", f)
|
||||
}
|
||||
|
||||
func eachName(longName string, fn func(string)) {
|
||||
parts := strings.Split(longName, ",")
|
||||
for _, name := range parts {
|
||||
name = strings.Trim(name, " ")
|
||||
fn(name)
|
||||
}
|
||||
}
|
||||
336
Godeps/_workspace/src/github.com/codegangsta/cli/altsrc/flag_test.go
generated
vendored
Normal file
336
Godeps/_workspace/src/github.com/codegangsta/cli/altsrc/flag_test.go
generated
vendored
Normal file
@ -0,0 +1,336 @@
|
||||
package altsrc
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
type testApplyInputSource struct {
|
||||
Flag FlagInputSourceExtension
|
||||
FlagName string
|
||||
FlagSetName string
|
||||
Expected string
|
||||
ContextValueString string
|
||||
ContextValue flag.Value
|
||||
EnvVarValue string
|
||||
EnvVarName string
|
||||
MapValue interface{}
|
||||
}
|
||||
|
||||
func TestGenericApplyInputSourceValue(t *testing.T) {
|
||||
v := &Parser{"abc", "def"}
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewGenericFlag(cli.GenericFlag{Name: "test", Value: &Parser{}}),
|
||||
FlagName: "test",
|
||||
MapValue: v,
|
||||
})
|
||||
expect(t, v, c.Generic("test"))
|
||||
}
|
||||
|
||||
func TestGenericApplyInputSourceMethodContextSet(t *testing.T) {
|
||||
p := &Parser{"abc", "def"}
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewGenericFlag(cli.GenericFlag{Name: "test", Value: &Parser{}}),
|
||||
FlagName: "test",
|
||||
MapValue: &Parser{"efg", "hig"},
|
||||
ContextValueString: p.String(),
|
||||
})
|
||||
expect(t, p, c.Generic("test"))
|
||||
}
|
||||
|
||||
func TestGenericApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewGenericFlag(cli.GenericFlag{Name: "test", Value: &Parser{}, EnvVar: "TEST"}),
|
||||
FlagName: "test",
|
||||
MapValue: &Parser{"efg", "hij"},
|
||||
EnvVarName: "TEST",
|
||||
EnvVarValue: "abc,def",
|
||||
})
|
||||
expect(t, &Parser{"abc", "def"}, c.Generic("test"))
|
||||
}
|
||||
|
||||
func TestStringSliceApplyInputSourceValue(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: []string{"hello", "world"},
|
||||
})
|
||||
expect(t, c.StringSlice("test"), []string{"hello", "world"})
|
||||
}
|
||||
|
||||
func TestStringSliceApplyInputSourceMethodContextSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: []string{"hello", "world"},
|
||||
ContextValueString: "ohno",
|
||||
})
|
||||
expect(t, c.StringSlice("test"), []string{"ohno"})
|
||||
}
|
||||
|
||||
func TestStringSliceApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test", EnvVar: "TEST"}),
|
||||
FlagName: "test",
|
||||
MapValue: []string{"hello", "world"},
|
||||
EnvVarName: "TEST",
|
||||
EnvVarValue: "oh,no",
|
||||
})
|
||||
expect(t, c.StringSlice("test"), []string{"oh", "no"})
|
||||
}
|
||||
|
||||
func TestIntSliceApplyInputSourceValue(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: []int{1, 2},
|
||||
})
|
||||
expect(t, c.IntSlice("test"), []int{1, 2})
|
||||
}
|
||||
|
||||
func TestIntSliceApplyInputSourceMethodContextSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: []int{1, 2},
|
||||
ContextValueString: "3",
|
||||
})
|
||||
expect(t, c.IntSlice("test"), []int{3})
|
||||
}
|
||||
|
||||
func TestIntSliceApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test", EnvVar: "TEST"}),
|
||||
FlagName: "test",
|
||||
MapValue: []int{1, 2},
|
||||
EnvVarName: "TEST",
|
||||
EnvVarValue: "3,4",
|
||||
})
|
||||
expect(t, c.IntSlice("test"), []int{3, 4})
|
||||
}
|
||||
|
||||
func TestBoolApplyInputSourceMethodSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewBoolFlag(cli.BoolFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: true,
|
||||
})
|
||||
expect(t, true, c.Bool("test"))
|
||||
}
|
||||
|
||||
func TestBoolApplyInputSourceMethodContextSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewBoolFlag(cli.BoolFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: false,
|
||||
ContextValueString: "true",
|
||||
})
|
||||
expect(t, true, c.Bool("test"))
|
||||
}
|
||||
|
||||
func TestBoolApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewBoolFlag(cli.BoolFlag{Name: "test", EnvVar: "TEST"}),
|
||||
FlagName: "test",
|
||||
MapValue: false,
|
||||
EnvVarName: "TEST",
|
||||
EnvVarValue: "true",
|
||||
})
|
||||
expect(t, true, c.Bool("test"))
|
||||
}
|
||||
|
||||
func TestBoolTApplyInputSourceMethodSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewBoolTFlag(cli.BoolTFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: false,
|
||||
})
|
||||
expect(t, false, c.BoolT("test"))
|
||||
}
|
||||
|
||||
func TestBoolTApplyInputSourceMethodContextSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewBoolTFlag(cli.BoolTFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: true,
|
||||
ContextValueString: "false",
|
||||
})
|
||||
expect(t, false, c.BoolT("test"))
|
||||
}
|
||||
|
||||
func TestBoolTApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewBoolTFlag(cli.BoolTFlag{Name: "test", EnvVar: "TEST"}),
|
||||
FlagName: "test",
|
||||
MapValue: true,
|
||||
EnvVarName: "TEST",
|
||||
EnvVarValue: "false",
|
||||
})
|
||||
expect(t, false, c.BoolT("test"))
|
||||
}
|
||||
|
||||
func TestStringApplyInputSourceMethodSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewStringFlag(cli.StringFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: "hello",
|
||||
})
|
||||
expect(t, "hello", c.String("test"))
|
||||
}
|
||||
|
||||
func TestStringApplyInputSourceMethodContextSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewStringFlag(cli.StringFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: "hello",
|
||||
ContextValueString: "goodbye",
|
||||
})
|
||||
expect(t, "goodbye", c.String("test"))
|
||||
}
|
||||
|
||||
func TestStringApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewStringFlag(cli.StringFlag{Name: "test", EnvVar: "TEST"}),
|
||||
FlagName: "test",
|
||||
MapValue: "hello",
|
||||
EnvVarName: "TEST",
|
||||
EnvVarValue: "goodbye",
|
||||
})
|
||||
expect(t, "goodbye", c.String("test"))
|
||||
}
|
||||
|
||||
func TestIntApplyInputSourceMethodSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewIntFlag(cli.IntFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: 15,
|
||||
})
|
||||
expect(t, 15, c.Int("test"))
|
||||
}
|
||||
|
||||
func TestIntApplyInputSourceMethodContextSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewIntFlag(cli.IntFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: 15,
|
||||
ContextValueString: "7",
|
||||
})
|
||||
expect(t, 7, c.Int("test"))
|
||||
}
|
||||
|
||||
func TestIntApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "TEST"}),
|
||||
FlagName: "test",
|
||||
MapValue: 15,
|
||||
EnvVarName: "TEST",
|
||||
EnvVarValue: "12",
|
||||
})
|
||||
expect(t, 12, c.Int("test"))
|
||||
}
|
||||
|
||||
func TestDurationApplyInputSourceMethodSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewDurationFlag(cli.DurationFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: time.Duration(30 * time.Second),
|
||||
})
|
||||
expect(t, time.Duration(30*time.Second), c.Duration("test"))
|
||||
}
|
||||
|
||||
func TestDurationApplyInputSourceMethodContextSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewDurationFlag(cli.DurationFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: time.Duration(30 * time.Second),
|
||||
ContextValueString: time.Duration(15 * time.Second).String(),
|
||||
})
|
||||
expect(t, time.Duration(15*time.Second), c.Duration("test"))
|
||||
}
|
||||
|
||||
func TestDurationApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewDurationFlag(cli.DurationFlag{Name: "test", EnvVar: "TEST"}),
|
||||
FlagName: "test",
|
||||
MapValue: time.Duration(30 * time.Second),
|
||||
EnvVarName: "TEST",
|
||||
EnvVarValue: time.Duration(15 * time.Second).String(),
|
||||
})
|
||||
expect(t, time.Duration(15*time.Second), c.Duration("test"))
|
||||
}
|
||||
|
||||
func TestFloat64ApplyInputSourceMethodSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewFloat64Flag(cli.Float64Flag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: 1.3,
|
||||
})
|
||||
expect(t, 1.3, c.Float64("test"))
|
||||
}
|
||||
|
||||
func TestFloat64ApplyInputSourceMethodContextSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewFloat64Flag(cli.Float64Flag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: 1.3,
|
||||
ContextValueString: fmt.Sprintf("%v", 1.4),
|
||||
})
|
||||
expect(t, 1.4, c.Float64("test"))
|
||||
}
|
||||
|
||||
func TestFloat64ApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewFloat64Flag(cli.Float64Flag{Name: "test", EnvVar: "TEST"}),
|
||||
FlagName: "test",
|
||||
MapValue: 1.3,
|
||||
EnvVarName: "TEST",
|
||||
EnvVarValue: fmt.Sprintf("%v", 1.4),
|
||||
})
|
||||
expect(t, 1.4, c.Float64("test"))
|
||||
}
|
||||
|
||||
func runTest(t *testing.T, test testApplyInputSource) *cli.Context {
|
||||
inputSource := &MapInputSource{valueMap: map[string]interface{}{test.FlagName: test.MapValue}}
|
||||
set := flag.NewFlagSet(test.FlagSetName, flag.ContinueOnError)
|
||||
c := cli.NewContext(nil, set, nil)
|
||||
if test.EnvVarName != "" && test.EnvVarValue != "" {
|
||||
os.Setenv(test.EnvVarName, test.EnvVarValue)
|
||||
defer os.Setenv(test.EnvVarName, "")
|
||||
}
|
||||
|
||||
test.Flag.Apply(set)
|
||||
if test.ContextValue != nil {
|
||||
flag := set.Lookup(test.FlagName)
|
||||
flag.Value = test.ContextValue
|
||||
}
|
||||
if test.ContextValueString != "" {
|
||||
set.Set(test.FlagName, test.ContextValueString)
|
||||
}
|
||||
test.Flag.ApplyInputSourceValue(c, inputSource)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
type Parser [2]string
|
||||
|
||||
func (p *Parser) Set(value string) error {
|
||||
parts := strings.Split(value, ",")
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("invalid format")
|
||||
}
|
||||
|
||||
(*p)[0] = parts[0]
|
||||
(*p)[1] = parts[1]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Parser) String() string {
|
||||
return fmt.Sprintf("%s,%s", p[0], p[1])
|
||||
}
|
||||
18
Godeps/_workspace/src/github.com/codegangsta/cli/altsrc/helpers_test.go
generated
vendored
Normal file
18
Godeps/_workspace/src/github.com/codegangsta/cli/altsrc/helpers_test.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
package altsrc
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func expect(t *testing.T, a interface{}, b interface{}) {
|
||||
if !reflect.DeepEqual(b, a) {
|
||||
t.Errorf("Expected %#v (type %v) - Got %#v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
||||
}
|
||||
}
|
||||
|
||||
func refute(t *testing.T, a interface{}, b interface{}) {
|
||||
if a == b {
|
||||
t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
||||
}
|
||||
}
|
||||
21
Godeps/_workspace/src/github.com/codegangsta/cli/altsrc/input_source_context.go
generated
vendored
Normal file
21
Godeps/_workspace/src/github.com/codegangsta/cli/altsrc/input_source_context.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
package altsrc
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
// InputSourceContext is an interface used to allow
|
||||
// other input sources to be implemented as needed.
|
||||
type InputSourceContext interface {
|
||||
Int(name string) (int, error)
|
||||
Duration(name string) (time.Duration, error)
|
||||
Float64(name string) (float64, error)
|
||||
String(name string) (string, error)
|
||||
StringSlice(name string) ([]string, error)
|
||||
IntSlice(name string) ([]int, error)
|
||||
Generic(name string) (cli.Generic, error)
|
||||
Bool(name string) (bool, error)
|
||||
BoolT(name string) (bool, error)
|
||||
}
|
||||
152
Godeps/_workspace/src/github.com/codegangsta/cli/altsrc/map_input_source.go
generated
vendored
Normal file
152
Godeps/_workspace/src/github.com/codegangsta/cli/altsrc/map_input_source.go
generated
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
package altsrc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
// MapInputSource implements InputSourceContext to return
|
||||
// data from the map that is loaded.
|
||||
type MapInputSource struct {
|
||||
valueMap map[string]interface{}
|
||||
}
|
||||
|
||||
// Int returns an int from the map if it exists otherwise returns 0
|
||||
func (fsm *MapInputSource) Int(name string) (int, error) {
|
||||
otherGenericValue, exists := fsm.valueMap[name]
|
||||
if exists {
|
||||
otherValue, isType := otherGenericValue.(int)
|
||||
if !isType {
|
||||
return 0, incorrectTypeForFlagError(name, "int", otherGenericValue)
|
||||
}
|
||||
|
||||
return otherValue, nil
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Duration returns a duration from the map if it exists otherwise returns 0
|
||||
func (fsm *MapInputSource) Duration(name string) (time.Duration, error) {
|
||||
otherGenericValue, exists := fsm.valueMap[name]
|
||||
if exists {
|
||||
otherValue, isType := otherGenericValue.(time.Duration)
|
||||
if !isType {
|
||||
return 0, incorrectTypeForFlagError(name, "duration", otherGenericValue)
|
||||
}
|
||||
return otherValue, nil
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Float64 returns an float64 from the map if it exists otherwise returns 0
|
||||
func (fsm *MapInputSource) Float64(name string) (float64, error) {
|
||||
otherGenericValue, exists := fsm.valueMap[name]
|
||||
if exists {
|
||||
otherValue, isType := otherGenericValue.(float64)
|
||||
if !isType {
|
||||
return 0, incorrectTypeForFlagError(name, "float64", otherGenericValue)
|
||||
}
|
||||
return otherValue, nil
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// String returns a string from the map if it exists otherwise returns an empty string
|
||||
func (fsm *MapInputSource) String(name string) (string, error) {
|
||||
otherGenericValue, exists := fsm.valueMap[name]
|
||||
if exists {
|
||||
otherValue, isType := otherGenericValue.(string)
|
||||
if !isType {
|
||||
return "", incorrectTypeForFlagError(name, "string", otherGenericValue)
|
||||
}
|
||||
return otherValue, nil
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// StringSlice returns an []string from the map if it exists otherwise returns nil
|
||||
func (fsm *MapInputSource) StringSlice(name string) ([]string, error) {
|
||||
otherGenericValue, exists := fsm.valueMap[name]
|
||||
if exists {
|
||||
otherValue, isType := otherGenericValue.([]string)
|
||||
if !isType {
|
||||
return nil, incorrectTypeForFlagError(name, "[]string", otherGenericValue)
|
||||
}
|
||||
return otherValue, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// IntSlice returns an []int from the map if it exists otherwise returns nil
|
||||
func (fsm *MapInputSource) IntSlice(name string) ([]int, error) {
|
||||
otherGenericValue, exists := fsm.valueMap[name]
|
||||
if exists {
|
||||
otherValue, isType := otherGenericValue.([]int)
|
||||
if !isType {
|
||||
return nil, incorrectTypeForFlagError(name, "[]int", otherGenericValue)
|
||||
}
|
||||
return otherValue, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Generic returns an cli.Generic from the map if it exists otherwise returns nil
|
||||
func (fsm *MapInputSource) Generic(name string) (cli.Generic, error) {
|
||||
otherGenericValue, exists := fsm.valueMap[name]
|
||||
if exists {
|
||||
otherValue, isType := otherGenericValue.(cli.Generic)
|
||||
if !isType {
|
||||
return nil, incorrectTypeForFlagError(name, "cli.Generic", otherGenericValue)
|
||||
}
|
||||
return otherValue, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Bool returns an bool from the map otherwise returns false
|
||||
func (fsm *MapInputSource) Bool(name string) (bool, error) {
|
||||
otherGenericValue, exists := fsm.valueMap[name]
|
||||
if exists {
|
||||
otherValue, isType := otherGenericValue.(bool)
|
||||
if !isType {
|
||||
return false, incorrectTypeForFlagError(name, "bool", otherGenericValue)
|
||||
}
|
||||
return otherValue, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// BoolT returns an bool from the map otherwise returns true
|
||||
func (fsm *MapInputSource) BoolT(name string) (bool, error) {
|
||||
otherGenericValue, exists := fsm.valueMap[name]
|
||||
if exists {
|
||||
otherValue, isType := otherGenericValue.(bool)
|
||||
if !isType {
|
||||
return true, incorrectTypeForFlagError(name, "bool", otherGenericValue)
|
||||
}
|
||||
return otherValue, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func incorrectTypeForFlagError(name, expectedTypeName string, value interface{}) error {
|
||||
valueType := reflect.TypeOf(value)
|
||||
valueTypeName := ""
|
||||
if valueType != nil {
|
||||
valueTypeName = valueType.Name()
|
||||
}
|
||||
|
||||
return fmt.Errorf("Mismatched type for flag '%s'. Expected '%s' but actual is '%s'", name, expectedTypeName, valueTypeName)
|
||||
}
|
||||
172
Godeps/_workspace/src/github.com/codegangsta/cli/altsrc/yaml_command_test.go
generated
vendored
Normal file
172
Godeps/_workspace/src/github.com/codegangsta/cli/altsrc/yaml_command_test.go
generated
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
// Disabling building of yaml support in cases where golang is 1.0 or 1.1
|
||||
// as the encoding library is not implemented or supported.
|
||||
|
||||
// +build !go1,!go1.1
|
||||
|
||||
package altsrc
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
func TestCommandYamlFileTest(t *testing.T) {
|
||||
app := cli.NewApp()
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
||||
defer os.Remove("current.yaml")
|
||||
test := []string{"test-cmd", "--load", "current.yaml"}
|
||||
set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
command := &cli.Command{
|
||||
Name: "test-cmd",
|
||||
Aliases: []string{"tc"},
|
||||
Usage: "this is for testing",
|
||||
Description: "testing",
|
||||
Action: func(c *cli.Context) {
|
||||
val := c.Int("test")
|
||||
expect(t, val, 15)
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
NewIntFlag(cli.IntFlag{Name: "test"}),
|
||||
cli.StringFlag{Name: "load"}},
|
||||
}
|
||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
||||
err := command.Run(c)
|
||||
|
||||
expect(t, err, nil)
|
||||
}
|
||||
|
||||
func TestCommandYamlFileTestGlobalEnvVarWins(t *testing.T) {
|
||||
app := cli.NewApp()
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
||||
defer os.Remove("current.yaml")
|
||||
|
||||
os.Setenv("THE_TEST", "10")
|
||||
defer os.Setenv("THE_TEST", "")
|
||||
test := []string{"test-cmd", "--load", "current.yaml"}
|
||||
set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
command := &cli.Command{
|
||||
Name: "test-cmd",
|
||||
Aliases: []string{"tc"},
|
||||
Usage: "this is for testing",
|
||||
Description: "testing",
|
||||
Action: func(c *cli.Context) {
|
||||
val := c.Int("test")
|
||||
expect(t, val, 10)
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "THE_TEST"}),
|
||||
cli.StringFlag{Name: "load"}},
|
||||
}
|
||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
||||
|
||||
err := command.Run(c)
|
||||
|
||||
expect(t, err, nil)
|
||||
}
|
||||
|
||||
func TestCommandYamlFileTestSpecifiedFlagWins(t *testing.T) {
|
||||
app := cli.NewApp()
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
||||
defer os.Remove("current.yaml")
|
||||
|
||||
test := []string{"test-cmd", "--load", "current.yaml", "--test", "7"}
|
||||
set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
command := &cli.Command{
|
||||
Name: "test-cmd",
|
||||
Aliases: []string{"tc"},
|
||||
Usage: "this is for testing",
|
||||
Description: "testing",
|
||||
Action: func(c *cli.Context) {
|
||||
val := c.Int("test")
|
||||
expect(t, val, 7)
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
NewIntFlag(cli.IntFlag{Name: "test"}),
|
||||
cli.StringFlag{Name: "load"}},
|
||||
}
|
||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
||||
|
||||
err := command.Run(c)
|
||||
|
||||
expect(t, err, nil)
|
||||
}
|
||||
|
||||
func TestCommandYamlFileTestDefaultValueFileWins(t *testing.T) {
|
||||
app := cli.NewApp()
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
||||
defer os.Remove("current.yaml")
|
||||
|
||||
test := []string{"test-cmd", "--load", "current.yaml"}
|
||||
set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
command := &cli.Command{
|
||||
Name: "test-cmd",
|
||||
Aliases: []string{"tc"},
|
||||
Usage: "this is for testing",
|
||||
Description: "testing",
|
||||
Action: func(c *cli.Context) {
|
||||
val := c.Int("test")
|
||||
expect(t, val, 15)
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
NewIntFlag(cli.IntFlag{Name: "test", Value: 7}),
|
||||
cli.StringFlag{Name: "load"}},
|
||||
}
|
||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
||||
|
||||
err := command.Run(c)
|
||||
|
||||
expect(t, err, nil)
|
||||
}
|
||||
|
||||
func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWins(t *testing.T) {
|
||||
app := cli.NewApp()
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
||||
defer os.Remove("current.yaml")
|
||||
|
||||
os.Setenv("THE_TEST", "11")
|
||||
defer os.Setenv("THE_TEST", "")
|
||||
|
||||
test := []string{"test-cmd", "--load", "current.yaml"}
|
||||
set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
command := &cli.Command{
|
||||
Name: "test-cmd",
|
||||
Aliases: []string{"tc"},
|
||||
Usage: "this is for testing",
|
||||
Description: "testing",
|
||||
Action: func(c *cli.Context) {
|
||||
val := c.Int("test")
|
||||
expect(t, val, 11)
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
NewIntFlag(cli.IntFlag{Name: "test", Value: 7, EnvVar: "THE_TEST"}),
|
||||
cli.StringFlag{Name: "load"}},
|
||||
}
|
||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
||||
err := command.Run(c)
|
||||
|
||||
expect(t, err, nil)
|
||||
}
|
||||
84
Godeps/_workspace/src/github.com/codegangsta/cli/altsrc/yaml_file_loader.go
generated
vendored
Normal file
84
Godeps/_workspace/src/github.com/codegangsta/cli/altsrc/yaml_file_loader.go
generated
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
// Disabling building of yaml support in cases where golang is 1.0 or 1.1
|
||||
// as the encoding library is not implemented or supported.
|
||||
|
||||
// +build !go1,!go1.1
|
||||
|
||||
package altsrc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type yamlSourceContext struct {
|
||||
FilePath string
|
||||
}
|
||||
|
||||
// NewYamlSourceFromFile creates a new Yaml InputSourceContext from a filepath.
|
||||
func NewYamlSourceFromFile(file string) (InputSourceContext, error) {
|
||||
ymlLoader := &yamlSourceLoader{FilePath: file}
|
||||
var results map[string]interface{}
|
||||
err := readCommandYaml(ysl.FilePath, &results)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to load Yaml file '%s': inner error: \n'%v'", filePath, err.Error())
|
||||
}
|
||||
|
||||
return &MapInputSource{valueMap: results}, nil
|
||||
}
|
||||
|
||||
// NewYamlSourceFromFlagFunc creates a new Yaml InputSourceContext from a provided flag name and source context.
|
||||
func NewYamlSourceFromFlagFunc(flagFileName string) func(InputSourceContext, error) {
|
||||
return func(context cli.Context) {
|
||||
filePath := context.String(flagFileName)
|
||||
return NewYamlSourceFromFile(filePath)
|
||||
}
|
||||
}
|
||||
|
||||
func readCommandYaml(filePath string, container interface{}) (err error) {
|
||||
b, err := loadDataFrom(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(b, container)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
func loadDataFrom(filePath string) ([]byte, error) {
|
||||
u, err := url.Parse(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if u.Host != "" { // i have a host, now do i support the scheme?
|
||||
switch u.Scheme {
|
||||
case "http", "https":
|
||||
res, err := http.Get(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ioutil.ReadAll(res.Body)
|
||||
default:
|
||||
return nil, fmt.Errorf("scheme of %s is unsupported", filePath)
|
||||
}
|
||||
} else if u.Path != "" { // i dont have a host, but I have a path. I am a local file.
|
||||
if _, notFoundFileErr := os.Stat(filePath); notFoundFileErr != nil {
|
||||
return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath)
|
||||
}
|
||||
return ioutil.ReadFile(filePath)
|
||||
} else {
|
||||
return nil, fmt.Errorf("unable to determine how to load from path %s", filePath)
|
||||
}
|
||||
}
|
||||
349
Godeps/_workspace/src/github.com/codegangsta/cli/app.go
generated
vendored
Normal file
349
Godeps/_workspace/src/github.com/codegangsta/cli/app.go
generated
vendored
Normal file
@ -0,0 +1,349 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
// App is the main structure of a cli application. It is recommended that
|
||||
// an app be created with the cli.NewApp() function
|
||||
type App struct {
|
||||
// The name of the program. Defaults to path.Base(os.Args[0])
|
||||
Name string
|
||||
// Full name of command for help, defaults to Name
|
||||
HelpName string
|
||||
// Description of the program.
|
||||
Usage string
|
||||
// Text to override the USAGE section of help
|
||||
UsageText string
|
||||
// Description of the program argument format.
|
||||
ArgsUsage string
|
||||
// Version of the program
|
||||
Version string
|
||||
// List of commands to execute
|
||||
Commands []Command
|
||||
// List of flags to parse
|
||||
Flags []Flag
|
||||
// Boolean to enable bash completion commands
|
||||
EnableBashCompletion bool
|
||||
// Boolean to hide built-in help command
|
||||
HideHelp bool
|
||||
// Boolean to hide built-in version flag
|
||||
HideVersion bool
|
||||
// An action to execute when the bash-completion flag is set
|
||||
BashComplete func(context *Context)
|
||||
// An action to execute before any subcommands are run, but after the context is ready
|
||||
// If a non-nil error is returned, no subcommands are run
|
||||
Before func(context *Context) error
|
||||
// An action to execute after any subcommands are run, but after the subcommand has finished
|
||||
// It is run even if Action() panics
|
||||
After func(context *Context) error
|
||||
// The action to execute when no subcommands are specified
|
||||
Action func(context *Context)
|
||||
// Execute this function if the proper command cannot be found
|
||||
CommandNotFound func(context *Context, command string)
|
||||
// Execute this function, if an usage error occurs. This is useful for displaying customized usage error messages.
|
||||
// This function is able to replace the original error messages.
|
||||
// If this function is not set, the "Incorrect usage" is displayed and the execution is interrupted.
|
||||
OnUsageError func(context *Context, err error, isSubcommand bool) error
|
||||
// Compilation date
|
||||
Compiled time.Time
|
||||
// List of all authors who contributed
|
||||
Authors []Author
|
||||
// Copyright of the binary if any
|
||||
Copyright string
|
||||
// Name of Author (Note: Use App.Authors, this is deprecated)
|
||||
Author string
|
||||
// Email of Author (Note: Use App.Authors, this is deprecated)
|
||||
Email string
|
||||
// Writer writer to write output to
|
||||
Writer io.Writer
|
||||
}
|
||||
|
||||
// Tries to find out when this binary was compiled.
|
||||
// Returns the current time if it fails to find it.
|
||||
func compileTime() time.Time {
|
||||
info, err := os.Stat(os.Args[0])
|
||||
if err != nil {
|
||||
return time.Now()
|
||||
}
|
||||
return info.ModTime()
|
||||
}
|
||||
|
||||
// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action.
|
||||
func NewApp() *App {
|
||||
return &App{
|
||||
Name: path.Base(os.Args[0]),
|
||||
HelpName: path.Base(os.Args[0]),
|
||||
Usage: "A new cli application",
|
||||
UsageText: "",
|
||||
Version: "0.0.0",
|
||||
BashComplete: DefaultAppComplete,
|
||||
Action: helpCommand.Action,
|
||||
Compiled: compileTime(),
|
||||
Writer: os.Stdout,
|
||||
}
|
||||
}
|
||||
|
||||
// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination
|
||||
func (a *App) Run(arguments []string) (err error) {
|
||||
if a.Author != "" || a.Email != "" {
|
||||
a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
|
||||
}
|
||||
|
||||
newCmds := []Command{}
|
||||
for _, c := range a.Commands {
|
||||
if c.HelpName == "" {
|
||||
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
|
||||
}
|
||||
newCmds = append(newCmds, c)
|
||||
}
|
||||
a.Commands = newCmds
|
||||
|
||||
// append help to commands
|
||||
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
|
||||
a.Commands = append(a.Commands, helpCommand)
|
||||
if (HelpFlag != BoolFlag{}) {
|
||||
a.appendFlag(HelpFlag)
|
||||
}
|
||||
}
|
||||
|
||||
//append version/help flags
|
||||
if a.EnableBashCompletion {
|
||||
a.appendFlag(BashCompletionFlag)
|
||||
}
|
||||
|
||||
if !a.HideVersion {
|
||||
a.appendFlag(VersionFlag)
|
||||
}
|
||||
|
||||
// parse flags
|
||||
set := flagSet(a.Name, a.Flags)
|
||||
set.SetOutput(ioutil.Discard)
|
||||
err = set.Parse(arguments[1:])
|
||||
nerr := normalizeFlags(a.Flags, set)
|
||||
context := NewContext(a, set, nil)
|
||||
if nerr != nil {
|
||||
fmt.Fprintln(a.Writer, nerr)
|
||||
ShowAppHelp(context)
|
||||
return nerr
|
||||
}
|
||||
|
||||
if checkCompletions(context) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if a.OnUsageError != nil {
|
||||
err := a.OnUsageError(context, err, false)
|
||||
return err
|
||||
} else {
|
||||
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
|
||||
ShowAppHelp(context)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !a.HideHelp && checkHelp(context) {
|
||||
ShowAppHelp(context)
|
||||
return nil
|
||||
}
|
||||
|
||||
if !a.HideVersion && checkVersion(context) {
|
||||
ShowVersion(context)
|
||||
return nil
|
||||
}
|
||||
|
||||
if a.After != nil {
|
||||
defer func() {
|
||||
if afterErr := a.After(context); afterErr != nil {
|
||||
if err != nil {
|
||||
err = NewMultiError(err, afterErr)
|
||||
} else {
|
||||
err = afterErr
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if a.Before != nil {
|
||||
err = a.Before(context)
|
||||
if err != nil {
|
||||
fmt.Fprintf(a.Writer, "%v\n\n", err)
|
||||
ShowAppHelp(context)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
args := context.Args()
|
||||
if args.Present() {
|
||||
name := args.First()
|
||||
c := a.Command(name)
|
||||
if c != nil {
|
||||
return c.Run(context)
|
||||
}
|
||||
}
|
||||
|
||||
// Run default Action
|
||||
a.Action(context)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Another entry point to the cli app, takes care of passing arguments and error handling
|
||||
func (a *App) RunAndExitOnError() {
|
||||
if err := a.Run(os.Args); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags
|
||||
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
||||
// append help to commands
|
||||
if len(a.Commands) > 0 {
|
||||
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
|
||||
a.Commands = append(a.Commands, helpCommand)
|
||||
if (HelpFlag != BoolFlag{}) {
|
||||
a.appendFlag(HelpFlag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newCmds := []Command{}
|
||||
for _, c := range a.Commands {
|
||||
if c.HelpName == "" {
|
||||
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
|
||||
}
|
||||
newCmds = append(newCmds, c)
|
||||
}
|
||||
a.Commands = newCmds
|
||||
|
||||
// append flags
|
||||
if a.EnableBashCompletion {
|
||||
a.appendFlag(BashCompletionFlag)
|
||||
}
|
||||
|
||||
// parse flags
|
||||
set := flagSet(a.Name, a.Flags)
|
||||
set.SetOutput(ioutil.Discard)
|
||||
err = set.Parse(ctx.Args().Tail())
|
||||
nerr := normalizeFlags(a.Flags, set)
|
||||
context := NewContext(a, set, ctx)
|
||||
|
||||
if nerr != nil {
|
||||
fmt.Fprintln(a.Writer, nerr)
|
||||
fmt.Fprintln(a.Writer)
|
||||
if len(a.Commands) > 0 {
|
||||
ShowSubcommandHelp(context)
|
||||
} else {
|
||||
ShowCommandHelp(ctx, context.Args().First())
|
||||
}
|
||||
return nerr
|
||||
}
|
||||
|
||||
if checkCompletions(context) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if a.OnUsageError != nil {
|
||||
err = a.OnUsageError(context, err, true)
|
||||
return err
|
||||
} else {
|
||||
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
|
||||
ShowSubcommandHelp(context)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(a.Commands) > 0 {
|
||||
if checkSubcommandHelp(context) {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
if checkCommandHelp(ctx, context.Args().First()) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if a.After != nil {
|
||||
defer func() {
|
||||
afterErr := a.After(context)
|
||||
if afterErr != nil {
|
||||
if err != nil {
|
||||
err = NewMultiError(err, afterErr)
|
||||
} else {
|
||||
err = afterErr
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if a.Before != nil {
|
||||
err := a.Before(context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
args := context.Args()
|
||||
if args.Present() {
|
||||
name := args.First()
|
||||
c := a.Command(name)
|
||||
if c != nil {
|
||||
return c.Run(context)
|
||||
}
|
||||
}
|
||||
|
||||
// Run default Action
|
||||
a.Action(context)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns the named command on App. Returns nil if the command does not exist
|
||||
func (a *App) Command(name string) *Command {
|
||||
for _, c := range a.Commands {
|
||||
if c.HasName(name) {
|
||||
return &c
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) hasFlag(flag Flag) bool {
|
||||
for _, f := range a.Flags {
|
||||
if flag == f {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *App) appendFlag(flag Flag) {
|
||||
if !a.hasFlag(flag) {
|
||||
a.Flags = append(a.Flags, flag)
|
||||
}
|
||||
}
|
||||
|
||||
// Author represents someone who has contributed to a cli project.
|
||||
type Author struct {
|
||||
Name string // The Authors name
|
||||
Email string // The Authors email
|
||||
}
|
||||
|
||||
// String makes Author comply to the Stringer interface, to allow an easy print in the templating process
|
||||
func (a Author) String() string {
|
||||
e := ""
|
||||
if a.Email != "" {
|
||||
e = "<" + a.Email + "> "
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v %v", a.Name, e)
|
||||
}
|
||||
1047
Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go
generated
vendored
Normal file
1047
Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
16
Godeps/_workspace/src/github.com/codegangsta/cli/appveyor.yml
generated
vendored
Normal file
16
Godeps/_workspace/src/github.com/codegangsta/cli/appveyor.yml
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
version: "{build}"
|
||||
|
||||
os: Windows Server 2012 R2
|
||||
|
||||
install:
|
||||
- go version
|
||||
- go env
|
||||
|
||||
build_script:
|
||||
- cd %APPVEYOR_BUILD_FOLDER%
|
||||
- go vet ./...
|
||||
- go test -v ./...
|
||||
|
||||
test: off
|
||||
|
||||
deploy: off
|
||||
14
Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/bash_autocomplete
generated
vendored
Normal file
14
Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/bash_autocomplete
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
#! /bin/bash
|
||||
|
||||
: ${PROG:=$(basename ${BASH_SOURCE})}
|
||||
|
||||
_cli_bash_autocomplete() {
|
||||
local cur opts base
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion )
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
}
|
||||
|
||||
complete -F _cli_bash_autocomplete $PROG
|
||||
5
Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/zsh_autocomplete
generated
vendored
Normal file
5
Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/zsh_autocomplete
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
autoload -U compinit && compinit
|
||||
autoload -U bashcompinit && bashcompinit
|
||||
|
||||
script_dir=$(dirname $0)
|
||||
source ${script_dir}/bash_autocomplete
|
||||
40
Godeps/_workspace/src/github.com/codegangsta/cli/cli.go
generated
vendored
Normal file
40
Godeps/_workspace/src/github.com/codegangsta/cli/cli.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
// Package cli provides a minimal framework for creating and organizing command line
|
||||
// Go applications. cli is designed to be easy to understand and write, the most simple
|
||||
// cli application can be written as follows:
|
||||
// func main() {
|
||||
// cli.NewApp().Run(os.Args)
|
||||
// }
|
||||
//
|
||||
// Of course this application does not do much, so let's make this an actual application:
|
||||
// func main() {
|
||||
// app := cli.NewApp()
|
||||
// app.Name = "greet"
|
||||
// app.Usage = "say a greeting"
|
||||
// app.Action = func(c *cli.Context) {
|
||||
// println("Greetings")
|
||||
// }
|
||||
//
|
||||
// app.Run(os.Args)
|
||||
// }
|
||||
package cli
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type MultiError struct {
|
||||
Errors []error
|
||||
}
|
||||
|
||||
func NewMultiError(err ...error) MultiError {
|
||||
return MultiError{Errors: err}
|
||||
}
|
||||
|
||||
func (m MultiError) Error() string {
|
||||
errs := make([]string, len(m.Errors))
|
||||
for i, err := range m.Errors {
|
||||
errs[i] = err.Error()
|
||||
}
|
||||
|
||||
return strings.Join(errs, "\n")
|
||||
}
|
||||
250
Godeps/_workspace/src/github.com/codegangsta/cli/command.go
generated
vendored
Normal file
250
Godeps/_workspace/src/github.com/codegangsta/cli/command.go
generated
vendored
Normal file
@ -0,0 +1,250 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Command is a subcommand for a cli.App.
|
||||
type Command struct {
|
||||
// The name of the command
|
||||
Name string
|
||||
// short name of the command. Typically one character (deprecated, use `Aliases`)
|
||||
ShortName string
|
||||
// A list of aliases for the command
|
||||
Aliases []string
|
||||
// A short description of the usage of this command
|
||||
Usage string
|
||||
// Custom text to show on USAGE section of help
|
||||
UsageText string
|
||||
// A longer explanation of how the command works
|
||||
Description string
|
||||
// A short description of the arguments of this command
|
||||
ArgsUsage string
|
||||
// The function to call when checking for bash command completions
|
||||
BashComplete func(context *Context)
|
||||
// An action to execute before any sub-subcommands are run, but after the context is ready
|
||||
// If a non-nil error is returned, no sub-subcommands are run
|
||||
Before func(context *Context) error
|
||||
// An action to execute after any subcommands are run, but before the subcommand has finished
|
||||
// It is run even if Action() panics
|
||||
After func(context *Context) error
|
||||
// The function to call when this command is invoked
|
||||
Action func(context *Context)
|
||||
// Execute this function, if an usage error occurs. This is useful for displaying customized usage error messages.
|
||||
// This function is able to replace the original error messages.
|
||||
// If this function is not set, the "Incorrect usage" is displayed and the execution is interrupted.
|
||||
OnUsageError func(context *Context, err error) error
|
||||
// List of child commands
|
||||
Subcommands []Command
|
||||
// List of flags to parse
|
||||
Flags []Flag
|
||||
// Treat all flags as normal arguments if true
|
||||
SkipFlagParsing bool
|
||||
// Boolean to hide built-in help command
|
||||
HideHelp bool
|
||||
|
||||
// Full name of command for help, defaults to full command name, including parent commands.
|
||||
HelpName string
|
||||
commandNamePath []string
|
||||
}
|
||||
|
||||
// Returns the full name of the command.
|
||||
// For subcommands this ensures that parent commands are part of the command path
|
||||
func (c Command) FullName() string {
|
||||
if c.commandNamePath == nil {
|
||||
return c.Name
|
||||
}
|
||||
return strings.Join(c.commandNamePath, " ")
|
||||
}
|
||||
|
||||
// Invokes the command given the context, parses ctx.Args() to generate command-specific flags
|
||||
func (c Command) Run(ctx *Context) (err error) {
|
||||
if len(c.Subcommands) > 0 {
|
||||
return c.startApp(ctx)
|
||||
}
|
||||
|
||||
if !c.HideHelp && (HelpFlag != BoolFlag{}) {
|
||||
// append help to flags
|
||||
c.Flags = append(
|
||||
c.Flags,
|
||||
HelpFlag,
|
||||
)
|
||||
}
|
||||
|
||||
if ctx.App.EnableBashCompletion {
|
||||
c.Flags = append(c.Flags, BashCompletionFlag)
|
||||
}
|
||||
|
||||
set := flagSet(c.Name, c.Flags)
|
||||
set.SetOutput(ioutil.Discard)
|
||||
|
||||
if !c.SkipFlagParsing {
|
||||
firstFlagIndex := -1
|
||||
terminatorIndex := -1
|
||||
for index, arg := range ctx.Args() {
|
||||
if arg == "--" {
|
||||
terminatorIndex = index
|
||||
break
|
||||
} else if arg == "-" {
|
||||
// Do nothing. A dash alone is not really a flag.
|
||||
continue
|
||||
} else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 {
|
||||
firstFlagIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
if firstFlagIndex > -1 {
|
||||
args := ctx.Args()
|
||||
regularArgs := make([]string, len(args[1:firstFlagIndex]))
|
||||
copy(regularArgs, args[1:firstFlagIndex])
|
||||
|
||||
var flagArgs []string
|
||||
if terminatorIndex > -1 {
|
||||
flagArgs = args[firstFlagIndex:terminatorIndex]
|
||||
regularArgs = append(regularArgs, args[terminatorIndex:]...)
|
||||
} else {
|
||||
flagArgs = args[firstFlagIndex:]
|
||||
}
|
||||
|
||||
err = set.Parse(append(flagArgs, regularArgs...))
|
||||
} else {
|
||||
err = set.Parse(ctx.Args().Tail())
|
||||
}
|
||||
} else {
|
||||
if c.SkipFlagParsing {
|
||||
err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...))
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if c.OnUsageError != nil {
|
||||
err := c.OnUsageError(ctx, err)
|
||||
return err
|
||||
} else {
|
||||
fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.")
|
||||
fmt.Fprintln(ctx.App.Writer)
|
||||
ShowCommandHelp(ctx, c.Name)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
nerr := normalizeFlags(c.Flags, set)
|
||||
if nerr != nil {
|
||||
fmt.Fprintln(ctx.App.Writer, nerr)
|
||||
fmt.Fprintln(ctx.App.Writer)
|
||||
ShowCommandHelp(ctx, c.Name)
|
||||
return nerr
|
||||
}
|
||||
context := NewContext(ctx.App, set, ctx)
|
||||
|
||||
if checkCommandCompletions(context, c.Name) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if checkCommandHelp(context, c.Name) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.After != nil {
|
||||
defer func() {
|
||||
afterErr := c.After(context)
|
||||
if afterErr != nil {
|
||||
if err != nil {
|
||||
err = NewMultiError(err, afterErr)
|
||||
} else {
|
||||
err = afterErr
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if c.Before != nil {
|
||||
err := c.Before(context)
|
||||
if err != nil {
|
||||
fmt.Fprintln(ctx.App.Writer, err)
|
||||
fmt.Fprintln(ctx.App.Writer)
|
||||
ShowCommandHelp(ctx, c.Name)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
context.Command = c
|
||||
c.Action(context)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Command) Names() []string {
|
||||
names := []string{c.Name}
|
||||
|
||||
if c.ShortName != "" {
|
||||
names = append(names, c.ShortName)
|
||||
}
|
||||
|
||||
return append(names, c.Aliases...)
|
||||
}
|
||||
|
||||
// Returns true if Command.Name or Command.ShortName matches given name
|
||||
func (c Command) HasName(name string) bool {
|
||||
for _, n := range c.Names() {
|
||||
if n == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c Command) startApp(ctx *Context) error {
|
||||
app := NewApp()
|
||||
|
||||
// set the name and usage
|
||||
app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name)
|
||||
if c.HelpName == "" {
|
||||
app.HelpName = c.HelpName
|
||||
} else {
|
||||
app.HelpName = app.Name
|
||||
}
|
||||
|
||||
if c.Description != "" {
|
||||
app.Usage = c.Description
|
||||
} else {
|
||||
app.Usage = c.Usage
|
||||
}
|
||||
|
||||
// set CommandNotFound
|
||||
app.CommandNotFound = ctx.App.CommandNotFound
|
||||
|
||||
// set the flags and commands
|
||||
app.Commands = c.Subcommands
|
||||
app.Flags = c.Flags
|
||||
app.HideHelp = c.HideHelp
|
||||
|
||||
app.Version = ctx.App.Version
|
||||
app.HideVersion = ctx.App.HideVersion
|
||||
app.Compiled = ctx.App.Compiled
|
||||
app.Author = ctx.App.Author
|
||||
app.Email = ctx.App.Email
|
||||
app.Writer = ctx.App.Writer
|
||||
|
||||
// bash completion
|
||||
app.EnableBashCompletion = ctx.App.EnableBashCompletion
|
||||
if c.BashComplete != nil {
|
||||
app.BashComplete = c.BashComplete
|
||||
}
|
||||
|
||||
// set the actions
|
||||
app.Before = c.Before
|
||||
app.After = c.After
|
||||
if c.Action != nil {
|
||||
app.Action = c.Action
|
||||
} else {
|
||||
app.Action = helpSubcommand.Action
|
||||
}
|
||||
|
||||
for index, cc := range app.Commands {
|
||||
app.Commands[index].commandNamePath = []string{c.Name, cc.Name}
|
||||
}
|
||||
|
||||
return app.RunAsSubcommand(ctx)
|
||||
}
|
||||
97
Godeps/_workspace/src/github.com/codegangsta/cli/command_test.go
generated
vendored
Normal file
97
Godeps/_workspace/src/github.com/codegangsta/cli/command_test.go
generated
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandFlagParsing(t *testing.T) {
|
||||
cases := []struct {
|
||||
testArgs []string
|
||||
skipFlagParsing bool
|
||||
expectedErr error
|
||||
}{
|
||||
{[]string{"blah", "blah", "-break"}, false, errors.New("flag provided but not defined: -break")}, // Test normal "not ignoring flags" flow
|
||||
{[]string{"blah", "blah"}, true, nil}, // Test SkipFlagParsing without any args that look like flags
|
||||
{[]string{"blah", "-break"}, true, nil}, // Test SkipFlagParsing with random flag arg
|
||||
{[]string{"blah", "-help"}, true, nil}, // Test SkipFlagParsing with "special" help flag arg
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
app := NewApp()
|
||||
app.Writer = ioutil.Discard
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Parse(c.testArgs)
|
||||
|
||||
context := NewContext(app, set, nil)
|
||||
|
||||
command := Command{
|
||||
Name: "test-cmd",
|
||||
Aliases: []string{"tc"},
|
||||
Usage: "this is for testing",
|
||||
Description: "testing",
|
||||
Action: func(_ *Context) {},
|
||||
}
|
||||
|
||||
command.SkipFlagParsing = c.skipFlagParsing
|
||||
|
||||
err := command.Run(context)
|
||||
|
||||
expect(t, err, c.expectedErr)
|
||||
expect(t, []string(context.Args()), c.testArgs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommand_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) {
|
||||
app := NewApp()
|
||||
app.Commands = []Command{
|
||||
Command{
|
||||
Name: "bar",
|
||||
Before: func(c *Context) error { return fmt.Errorf("before error") },
|
||||
After: func(c *Context) error { return fmt.Errorf("after error") },
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run([]string{"foo", "bar"})
|
||||
if err == nil {
|
||||
t.Fatalf("expected to receive error from Run, got none")
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), "before error") {
|
||||
t.Errorf("expected text of error from Before method, but got none in \"%v\"", err)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "after error") {
|
||||
t.Errorf("expected text of error from After method, but got none in \"%v\"", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) {
|
||||
app := NewApp()
|
||||
app.Commands = []Command{
|
||||
Command{
|
||||
Name: "bar",
|
||||
Flags: []Flag{
|
||||
IntFlag{Name: "flag"},
|
||||
},
|
||||
OnUsageError: func(c *Context, err error) error {
|
||||
if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") {
|
||||
t.Errorf("Expect an invalid value error, but got \"%v\"", err)
|
||||
}
|
||||
return errors.New("intercepted: " + err.Error())
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run([]string{"foo", "bar", "--flag=wrong"})
|
||||
if err == nil {
|
||||
t.Fatalf("expected to receive error from Run, got none")
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(err.Error(), "intercepted: invalid value") {
|
||||
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
|
||||
}
|
||||
}
|
||||
393
Godeps/_workspace/src/github.com/codegangsta/cli/context.go
generated
vendored
Normal file
393
Godeps/_workspace/src/github.com/codegangsta/cli/context.go
generated
vendored
Normal file
@ -0,0 +1,393 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Context is a type that is passed through to
|
||||
// each Handler action in a cli application. Context
|
||||
// can be used to retrieve context-specific Args and
|
||||
// parsed command-line options.
|
||||
type Context struct {
|
||||
App *App
|
||||
Command Command
|
||||
flagSet *flag.FlagSet
|
||||
setFlags map[string]bool
|
||||
globalSetFlags map[string]bool
|
||||
parentContext *Context
|
||||
}
|
||||
|
||||
// Creates a new context. For use in when invoking an App or Command action.
|
||||
func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context {
|
||||
return &Context{App: app, flagSet: set, parentContext: parentCtx}
|
||||
}
|
||||
|
||||
// Looks up the value of a local int flag, returns 0 if no int flag exists
|
||||
func (c *Context) Int(name string) int {
|
||||
return lookupInt(name, c.flagSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a local time.Duration flag, returns 0 if no time.Duration flag exists
|
||||
func (c *Context) Duration(name string) time.Duration {
|
||||
return lookupDuration(name, c.flagSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a local float64 flag, returns 0 if no float64 flag exists
|
||||
func (c *Context) Float64(name string) float64 {
|
||||
return lookupFloat64(name, c.flagSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a local bool flag, returns false if no bool flag exists
|
||||
func (c *Context) Bool(name string) bool {
|
||||
return lookupBool(name, c.flagSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a local boolT flag, returns false if no bool flag exists
|
||||
func (c *Context) BoolT(name string) bool {
|
||||
return lookupBoolT(name, c.flagSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a local string flag, returns "" if no string flag exists
|
||||
func (c *Context) String(name string) string {
|
||||
return lookupString(name, c.flagSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a local string slice flag, returns nil if no string slice flag exists
|
||||
func (c *Context) StringSlice(name string) []string {
|
||||
return lookupStringSlice(name, c.flagSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a local int slice flag, returns nil if no int slice flag exists
|
||||
func (c *Context) IntSlice(name string) []int {
|
||||
return lookupIntSlice(name, c.flagSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a local generic flag, returns nil if no generic flag exists
|
||||
func (c *Context) Generic(name string) interface{} {
|
||||
return lookupGeneric(name, c.flagSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a global int flag, returns 0 if no int flag exists
|
||||
func (c *Context) GlobalInt(name string) int {
|
||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
||||
return lookupInt(name, fs)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Looks up the value of a global time.Duration flag, returns 0 if no time.Duration flag exists
|
||||
func (c *Context) GlobalDuration(name string) time.Duration {
|
||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
||||
return lookupDuration(name, fs)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Looks up the value of a global bool flag, returns false if no bool flag exists
|
||||
func (c *Context) GlobalBool(name string) bool {
|
||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
||||
return lookupBool(name, fs)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Looks up the value of a global string flag, returns "" if no string flag exists
|
||||
func (c *Context) GlobalString(name string) string {
|
||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
||||
return lookupString(name, fs)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Looks up the value of a global string slice flag, returns nil if no string slice flag exists
|
||||
func (c *Context) GlobalStringSlice(name string) []string {
|
||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
||||
return lookupStringSlice(name, fs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Looks up the value of a global int slice flag, returns nil if no int slice flag exists
|
||||
func (c *Context) GlobalIntSlice(name string) []int {
|
||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
||||
return lookupIntSlice(name, fs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Looks up the value of a global generic flag, returns nil if no generic flag exists
|
||||
func (c *Context) GlobalGeneric(name string) interface{} {
|
||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
||||
return lookupGeneric(name, fs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns the number of flags set
|
||||
func (c *Context) NumFlags() int {
|
||||
return c.flagSet.NFlag()
|
||||
}
|
||||
|
||||
// Determines if the flag was actually set
|
||||
func (c *Context) IsSet(name string) bool {
|
||||
if c.setFlags == nil {
|
||||
c.setFlags = make(map[string]bool)
|
||||
c.flagSet.Visit(func(f *flag.Flag) {
|
||||
c.setFlags[f.Name] = true
|
||||
})
|
||||
}
|
||||
return c.setFlags[name] == true
|
||||
}
|
||||
|
||||
// Determines if the global flag was actually set
|
||||
func (c *Context) GlobalIsSet(name string) bool {
|
||||
if c.globalSetFlags == nil {
|
||||
c.globalSetFlags = make(map[string]bool)
|
||||
ctx := c
|
||||
if ctx.parentContext != nil {
|
||||
ctx = ctx.parentContext
|
||||
}
|
||||
for ; ctx != nil && c.globalSetFlags[name] == false; ctx = ctx.parentContext {
|
||||
ctx.flagSet.Visit(func(f *flag.Flag) {
|
||||
c.globalSetFlags[f.Name] = true
|
||||
})
|
||||
}
|
||||
}
|
||||
return c.globalSetFlags[name]
|
||||
}
|
||||
|
||||
// Returns a slice of flag names used in this context.
|
||||
func (c *Context) FlagNames() (names []string) {
|
||||
for _, flag := range c.Command.Flags {
|
||||
name := strings.Split(flag.GetName(), ",")[0]
|
||||
if name == "help" {
|
||||
continue
|
||||
}
|
||||
names = append(names, name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a slice of global flag names used by the app.
|
||||
func (c *Context) GlobalFlagNames() (names []string) {
|
||||
for _, flag := range c.App.Flags {
|
||||
name := strings.Split(flag.GetName(), ",")[0]
|
||||
if name == "help" || name == "version" {
|
||||
continue
|
||||
}
|
||||
names = append(names, name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Returns the parent context, if any
|
||||
func (c *Context) Parent() *Context {
|
||||
return c.parentContext
|
||||
}
|
||||
|
||||
type Args []string
|
||||
|
||||
// Returns the command line arguments associated with the context.
|
||||
func (c *Context) Args() Args {
|
||||
args := Args(c.flagSet.Args())
|
||||
return args
|
||||
}
|
||||
|
||||
// Returns the number of the command line arguments.
|
||||
func (c *Context) NArg() int {
|
||||
return len(c.Args())
|
||||
}
|
||||
|
||||
// Returns the nth argument, or else a blank string
|
||||
func (a Args) Get(n int) string {
|
||||
if len(a) > n {
|
||||
return a[n]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Returns the first argument, or else a blank string
|
||||
func (a Args) First() string {
|
||||
return a.Get(0)
|
||||
}
|
||||
|
||||
// Return the rest of the arguments (not the first one)
|
||||
// or else an empty string slice
|
||||
func (a Args) Tail() []string {
|
||||
if len(a) >= 2 {
|
||||
return []string(a)[1:]
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// Checks if there are any arguments present
|
||||
func (a Args) Present() bool {
|
||||
return len(a) != 0
|
||||
}
|
||||
|
||||
// Swaps arguments at the given indexes
|
||||
func (a Args) Swap(from, to int) error {
|
||||
if from >= len(a) || to >= len(a) {
|
||||
return errors.New("index out of range")
|
||||
}
|
||||
a[from], a[to] = a[to], a[from]
|
||||
return nil
|
||||
}
|
||||
|
||||
func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet {
|
||||
if ctx.parentContext != nil {
|
||||
ctx = ctx.parentContext
|
||||
}
|
||||
for ; ctx != nil; ctx = ctx.parentContext {
|
||||
if f := ctx.flagSet.Lookup(name); f != nil {
|
||||
return ctx.flagSet
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func lookupInt(name string, set *flag.FlagSet) int {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
val, err := strconv.Atoi(f.Value.String())
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func lookupDuration(name string, set *flag.FlagSet) time.Duration {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
val, err := time.ParseDuration(f.Value.String())
|
||||
if err == nil {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func lookupFloat64(name string, set *flag.FlagSet) float64 {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
val, err := strconv.ParseFloat(f.Value.String(), 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func lookupString(name string, set *flag.FlagSet) string {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
return f.Value.String()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func lookupStringSlice(name string, set *flag.FlagSet) []string {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
return (f.Value.(*StringSlice)).Value()
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func lookupIntSlice(name string, set *flag.FlagSet) []int {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
return (f.Value.(*IntSlice)).Value()
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func lookupGeneric(name string, set *flag.FlagSet) interface{} {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
return f.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func lookupBool(name string, set *flag.FlagSet) bool {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
val, err := strconv.ParseBool(f.Value.String())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func lookupBoolT(name string, set *flag.FlagSet) bool {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
val, err := strconv.ParseBool(f.Value.String())
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
|
||||
switch ff.Value.(type) {
|
||||
case *StringSlice:
|
||||
default:
|
||||
set.Set(name, ff.Value.String())
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
|
||||
visited := make(map[string]bool)
|
||||
set.Visit(func(f *flag.Flag) {
|
||||
visited[f.Name] = true
|
||||
})
|
||||
for _, f := range flags {
|
||||
parts := strings.Split(f.GetName(), ",")
|
||||
if len(parts) == 1 {
|
||||
continue
|
||||
}
|
||||
var ff *flag.Flag
|
||||
for _, name := range parts {
|
||||
name = strings.Trim(name, " ")
|
||||
if visited[name] {
|
||||
if ff != nil {
|
||||
return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name)
|
||||
}
|
||||
ff = set.Lookup(name)
|
||||
}
|
||||
}
|
||||
if ff == nil {
|
||||
continue
|
||||
}
|
||||
for _, name := range parts {
|
||||
name = strings.Trim(name, " ")
|
||||
if !visited[name] {
|
||||
copyFlag(name, ff, set)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
121
Godeps/_workspace/src/github.com/codegangsta/cli/context_test.go
generated
vendored
Normal file
121
Godeps/_workspace/src/github.com/codegangsta/cli/context_test.go
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNewContext(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Int("myflag", 12, "doc")
|
||||
globalSet := flag.NewFlagSet("test", 0)
|
||||
globalSet.Int("myflag", 42, "doc")
|
||||
globalCtx := NewContext(nil, globalSet, nil)
|
||||
command := Command{Name: "mycommand"}
|
||||
c := NewContext(nil, set, globalCtx)
|
||||
c.Command = command
|
||||
expect(t, c.Int("myflag"), 12)
|
||||
expect(t, c.GlobalInt("myflag"), 42)
|
||||
expect(t, c.Command.Name, "mycommand")
|
||||
}
|
||||
|
||||
func TestContext_Int(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Int("myflag", 12, "doc")
|
||||
c := NewContext(nil, set, nil)
|
||||
expect(t, c.Int("myflag"), 12)
|
||||
}
|
||||
|
||||
func TestContext_Duration(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Duration("myflag", time.Duration(12*time.Second), "doc")
|
||||
c := NewContext(nil, set, nil)
|
||||
expect(t, c.Duration("myflag"), time.Duration(12*time.Second))
|
||||
}
|
||||
|
||||
func TestContext_String(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.String("myflag", "hello world", "doc")
|
||||
c := NewContext(nil, set, nil)
|
||||
expect(t, c.String("myflag"), "hello world")
|
||||
}
|
||||
|
||||
func TestContext_Bool(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", false, "doc")
|
||||
c := NewContext(nil, set, nil)
|
||||
expect(t, c.Bool("myflag"), false)
|
||||
}
|
||||
|
||||
func TestContext_BoolT(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", true, "doc")
|
||||
c := NewContext(nil, set, nil)
|
||||
expect(t, c.BoolT("myflag"), true)
|
||||
}
|
||||
|
||||
func TestContext_Args(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", false, "doc")
|
||||
c := NewContext(nil, set, nil)
|
||||
set.Parse([]string{"--myflag", "bat", "baz"})
|
||||
expect(t, len(c.Args()), 2)
|
||||
expect(t, c.Bool("myflag"), true)
|
||||
}
|
||||
|
||||
func TestContext_NArg(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", false, "doc")
|
||||
c := NewContext(nil, set, nil)
|
||||
set.Parse([]string{"--myflag", "bat", "baz"})
|
||||
expect(t, c.NArg(), 2)
|
||||
}
|
||||
|
||||
func TestContext_IsSet(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", false, "doc")
|
||||
set.String("otherflag", "hello world", "doc")
|
||||
globalSet := flag.NewFlagSet("test", 0)
|
||||
globalSet.Bool("myflagGlobal", true, "doc")
|
||||
globalCtx := NewContext(nil, globalSet, nil)
|
||||
c := NewContext(nil, set, globalCtx)
|
||||
set.Parse([]string{"--myflag", "bat", "baz"})
|
||||
globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"})
|
||||
expect(t, c.IsSet("myflag"), true)
|
||||
expect(t, c.IsSet("otherflag"), false)
|
||||
expect(t, c.IsSet("bogusflag"), false)
|
||||
expect(t, c.IsSet("myflagGlobal"), false)
|
||||
}
|
||||
|
||||
func TestContext_GlobalIsSet(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", false, "doc")
|
||||
set.String("otherflag", "hello world", "doc")
|
||||
globalSet := flag.NewFlagSet("test", 0)
|
||||
globalSet.Bool("myflagGlobal", true, "doc")
|
||||
globalSet.Bool("myflagGlobalUnset", true, "doc")
|
||||
globalCtx := NewContext(nil, globalSet, nil)
|
||||
c := NewContext(nil, set, globalCtx)
|
||||
set.Parse([]string{"--myflag", "bat", "baz"})
|
||||
globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"})
|
||||
expect(t, c.GlobalIsSet("myflag"), false)
|
||||
expect(t, c.GlobalIsSet("otherflag"), false)
|
||||
expect(t, c.GlobalIsSet("bogusflag"), false)
|
||||
expect(t, c.GlobalIsSet("myflagGlobal"), true)
|
||||
expect(t, c.GlobalIsSet("myflagGlobalUnset"), false)
|
||||
expect(t, c.GlobalIsSet("bogusGlobal"), false)
|
||||
}
|
||||
|
||||
func TestContext_NumFlags(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", false, "doc")
|
||||
set.String("otherflag", "hello world", "doc")
|
||||
globalSet := flag.NewFlagSet("test", 0)
|
||||
globalSet.Bool("myflagGlobal", true, "doc")
|
||||
globalCtx := NewContext(nil, globalSet, nil)
|
||||
c := NewContext(nil, set, globalCtx)
|
||||
set.Parse([]string{"--myflag", "--otherflag=foo"})
|
||||
globalSet.Parse([]string{"--myflagGlobal"})
|
||||
expect(t, c.NumFlags(), 2)
|
||||
}
|
||||
546
Godeps/_workspace/src/github.com/codegangsta/cli/flag.go
generated
vendored
Normal file
546
Godeps/_workspace/src/github.com/codegangsta/cli/flag.go
generated
vendored
Normal file
@ -0,0 +1,546 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// This flag enables bash-completion for all commands and subcommands
|
||||
var BashCompletionFlag = BoolFlag{
|
||||
Name: "generate-bash-completion",
|
||||
}
|
||||
|
||||
// This flag prints the version for the application
|
||||
var VersionFlag = BoolFlag{
|
||||
Name: "version, v",
|
||||
Usage: "print the version",
|
||||
}
|
||||
|
||||
// This flag prints the help for all commands and subcommands
|
||||
// Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand
|
||||
// unless HideHelp is set to true)
|
||||
var HelpFlag = BoolFlag{
|
||||
Name: "help, h",
|
||||
Usage: "show help",
|
||||
}
|
||||
|
||||
// Flag is a common interface related to parsing flags in cli.
|
||||
// For more advanced flag parsing techniques, it is recommended that
|
||||
// this interface be implemented.
|
||||
type Flag interface {
|
||||
fmt.Stringer
|
||||
// Apply Flag settings to the given flag set
|
||||
Apply(*flag.FlagSet)
|
||||
GetName() string
|
||||
}
|
||||
|
||||
func flagSet(name string, flags []Flag) *flag.FlagSet {
|
||||
set := flag.NewFlagSet(name, flag.ContinueOnError)
|
||||
|
||||
for _, f := range flags {
|
||||
f.Apply(set)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
func eachName(longName string, fn func(string)) {
|
||||
parts := strings.Split(longName, ",")
|
||||
for _, name := range parts {
|
||||
name = strings.Trim(name, " ")
|
||||
fn(name)
|
||||
}
|
||||
}
|
||||
|
||||
// Generic is a generic parseable type identified by a specific flag
|
||||
type Generic interface {
|
||||
Set(value string) error
|
||||
String() string
|
||||
}
|
||||
|
||||
// GenericFlag is the flag type for types implementing Generic
|
||||
type GenericFlag struct {
|
||||
Name string
|
||||
Value Generic
|
||||
Usage string
|
||||
EnvVar string
|
||||
}
|
||||
|
||||
// String returns the string representation of the generic flag to display the
|
||||
// help text to the user (uses the String() method of the generic flag to show
|
||||
// the value)
|
||||
func (f GenericFlag) String() string {
|
||||
return withEnvHint(f.EnvVar, fmt.Sprintf("%s %v\t%v", prefixedNames(f.Name), f.FormatValueHelp(), f.Usage))
|
||||
}
|
||||
|
||||
func (f GenericFlag) FormatValueHelp() string {
|
||||
if f.Value == nil {
|
||||
return ""
|
||||
}
|
||||
s := f.Value.String()
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("\"%s\"", s)
|
||||
}
|
||||
|
||||
// Apply takes the flagset and calls Set on the generic flag with the value
|
||||
// provided by the user for parsing by the flag
|
||||
func (f GenericFlag) Apply(set *flag.FlagSet) {
|
||||
val := f.Value
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
val.Set(envVal)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
set.Var(f.Value, name, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func (f GenericFlag) GetName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
// StringSlice is an opaque type for []string to satisfy flag.Value
|
||||
type StringSlice []string
|
||||
|
||||
// Set appends the string value to the list of values
|
||||
func (f *StringSlice) Set(value string) error {
|
||||
*f = append(*f, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value (for usage defaults)
|
||||
func (f *StringSlice) String() string {
|
||||
return fmt.Sprintf("%s", *f)
|
||||
}
|
||||
|
||||
// Value returns the slice of strings set by this flag
|
||||
func (f *StringSlice) Value() []string {
|
||||
return *f
|
||||
}
|
||||
|
||||
// StringSlice is a string flag that can be specified multiple times on the
|
||||
// command-line
|
||||
type StringSliceFlag struct {
|
||||
Name string
|
||||
Value *StringSlice
|
||||
Usage string
|
||||
EnvVar string
|
||||
}
|
||||
|
||||
// String returns the usage
|
||||
func (f StringSliceFlag) String() string {
|
||||
firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ")
|
||||
pref := prefixFor(firstName)
|
||||
return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f StringSliceFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
newVal := &StringSlice{}
|
||||
for _, s := range strings.Split(envVal, ",") {
|
||||
s = strings.TrimSpace(s)
|
||||
newVal.Set(s)
|
||||
}
|
||||
f.Value = newVal
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
if f.Value == nil {
|
||||
f.Value = &StringSlice{}
|
||||
}
|
||||
set.Var(f.Value, name, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func (f StringSliceFlag) GetName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
// StringSlice is an opaque type for []int to satisfy flag.Value
|
||||
type IntSlice []int
|
||||
|
||||
// Set parses the value into an integer and appends it to the list of values
|
||||
func (f *IntSlice) Set(value string) error {
|
||||
tmp, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
*f = append(*f, tmp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value (for usage defaults)
|
||||
func (f *IntSlice) String() string {
|
||||
return fmt.Sprintf("%d", *f)
|
||||
}
|
||||
|
||||
// Value returns the slice of ints set by this flag
|
||||
func (f *IntSlice) Value() []int {
|
||||
return *f
|
||||
}
|
||||
|
||||
// IntSliceFlag is an int flag that can be specified multiple times on the
|
||||
// command-line
|
||||
type IntSliceFlag struct {
|
||||
Name string
|
||||
Value *IntSlice
|
||||
Usage string
|
||||
EnvVar string
|
||||
}
|
||||
|
||||
// String returns the usage
|
||||
func (f IntSliceFlag) String() string {
|
||||
firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ")
|
||||
pref := prefixFor(firstName)
|
||||
return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f IntSliceFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
newVal := &IntSlice{}
|
||||
for _, s := range strings.Split(envVal, ",") {
|
||||
s = strings.TrimSpace(s)
|
||||
err := newVal.Set(s)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, err.Error())
|
||||
}
|
||||
}
|
||||
f.Value = newVal
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
if f.Value == nil {
|
||||
f.Value = &IntSlice{}
|
||||
}
|
||||
set.Var(f.Value, name, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func (f IntSliceFlag) GetName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
// BoolFlag is a switch that defaults to false
|
||||
type BoolFlag struct {
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
Destination *bool
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value (for usage defaults)
|
||||
func (f BoolFlag) String() string {
|
||||
return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f BoolFlag) Apply(set *flag.FlagSet) {
|
||||
val := false
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
envValBool, err := strconv.ParseBool(envVal)
|
||||
if err == nil {
|
||||
val = envValBool
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
if f.Destination != nil {
|
||||
set.BoolVar(f.Destination, name, val, f.Usage)
|
||||
return
|
||||
}
|
||||
set.Bool(name, val, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func (f BoolFlag) GetName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
// BoolTFlag this represents a boolean flag that is true by default, but can
|
||||
// still be set to false by --some-flag=false
|
||||
type BoolTFlag struct {
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
Destination *bool
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value (for usage defaults)
|
||||
func (f BoolTFlag) String() string {
|
||||
return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f BoolTFlag) Apply(set *flag.FlagSet) {
|
||||
val := true
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
envValBool, err := strconv.ParseBool(envVal)
|
||||
if err == nil {
|
||||
val = envValBool
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
if f.Destination != nil {
|
||||
set.BoolVar(f.Destination, name, val, f.Usage)
|
||||
return
|
||||
}
|
||||
set.Bool(name, val, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func (f BoolTFlag) GetName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
// StringFlag represents a flag that takes as string value
|
||||
type StringFlag struct {
|
||||
Name string
|
||||
Value string
|
||||
Usage string
|
||||
EnvVar string
|
||||
Destination *string
|
||||
}
|
||||
|
||||
// String returns the usage
|
||||
func (f StringFlag) String() string {
|
||||
return withEnvHint(f.EnvVar, fmt.Sprintf("%s %v\t%v", prefixedNames(f.Name), f.FormatValueHelp(), f.Usage))
|
||||
}
|
||||
|
||||
func (f StringFlag) FormatValueHelp() string {
|
||||
s := f.Value
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("\"%s\"", s)
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f StringFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
f.Value = envVal
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
if f.Destination != nil {
|
||||
set.StringVar(f.Destination, name, f.Value, f.Usage)
|
||||
return
|
||||
}
|
||||
set.String(name, f.Value, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func (f StringFlag) GetName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
// IntFlag is a flag that takes an integer
|
||||
// Errors if the value provided cannot be parsed
|
||||
type IntFlag struct {
|
||||
Name string
|
||||
Value int
|
||||
Usage string
|
||||
EnvVar string
|
||||
Destination *int
|
||||
}
|
||||
|
||||
// String returns the usage
|
||||
func (f IntFlag) String() string {
|
||||
return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f IntFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
envValInt, err := strconv.ParseInt(envVal, 0, 64)
|
||||
if err == nil {
|
||||
f.Value = int(envValInt)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
if f.Destination != nil {
|
||||
set.IntVar(f.Destination, name, f.Value, f.Usage)
|
||||
return
|
||||
}
|
||||
set.Int(name, f.Value, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func (f IntFlag) GetName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
// DurationFlag is a flag that takes a duration specified in Go's duration
|
||||
// format: https://golang.org/pkg/time/#ParseDuration
|
||||
type DurationFlag struct {
|
||||
Name string
|
||||
Value time.Duration
|
||||
Usage string
|
||||
EnvVar string
|
||||
Destination *time.Duration
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value (for usage defaults)
|
||||
func (f DurationFlag) String() string {
|
||||
return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f DurationFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
envValDuration, err := time.ParseDuration(envVal)
|
||||
if err == nil {
|
||||
f.Value = envValDuration
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
if f.Destination != nil {
|
||||
set.DurationVar(f.Destination, name, f.Value, f.Usage)
|
||||
return
|
||||
}
|
||||
set.Duration(name, f.Value, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func (f DurationFlag) GetName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
// Float64Flag is a flag that takes an float value
|
||||
// Errors if the value provided cannot be parsed
|
||||
type Float64Flag struct {
|
||||
Name string
|
||||
Value float64
|
||||
Usage string
|
||||
EnvVar string
|
||||
Destination *float64
|
||||
}
|
||||
|
||||
// String returns the usage
|
||||
func (f Float64Flag) String() string {
|
||||
return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f Float64Flag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
envValFloat, err := strconv.ParseFloat(envVal, 10)
|
||||
if err == nil {
|
||||
f.Value = float64(envValFloat)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
if f.Destination != nil {
|
||||
set.Float64Var(f.Destination, name, f.Value, f.Usage)
|
||||
return
|
||||
}
|
||||
set.Float64(name, f.Value, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func (f Float64Flag) GetName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
func prefixFor(name string) (prefix string) {
|
||||
if len(name) == 1 {
|
||||
prefix = "-"
|
||||
} else {
|
||||
prefix = "--"
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func prefixedNames(fullName string) (prefixed string) {
|
||||
parts := strings.Split(fullName, ",")
|
||||
for i, name := range parts {
|
||||
name = strings.Trim(name, " ")
|
||||
prefixed += prefixFor(name) + name
|
||||
if i < len(parts)-1 {
|
||||
prefixed += ", "
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func withEnvHint(envVar, str string) string {
|
||||
envText := ""
|
||||
if envVar != "" {
|
||||
prefix := "$"
|
||||
suffix := ""
|
||||
sep := ", $"
|
||||
if runtime.GOOS == "windows" {
|
||||
prefix = "%"
|
||||
suffix = "%"
|
||||
sep = "%, %"
|
||||
}
|
||||
envText = fmt.Sprintf(" [%s%s%s]", prefix, strings.Join(strings.Split(envVar, ","), sep), suffix)
|
||||
}
|
||||
return str + envText
|
||||
}
|
||||
859
Godeps/_workspace/src/github.com/codegangsta/cli/flag_test.go
generated
vendored
Normal file
859
Godeps/_workspace/src/github.com/codegangsta/cli/flag_test.go
generated
vendored
Normal file
@ -0,0 +1,859 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var boolFlagTests = []struct {
|
||||
name string
|
||||
expected string
|
||||
}{
|
||||
{"help", "--help\t"},
|
||||
{"h", "-h\t"},
|
||||
}
|
||||
|
||||
func TestBoolFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range boolFlagTests {
|
||||
flag := BoolFlag{Name: test.name}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%s does not match %s", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var stringFlagTests = []struct {
|
||||
name string
|
||||
value string
|
||||
expected string
|
||||
}{
|
||||
{"help", "", "--help \t"},
|
||||
{"h", "", "-h \t"},
|
||||
{"h", "", "-h \t"},
|
||||
{"test", "Something", "--test \"Something\"\t"},
|
||||
}
|
||||
|
||||
func TestStringFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range stringFlagTests {
|
||||
flag := StringFlag{Name: test.name, Value: test.value}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%s does not match %s", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_FOO", "derp")
|
||||
for _, test := range stringFlagTests {
|
||||
flag := StringFlag{Name: test.name, Value: test.value, EnvVar: "APP_FOO"}
|
||||
output := flag.String()
|
||||
|
||||
expectedSuffix := " [$APP_FOO]"
|
||||
if runtime.GOOS == "windows" {
|
||||
expectedSuffix = " [%APP_FOO%]"
|
||||
}
|
||||
if !strings.HasSuffix(output, expectedSuffix) {
|
||||
t.Errorf("%s does not end with" + expectedSuffix, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var stringSliceFlagTests = []struct {
|
||||
name string
|
||||
value *StringSlice
|
||||
expected string
|
||||
}{
|
||||
{"help", func() *StringSlice {
|
||||
s := &StringSlice{}
|
||||
s.Set("")
|
||||
return s
|
||||
}(), "--help [--help option --help option]\t"},
|
||||
{"h", func() *StringSlice {
|
||||
s := &StringSlice{}
|
||||
s.Set("")
|
||||
return s
|
||||
}(), "-h [-h option -h option]\t"},
|
||||
{"h", func() *StringSlice {
|
||||
s := &StringSlice{}
|
||||
s.Set("")
|
||||
return s
|
||||
}(), "-h [-h option -h option]\t"},
|
||||
{"test", func() *StringSlice {
|
||||
s := &StringSlice{}
|
||||
s.Set("Something")
|
||||
return s
|
||||
}(), "--test [--test option --test option]\t"},
|
||||
}
|
||||
|
||||
func TestStringSliceFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range stringSliceFlagTests {
|
||||
flag := StringSliceFlag{Name: test.name, Value: test.value}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%q does not match %q", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_QWWX", "11,4")
|
||||
for _, test := range stringSliceFlagTests {
|
||||
flag := StringSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_QWWX"}
|
||||
output := flag.String()
|
||||
|
||||
expectedSuffix := " [$APP_QWWX]"
|
||||
if runtime.GOOS == "windows" {
|
||||
expectedSuffix = " [%APP_QWWX%]"
|
||||
}
|
||||
if !strings.HasSuffix(output, expectedSuffix) {
|
||||
t.Errorf("%q does not end with" + expectedSuffix, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var intFlagTests = []struct {
|
||||
name string
|
||||
expected string
|
||||
}{
|
||||
{"help", "--help \"0\"\t"},
|
||||
{"h", "-h \"0\"\t"},
|
||||
}
|
||||
|
||||
func TestIntFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range intFlagTests {
|
||||
flag := IntFlag{Name: test.name}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%s does not match %s", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_BAR", "2")
|
||||
for _, test := range intFlagTests {
|
||||
flag := IntFlag{Name: test.name, EnvVar: "APP_BAR"}
|
||||
output := flag.String()
|
||||
|
||||
expectedSuffix := " [$APP_BAR]"
|
||||
if runtime.GOOS == "windows" {
|
||||
expectedSuffix = " [%APP_BAR%]"
|
||||
}
|
||||
if !strings.HasSuffix(output, expectedSuffix) {
|
||||
t.Errorf("%s does not end with" + expectedSuffix, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var durationFlagTests = []struct {
|
||||
name string
|
||||
expected string
|
||||
}{
|
||||
{"help", "--help \"0\"\t"},
|
||||
{"h", "-h \"0\"\t"},
|
||||
}
|
||||
|
||||
func TestDurationFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range durationFlagTests {
|
||||
flag := DurationFlag{Name: test.name}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%s does not match %s", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_BAR", "2h3m6s")
|
||||
for _, test := range durationFlagTests {
|
||||
flag := DurationFlag{Name: test.name, EnvVar: "APP_BAR"}
|
||||
output := flag.String()
|
||||
|
||||
expectedSuffix := " [$APP_BAR]"
|
||||
if runtime.GOOS == "windows" {
|
||||
expectedSuffix = " [%APP_BAR%]"
|
||||
}
|
||||
if !strings.HasSuffix(output, expectedSuffix) {
|
||||
t.Errorf("%s does not end with" + expectedSuffix, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var intSliceFlagTests = []struct {
|
||||
name string
|
||||
value *IntSlice
|
||||
expected string
|
||||
}{
|
||||
{"help", &IntSlice{}, "--help [--help option --help option]\t"},
|
||||
{"h", &IntSlice{}, "-h [-h option -h option]\t"},
|
||||
{"h", &IntSlice{}, "-h [-h option -h option]\t"},
|
||||
{"test", func() *IntSlice {
|
||||
i := &IntSlice{}
|
||||
i.Set("9")
|
||||
return i
|
||||
}(), "--test [--test option --test option]\t"},
|
||||
}
|
||||
|
||||
func TestIntSliceFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range intSliceFlagTests {
|
||||
flag := IntSliceFlag{Name: test.name, Value: test.value}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%q does not match %q", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_SMURF", "42,3")
|
||||
for _, test := range intSliceFlagTests {
|
||||
flag := IntSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"}
|
||||
output := flag.String()
|
||||
|
||||
expectedSuffix := " [$APP_SMURF]"
|
||||
if runtime.GOOS == "windows" {
|
||||
expectedSuffix = " [%APP_SMURF%]"
|
||||
}
|
||||
if !strings.HasSuffix(output, expectedSuffix) {
|
||||
t.Errorf("%q does not end with" + expectedSuffix, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var float64FlagTests = []struct {
|
||||
name string
|
||||
expected string
|
||||
}{
|
||||
{"help", "--help \"0\"\t"},
|
||||
{"h", "-h \"0\"\t"},
|
||||
}
|
||||
|
||||
func TestFloat64FlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range float64FlagTests {
|
||||
flag := Float64Flag{Name: test.name}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%s does not match %s", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_BAZ", "99.4")
|
||||
for _, test := range float64FlagTests {
|
||||
flag := Float64Flag{Name: test.name, EnvVar: "APP_BAZ"}
|
||||
output := flag.String()
|
||||
|
||||
expectedSuffix := " [$APP_BAZ]"
|
||||
if runtime.GOOS == "windows" {
|
||||
expectedSuffix = " [%APP_BAZ%]"
|
||||
}
|
||||
if !strings.HasSuffix(output, expectedSuffix) {
|
||||
t.Errorf("%s does not end with" + expectedSuffix, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var genericFlagTests = []struct {
|
||||
name string
|
||||
value Generic
|
||||
expected string
|
||||
}{
|
||||
{"test", &Parser{"abc", "def"}, "--test \"abc,def\"\ttest flag"},
|
||||
{"t", &Parser{"abc", "def"}, "-t \"abc,def\"\ttest flag"},
|
||||
}
|
||||
|
||||
func TestGenericFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range genericFlagTests {
|
||||
flag := GenericFlag{Name: test.name, Value: test.value, Usage: "test flag"}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%q does not match %q", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_ZAP", "3")
|
||||
for _, test := range genericFlagTests {
|
||||
flag := GenericFlag{Name: test.name, EnvVar: "APP_ZAP"}
|
||||
output := flag.String()
|
||||
|
||||
expectedSuffix := " [$APP_ZAP]"
|
||||
if runtime.GOOS == "windows" {
|
||||
expectedSuffix = " [%APP_ZAP%]"
|
||||
}
|
||||
if !strings.HasSuffix(output, expectedSuffix) {
|
||||
t.Errorf("%s does not end with" + expectedSuffix, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMultiString(t *testing.T) {
|
||||
(&App{
|
||||
Flags: []Flag{
|
||||
StringFlag{Name: "serve, s"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if ctx.String("serve") != "10" {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.String("s") != "10" {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run", "-s", "10"})
|
||||
}
|
||||
|
||||
func TestParseDestinationString(t *testing.T) {
|
||||
var dest string
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
StringFlag{
|
||||
Name: "dest",
|
||||
Destination: &dest,
|
||||
},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if dest != "10" {
|
||||
t.Errorf("expected destination String 10")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "--dest", "10"})
|
||||
}
|
||||
|
||||
func TestParseMultiStringFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_COUNT", "20")
|
||||
(&App{
|
||||
Flags: []Flag{
|
||||
StringFlag{Name: "count, c", EnvVar: "APP_COUNT"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if ctx.String("count") != "20" {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.String("c") != "20" {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiStringFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_COUNT", "20")
|
||||
(&App{
|
||||
Flags: []Flag{
|
||||
StringFlag{Name: "count, c", EnvVar: "COMPAT_COUNT,APP_COUNT"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if ctx.String("count") != "20" {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.String("c") != "20" {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiStringSlice(t *testing.T) {
|
||||
(&App{
|
||||
Flags: []Flag{
|
||||
StringSliceFlag{Name: "serve, s", Value: &StringSlice{}},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"10", "20"}) {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.StringSlice("s"), []string{"10", "20"}) {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run", "-s", "10", "-s", "20"})
|
||||
}
|
||||
|
||||
func TestParseMultiStringSliceFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||
|
||||
(&App{
|
||||
Flags: []Flag{
|
||||
StringSliceFlag{Name: "intervals, i", Value: &StringSlice{}, EnvVar: "APP_INTERVALS"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiStringSliceFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||
|
||||
(&App{
|
||||
Flags: []Flag{
|
||||
StringSliceFlag{Name: "intervals, i", Value: &StringSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiInt(t *testing.T) {
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
IntFlag{Name: "serve, s"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if ctx.Int("serve") != 10 {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Int("s") != 10 {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "-s", "10"})
|
||||
}
|
||||
|
||||
func TestParseDestinationInt(t *testing.T) {
|
||||
var dest int
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
IntFlag{
|
||||
Name: "dest",
|
||||
Destination: &dest,
|
||||
},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if dest != 10 {
|
||||
t.Errorf("expected destination Int 10")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "--dest", "10"})
|
||||
}
|
||||
|
||||
func TestParseMultiIntFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_TIMEOUT_SECONDS", "10")
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
IntFlag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if ctx.Int("timeout") != 10 {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Int("t") != 10 {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiIntFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_TIMEOUT_SECONDS", "10")
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
IntFlag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if ctx.Int("timeout") != 10 {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Int("t") != 10 {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiIntSlice(t *testing.T) {
|
||||
(&App{
|
||||
Flags: []Flag{
|
||||
IntSliceFlag{Name: "serve, s", Value: &IntSlice{}},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run", "-s", "10", "-s", "20"})
|
||||
}
|
||||
|
||||
func TestParseMultiIntSliceFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||
|
||||
(&App{
|
||||
Flags: []Flag{
|
||||
IntSliceFlag{Name: "intervals, i", Value: &IntSlice{}, EnvVar: "APP_INTERVALS"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiIntSliceFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||
|
||||
(&App{
|
||||
Flags: []Flag{
|
||||
IntSliceFlag{Name: "intervals, i", Value: &IntSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiFloat64(t *testing.T) {
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
Float64Flag{Name: "serve, s"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if ctx.Float64("serve") != 10.2 {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Float64("s") != 10.2 {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "-s", "10.2"})
|
||||
}
|
||||
|
||||
func TestParseDestinationFloat64(t *testing.T) {
|
||||
var dest float64
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
Float64Flag{
|
||||
Name: "dest",
|
||||
Destination: &dest,
|
||||
},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if dest != 10.2 {
|
||||
t.Errorf("expected destination Float64 10.2")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "--dest", "10.2"})
|
||||
}
|
||||
|
||||
func TestParseMultiFloat64FromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if ctx.Float64("timeout") != 15.5 {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Float64("t") != 15.5 {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiFloat64FromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
Float64Flag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if ctx.Float64("timeout") != 15.5 {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Float64("t") != 15.5 {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiBool(t *testing.T) {
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
BoolFlag{Name: "serve, s"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if ctx.Bool("serve") != true {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Bool("s") != true {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "--serve"})
|
||||
}
|
||||
|
||||
func TestParseDestinationBool(t *testing.T) {
|
||||
var dest bool
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
BoolFlag{
|
||||
Name: "dest",
|
||||
Destination: &dest,
|
||||
},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if dest != true {
|
||||
t.Errorf("expected destination Bool true")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "--dest"})
|
||||
}
|
||||
|
||||
func TestParseMultiBoolFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_DEBUG", "1")
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if ctx.Bool("debug") != true {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if ctx.Bool("d") != true {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiBoolFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_DEBUG", "1")
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
BoolFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if ctx.Bool("debug") != true {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if ctx.Bool("d") != true {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiBoolT(t *testing.T) {
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
BoolTFlag{Name: "serve, s"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if ctx.BoolT("serve") != true {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.BoolT("s") != true {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "--serve"})
|
||||
}
|
||||
|
||||
func TestParseDestinationBoolT(t *testing.T) {
|
||||
var dest bool
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
BoolTFlag{
|
||||
Name: "dest",
|
||||
Destination: &dest,
|
||||
},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if dest != true {
|
||||
t.Errorf("expected destination BoolT true")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "--dest"})
|
||||
}
|
||||
|
||||
func TestParseMultiBoolTFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_DEBUG", "0")
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
BoolTFlag{Name: "debug, d", EnvVar: "APP_DEBUG"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if ctx.BoolT("debug") != false {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if ctx.BoolT("d") != false {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiBoolTFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_DEBUG", "0")
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
BoolTFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if ctx.BoolT("debug") != false {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if ctx.BoolT("d") != false {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
type Parser [2]string
|
||||
|
||||
func (p *Parser) Set(value string) error {
|
||||
parts := strings.Split(value, ",")
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("invalid format")
|
||||
}
|
||||
|
||||
(*p)[0] = parts[0]
|
||||
(*p)[1] = parts[1]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Parser) String() string {
|
||||
return fmt.Sprintf("%s,%s", p[0], p[1])
|
||||
}
|
||||
|
||||
func TestParseGeneric(t *testing.T) {
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
GenericFlag{Name: "serve, s", Value: &Parser{}},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"10", "20"}) {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "-s", "10,20"})
|
||||
}
|
||||
|
||||
func TestParseGenericFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_SERVE", "20,30")
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
GenericFlag{Name: "serve, s", Value: &Parser{}, EnvVar: "APP_SERVE"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"20", "30"}) {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseGenericFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_FOO", "99,2000")
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
GenericFlag{Name: "foos", Value: &Parser{}, EnvVar: "COMPAT_FOO,APP_FOO"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if !reflect.DeepEqual(ctx.Generic("foos"), &Parser{"99", "2000"}) {
|
||||
t.Errorf("value not set from env")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
248
Godeps/_workspace/src/github.com/codegangsta/cli/help.go
generated
vendored
Normal file
248
Godeps/_workspace/src/github.com/codegangsta/cli/help.go
generated
vendored
Normal file
@ -0,0 +1,248 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// The text template for the Default help topic.
|
||||
// cli.go uses text/template to render templates. You can
|
||||
// render custom help text by setting this variable.
|
||||
var AppHelpTemplate = `NAME:
|
||||
{{.Name}} - {{.Usage}}
|
||||
|
||||
USAGE:
|
||||
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .Flags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}
|
||||
{{if .Version}}
|
||||
VERSION:
|
||||
{{.Version}}
|
||||
{{end}}{{if len .Authors}}
|
||||
AUTHOR(S):
|
||||
{{range .Authors}}{{ . }}{{end}}
|
||||
{{end}}{{if .Commands}}
|
||||
COMMANDS:
|
||||
{{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
|
||||
{{end}}{{end}}{{if .Flags}}
|
||||
GLOBAL OPTIONS:
|
||||
{{range .Flags}}{{.}}
|
||||
{{end}}{{end}}{{if .Copyright }}
|
||||
COPYRIGHT:
|
||||
{{.Copyright}}
|
||||
{{end}}
|
||||
`
|
||||
|
||||
// The text template for the command help topic.
|
||||
// cli.go uses text/template to render templates. You can
|
||||
// render custom help text by setting this variable.
|
||||
var CommandHelpTemplate = `NAME:
|
||||
{{.HelpName}} - {{.Usage}}
|
||||
|
||||
USAGE:
|
||||
{{.HelpName}}{{if .Flags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{if .Description}}
|
||||
|
||||
DESCRIPTION:
|
||||
{{.Description}}{{end}}{{if .Flags}}
|
||||
|
||||
OPTIONS:
|
||||
{{range .Flags}}{{.}}
|
||||
{{end}}{{ end }}
|
||||
`
|
||||
|
||||
// The text template for the subcommand help topic.
|
||||
// cli.go uses text/template to render templates. You can
|
||||
// render custom help text by setting this variable.
|
||||
var SubcommandHelpTemplate = `NAME:
|
||||
{{.HelpName}} - {{.Usage}}
|
||||
|
||||
USAGE:
|
||||
{{.HelpName}} command{{if .Flags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
|
||||
|
||||
COMMANDS:
|
||||
{{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
|
||||
{{end}}{{if .Flags}}
|
||||
OPTIONS:
|
||||
{{range .Flags}}{{.}}
|
||||
{{end}}{{end}}
|
||||
`
|
||||
|
||||
var helpCommand = Command{
|
||||
Name: "help",
|
||||
Aliases: []string{"h"},
|
||||
Usage: "Shows a list of commands or help for one command",
|
||||
ArgsUsage: "[command]",
|
||||
Action: func(c *Context) {
|
||||
args := c.Args()
|
||||
if args.Present() {
|
||||
ShowCommandHelp(c, args.First())
|
||||
} else {
|
||||
ShowAppHelp(c)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
var helpSubcommand = Command{
|
||||
Name: "help",
|
||||
Aliases: []string{"h"},
|
||||
Usage: "Shows a list of commands or help for one command",
|
||||
ArgsUsage: "[command]",
|
||||
Action: func(c *Context) {
|
||||
args := c.Args()
|
||||
if args.Present() {
|
||||
ShowCommandHelp(c, args.First())
|
||||
} else {
|
||||
ShowSubcommandHelp(c)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Prints help for the App or Command
|
||||
type helpPrinter func(w io.Writer, templ string, data interface{})
|
||||
|
||||
var HelpPrinter helpPrinter = printHelp
|
||||
|
||||
// Prints version for the App
|
||||
var VersionPrinter = printVersion
|
||||
|
||||
func ShowAppHelp(c *Context) {
|
||||
HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
|
||||
}
|
||||
|
||||
// Prints the list of subcommands as the default app completion method
|
||||
func DefaultAppComplete(c *Context) {
|
||||
for _, command := range c.App.Commands {
|
||||
for _, name := range command.Names() {
|
||||
fmt.Fprintln(c.App.Writer, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prints help for the given command
|
||||
func ShowCommandHelp(ctx *Context, command string) {
|
||||
// show the subcommand help for a command with subcommands
|
||||
if command == "" {
|
||||
HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App)
|
||||
return
|
||||
}
|
||||
|
||||
for _, c := range ctx.App.Commands {
|
||||
if c.HasName(command) {
|
||||
HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.App.CommandNotFound != nil {
|
||||
ctx.App.CommandNotFound(ctx, command)
|
||||
} else {
|
||||
fmt.Fprintf(ctx.App.Writer, "No help topic for '%v'\n", command)
|
||||
}
|
||||
}
|
||||
|
||||
// Prints help for the given subcommand
|
||||
func ShowSubcommandHelp(c *Context) {
|
||||
ShowCommandHelp(c, c.Command.Name)
|
||||
}
|
||||
|
||||
// Prints the version number of the App
|
||||
func ShowVersion(c *Context) {
|
||||
VersionPrinter(c)
|
||||
}
|
||||
|
||||
func printVersion(c *Context) {
|
||||
fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version)
|
||||
}
|
||||
|
||||
// Prints the lists of commands within a given context
|
||||
func ShowCompletions(c *Context) {
|
||||
a := c.App
|
||||
if a != nil && a.BashComplete != nil {
|
||||
a.BashComplete(c)
|
||||
}
|
||||
}
|
||||
|
||||
// Prints the custom completions for a given command
|
||||
func ShowCommandCompletions(ctx *Context, command string) {
|
||||
c := ctx.App.Command(command)
|
||||
if c != nil && c.BashComplete != nil {
|
||||
c.BashComplete(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func printHelp(out io.Writer, templ string, data interface{}) {
|
||||
funcMap := template.FuncMap{
|
||||
"join": strings.Join,
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(out, 0, 8, 1, '\t', 0)
|
||||
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
|
||||
err := t.Execute(w, data)
|
||||
if err != nil {
|
||||
// If the writer is closed, t.Execute will fail, and there's nothing
|
||||
// we can do to recover. We could send this to os.Stderr if we need.
|
||||
return
|
||||
}
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
func checkVersion(c *Context) bool {
|
||||
found := false
|
||||
if VersionFlag.Name != "" {
|
||||
eachName(VersionFlag.Name, func(name string) {
|
||||
if c.GlobalBool(name) || c.Bool(name) {
|
||||
found = true
|
||||
}
|
||||
})
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
func checkHelp(c *Context) bool {
|
||||
found := false
|
||||
if HelpFlag.Name != "" {
|
||||
eachName(HelpFlag.Name, func(name string) {
|
||||
if c.GlobalBool(name) || c.Bool(name) {
|
||||
found = true
|
||||
}
|
||||
})
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
func checkCommandHelp(c *Context, name string) bool {
|
||||
if c.Bool("h") || c.Bool("help") {
|
||||
ShowCommandHelp(c, name)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func checkSubcommandHelp(c *Context) bool {
|
||||
if c.GlobalBool("h") || c.GlobalBool("help") {
|
||||
ShowSubcommandHelp(c)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func checkCompletions(c *Context) bool {
|
||||
if (c.GlobalBool(BashCompletionFlag.Name) || c.Bool(BashCompletionFlag.Name)) && c.App.EnableBashCompletion {
|
||||
ShowCompletions(c)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func checkCommandCompletions(c *Context, name string) bool {
|
||||
if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion {
|
||||
ShowCommandCompletions(c, name)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
94
Godeps/_workspace/src/github.com/codegangsta/cli/help_test.go
generated
vendored
Normal file
94
Godeps/_workspace/src/github.com/codegangsta/cli/help_test.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_ShowAppHelp_NoAuthor(t *testing.T) {
|
||||
output := new(bytes.Buffer)
|
||||
app := NewApp()
|
||||
app.Writer = output
|
||||
|
||||
c := NewContext(app, nil, nil)
|
||||
|
||||
ShowAppHelp(c)
|
||||
|
||||
if bytes.Index(output.Bytes(), []byte("AUTHOR(S):")) != -1 {
|
||||
t.Errorf("expected\n%snot to include %s", output.String(), "AUTHOR(S):")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ShowAppHelp_NoVersion(t *testing.T) {
|
||||
output := new(bytes.Buffer)
|
||||
app := NewApp()
|
||||
app.Writer = output
|
||||
|
||||
app.Version = ""
|
||||
|
||||
c := NewContext(app, nil, nil)
|
||||
|
||||
ShowAppHelp(c)
|
||||
|
||||
if bytes.Index(output.Bytes(), []byte("VERSION:")) != -1 {
|
||||
t.Errorf("expected\n%snot to include %s", output.String(), "VERSION:")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Help_Custom_Flags(t *testing.T) {
|
||||
oldFlag := HelpFlag
|
||||
defer func() {
|
||||
HelpFlag = oldFlag
|
||||
}()
|
||||
|
||||
HelpFlag = BoolFlag{
|
||||
Name: "help, x",
|
||||
Usage: "show help",
|
||||
}
|
||||
|
||||
app := App{
|
||||
Flags: []Flag{
|
||||
BoolFlag{Name: "foo, h"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if ctx.Bool("h") != true {
|
||||
t.Errorf("custom help flag not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
output := new(bytes.Buffer)
|
||||
app.Writer = output
|
||||
app.Run([]string{"test", "-h"})
|
||||
if output.Len() > 0 {
|
||||
t.Errorf("unexpected output: %s", output.String())
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Version_Custom_Flags(t *testing.T) {
|
||||
oldFlag := VersionFlag
|
||||
defer func() {
|
||||
VersionFlag = oldFlag
|
||||
}()
|
||||
|
||||
VersionFlag = BoolFlag{
|
||||
Name: "version, V",
|
||||
Usage: "show version",
|
||||
}
|
||||
|
||||
app := App{
|
||||
Flags: []Flag{
|
||||
BoolFlag{Name: "foo, v"},
|
||||
},
|
||||
Action: func(ctx *Context) {
|
||||
if ctx.Bool("v") != true {
|
||||
t.Errorf("custom version flag not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
output := new(bytes.Buffer)
|
||||
app.Writer = output
|
||||
app.Run([]string{"test", "-v"})
|
||||
if output.Len() > 0 {
|
||||
t.Errorf("unexpected output: %s", output.String())
|
||||
}
|
||||
}
|
||||
19
Godeps/_workspace/src/github.com/codegangsta/cli/helpers_test.go
generated
vendored
Normal file
19
Godeps/_workspace/src/github.com/codegangsta/cli/helpers_test.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
/* Test Helpers */
|
||||
func expect(t *testing.T, a interface{}, b interface{}) {
|
||||
if !reflect.DeepEqual(a, b) {
|
||||
t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
||||
}
|
||||
}
|
||||
|
||||
func refute(t *testing.T, a interface{}, b interface{}) {
|
||||
if reflect.DeepEqual(a, b) {
|
||||
t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
||||
}
|
||||
}
|
||||
209
Godeps/_workspace/src/github.com/coreos/go-semver/semver/semver.go
generated
vendored
209
Godeps/_workspace/src/github.com/coreos/go-semver/semver/semver.go
generated
vendored
@ -1,209 +0,0 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Version struct {
|
||||
Major int64
|
||||
Minor int64
|
||||
Patch int64
|
||||
PreRelease PreRelease
|
||||
Metadata string
|
||||
}
|
||||
|
||||
type PreRelease string
|
||||
|
||||
func splitOff(input *string, delim string) (val string) {
|
||||
parts := strings.SplitN(*input, delim, 2)
|
||||
|
||||
if len(parts) == 2 {
|
||||
*input = parts[0]
|
||||
val = parts[1]
|
||||
}
|
||||
|
||||
return val
|
||||
}
|
||||
|
||||
func NewVersion(version string) (*Version, error) {
|
||||
v := Version{}
|
||||
|
||||
dotParts := strings.SplitN(version, ".", 3)
|
||||
|
||||
if len(dotParts) != 3 {
|
||||
return nil, errors.New(fmt.Sprintf("%s is not in dotted-tri format", version))
|
||||
}
|
||||
|
||||
v.Metadata = splitOff(&dotParts[2], "+")
|
||||
v.PreRelease = PreRelease(splitOff(&dotParts[2], "-"))
|
||||
|
||||
parsed := make([]int64, 3, 3)
|
||||
|
||||
for i, v := range dotParts[:3] {
|
||||
val, err := strconv.ParseInt(v, 10, 64)
|
||||
parsed[i] = val
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
v.Major = parsed[0]
|
||||
v.Minor = parsed[1]
|
||||
v.Patch = parsed[2]
|
||||
|
||||
return &v, nil
|
||||
}
|
||||
|
||||
func Must(v *Version, err error) *Version {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *Version) String() string {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
base := fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
|
||||
buffer.WriteString(base)
|
||||
|
||||
if v.PreRelease != "" {
|
||||
buffer.WriteString(fmt.Sprintf("-%s", v.PreRelease))
|
||||
}
|
||||
|
||||
if v.Metadata != "" {
|
||||
buffer.WriteString(fmt.Sprintf("+%s", v.Metadata))
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func (v *Version) LessThan(versionB Version) bool {
|
||||
versionA := *v
|
||||
cmp := recursiveCompare(versionA.Slice(), versionB.Slice())
|
||||
|
||||
if cmp == 0 {
|
||||
cmp = preReleaseCompare(versionA, versionB)
|
||||
}
|
||||
|
||||
if cmp == -1 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/* Slice converts the comparable parts of the semver into a slice of strings */
|
||||
func (v *Version) Slice() []int64 {
|
||||
return []int64{v.Major, v.Minor, v.Patch}
|
||||
}
|
||||
|
||||
func (p *PreRelease) Slice() []string {
|
||||
preRelease := string(*p)
|
||||
return strings.Split(preRelease, ".")
|
||||
}
|
||||
|
||||
func preReleaseCompare(versionA Version, versionB Version) int {
|
||||
a := versionA.PreRelease
|
||||
b := versionB.PreRelease
|
||||
|
||||
/* Handle the case where if two versions are otherwise equal it is the
|
||||
* one without a PreRelease that is greater */
|
||||
if len(a) == 0 && (len(b) > 0) {
|
||||
return 1
|
||||
} else if len(b) == 0 && (len(a) > 0) {
|
||||
return -1
|
||||
}
|
||||
|
||||
// If there is a prelease, check and compare each part.
|
||||
return recursivePreReleaseCompare(a.Slice(), b.Slice())
|
||||
}
|
||||
|
||||
func recursiveCompare(versionA []int64, versionB []int64) int {
|
||||
if len(versionA) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
a := versionA[0]
|
||||
b := versionB[0]
|
||||
|
||||
if a > b {
|
||||
return 1
|
||||
} else if a < b {
|
||||
return -1
|
||||
}
|
||||
|
||||
return recursiveCompare(versionA[1:], versionB[1:])
|
||||
}
|
||||
|
||||
func recursivePreReleaseCompare(versionA []string, versionB []string) int {
|
||||
// Handle slice length disparity.
|
||||
if len(versionA) == 0 {
|
||||
// Nothing to compare too, so we return 0
|
||||
return 0
|
||||
} else if len(versionB) == 0 {
|
||||
// We're longer than versionB so return 1.
|
||||
return 1
|
||||
}
|
||||
|
||||
a := versionA[0]
|
||||
b := versionB[0]
|
||||
|
||||
aInt := false; bInt := false
|
||||
|
||||
aI, err := strconv.Atoi(versionA[0])
|
||||
if err == nil {
|
||||
aInt = true
|
||||
}
|
||||
|
||||
bI, err := strconv.Atoi(versionB[0])
|
||||
if err == nil {
|
||||
bInt = true
|
||||
}
|
||||
|
||||
// Handle Integer Comparison
|
||||
if aInt && bInt {
|
||||
if aI > bI {
|
||||
return 1
|
||||
} else if aI < bI {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// Handle String Comparison
|
||||
if a > b {
|
||||
return 1
|
||||
} else if a < b {
|
||||
return -1
|
||||
}
|
||||
|
||||
return recursivePreReleaseCompare(versionA[1:], versionB[1:])
|
||||
}
|
||||
|
||||
// BumpMajor increments the Major field by 1 and resets all other fields to their default values
|
||||
func (v *Version) BumpMajor() {
|
||||
v.Major += 1
|
||||
v.Minor = 0
|
||||
v.Patch = 0
|
||||
v.PreRelease = PreRelease("")
|
||||
v.Metadata = ""
|
||||
}
|
||||
|
||||
// BumpMinor increments the Minor field by 1 and resets all other fields to their default values
|
||||
func (v *Version) BumpMinor() {
|
||||
v.Minor += 1
|
||||
v.Patch = 0
|
||||
v.PreRelease = PreRelease("")
|
||||
v.Metadata = ""
|
||||
}
|
||||
|
||||
// BumpPatch increments the Patch field by 1 and resets all other fields to their default values
|
||||
func (v *Version) BumpPatch() {
|
||||
v.Patch += 1
|
||||
v.PreRelease = PreRelease("")
|
||||
v.Metadata = ""
|
||||
}
|
||||
223
Godeps/_workspace/src/github.com/coreos/go-semver/semver/semver_test.go
generated
vendored
223
Godeps/_workspace/src/github.com/coreos/go-semver/semver/semver_test.go
generated
vendored
@ -1,223 +0,0 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type fixture struct {
|
||||
greaterVersion string
|
||||
lesserVersion string
|
||||
}
|
||||
|
||||
var fixtures = []fixture{
|
||||
fixture{"0.0.0", "0.0.0-foo"},
|
||||
fixture{"0.0.1", "0.0.0"},
|
||||
fixture{"1.0.0", "0.9.9"},
|
||||
fixture{"0.10.0", "0.9.0"},
|
||||
fixture{"0.99.0", "0.10.0"},
|
||||
fixture{"2.0.0", "1.2.3"},
|
||||
fixture{"0.0.0", "0.0.0-foo"},
|
||||
fixture{"0.0.1", "0.0.0"},
|
||||
fixture{"1.0.0", "0.9.9"},
|
||||
fixture{"0.10.0", "0.9.0"},
|
||||
fixture{"0.99.0", "0.10.0"},
|
||||
fixture{"2.0.0", "1.2.3"},
|
||||
fixture{"0.0.0", "0.0.0-foo"},
|
||||
fixture{"0.0.1", "0.0.0"},
|
||||
fixture{"1.0.0", "0.9.9"},
|
||||
fixture{"0.10.0", "0.9.0"},
|
||||
fixture{"0.99.0", "0.10.0"},
|
||||
fixture{"2.0.0", "1.2.3"},
|
||||
fixture{"1.2.3", "1.2.3-asdf"},
|
||||
fixture{"1.2.3", "1.2.3-4"},
|
||||
fixture{"1.2.3", "1.2.3-4-foo"},
|
||||
fixture{"1.2.3-5-foo", "1.2.3-5"},
|
||||
fixture{"1.2.3-5", "1.2.3-4"},
|
||||
fixture{"1.2.3-5-foo", "1.2.3-5-Foo"},
|
||||
fixture{"3.0.0", "2.7.2+asdf"},
|
||||
fixture{"3.0.0+foobar", "2.7.2"},
|
||||
fixture{"1.2.3-a.10", "1.2.3-a.5"},
|
||||
fixture{"1.2.3-a.b", "1.2.3-a.5"},
|
||||
fixture{"1.2.3-a.b", "1.2.3-a"},
|
||||
fixture{"1.2.3-a.b.c.10.d.5", "1.2.3-a.b.c.5.d.100"},
|
||||
fixture{"1.0.0", "1.0.0-rc.1"},
|
||||
fixture{"1.0.0-rc.2", "1.0.0-rc.1"},
|
||||
fixture{"1.0.0-rc.1", "1.0.0-beta.11"},
|
||||
fixture{"1.0.0-beta.11", "1.0.0-beta.2"},
|
||||
fixture{"1.0.0-beta.2", "1.0.0-beta"},
|
||||
fixture{"1.0.0-beta", "1.0.0-alpha.beta"},
|
||||
fixture{"1.0.0-alpha.beta", "1.0.0-alpha.1"},
|
||||
fixture{"1.0.0-alpha.1", "1.0.0-alpha"},
|
||||
}
|
||||
|
||||
func TestCompare(t *testing.T) {
|
||||
for _, v := range fixtures {
|
||||
gt, err := NewVersion(v.greaterVersion)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
lt, err := NewVersion(v.lesserVersion)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if gt.LessThan(*lt) == true {
|
||||
t.Errorf("%s should not be less than %s", gt, lt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testString(t *testing.T, orig string, version *Version) {
|
||||
if orig != version.String() {
|
||||
t.Errorf("%s != %s", orig, version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
for _, v := range fixtures {
|
||||
gt, err := NewVersion(v.greaterVersion)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
testString(t, v.greaterVersion, gt)
|
||||
|
||||
lt, err := NewVersion(v.lesserVersion)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
testString(t, v.lesserVersion, lt)
|
||||
}
|
||||
}
|
||||
|
||||
func shuffleStringSlice(src []string) []string {
|
||||
dest := make([]string, len(src))
|
||||
rand.Seed(time.Now().Unix())
|
||||
perm := rand.Perm(len(src))
|
||||
for i, v := range perm {
|
||||
dest[v] = src[i]
|
||||
}
|
||||
return dest
|
||||
}
|
||||
|
||||
func TestSort(t *testing.T) {
|
||||
sortedVersions := []string{"1.0.0", "1.0.2", "1.2.0", "3.1.1"}
|
||||
unsortedVersions := shuffleStringSlice(sortedVersions)
|
||||
|
||||
semvers := []*Version{}
|
||||
for _, v := range unsortedVersions {
|
||||
sv, err := NewVersion(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
semvers = append(semvers, sv)
|
||||
}
|
||||
|
||||
Sort(semvers)
|
||||
|
||||
for idx, sv := range semvers {
|
||||
if sv.String() != sortedVersions[idx] {
|
||||
t.Fatalf("incorrect sort at index %v", idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBumpMajor(t *testing.T) {
|
||||
version, _ := NewVersion("1.0.0")
|
||||
version.BumpMajor()
|
||||
if version.Major != 2 {
|
||||
t.Fatalf("bumping major on 1.0.0 resulted in %v", version)
|
||||
}
|
||||
|
||||
version, _ = NewVersion("1.5.2")
|
||||
version.BumpMajor()
|
||||
if version.Minor != 0 && version.Patch != 0 {
|
||||
t.Fatalf("bumping major on 1.5.2 resulted in %v", version)
|
||||
}
|
||||
|
||||
version, _ = NewVersion("1.0.0+build.1-alpha.1")
|
||||
version.BumpMajor()
|
||||
if version.PreRelease != "" && version.PreRelease != "" {
|
||||
t.Fatalf("bumping major on 1.0.0+build.1-alpha.1 resulted in %v", version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBumpMinor(t *testing.T) {
|
||||
version, _ := NewVersion("1.0.0")
|
||||
version.BumpMinor()
|
||||
|
||||
if version.Major != 1 {
|
||||
t.Fatalf("bumping minor on 1.0.0 resulted in %v", version)
|
||||
}
|
||||
|
||||
if version.Minor != 1 {
|
||||
t.Fatalf("bumping major on 1.0.0 resulted in %v", version)
|
||||
}
|
||||
|
||||
version, _ = NewVersion("1.0.0+build.1-alpha.1")
|
||||
version.BumpMinor()
|
||||
if version.PreRelease != "" && version.PreRelease != "" {
|
||||
t.Fatalf("bumping major on 1.0.0+build.1-alpha.1 resulted in %v", version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBumpPatch(t *testing.T) {
|
||||
version, _ := NewVersion("1.0.0")
|
||||
version.BumpPatch()
|
||||
|
||||
if version.Major != 1 {
|
||||
t.Fatalf("bumping minor on 1.0.0 resulted in %v", version)
|
||||
}
|
||||
|
||||
if version.Minor != 0 {
|
||||
t.Fatalf("bumping major on 1.0.0 resulted in %v", version)
|
||||
}
|
||||
|
||||
if version.Patch != 1 {
|
||||
t.Fatalf("bumping major on 1.0.0 resulted in %v", version)
|
||||
}
|
||||
|
||||
version, _ = NewVersion("1.0.0+build.1-alpha.1")
|
||||
version.BumpPatch()
|
||||
if version.PreRelease != "" && version.PreRelease != "" {
|
||||
t.Fatalf("bumping major on 1.0.0+build.1-alpha.1 resulted in %v", version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMust(t *testing.T) {
|
||||
tests := []struct {
|
||||
versionStr string
|
||||
|
||||
version *Version
|
||||
recov interface{}
|
||||
}{
|
||||
{
|
||||
versionStr: "1.0.0",
|
||||
version: &Version{Major: 1},
|
||||
},
|
||||
{
|
||||
versionStr: "version number",
|
||||
recov: errors.New("version number is not in dotted-tri format"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
func() {
|
||||
defer func() {
|
||||
recov := recover()
|
||||
if !reflect.DeepEqual(tt.recov, recov) {
|
||||
t.Fatalf("incorrect panic for %q: want %v, got %v", tt.versionStr, tt.recov, recov)
|
||||
}
|
||||
}()
|
||||
|
||||
version := Must(NewVersion(tt.versionStr))
|
||||
if !reflect.DeepEqual(tt.version, version) {
|
||||
t.Fatalf("incorrect version for %q: want %+v, got %+v", tt.versionStr, tt.version, version)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
24
Godeps/_workspace/src/github.com/coreos/go-semver/semver/sort.go
generated
vendored
24
Godeps/_workspace/src/github.com/coreos/go-semver/semver/sort.go
generated
vendored
@ -1,24 +0,0 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
type Versions []*Version
|
||||
|
||||
func (s Versions) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func (s Versions) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
func (s Versions) Less(i, j int) bool {
|
||||
return s[i].LessThan(*s[j])
|
||||
}
|
||||
|
||||
// Sort sorts the given slice of Version
|
||||
func Sort(versions []*Version) {
|
||||
sort.Sort(Versions(versions))
|
||||
}
|
||||
74
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/attempt.go
generated
vendored
74
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/attempt.go
generated
vendored
@ -1,74 +0,0 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// AttemptStrategy represents a strategy for waiting for an action
|
||||
// to complete successfully. This is an internal type used by the
|
||||
// implementation of other goamz packages.
|
||||
type AttemptStrategy struct {
|
||||
Total time.Duration // total duration of attempt.
|
||||
Delay time.Duration // interval between each try in the burst.
|
||||
Min int // minimum number of retries; overrides Total
|
||||
}
|
||||
|
||||
type Attempt struct {
|
||||
strategy AttemptStrategy
|
||||
last time.Time
|
||||
end time.Time
|
||||
force bool
|
||||
count int
|
||||
}
|
||||
|
||||
// Start begins a new sequence of attempts for the given strategy.
|
||||
func (s AttemptStrategy) Start() *Attempt {
|
||||
now := time.Now()
|
||||
return &Attempt{
|
||||
strategy: s,
|
||||
last: now,
|
||||
end: now.Add(s.Total),
|
||||
force: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Next waits until it is time to perform the next attempt or returns
|
||||
// false if it is time to stop trying.
|
||||
func (a *Attempt) Next() bool {
|
||||
now := time.Now()
|
||||
sleep := a.nextSleep(now)
|
||||
if !a.force && !now.Add(sleep).Before(a.end) && a.strategy.Min <= a.count {
|
||||
return false
|
||||
}
|
||||
a.force = false
|
||||
if sleep > 0 && a.count > 0 {
|
||||
time.Sleep(sleep)
|
||||
now = time.Now()
|
||||
}
|
||||
a.count++
|
||||
a.last = now
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *Attempt) nextSleep(now time.Time) time.Duration {
|
||||
sleep := a.strategy.Delay - now.Sub(a.last)
|
||||
if sleep < 0 {
|
||||
return 0
|
||||
}
|
||||
return sleep
|
||||
}
|
||||
|
||||
// HasNext returns whether another attempt will be made if the current
|
||||
// one fails. If it returns true, the following call to Next is
|
||||
// guaranteed to return true.
|
||||
func (a *Attempt) HasNext() bool {
|
||||
if a.force || a.strategy.Min > a.count {
|
||||
return true
|
||||
}
|
||||
now := time.Now()
|
||||
if now.Add(a.nextSleep(now)).Before(a.end) {
|
||||
a.force = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
57
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/attempt_test.go
generated
vendored
57
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/attempt_test.go
generated
vendored
@ -1,57 +0,0 @@
|
||||
package aws_test
|
||||
|
||||
import (
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/crowdmob/goamz/aws"
|
||||
"gopkg.in/check.v1"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (S) TestAttemptTiming(c *check.C) {
|
||||
testAttempt := aws.AttemptStrategy{
|
||||
Total: 0.25e9,
|
||||
Delay: 0.1e9,
|
||||
}
|
||||
want := []time.Duration{0, 0.1e9, 0.2e9, 0.2e9}
|
||||
got := make([]time.Duration, 0, len(want)) // avoid allocation when testing timing
|
||||
t0 := time.Now()
|
||||
for a := testAttempt.Start(); a.Next(); {
|
||||
got = append(got, time.Now().Sub(t0))
|
||||
}
|
||||
got = append(got, time.Now().Sub(t0))
|
||||
c.Assert(got, check.HasLen, len(want))
|
||||
const margin = 0.01e9
|
||||
for i, got := range want {
|
||||
lo := want[i] - margin
|
||||
hi := want[i] + margin
|
||||
if got < lo || got > hi {
|
||||
c.Errorf("attempt %d want %g got %g", i, want[i].Seconds(), got.Seconds())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (S) TestAttemptNextHasNext(c *check.C) {
|
||||
a := aws.AttemptStrategy{}.Start()
|
||||
c.Assert(a.Next(), check.Equals, true)
|
||||
c.Assert(a.Next(), check.Equals, false)
|
||||
|
||||
a = aws.AttemptStrategy{}.Start()
|
||||
c.Assert(a.Next(), check.Equals, true)
|
||||
c.Assert(a.HasNext(), check.Equals, false)
|
||||
c.Assert(a.Next(), check.Equals, false)
|
||||
|
||||
a = aws.AttemptStrategy{Total: 2e8}.Start()
|
||||
c.Assert(a.Next(), check.Equals, true)
|
||||
c.Assert(a.HasNext(), check.Equals, true)
|
||||
time.Sleep(2e8)
|
||||
c.Assert(a.HasNext(), check.Equals, true)
|
||||
c.Assert(a.Next(), check.Equals, true)
|
||||
c.Assert(a.Next(), check.Equals, false)
|
||||
|
||||
a = aws.AttemptStrategy{Total: 1e8, Min: 2}.Start()
|
||||
time.Sleep(1e8)
|
||||
c.Assert(a.Next(), check.Equals, true)
|
||||
c.Assert(a.HasNext(), check.Equals, true)
|
||||
c.Assert(a.Next(), check.Equals, true)
|
||||
c.Assert(a.HasNext(), check.Equals, false)
|
||||
c.Assert(a.Next(), check.Equals, false)
|
||||
}
|
||||
624
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/aws.go
generated
vendored
624
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/aws.go
generated
vendored
@ -1,624 +0,0 @@
|
||||
//
|
||||
// goamz - Go packages to interact with the Amazon Web Services.
|
||||
//
|
||||
// https://wiki.ubuntu.com/goamz
|
||||
//
|
||||
// Copyright (c) 2011 Canonical Ltd.
|
||||
//
|
||||
// Written by Gustavo Niemeyer <gustavo.niemeyer@canonical.com>
|
||||
//
|
||||
package aws
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Regular expressions for INI files
|
||||
var (
|
||||
iniSectionRegexp = regexp.MustCompile(`^\s*\[([^\[\]]+)\]\s*$`)
|
||||
iniSettingRegexp = regexp.MustCompile(`^\s*(.+?)\s*=\s*(.*\S)\s*$`)
|
||||
)
|
||||
|
||||
// Defines the valid signers
|
||||
const (
|
||||
V2Signature = iota
|
||||
V4Signature = iota
|
||||
Route53Signature = iota
|
||||
)
|
||||
|
||||
// Defines the service endpoint and correct Signer implementation to use
|
||||
// to sign requests for this endpoint
|
||||
type ServiceInfo struct {
|
||||
Endpoint string
|
||||
Signer uint
|
||||
}
|
||||
|
||||
// Region defines the URLs where AWS services may be accessed.
|
||||
//
|
||||
// See http://goo.gl/d8BP1 for more details.
|
||||
type Region struct {
|
||||
Name string // the canonical name of this region.
|
||||
EC2Endpoint string
|
||||
S3Endpoint string
|
||||
S3BucketEndpoint string // Not needed by AWS S3. Use ${bucket} for bucket name.
|
||||
S3LocationConstraint bool // true if this region requires a LocationConstraint declaration.
|
||||
S3LowercaseBucket bool // true if the region requires bucket names to be lower case.
|
||||
SDBEndpoint string
|
||||
SNSEndpoint string
|
||||
SQSEndpoint string
|
||||
SESEndpoint string
|
||||
IAMEndpoint string
|
||||
ELBEndpoint string
|
||||
DynamoDBEndpoint string
|
||||
CloudWatchServicepoint ServiceInfo
|
||||
AutoScalingEndpoint string
|
||||
RDSEndpoint ServiceInfo
|
||||
KinesisEndpoint string
|
||||
STSEndpoint string
|
||||
CloudFormationEndpoint string
|
||||
ElastiCacheEndpoint string
|
||||
}
|
||||
|
||||
var Regions = map[string]Region{
|
||||
APNortheast.Name: APNortheast,
|
||||
APSoutheast.Name: APSoutheast,
|
||||
APSoutheast2.Name: APSoutheast2,
|
||||
EUCentral.Name: EUCentral,
|
||||
EUWest.Name: EUWest,
|
||||
USEast.Name: USEast,
|
||||
USWest.Name: USWest,
|
||||
USWest2.Name: USWest2,
|
||||
USGovWest.Name: USGovWest,
|
||||
SAEast.Name: SAEast,
|
||||
}
|
||||
|
||||
// Designates a signer interface suitable for signing AWS requests, params
|
||||
// should be appropriately encoded for the request before signing.
|
||||
//
|
||||
// A signer should be initialized with Auth and the appropriate endpoint.
|
||||
type Signer interface {
|
||||
Sign(method, path string, params map[string]string)
|
||||
}
|
||||
|
||||
// An AWS Service interface with the API to query the AWS service
|
||||
//
|
||||
// Supplied as an easy way to mock out service calls during testing.
|
||||
type AWSService interface {
|
||||
// Queries the AWS service at a given method/path with the params and
|
||||
// returns an http.Response and error
|
||||
Query(method, path string, params map[string]string) (*http.Response, error)
|
||||
// Builds an error given an XML payload in the http.Response, can be used
|
||||
// to process an error if the status code is not 200 for example.
|
||||
BuildError(r *http.Response) error
|
||||
}
|
||||
|
||||
// Implements a Server Query/Post API to easily query AWS services and build
|
||||
// errors when desired
|
||||
type Service struct {
|
||||
service ServiceInfo
|
||||
signer Signer
|
||||
}
|
||||
|
||||
// Create a base set of params for an action
|
||||
func MakeParams(action string) map[string]string {
|
||||
params := make(map[string]string)
|
||||
params["Action"] = action
|
||||
return params
|
||||
}
|
||||
|
||||
// Create a new AWS server to handle making requests
|
||||
func NewService(auth Auth, service ServiceInfo) (s *Service, err error) {
|
||||
var signer Signer
|
||||
switch service.Signer {
|
||||
case V2Signature:
|
||||
signer, err = NewV2Signer(auth, service)
|
||||
// case V4Signature:
|
||||
// signer, err = NewV4Signer(auth, service, Regions["eu-west-1"])
|
||||
default:
|
||||
err = fmt.Errorf("Unsupported signer for service")
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s = &Service{service: service, signer: signer}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Service) Query(method, path string, params map[string]string) (resp *http.Response, err error) {
|
||||
params["Timestamp"] = time.Now().UTC().Format(time.RFC3339)
|
||||
u, err := url.Parse(s.service.Endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Path = path
|
||||
|
||||
s.signer.Sign(method, path, params)
|
||||
if method == "GET" {
|
||||
u.RawQuery = multimap(params).Encode()
|
||||
resp, err = http.Get(u.String())
|
||||
} else if method == "POST" {
|
||||
resp, err = http.PostForm(u.String(), multimap(params))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Service) BuildError(r *http.Response) error {
|
||||
errors := ErrorResponse{}
|
||||
xml.NewDecoder(r.Body).Decode(&errors)
|
||||
var err Error
|
||||
err = errors.Errors
|
||||
err.RequestId = errors.RequestId
|
||||
err.StatusCode = r.StatusCode
|
||||
if err.Message == "" {
|
||||
err.Message = r.Status
|
||||
}
|
||||
return &err
|
||||
}
|
||||
|
||||
type ServiceError interface {
|
||||
error
|
||||
ErrorCode() string
|
||||
}
|
||||
|
||||
type ErrorResponse struct {
|
||||
Errors Error `xml:"Error"`
|
||||
RequestId string // A unique ID for tracking the request
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
StatusCode int
|
||||
Type string
|
||||
Code string
|
||||
Message string
|
||||
RequestId string
|
||||
}
|
||||
|
||||
func (err *Error) Error() string {
|
||||
return fmt.Sprintf("Type: %s, Code: %s, Message: %s",
|
||||
err.Type, err.Code, err.Message,
|
||||
)
|
||||
}
|
||||
|
||||
func (err *Error) ErrorCode() string {
|
||||
return err.Code
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
AccessKey, SecretKey string
|
||||
token string
|
||||
expiration time.Time
|
||||
}
|
||||
|
||||
func (a *Auth) Token() string {
|
||||
if a.token == "" {
|
||||
return ""
|
||||
}
|
||||
if time.Since(a.expiration) >= -30*time.Second { //in an ideal world this should be zero assuming the instance is synching it's clock
|
||||
*a, _ = GetAuth("", "", "", time.Time{})
|
||||
}
|
||||
return a.token
|
||||
}
|
||||
|
||||
func (a *Auth) Expiration() time.Time {
|
||||
return a.expiration
|
||||
}
|
||||
|
||||
// To be used with other APIs that return auth credentials such as STS
|
||||
func NewAuth(accessKey, secretKey, token string, expiration time.Time) *Auth {
|
||||
return &Auth{
|
||||
AccessKey: accessKey,
|
||||
SecretKey: secretKey,
|
||||
token: token,
|
||||
expiration: expiration,
|
||||
}
|
||||
}
|
||||
|
||||
// ResponseMetadata
|
||||
type ResponseMetadata struct {
|
||||
RequestId string // A unique ID for tracking the request
|
||||
}
|
||||
|
||||
type BaseResponse struct {
|
||||
ResponseMetadata ResponseMetadata
|
||||
}
|
||||
|
||||
var unreserved = make([]bool, 128)
|
||||
var hex = "0123456789ABCDEF"
|
||||
|
||||
func init() {
|
||||
// RFC3986
|
||||
u := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890-_.~"
|
||||
for _, c := range u {
|
||||
unreserved[c] = true
|
||||
}
|
||||
}
|
||||
|
||||
func multimap(p map[string]string) url.Values {
|
||||
q := make(url.Values, len(p))
|
||||
for k, v := range p {
|
||||
q[k] = []string{v}
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
type credentials struct {
|
||||
Code string
|
||||
LastUpdated string
|
||||
Type string
|
||||
AccessKeyId string
|
||||
SecretAccessKey string
|
||||
Token string
|
||||
Expiration string
|
||||
}
|
||||
|
||||
// GetMetaData retrieves instance metadata about the current machine.
|
||||
//
|
||||
// See http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AESDG-chapter-instancedata.html for more details.
|
||||
func GetMetaData(path string) (contents []byte, err error) {
|
||||
c := http.Client{
|
||||
Transport: &http.Transport{
|
||||
Dial: func(netw, addr string) (net.Conn, error) {
|
||||
deadline := time.Now().Add(5 * time.Second)
|
||||
c, err := net.DialTimeout(netw, addr, time.Second*2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.SetDeadline(deadline)
|
||||
return c, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
url := "http://169.254.169.254/latest/meta-data/" + path
|
||||
|
||||
resp, err := c.Get(url)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
err = fmt.Errorf("Code %d returned for url %s", resp.StatusCode, url)
|
||||
return
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return []byte(body), err
|
||||
}
|
||||
|
||||
func GetRegion(regionName string) (region Region) {
|
||||
region = Regions[regionName]
|
||||
return
|
||||
}
|
||||
|
||||
// GetInstanceCredentials creates an Auth based on the instance's role credentials.
|
||||
// If the running instance is not in EC2 or does not have a valid IAM role, an error will be returned.
|
||||
// For more info about setting up IAM roles, see http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html
|
||||
func GetInstanceCredentials() (cred credentials, err error) {
|
||||
credentialPath := "iam/security-credentials/"
|
||||
|
||||
// Get the instance role
|
||||
role, err := GetMetaData(credentialPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the instance role credentials
|
||||
credentialJSON, err := GetMetaData(credentialPath + string(role))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(credentialJSON), &cred)
|
||||
return
|
||||
}
|
||||
|
||||
// GetAuth creates an Auth based on either passed in credentials,
|
||||
// environment information or instance based role credentials.
|
||||
func GetAuth(accessKey string, secretKey, token string, expiration time.Time) (auth Auth, err error) {
|
||||
// First try passed in credentials
|
||||
if accessKey != "" && secretKey != "" {
|
||||
return Auth{accessKey, secretKey, token, expiration}, nil
|
||||
}
|
||||
|
||||
// Next try to get auth from the environment
|
||||
auth, err = EnvAuth()
|
||||
if err == nil {
|
||||
// Found auth, return
|
||||
return
|
||||
}
|
||||
|
||||
// Next try getting auth from the instance role
|
||||
cred, err := GetInstanceCredentials()
|
||||
if err == nil {
|
||||
// Found auth, return
|
||||
auth.AccessKey = cred.AccessKeyId
|
||||
auth.SecretKey = cred.SecretAccessKey
|
||||
auth.token = cred.Token
|
||||
exptdate, err := time.Parse("2006-01-02T15:04:05Z", cred.Expiration)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error Parsing expiration date: cred.Expiration :%s , error: %s \n", cred.Expiration, err)
|
||||
}
|
||||
auth.expiration = exptdate
|
||||
return auth, err
|
||||
}
|
||||
|
||||
// Next try getting auth from the credentials file
|
||||
auth, err = CredentialFileAuth("", "", time.Minute*5)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
//err = errors.New("No valid AWS authentication found")
|
||||
err = fmt.Errorf("No valid AWS authentication found: %s", err)
|
||||
return auth, err
|
||||
}
|
||||
|
||||
// EnvAuth creates an Auth based on environment information.
|
||||
// The AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment
|
||||
// variables are used.
|
||||
func EnvAuth() (auth Auth, err error) {
|
||||
auth.AccessKey = os.Getenv("AWS_ACCESS_KEY_ID")
|
||||
if auth.AccessKey == "" {
|
||||
auth.AccessKey = os.Getenv("AWS_ACCESS_KEY")
|
||||
}
|
||||
|
||||
auth.SecretKey = os.Getenv("AWS_SECRET_ACCESS_KEY")
|
||||
if auth.SecretKey == "" {
|
||||
auth.SecretKey = os.Getenv("AWS_SECRET_KEY")
|
||||
}
|
||||
if auth.AccessKey == "" {
|
||||
err = errors.New("AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY not found in environment")
|
||||
}
|
||||
if auth.SecretKey == "" {
|
||||
err = errors.New("AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY not found in environment")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CredentialFileAuth creates and Auth based on a credentials file. The file
|
||||
// contains various authentication profiles for use with AWS.
|
||||
//
|
||||
// The credentials file, which is used by other AWS SDKs, is documented at
|
||||
// http://blogs.aws.amazon.com/security/post/Tx3D6U6WSFGOK2H/A-New-and-Standardized-Way-to-Manage-Credentials-in-the-AWS-SDKs
|
||||
func CredentialFileAuth(filePath string, profile string, expiration time.Duration) (auth Auth, err error) {
|
||||
if profile == "" {
|
||||
profile = "default"
|
||||
}
|
||||
|
||||
if filePath == "" {
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
return auth, err
|
||||
}
|
||||
|
||||
filePath = path.Join(u.HomeDir, ".aws", "credentials")
|
||||
}
|
||||
|
||||
// read the file, then parse the INI
|
||||
contents, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
profiles := parseINI(string(contents))
|
||||
profileData, ok := profiles[profile]
|
||||
|
||||
if !ok {
|
||||
err = errors.New("The credentials file did not contain the profile")
|
||||
return
|
||||
}
|
||||
|
||||
keyId, ok := profileData["aws_access_key_id"]
|
||||
if !ok {
|
||||
err = errors.New("The credentials file did not contain required attribute aws_access_key_id")
|
||||
return
|
||||
}
|
||||
|
||||
secretKey, ok := profileData["aws_secret_access_key"]
|
||||
if !ok {
|
||||
err = errors.New("The credentials file did not contain required attribute aws_secret_access_key")
|
||||
return
|
||||
}
|
||||
|
||||
auth.AccessKey = keyId
|
||||
auth.SecretKey = secretKey
|
||||
|
||||
if token, ok := profileData["aws_session_token"]; ok {
|
||||
auth.token = token
|
||||
}
|
||||
|
||||
auth.expiration = time.Now().Add(expiration)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// parseINI takes the contents of a credentials file and returns a map, whose keys
|
||||
// are the various profiles, and whose values are maps of the settings for the
|
||||
// profiles
|
||||
func parseINI(fileContents string) map[string]map[string]string {
|
||||
profiles := make(map[string]map[string]string)
|
||||
|
||||
lines := strings.Split(fileContents, "\n")
|
||||
|
||||
var currentSection map[string]string
|
||||
for _, line := range lines {
|
||||
// remove comments, which start with a semi-colon
|
||||
if split := strings.Split(line, ";"); len(split) > 1 {
|
||||
line = split[0]
|
||||
}
|
||||
|
||||
// check if the line is the start of a profile.
|
||||
//
|
||||
// for example:
|
||||
// [default]
|
||||
//
|
||||
// otherwise, check for the proper setting
|
||||
// property=value
|
||||
if sectMatch := iniSectionRegexp.FindStringSubmatch(line); len(sectMatch) == 2 {
|
||||
currentSection = make(map[string]string)
|
||||
profiles[sectMatch[1]] = currentSection
|
||||
} else if setMatch := iniSettingRegexp.FindStringSubmatch(line); len(setMatch) == 3 && currentSection != nil {
|
||||
currentSection[setMatch[1]] = setMatch[2]
|
||||
}
|
||||
}
|
||||
|
||||
return profiles
|
||||
}
|
||||
|
||||
// Encode takes a string and URI-encodes it in a way suitable
|
||||
// to be used in AWS signatures.
|
||||
func Encode(s string) string {
|
||||
encode := false
|
||||
for i := 0; i != len(s); i++ {
|
||||
c := s[i]
|
||||
if c > 127 || !unreserved[c] {
|
||||
encode = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !encode {
|
||||
return s
|
||||
}
|
||||
e := make([]byte, len(s)*3)
|
||||
ei := 0
|
||||
for i := 0; i != len(s); i++ {
|
||||
c := s[i]
|
||||
if c > 127 || !unreserved[c] {
|
||||
e[ei] = '%'
|
||||
e[ei+1] = hex[c>>4]
|
||||
e[ei+2] = hex[c&0xF]
|
||||
ei += 3
|
||||
} else {
|
||||
e[ei] = c
|
||||
ei += 1
|
||||
}
|
||||
}
|
||||
return string(e[:ei])
|
||||
}
|
||||
|
||||
func dialTimeout(network, addr string) (net.Conn, error) {
|
||||
return net.DialTimeout(network, addr, time.Duration(2*time.Second))
|
||||
}
|
||||
|
||||
func AvailabilityZone() string {
|
||||
transport := http.Transport{Dial: dialTimeout}
|
||||
client := http.Client{
|
||||
Transport: &transport,
|
||||
}
|
||||
resp, err := client.Get("http://169.254.169.254/latest/meta-data/placement/availability-zone")
|
||||
if err != nil {
|
||||
return "unknown"
|
||||
} else {
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "unknown"
|
||||
} else {
|
||||
return string(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func InstanceRegion() string {
|
||||
az := AvailabilityZone()
|
||||
if az == "unknown" {
|
||||
return az
|
||||
} else {
|
||||
region := az[:len(az)-1]
|
||||
return region
|
||||
}
|
||||
}
|
||||
|
||||
func InstanceId() string {
|
||||
transport := http.Transport{Dial: dialTimeout}
|
||||
client := http.Client{
|
||||
Transport: &transport,
|
||||
}
|
||||
resp, err := client.Get("http://169.254.169.254/latest/meta-data/instance-id")
|
||||
if err != nil {
|
||||
return "unknown"
|
||||
} else {
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "unknown"
|
||||
} else {
|
||||
return string(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func InstanceType() string {
|
||||
transport := http.Transport{Dial: dialTimeout}
|
||||
client := http.Client{
|
||||
Transport: &transport,
|
||||
}
|
||||
resp, err := client.Get("http://169.254.169.254/latest/meta-data/instance-type")
|
||||
if err != nil {
|
||||
return "unknown"
|
||||
} else {
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "unknown"
|
||||
} else {
|
||||
return string(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ServerLocalIp() string {
|
||||
transport := http.Transport{Dial: dialTimeout}
|
||||
client := http.Client{
|
||||
Transport: &transport,
|
||||
}
|
||||
resp, err := client.Get("http://169.254.169.254/latest/meta-data/local-ipv4")
|
||||
if err != nil {
|
||||
return "127.0.0.1"
|
||||
} else {
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "127.0.0.1"
|
||||
} else {
|
||||
return string(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ServerPublicIp() string {
|
||||
transport := http.Transport{Dial: dialTimeout}
|
||||
client := http.Client{
|
||||
Transport: &transport,
|
||||
}
|
||||
resp, err := client.Get("http://169.254.169.254/latest/meta-data/public-ipv4")
|
||||
if err != nil {
|
||||
return "127.0.0.1"
|
||||
} else {
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "127.0.0.1"
|
||||
} else {
|
||||
return string(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
140
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/aws_test.go
generated
vendored
140
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/aws_test.go
generated
vendored
@ -1,140 +0,0 @@
|
||||
package aws_test
|
||||
|
||||
import (
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/crowdmob/goamz/aws"
|
||||
"gopkg.in/check.v1"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
check.TestingT(t)
|
||||
}
|
||||
|
||||
var _ = check.Suite(&S{})
|
||||
|
||||
type S struct {
|
||||
environ []string
|
||||
}
|
||||
|
||||
func (s *S) SetUpSuite(c *check.C) {
|
||||
s.environ = os.Environ()
|
||||
}
|
||||
|
||||
func (s *S) TearDownTest(c *check.C) {
|
||||
os.Clearenv()
|
||||
for _, kv := range s.environ {
|
||||
l := strings.SplitN(kv, "=", 2)
|
||||
os.Setenv(l[0], l[1])
|
||||
}
|
||||
}
|
||||
|
||||
func (s *S) TestEnvAuthNoSecret(c *check.C) {
|
||||
os.Clearenv()
|
||||
_, err := aws.EnvAuth()
|
||||
c.Assert(err, check.ErrorMatches, "AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY not found in environment")
|
||||
}
|
||||
|
||||
func (s *S) TestEnvAuthNoAccess(c *check.C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_SECRET_ACCESS_KEY", "foo")
|
||||
_, err := aws.EnvAuth()
|
||||
c.Assert(err, check.ErrorMatches, "AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY not found in environment")
|
||||
}
|
||||
|
||||
func (s *S) TestEnvAuth(c *check.C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_SECRET_ACCESS_KEY", "secret")
|
||||
os.Setenv("AWS_ACCESS_KEY_ID", "access")
|
||||
auth, err := aws.EnvAuth()
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(auth, check.Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"})
|
||||
}
|
||||
|
||||
func (s *S) TestEnvAuthAlt(c *check.C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_SECRET_KEY", "secret")
|
||||
os.Setenv("AWS_ACCESS_KEY", "access")
|
||||
auth, err := aws.EnvAuth()
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(auth, check.Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"})
|
||||
}
|
||||
|
||||
func (s *S) TestGetAuthStatic(c *check.C) {
|
||||
exptdate := time.Now().Add(time.Hour)
|
||||
auth, err := aws.GetAuth("access", "secret", "token", exptdate)
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(auth.AccessKey, check.Equals, "access")
|
||||
c.Assert(auth.SecretKey, check.Equals, "secret")
|
||||
c.Assert(auth.Token(), check.Equals, "token")
|
||||
c.Assert(auth.Expiration(), check.Equals, exptdate)
|
||||
}
|
||||
|
||||
func (s *S) TestGetAuthEnv(c *check.C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_SECRET_ACCESS_KEY", "secret")
|
||||
os.Setenv("AWS_ACCESS_KEY_ID", "access")
|
||||
auth, err := aws.GetAuth("", "", "", time.Time{})
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(auth, check.Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"})
|
||||
}
|
||||
|
||||
func (s *S) TestEncode(c *check.C) {
|
||||
c.Assert(aws.Encode("foo"), check.Equals, "foo")
|
||||
c.Assert(aws.Encode("/"), check.Equals, "%2F")
|
||||
}
|
||||
|
||||
func (s *S) TestRegionsAreNamed(c *check.C) {
|
||||
for n, r := range aws.Regions {
|
||||
c.Assert(n, check.Equals, r.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *S) TestCredentialsFileAuth(c *check.C) {
|
||||
file, err := ioutil.TempFile("", "creds")
|
||||
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
iniFile := `
|
||||
|
||||
[default] ; comment 123
|
||||
aws_access_key_id = keyid1 ;comment
|
||||
aws_secret_access_key=key1
|
||||
|
||||
[profile2]
|
||||
aws_access_key_id = keyid2 ;comment
|
||||
aws_secret_access_key=key2
|
||||
aws_session_token=token1
|
||||
|
||||
`
|
||||
_, err = file.WriteString(iniFile)
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
// check non-existant profile
|
||||
_, err = aws.CredentialFileAuth(file.Name(), "no profile", 30*time.Minute)
|
||||
c.Assert(err, check.Not(check.Equals), nil)
|
||||
|
||||
defaultProfile, err := aws.CredentialFileAuth(file.Name(), "default", 30*time.Minute)
|
||||
c.Assert(err, check.Equals, nil)
|
||||
c.Assert(defaultProfile.AccessKey, check.Equals, "keyid1")
|
||||
c.Assert(defaultProfile.SecretKey, check.Equals, "key1")
|
||||
c.Assert(defaultProfile.Token(), check.Equals, "")
|
||||
|
||||
profile2, err := aws.CredentialFileAuth(file.Name(), "profile2", 30*time.Minute)
|
||||
c.Assert(err, check.Equals, nil)
|
||||
c.Assert(profile2.AccessKey, check.Equals, "keyid2")
|
||||
c.Assert(profile2.SecretKey, check.Equals, "key2")
|
||||
c.Assert(profile2.Token(), check.Equals, "token1")
|
||||
}
|
||||
124
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/client.go
generated
vendored
124
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/client.go
generated
vendored
@ -1,124 +0,0 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RetryableFunc func(*http.Request, *http.Response, error) bool
|
||||
type WaitFunc func(try int)
|
||||
type DeadlineFunc func() time.Time
|
||||
|
||||
type ResilientTransport struct {
|
||||
// Timeout is the maximum amount of time a dial will wait for
|
||||
// a connect to complete.
|
||||
//
|
||||
// The default is no timeout.
|
||||
//
|
||||
// With or without a timeout, the operating system may impose
|
||||
// its own earlier timeout. For instance, TCP timeouts are
|
||||
// often around 3 minutes.
|
||||
DialTimeout time.Duration
|
||||
|
||||
// MaxTries, if non-zero, specifies the number of times we will retry on
|
||||
// failure. Retries are only attempted for temporary network errors or known
|
||||
// safe failures.
|
||||
MaxTries int
|
||||
Deadline DeadlineFunc
|
||||
ShouldRetry RetryableFunc
|
||||
Wait WaitFunc
|
||||
transport *http.Transport
|
||||
}
|
||||
|
||||
// Convenience method for creating an http client
|
||||
func NewClient(rt *ResilientTransport) *http.Client {
|
||||
rt.transport = &http.Transport{
|
||||
Dial: func(netw, addr string) (net.Conn, error) {
|
||||
c, err := net.DialTimeout(netw, addr, rt.DialTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.SetDeadline(rt.Deadline())
|
||||
return c, nil
|
||||
},
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
}
|
||||
// TODO: Would be nice is ResilientTransport allowed clients to initialize
|
||||
// with http.Transport attributes.
|
||||
return &http.Client{
|
||||
Transport: rt,
|
||||
}
|
||||
}
|
||||
|
||||
var retryingTransport = &ResilientTransport{
|
||||
Deadline: func() time.Time {
|
||||
return time.Now().Add(5 * time.Second)
|
||||
},
|
||||
DialTimeout: 10 * time.Second,
|
||||
MaxTries: 3,
|
||||
ShouldRetry: awsRetry,
|
||||
Wait: ExpBackoff,
|
||||
}
|
||||
|
||||
// Exported default client
|
||||
var RetryingClient = NewClient(retryingTransport)
|
||||
|
||||
func (t *ResilientTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return t.tries(req)
|
||||
}
|
||||
|
||||
// Retry a request a maximum of t.MaxTries times.
|
||||
// We'll only retry if the proper criteria are met.
|
||||
// If a wait function is specified, wait that amount of time
|
||||
// In between requests.
|
||||
func (t *ResilientTransport) tries(req *http.Request) (res *http.Response, err error) {
|
||||
for try := 0; try < t.MaxTries; try += 1 {
|
||||
res, err = t.transport.RoundTrip(req)
|
||||
|
||||
if !t.ShouldRetry(req, res, err) {
|
||||
break
|
||||
}
|
||||
if res != nil {
|
||||
res.Body.Close()
|
||||
}
|
||||
if t.Wait != nil {
|
||||
t.Wait(try)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func ExpBackoff(try int) {
|
||||
time.Sleep(100 * time.Millisecond *
|
||||
time.Duration(math.Exp2(float64(try))))
|
||||
}
|
||||
|
||||
func LinearBackoff(try int) {
|
||||
time.Sleep(time.Duration(try*100) * time.Millisecond)
|
||||
}
|
||||
|
||||
// Decide if we should retry a request.
|
||||
// In general, the criteria for retrying a request is described here
|
||||
// http://docs.aws.amazon.com/general/latest/gr/api-retries.html
|
||||
func awsRetry(req *http.Request, res *http.Response, err error) bool {
|
||||
retry := false
|
||||
|
||||
// Retry if there's a temporary network error.
|
||||
if neterr, ok := err.(net.Error); ok {
|
||||
if neterr.Temporary() {
|
||||
retry = true
|
||||
}
|
||||
}
|
||||
|
||||
// Retry if we get a 5xx series error.
|
||||
if res != nil {
|
||||
if res.StatusCode >= 500 && res.StatusCode < 600 {
|
||||
retry = true
|
||||
}
|
||||
}
|
||||
|
||||
return retry
|
||||
}
|
||||
29
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/export_test.go
generated
vendored
29
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/export_test.go
generated
vendored
@ -1,29 +0,0 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// V4Signer:
|
||||
// Exporting methods for testing
|
||||
|
||||
func (s *V4Signer) RequestTime(req *http.Request) time.Time {
|
||||
return s.requestTime(req)
|
||||
}
|
||||
|
||||
func (s *V4Signer) CanonicalRequest(req *http.Request) string {
|
||||
return s.canonicalRequest(req, "")
|
||||
}
|
||||
|
||||
func (s *V4Signer) StringToSign(t time.Time, creq string) string {
|
||||
return s.stringToSign(t, creq)
|
||||
}
|
||||
|
||||
func (s *V4Signer) Signature(t time.Time, sts string) string {
|
||||
return s.signature(t, sts)
|
||||
}
|
||||
|
||||
func (s *V4Signer) Authorization(header http.Header, t time.Time, signature string) string {
|
||||
return s.authorization(header, t, signature)
|
||||
}
|
||||
231
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/regions.go
generated
vendored
231
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/regions.go
generated
vendored
@ -1,231 +0,0 @@
|
||||
package aws
|
||||
|
||||
var USGovWest = Region{
|
||||
"us-gov-west-1",
|
||||
"https://ec2.us-gov-west-1.amazonaws.com",
|
||||
"https://s3-fips-us-gov-west-1.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"",
|
||||
"https://sns.us-gov-west-1.amazonaws.com",
|
||||
"https://sqs.us-gov-west-1.amazonaws.com",
|
||||
"",
|
||||
"https://iam.us-gov.amazonaws.com",
|
||||
"https://elasticloadbalancing.us-gov-west-1.amazonaws.com",
|
||||
"https://dynamodb.us-gov-west-1.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.us-gov-west-1.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.us-gov-west-1.amazonaws.com",
|
||||
ServiceInfo{"https://rds.us-gov-west-1.amazonaws.com", V2Signature},
|
||||
"",
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.us-gov-west-1.amazonaws.com",
|
||||
"",
|
||||
}
|
||||
|
||||
var USEast = Region{
|
||||
"us-east-1",
|
||||
"https://ec2.us-east-1.amazonaws.com",
|
||||
"https://s3.amazonaws.com",
|
||||
"",
|
||||
false,
|
||||
false,
|
||||
"https://sdb.amazonaws.com",
|
||||
"https://sns.us-east-1.amazonaws.com",
|
||||
"https://sqs.us-east-1.amazonaws.com",
|
||||
"https://email.us-east-1.amazonaws.com",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.us-east-1.amazonaws.com",
|
||||
"https://dynamodb.us-east-1.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.us-east-1.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.us-east-1.amazonaws.com",
|
||||
ServiceInfo{"https://rds.us-east-1.amazonaws.com", V2Signature},
|
||||
"https://kinesis.us-east-1.amazonaws.com",
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.us-east-1.amazonaws.com",
|
||||
"https://elasticache.us-east-1.amazonaws.com",
|
||||
}
|
||||
|
||||
var USWest = Region{
|
||||
"us-west-1",
|
||||
"https://ec2.us-west-1.amazonaws.com",
|
||||
"https://s3-us-west-1.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"https://sdb.us-west-1.amazonaws.com",
|
||||
"https://sns.us-west-1.amazonaws.com",
|
||||
"https://sqs.us-west-1.amazonaws.com",
|
||||
"",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.us-west-1.amazonaws.com",
|
||||
"https://dynamodb.us-west-1.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.us-west-1.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.us-west-1.amazonaws.com",
|
||||
ServiceInfo{"https://rds.us-west-1.amazonaws.com", V2Signature},
|
||||
"",
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.us-west-1.amazonaws.com",
|
||||
"https://elasticache.us-west-1.amazonaws.com",
|
||||
}
|
||||
|
||||
var USWest2 = Region{
|
||||
"us-west-2",
|
||||
"https://ec2.us-west-2.amazonaws.com",
|
||||
"https://s3-us-west-2.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"https://sdb.us-west-2.amazonaws.com",
|
||||
"https://sns.us-west-2.amazonaws.com",
|
||||
"https://sqs.us-west-2.amazonaws.com",
|
||||
"https://email.us-west-2.amazonaws.com",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.us-west-2.amazonaws.com",
|
||||
"https://dynamodb.us-west-2.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.us-west-2.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.us-west-2.amazonaws.com",
|
||||
ServiceInfo{"https://rds.us-west-2.amazonaws.com", V2Signature},
|
||||
"https://kinesis.us-west-2.amazonaws.com",
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.us-west-2.amazonaws.com",
|
||||
"https://elasticache.us-west-2.amazonaws.com",
|
||||
}
|
||||
|
||||
var EUWest = Region{
|
||||
"eu-west-1",
|
||||
"https://ec2.eu-west-1.amazonaws.com",
|
||||
"https://s3-eu-west-1.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"https://sdb.eu-west-1.amazonaws.com",
|
||||
"https://sns.eu-west-1.amazonaws.com",
|
||||
"https://sqs.eu-west-1.amazonaws.com",
|
||||
"https://email.eu-west-1.amazonaws.com",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.eu-west-1.amazonaws.com",
|
||||
"https://dynamodb.eu-west-1.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.eu-west-1.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.eu-west-1.amazonaws.com",
|
||||
ServiceInfo{"https://rds.eu-west-1.amazonaws.com", V2Signature},
|
||||
"https://kinesis.eu-west-1.amazonaws.com",
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.eu-west-1.amazonaws.com",
|
||||
"https://elasticache.eu-west-1.amazonaws.com",
|
||||
}
|
||||
|
||||
var EUCentral = Region{
|
||||
"eu-central-1",
|
||||
"https://ec2.eu-central-1.amazonaws.com",
|
||||
"https://s3-eu-central-1.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"https://sdb.eu-central-1.amazonaws.com",
|
||||
"https://sns.eu-central-1.amazonaws.com",
|
||||
"https://sqs.eu-central-1.amazonaws.com",
|
||||
"",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.eu-central-1.amazonaws.com",
|
||||
"https://dynamodb.eu-central-1.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.eu-central-1.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.eu-central-1.amazonaws.com",
|
||||
ServiceInfo{"https://rds.eu-central-1.amazonaws.com", V2Signature},
|
||||
"https://kinesis.eu-central-1.amazonaws.com",
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.eu-central-1.amazonaws.com",
|
||||
"",
|
||||
}
|
||||
|
||||
var APSoutheast = Region{
|
||||
"ap-southeast-1",
|
||||
"https://ec2.ap-southeast-1.amazonaws.com",
|
||||
"https://s3-ap-southeast-1.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"https://sdb.ap-southeast-1.amazonaws.com",
|
||||
"https://sns.ap-southeast-1.amazonaws.com",
|
||||
"https://sqs.ap-southeast-1.amazonaws.com",
|
||||
"",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.ap-southeast-1.amazonaws.com",
|
||||
"https://dynamodb.ap-southeast-1.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.ap-southeast-1.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.ap-southeast-1.amazonaws.com",
|
||||
ServiceInfo{"https://rds.ap-southeast-1.amazonaws.com", V2Signature},
|
||||
"https://kinesis.ap-southeast-1.amazonaws.com",
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.ap-southeast-1.amazonaws.com",
|
||||
"https://elasticache.ap-southeast-1.amazonaws.com",
|
||||
}
|
||||
|
||||
var APSoutheast2 = Region{
|
||||
"ap-southeast-2",
|
||||
"https://ec2.ap-southeast-2.amazonaws.com",
|
||||
"https://s3-ap-southeast-2.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"https://sdb.ap-southeast-2.amazonaws.com",
|
||||
"https://sns.ap-southeast-2.amazonaws.com",
|
||||
"https://sqs.ap-southeast-2.amazonaws.com",
|
||||
"",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.ap-southeast-2.amazonaws.com",
|
||||
"https://dynamodb.ap-southeast-2.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.ap-southeast-2.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.ap-southeast-2.amazonaws.com",
|
||||
ServiceInfo{"https://rds.ap-southeast-2.amazonaws.com", V2Signature},
|
||||
"https://kinesis.ap-southeast-2.amazonaws.com",
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.ap-southeast-2.amazonaws.com",
|
||||
"https://elasticache.ap-southeast-2.amazonaws.com",
|
||||
}
|
||||
|
||||
var APNortheast = Region{
|
||||
"ap-northeast-1",
|
||||
"https://ec2.ap-northeast-1.amazonaws.com",
|
||||
"https://s3-ap-northeast-1.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"https://sdb.ap-northeast-1.amazonaws.com",
|
||||
"https://sns.ap-northeast-1.amazonaws.com",
|
||||
"https://sqs.ap-northeast-1.amazonaws.com",
|
||||
"",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.ap-northeast-1.amazonaws.com",
|
||||
"https://dynamodb.ap-northeast-1.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.ap-northeast-1.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.ap-northeast-1.amazonaws.com",
|
||||
ServiceInfo{"https://rds.ap-northeast-1.amazonaws.com", V2Signature},
|
||||
"https://kinesis.ap-northeast-1.amazonaws.com",
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.ap-northeast-1.amazonaws.com",
|
||||
"https://elasticache.ap-northeast-1.amazonaws.com",
|
||||
}
|
||||
|
||||
var SAEast = Region{
|
||||
"sa-east-1",
|
||||
"https://ec2.sa-east-1.amazonaws.com",
|
||||
"https://s3-sa-east-1.amazonaws.com",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
"https://sdb.sa-east-1.amazonaws.com",
|
||||
"https://sns.sa-east-1.amazonaws.com",
|
||||
"https://sqs.sa-east-1.amazonaws.com",
|
||||
"",
|
||||
"https://iam.amazonaws.com",
|
||||
"https://elasticloadbalancing.sa-east-1.amazonaws.com",
|
||||
"https://dynamodb.sa-east-1.amazonaws.com",
|
||||
ServiceInfo{"https://monitoring.sa-east-1.amazonaws.com", V2Signature},
|
||||
"https://autoscaling.sa-east-1.amazonaws.com",
|
||||
ServiceInfo{"https://rds.sa-east-1.amazonaws.com", V2Signature},
|
||||
"",
|
||||
"https://sts.amazonaws.com",
|
||||
"https://cloudformation.sa-east-1.amazonaws.com",
|
||||
"https://elasticache.sa-east-1.amazonaws.com",
|
||||
}
|
||||
136
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/retry.go
generated
vendored
136
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/retry.go
generated
vendored
@ -1,136 +0,0 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
maxDelay = 20 * time.Second
|
||||
defaultScale = 300 * time.Millisecond
|
||||
throttlingScale = 500 * time.Millisecond
|
||||
throttlingScaleRange = throttlingScale / 4
|
||||
defaultMaxRetries = 3
|
||||
dynamoDBScale = 25 * time.Millisecond
|
||||
dynamoDBMaxRetries = 10
|
||||
)
|
||||
|
||||
// A RetryPolicy encapsulates a strategy for implementing client retries.
|
||||
//
|
||||
// Default implementations are provided which match the AWS SDKs.
|
||||
type RetryPolicy interface {
|
||||
// ShouldRetry returns whether a client should retry a failed request.
|
||||
ShouldRetry(target string, r *http.Response, err error, numRetries int) bool
|
||||
|
||||
// Delay returns the time a client should wait before issuing a retry.
|
||||
Delay(target string, r *http.Response, err error, numRetries int) time.Duration
|
||||
}
|
||||
|
||||
// DefaultRetryPolicy implements the AWS SDK default retry policy.
|
||||
//
|
||||
// It will retry up to 3 times, and uses an exponential backoff with a scale
|
||||
// factor of 300ms (300ms, 600ms, 1200ms). If the retry is because of
|
||||
// throttling, the delay will also include some randomness.
|
||||
//
|
||||
// See https://github.com/aws/aws-sdk-java/blob/master/aws-java-sdk-core/src/main/java/com/amazonaws/retry/PredefinedRetryPolicies.java#L90.
|
||||
type DefaultRetryPolicy struct {
|
||||
}
|
||||
|
||||
// ShouldRetry implements the RetryPolicy ShouldRetry method.
|
||||
func (policy DefaultRetryPolicy) ShouldRetry(target string, r *http.Response, err error, numRetries int) bool {
|
||||
return shouldRetry(r, err, numRetries, defaultMaxRetries)
|
||||
}
|
||||
|
||||
// Delay implements the RetryPolicy Delay method.
|
||||
func (policy DefaultRetryPolicy) Delay(target string, r *http.Response, err error, numRetries int) time.Duration {
|
||||
scale := defaultScale
|
||||
if err, ok := err.(*Error); ok && isThrottlingException(err) {
|
||||
scale = throttlingScale + time.Duration(rand.Int63n(int64(throttlingScaleRange)))
|
||||
}
|
||||
return exponentialBackoff(numRetries, scale)
|
||||
}
|
||||
|
||||
// DynamoDBRetryPolicy implements the AWS SDK DynamoDB retry policy.
|
||||
//
|
||||
// It will retry up to 10 times, and uses an exponential backoff with a scale
|
||||
// factor of 25ms (25ms, 50ms, 100ms, ...).
|
||||
//
|
||||
// See https://github.com/aws/aws-sdk-java/blob/master/aws-java-sdk-core/src/main/java/com/amazonaws/retry/PredefinedRetryPolicies.java#L103.
|
||||
type DynamoDBRetryPolicy struct {
|
||||
}
|
||||
|
||||
// ShouldRetry implements the RetryPolicy ShouldRetry method.
|
||||
func (policy DynamoDBRetryPolicy) ShouldRetry(target string, r *http.Response, err error, numRetries int) bool {
|
||||
return shouldRetry(r, err, numRetries, dynamoDBMaxRetries)
|
||||
}
|
||||
|
||||
// Delay implements the RetryPolicy Delay method.
|
||||
func (policy DynamoDBRetryPolicy) Delay(target string, r *http.Response, err error, numRetries int) time.Duration {
|
||||
return exponentialBackoff(numRetries, dynamoDBScale)
|
||||
}
|
||||
|
||||
// NeverRetryPolicy never retries requests and returns immediately on failure.
|
||||
type NeverRetryPolicy struct {
|
||||
}
|
||||
|
||||
// ShouldRetry implements the RetryPolicy ShouldRetry method.
|
||||
func (policy NeverRetryPolicy) ShouldRetry(target string, r *http.Response, err error, numRetries int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Delay implements the RetryPolicy Delay method.
|
||||
func (policy NeverRetryPolicy) Delay(target string, r *http.Response, err error, numRetries int) time.Duration {
|
||||
return time.Duration(0)
|
||||
}
|
||||
|
||||
// shouldRetry determines if we should retry the request.
|
||||
//
|
||||
// See http://docs.aws.amazon.com/general/latest/gr/api-retries.html.
|
||||
func shouldRetry(r *http.Response, err error, numRetries int, maxRetries int) bool {
|
||||
// Once we've exceeded the max retry attempts, game over.
|
||||
if numRetries >= maxRetries {
|
||||
return false
|
||||
}
|
||||
|
||||
// Always retry temporary network errors.
|
||||
if err, ok := err.(net.Error); ok && err.Temporary() {
|
||||
return true
|
||||
}
|
||||
|
||||
// Always retry 5xx responses.
|
||||
if r != nil && r.StatusCode >= 500 {
|
||||
return true
|
||||
}
|
||||
|
||||
// Always retry throttling exceptions.
|
||||
if err, ok := err.(ServiceError); ok && isThrottlingException(err) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Other classes of failures indicate a problem with the request. Retrying
|
||||
// won't help.
|
||||
return false
|
||||
}
|
||||
|
||||
func exponentialBackoff(numRetries int, scale time.Duration) time.Duration {
|
||||
if numRetries < 0 {
|
||||
return time.Duration(0)
|
||||
}
|
||||
|
||||
delay := (1 << uint(numRetries)) * scale
|
||||
if delay > maxDelay {
|
||||
return maxDelay
|
||||
}
|
||||
return delay
|
||||
}
|
||||
|
||||
func isThrottlingException(err ServiceError) bool {
|
||||
switch err.ErrorCode() {
|
||||
case "Throttling", "ThrottlingException", "ProvisionedThroughputExceededException":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
303
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/retry_test.go
generated
vendored
303
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/retry_test.go
generated
vendored
@ -1,303 +0,0 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type testInput struct {
|
||||
res *http.Response
|
||||
err error
|
||||
numRetries int
|
||||
}
|
||||
|
||||
type testResult struct {
|
||||
shouldRetry bool
|
||||
delay time.Duration
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
input testInput
|
||||
defaultResult testResult
|
||||
dynamoDBResult testResult
|
||||
}
|
||||
|
||||
var testCases = []testCase{
|
||||
// Test nil fields
|
||||
testCase{
|
||||
input: testInput{
|
||||
err: nil,
|
||||
res: nil,
|
||||
numRetries: 0,
|
||||
},
|
||||
defaultResult: testResult{
|
||||
shouldRetry: false,
|
||||
delay: 300 * time.Millisecond,
|
||||
},
|
||||
dynamoDBResult: testResult{
|
||||
shouldRetry: false,
|
||||
delay: 25 * time.Millisecond,
|
||||
},
|
||||
},
|
||||
// Test 3 different throttling exceptions
|
||||
testCase{
|
||||
input: testInput{
|
||||
err: &Error{
|
||||
Code: "Throttling",
|
||||
},
|
||||
numRetries: 0,
|
||||
},
|
||||
defaultResult: testResult{
|
||||
shouldRetry: true,
|
||||
delay: 617165505 * time.Nanosecond, // account for randomness with known seed
|
||||
},
|
||||
dynamoDBResult: testResult{
|
||||
shouldRetry: true,
|
||||
delay: 25 * time.Millisecond,
|
||||
},
|
||||
},
|
||||
testCase{
|
||||
input: testInput{
|
||||
err: &Error{
|
||||
Code: "ThrottlingException",
|
||||
},
|
||||
numRetries: 0,
|
||||
},
|
||||
defaultResult: testResult{
|
||||
shouldRetry: true,
|
||||
delay: 579393152 * time.Nanosecond, // account for randomness with known seed
|
||||
},
|
||||
dynamoDBResult: testResult{
|
||||
shouldRetry: true,
|
||||
delay: 25 * time.Millisecond,
|
||||
},
|
||||
},
|
||||
testCase{
|
||||
input: testInput{
|
||||
err: &Error{
|
||||
Code: "ProvisionedThroughputExceededException",
|
||||
},
|
||||
numRetries: 1,
|
||||
},
|
||||
defaultResult: testResult{
|
||||
shouldRetry: true,
|
||||
delay: 1105991654 * time.Nanosecond, // account for randomness with known seed
|
||||
},
|
||||
dynamoDBResult: testResult{
|
||||
shouldRetry: true,
|
||||
delay: 50 * time.Millisecond,
|
||||
},
|
||||
},
|
||||
// Test a fake throttling exception
|
||||
testCase{
|
||||
input: testInput{
|
||||
err: &Error{
|
||||
Code: "MyMadeUpThrottlingCode",
|
||||
},
|
||||
numRetries: 0,
|
||||
},
|
||||
defaultResult: testResult{
|
||||
shouldRetry: false,
|
||||
delay: 300 * time.Millisecond,
|
||||
},
|
||||
dynamoDBResult: testResult{
|
||||
shouldRetry: false,
|
||||
delay: 25 * time.Millisecond,
|
||||
},
|
||||
},
|
||||
// Test 5xx errors
|
||||
testCase{
|
||||
input: testInput{
|
||||
res: &http.Response{
|
||||
StatusCode: http.StatusInternalServerError,
|
||||
},
|
||||
numRetries: 1,
|
||||
},
|
||||
defaultResult: testResult{
|
||||
shouldRetry: true,
|
||||
delay: 600 * time.Millisecond,
|
||||
},
|
||||
dynamoDBResult: testResult{
|
||||
shouldRetry: true,
|
||||
delay: 50 * time.Millisecond,
|
||||
},
|
||||
},
|
||||
testCase{
|
||||
input: testInput{
|
||||
res: &http.Response{
|
||||
StatusCode: http.StatusServiceUnavailable,
|
||||
},
|
||||
numRetries: 1,
|
||||
},
|
||||
defaultResult: testResult{
|
||||
shouldRetry: true,
|
||||
delay: 600 * time.Millisecond,
|
||||
},
|
||||
dynamoDBResult: testResult{
|
||||
shouldRetry: true,
|
||||
delay: 50 * time.Millisecond,
|
||||
},
|
||||
},
|
||||
// Test a random 400 error
|
||||
testCase{
|
||||
input: testInput{
|
||||
res: &http.Response{
|
||||
StatusCode: http.StatusNotFound,
|
||||
},
|
||||
numRetries: 1,
|
||||
},
|
||||
defaultResult: testResult{
|
||||
shouldRetry: false,
|
||||
delay: 600 * time.Millisecond,
|
||||
},
|
||||
dynamoDBResult: testResult{
|
||||
shouldRetry: false,
|
||||
delay: 50 * time.Millisecond,
|
||||
},
|
||||
},
|
||||
// Test a temporary net.Error
|
||||
testCase{
|
||||
input: testInput{
|
||||
res: &http.Response{},
|
||||
err: &net.DNSError{
|
||||
IsTimeout: true,
|
||||
},
|
||||
numRetries: 2,
|
||||
},
|
||||
defaultResult: testResult{
|
||||
shouldRetry: true,
|
||||
delay: 1200 * time.Millisecond,
|
||||
},
|
||||
dynamoDBResult: testResult{
|
||||
shouldRetry: true,
|
||||
delay: 100 * time.Millisecond,
|
||||
},
|
||||
},
|
||||
// Test a non-temporary net.Error
|
||||
testCase{
|
||||
input: testInput{
|
||||
res: &http.Response{},
|
||||
err: &net.DNSError{
|
||||
IsTimeout: false,
|
||||
},
|
||||
numRetries: 3,
|
||||
},
|
||||
defaultResult: testResult{
|
||||
shouldRetry: false,
|
||||
delay: 2400 * time.Millisecond,
|
||||
},
|
||||
dynamoDBResult: testResult{
|
||||
shouldRetry: false,
|
||||
delay: 200 * time.Millisecond,
|
||||
},
|
||||
},
|
||||
// Assert failure after hitting max default retries
|
||||
testCase{
|
||||
input: testInput{
|
||||
err: &Error{
|
||||
Code: "ProvisionedThroughputExceededException",
|
||||
},
|
||||
numRetries: defaultMaxRetries,
|
||||
},
|
||||
defaultResult: testResult{
|
||||
shouldRetry: false,
|
||||
delay: 4313582352 * time.Nanosecond, // account for randomness with known seed
|
||||
},
|
||||
dynamoDBResult: testResult{
|
||||
shouldRetry: true,
|
||||
delay: 200 * time.Millisecond,
|
||||
},
|
||||
},
|
||||
// Assert failure after hitting max DynamoDB retries
|
||||
testCase{
|
||||
input: testInput{
|
||||
err: &Error{
|
||||
Code: "ProvisionedThroughputExceededException",
|
||||
},
|
||||
numRetries: dynamoDBMaxRetries,
|
||||
},
|
||||
defaultResult: testResult{
|
||||
shouldRetry: false,
|
||||
delay: maxDelay,
|
||||
},
|
||||
dynamoDBResult: testResult{
|
||||
shouldRetry: false,
|
||||
delay: maxDelay,
|
||||
},
|
||||
},
|
||||
// Assert we never go over the maxDelay value
|
||||
testCase{
|
||||
input: testInput{
|
||||
numRetries: 25,
|
||||
},
|
||||
defaultResult: testResult{
|
||||
shouldRetry: false,
|
||||
delay: maxDelay,
|
||||
},
|
||||
dynamoDBResult: testResult{
|
||||
shouldRetry: false,
|
||||
delay: maxDelay,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestDefaultRetryPolicy(t *testing.T) {
|
||||
rand.Seed(0)
|
||||
var policy RetryPolicy
|
||||
policy = &DefaultRetryPolicy{}
|
||||
for _, test := range testCases {
|
||||
res := test.input.res
|
||||
err := test.input.err
|
||||
numRetries := test.input.numRetries
|
||||
|
||||
shouldRetry := policy.ShouldRetry("", res, err, numRetries)
|
||||
if shouldRetry != test.defaultResult.shouldRetry {
|
||||
t.Errorf("ShouldRetry returned %v, expected %v res=%#v err=%#v numRetries=%d", shouldRetry, test.defaultResult.shouldRetry, res, err, numRetries)
|
||||
}
|
||||
delay := policy.Delay("", res, err, numRetries)
|
||||
if delay != test.defaultResult.delay {
|
||||
t.Errorf("Delay returned %v, expected %v res=%#v err=%#v numRetries=%d", delay, test.defaultResult.delay, res, err, numRetries)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDynamoDBRetryPolicy(t *testing.T) {
|
||||
var policy RetryPolicy
|
||||
policy = &DynamoDBRetryPolicy{}
|
||||
for _, test := range testCases {
|
||||
res := test.input.res
|
||||
err := test.input.err
|
||||
numRetries := test.input.numRetries
|
||||
|
||||
shouldRetry := policy.ShouldRetry("", res, err, numRetries)
|
||||
if shouldRetry != test.dynamoDBResult.shouldRetry {
|
||||
t.Errorf("ShouldRetry returned %v, expected %v res=%#v err=%#v numRetries=%d", shouldRetry, test.dynamoDBResult.shouldRetry, res, err, numRetries)
|
||||
}
|
||||
delay := policy.Delay("", res, err, numRetries)
|
||||
if delay != test.dynamoDBResult.delay {
|
||||
t.Errorf("Delay returned %v, expected %v res=%#v err=%#v numRetries=%d", delay, test.dynamoDBResult.delay, res, err, numRetries)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNeverRetryPolicy(t *testing.T) {
|
||||
var policy RetryPolicy
|
||||
policy = &NeverRetryPolicy{}
|
||||
for _, test := range testCases {
|
||||
res := test.input.res
|
||||
err := test.input.err
|
||||
numRetries := test.input.numRetries
|
||||
|
||||
shouldRetry := policy.ShouldRetry("", res, err, numRetries)
|
||||
if shouldRetry {
|
||||
t.Errorf("ShouldRetry returned %v, expected %v res=%#v err=%#v numRetries=%d", shouldRetry, false, res, err, numRetries)
|
||||
}
|
||||
delay := policy.Delay("", res, err, numRetries)
|
||||
if delay != time.Duration(0) {
|
||||
t.Errorf("Delay returned %v, expected %v res=%#v err=%#v numRetries=%d", delay, time.Duration(0), res, err, numRetries)
|
||||
}
|
||||
}
|
||||
}
|
||||
413
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/sign.go
generated
vendored
413
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/sign.go
generated
vendored
@ -1,413 +0,0 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type V2Signer struct {
|
||||
auth Auth
|
||||
service ServiceInfo
|
||||
host string
|
||||
}
|
||||
|
||||
var b64 = base64.StdEncoding
|
||||
|
||||
func NewV2Signer(auth Auth, service ServiceInfo) (*V2Signer, error) {
|
||||
u, err := url.Parse(service.Endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &V2Signer{auth: auth, service: service, host: u.Host}, nil
|
||||
}
|
||||
|
||||
func (s *V2Signer) Sign(method, path string, params map[string]string) {
|
||||
params["AWSAccessKeyId"] = s.auth.AccessKey
|
||||
params["SignatureVersion"] = "2"
|
||||
params["SignatureMethod"] = "HmacSHA256"
|
||||
if s.auth.Token() != "" {
|
||||
params["SecurityToken"] = s.auth.Token()
|
||||
}
|
||||
|
||||
// AWS specifies that the parameters in a signed request must
|
||||
// be provided in the natural order of the keys. This is distinct
|
||||
// from the natural order of the encoded value of key=value.
|
||||
// Percent and gocheck.Equals affect the sorting order.
|
||||
var keys, sarray []string
|
||||
for k, _ := range params {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
sarray = append(sarray, Encode(k)+"="+Encode(params[k]))
|
||||
}
|
||||
joined := strings.Join(sarray, "&")
|
||||
payload := method + "\n" + s.host + "\n" + path + "\n" + joined
|
||||
hash := hmac.New(sha256.New, []byte(s.auth.SecretKey))
|
||||
hash.Write([]byte(payload))
|
||||
signature := make([]byte, b64.EncodedLen(hash.Size()))
|
||||
b64.Encode(signature, hash.Sum(nil))
|
||||
|
||||
params["Signature"] = string(signature)
|
||||
}
|
||||
|
||||
// Common date formats for signing requests
|
||||
const (
|
||||
ISO8601BasicFormat = "20060102T150405Z"
|
||||
ISO8601BasicFormatShort = "20060102"
|
||||
)
|
||||
|
||||
type Route53Signer struct {
|
||||
auth Auth
|
||||
}
|
||||
|
||||
func NewRoute53Signer(auth Auth) *Route53Signer {
|
||||
return &Route53Signer{auth: auth}
|
||||
}
|
||||
|
||||
// getCurrentDate fetches the date stamp from the aws servers to
|
||||
// ensure the auth headers are within 5 minutes of the server time
|
||||
func (s *Route53Signer) getCurrentDate() string {
|
||||
response, err := http.Get("https://route53.amazonaws.com/date")
|
||||
if err != nil {
|
||||
fmt.Print("Unable to get date from amazon: ", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
response.Body.Close()
|
||||
return response.Header.Get("Date")
|
||||
}
|
||||
|
||||
// Creates the authorize signature based on the date stamp and secret key
|
||||
func (s *Route53Signer) getHeaderAuthorize(message string) string {
|
||||
hmacSha256 := hmac.New(sha256.New, []byte(s.auth.SecretKey))
|
||||
hmacSha256.Write([]byte(message))
|
||||
cryptedString := hmacSha256.Sum(nil)
|
||||
|
||||
return base64.StdEncoding.EncodeToString(cryptedString)
|
||||
}
|
||||
|
||||
// Adds all the required headers for AWS Route53 API to the request
|
||||
// including the authorization
|
||||
func (s *Route53Signer) Sign(req *http.Request) {
|
||||
date := s.getCurrentDate()
|
||||
authHeader := fmt.Sprintf("AWS3-HTTPS AWSAccessKeyId=%s,Algorithm=%s,Signature=%s",
|
||||
s.auth.AccessKey, "HmacSHA256", s.getHeaderAuthorize(date))
|
||||
|
||||
req.Header.Set("Host", req.Host)
|
||||
req.Header.Set("X-Amzn-Authorization", authHeader)
|
||||
req.Header.Set("X-Amz-Date", date)
|
||||
req.Header.Set("Content-Type", "application/xml")
|
||||
if s.auth.Token() != "" {
|
||||
req.Header.Set("X-Amzn-Security-Token", s.auth.Token())
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
The V4Signer encapsulates all of the functionality to sign a request with the AWS
|
||||
Signature Version 4 Signing Process. (http://goo.gl/u1OWZz)
|
||||
*/
|
||||
type V4Signer struct {
|
||||
auth Auth
|
||||
serviceName string
|
||||
region Region
|
||||
// Add the x-amz-content-sha256 header
|
||||
IncludeXAmzContentSha256 bool
|
||||
}
|
||||
|
||||
/*
|
||||
Return a new instance of a V4Signer capable of signing AWS requests.
|
||||
*/
|
||||
func NewV4Signer(auth Auth, serviceName string, region Region) *V4Signer {
|
||||
return &V4Signer{
|
||||
auth: auth,
|
||||
serviceName: serviceName,
|
||||
region: region,
|
||||
IncludeXAmzContentSha256: false,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Sign a request according to the AWS Signature Version 4 Signing Process. (http://goo.gl/u1OWZz)
|
||||
|
||||
The signed request will include an "x-amz-date" header with a current timestamp if a valid "x-amz-date"
|
||||
or "date" header was not available in the original request. In addition, AWS Signature Version 4 requires
|
||||
the "host" header to be a signed header, therefor the Sign method will manually set a "host" header from
|
||||
the request.Host.
|
||||
|
||||
The signed request will include a new "Authorization" header indicating that the request has been signed.
|
||||
|
||||
Any changes to the request after signing the request will invalidate the signature.
|
||||
*/
|
||||
func (s *V4Signer) Sign(req *http.Request) {
|
||||
req.Header.Set("host", req.Host) // host header must be included as a signed header
|
||||
t := s.requestTime(req) // Get request time
|
||||
|
||||
payloadHash := ""
|
||||
|
||||
if _, ok := req.Form["X-Amz-Expires"]; ok {
|
||||
// We are authenticating the the request by using query params
|
||||
// (also known as pre-signing a url, http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html)
|
||||
payloadHash = "UNSIGNED-PAYLOAD"
|
||||
req.Header.Del("x-amz-date")
|
||||
|
||||
req.Form["X-Amz-SignedHeaders"] = []string{s.signedHeaders(req.Header)}
|
||||
req.Form["X-Amz-Algorithm"] = []string{"AWS4-HMAC-SHA256"}
|
||||
req.Form["X-Amz-Credential"] = []string{s.auth.AccessKey + "/" + s.credentialScope(t)}
|
||||
req.Form["X-Amz-Date"] = []string{t.Format(ISO8601BasicFormat)}
|
||||
req.URL.RawQuery = req.Form.Encode()
|
||||
} else {
|
||||
payloadHash = s.payloadHash(req)
|
||||
if s.IncludeXAmzContentSha256 {
|
||||
req.Header.Set("x-amz-content-sha256", payloadHash) // x-amz-content-sha256 contains the payload hash
|
||||
}
|
||||
}
|
||||
creq := s.canonicalRequest(req, payloadHash) // Build canonical request
|
||||
sts := s.stringToSign(t, creq) // Build string to sign
|
||||
signature := s.signature(t, sts) // Calculate the AWS Signature Version 4
|
||||
auth := s.authorization(req.Header, t, signature) // Create Authorization header value
|
||||
|
||||
if _, ok := req.Form["X-Amz-Expires"]; ok {
|
||||
req.Form["X-Amz-Signature"] = []string{signature}
|
||||
} else {
|
||||
req.Header.Set("Authorization", auth) // Add Authorization header to request
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
requestTime method will parse the time from the request "x-amz-date" or "date" headers.
|
||||
If the "x-amz-date" header is present, that will take priority over the "date" header.
|
||||
If neither header is defined or we are unable to parse either header as a valid date
|
||||
then we will create a new "x-amz-date" header with the current time.
|
||||
*/
|
||||
func (s *V4Signer) requestTime(req *http.Request) time.Time {
|
||||
|
||||
// Get "x-amz-date" header
|
||||
date := req.Header.Get("x-amz-date")
|
||||
|
||||
// Attempt to parse as ISO8601BasicFormat
|
||||
t, err := time.Parse(ISO8601BasicFormat, date)
|
||||
if err == nil {
|
||||
return t
|
||||
}
|
||||
|
||||
// Attempt to parse as http.TimeFormat
|
||||
t, err = time.Parse(http.TimeFormat, date)
|
||||
if err == nil {
|
||||
req.Header.Set("x-amz-date", t.Format(ISO8601BasicFormat))
|
||||
return t
|
||||
}
|
||||
|
||||
// Get "date" header
|
||||
date = req.Header.Get("date")
|
||||
|
||||
// Attempt to parse as http.TimeFormat
|
||||
t, err = time.Parse(http.TimeFormat, date)
|
||||
if err == nil {
|
||||
return t
|
||||
}
|
||||
|
||||
// Create a current time header to be used
|
||||
t = time.Now().UTC()
|
||||
req.Header.Set("x-amz-date", t.Format(ISO8601BasicFormat))
|
||||
return t
|
||||
}
|
||||
|
||||
/*
|
||||
canonicalRequest method creates the canonical request according to Task 1 of the AWS Signature Version 4 Signing Process. (http://goo.gl/eUUZ3S)
|
||||
|
||||
CanonicalRequest =
|
||||
HTTPRequestMethod + '\n' +
|
||||
CanonicalURI + '\n' +
|
||||
CanonicalQueryString + '\n' +
|
||||
CanonicalHeaders + '\n' +
|
||||
SignedHeaders + '\n' +
|
||||
HexEncode(Hash(Payload))
|
||||
|
||||
payloadHash is optional; use the empty string and it will be calculated from the request
|
||||
*/
|
||||
func (s *V4Signer) canonicalRequest(req *http.Request, payloadHash string) string {
|
||||
if payloadHash == "" {
|
||||
payloadHash = s.payloadHash(req)
|
||||
}
|
||||
c := new(bytes.Buffer)
|
||||
fmt.Fprintf(c, "%s\n", req.Method)
|
||||
fmt.Fprintf(c, "%s\n", s.canonicalURI(req.URL))
|
||||
fmt.Fprintf(c, "%s\n", s.canonicalQueryString(req.URL))
|
||||
fmt.Fprintf(c, "%s\n\n", s.canonicalHeaders(req.Header))
|
||||
fmt.Fprintf(c, "%s\n", s.signedHeaders(req.Header))
|
||||
fmt.Fprintf(c, "%s", payloadHash)
|
||||
return c.String()
|
||||
}
|
||||
|
||||
func (s *V4Signer) canonicalURI(u *url.URL) string {
|
||||
u = &url.URL{Path: u.Path}
|
||||
canonicalPath := u.String()
|
||||
|
||||
slash := strings.HasSuffix(canonicalPath, "/")
|
||||
canonicalPath = path.Clean(canonicalPath)
|
||||
|
||||
if canonicalPath == "" || canonicalPath == "." {
|
||||
canonicalPath = "/"
|
||||
}
|
||||
|
||||
if canonicalPath != "/" && slash {
|
||||
canonicalPath += "/"
|
||||
}
|
||||
|
||||
return canonicalPath
|
||||
}
|
||||
|
||||
func (s *V4Signer) canonicalQueryString(u *url.URL) string {
|
||||
var a []string
|
||||
for k, vs := range u.Query() {
|
||||
k = url.QueryEscape(k)
|
||||
for _, v := range vs {
|
||||
if v == "" {
|
||||
a = append(a, k+"=")
|
||||
} else {
|
||||
v = url.QueryEscape(v)
|
||||
a = append(a, k+"="+v)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(a)
|
||||
return strings.Join(a, "&")
|
||||
}
|
||||
|
||||
func (s *V4Signer) canonicalHeaders(h http.Header) string {
|
||||
i, a, lowerCase := 0, make([]string, len(h)), make(map[string][]string)
|
||||
|
||||
for k, v := range h {
|
||||
lowerCase[strings.ToLower(k)] = v
|
||||
}
|
||||
|
||||
var keys []string
|
||||
for k := range lowerCase {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, k := range keys {
|
||||
v := lowerCase[k]
|
||||
for j, w := range v {
|
||||
v[j] = strings.Trim(w, " ")
|
||||
}
|
||||
sort.Strings(v)
|
||||
a[i] = strings.ToLower(k) + ":" + strings.Join(v, ",")
|
||||
i++
|
||||
}
|
||||
return strings.Join(a, "\n")
|
||||
}
|
||||
|
||||
func (s *V4Signer) signedHeaders(h http.Header) string {
|
||||
i, a := 0, make([]string, len(h))
|
||||
for k, _ := range h {
|
||||
a[i] = strings.ToLower(k)
|
||||
i++
|
||||
}
|
||||
sort.Strings(a)
|
||||
return strings.Join(a, ";")
|
||||
}
|
||||
|
||||
func (s *V4Signer) payloadHash(req *http.Request) string {
|
||||
var b []byte
|
||||
if req.Body == nil {
|
||||
b = []byte("")
|
||||
} else {
|
||||
var err error
|
||||
b, err = ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
// TODO: I REALLY DON'T LIKE THIS PANIC!!!!
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
req.Body = ioutil.NopCloser(bytes.NewBuffer(b))
|
||||
return s.hash(string(b))
|
||||
}
|
||||
|
||||
/*
|
||||
stringToSign method creates the string to sign accorting to Task 2 of the AWS Signature Version 4 Signing Process. (http://goo.gl/es1PAu)
|
||||
|
||||
StringToSign =
|
||||
Algorithm + '\n' +
|
||||
RequestDate + '\n' +
|
||||
CredentialScope + '\n' +
|
||||
HexEncode(Hash(CanonicalRequest))
|
||||
*/
|
||||
func (s *V4Signer) stringToSign(t time.Time, creq string) string {
|
||||
w := new(bytes.Buffer)
|
||||
fmt.Fprint(w, "AWS4-HMAC-SHA256\n")
|
||||
fmt.Fprintf(w, "%s\n", t.Format(ISO8601BasicFormat))
|
||||
fmt.Fprintf(w, "%s\n", s.credentialScope(t))
|
||||
fmt.Fprintf(w, "%s", s.hash(creq))
|
||||
return w.String()
|
||||
}
|
||||
|
||||
func (s *V4Signer) credentialScope(t time.Time) string {
|
||||
return fmt.Sprintf("%s/%s/%s/aws4_request", t.Format(ISO8601BasicFormatShort), s.region.Name, s.serviceName)
|
||||
}
|
||||
|
||||
/*
|
||||
signature method calculates the AWS Signature Version 4 according to Task 3 of the AWS Signature Version 4 Signing Process. (http://goo.gl/j0Yqe1)
|
||||
|
||||
signature = HexEncode(HMAC(derived-signing-key, string-to-sign))
|
||||
*/
|
||||
func (s *V4Signer) signature(t time.Time, sts string) string {
|
||||
h := s.hmac(s.derivedKey(t), []byte(sts))
|
||||
return fmt.Sprintf("%x", h)
|
||||
}
|
||||
|
||||
/*
|
||||
derivedKey method derives a signing key to be used for signing a request.
|
||||
|
||||
kSecret = Your AWS Secret Access Key
|
||||
kDate = HMAC("AWS4" + kSecret, Date)
|
||||
kRegion = HMAC(kDate, Region)
|
||||
kService = HMAC(kRegion, Service)
|
||||
kSigning = HMAC(kService, "aws4_request")
|
||||
*/
|
||||
func (s *V4Signer) derivedKey(t time.Time) []byte {
|
||||
h := s.hmac([]byte("AWS4"+s.auth.SecretKey), []byte(t.Format(ISO8601BasicFormatShort)))
|
||||
h = s.hmac(h, []byte(s.region.Name))
|
||||
h = s.hmac(h, []byte(s.serviceName))
|
||||
h = s.hmac(h, []byte("aws4_request"))
|
||||
return h
|
||||
}
|
||||
|
||||
/*
|
||||
authorization method generates the authorization header value.
|
||||
*/
|
||||
func (s *V4Signer) authorization(header http.Header, t time.Time, signature string) string {
|
||||
w := new(bytes.Buffer)
|
||||
fmt.Fprint(w, "AWS4-HMAC-SHA256 ")
|
||||
fmt.Fprintf(w, "Credential=%s/%s, ", s.auth.AccessKey, s.credentialScope(t))
|
||||
fmt.Fprintf(w, "SignedHeaders=%s, ", s.signedHeaders(header))
|
||||
fmt.Fprintf(w, "Signature=%s", signature)
|
||||
return w.String()
|
||||
}
|
||||
|
||||
// hash method calculates the sha256 hash for a given string
|
||||
func (s *V4Signer) hash(in string) string {
|
||||
h := sha256.New()
|
||||
fmt.Fprintf(h, "%s", in)
|
||||
return fmt.Sprintf("%x", h.Sum(nil))
|
||||
}
|
||||
|
||||
// hmac method calculates the sha256 hmac for a given slice of bytes
|
||||
func (s *V4Signer) hmac(key, data []byte) []byte {
|
||||
h := hmac.New(sha256.New, key)
|
||||
h.Write(data)
|
||||
return h.Sum(nil)
|
||||
}
|
||||
569
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/sign_test.go
generated
vendored
569
Godeps/_workspace/src/github.com/crowdmob/goamz/aws/sign_test.go
generated
vendored
@ -1,569 +0,0 @@
|
||||
package aws_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/crowdmob/goamz/aws"
|
||||
"gopkg.in/check.v1"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ = check.Suite(&V4SignerSuite{})
|
||||
|
||||
type V4SignerSuite struct {
|
||||
auth aws.Auth
|
||||
region aws.Region
|
||||
cases []V4SignerSuiteCase
|
||||
}
|
||||
|
||||
type V4SignerSuiteCase struct {
|
||||
label string
|
||||
request V4SignerSuiteCaseRequest
|
||||
canonicalRequest string
|
||||
stringToSign string
|
||||
signature string
|
||||
authorization string
|
||||
}
|
||||
|
||||
type V4SignerSuiteCaseRequest struct {
|
||||
method string
|
||||
host string
|
||||
url string
|
||||
headers []string
|
||||
body string
|
||||
}
|
||||
|
||||
func (s *V4SignerSuite) SetUpSuite(c *check.C) {
|
||||
s.auth = aws.Auth{AccessKey: "AKIDEXAMPLE", SecretKey: "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"}
|
||||
s.region = aws.USEast
|
||||
|
||||
// Test cases from the Signature Version 4 Test Suite (http://goo.gl/nguvs0)
|
||||
s.cases = append(s.cases,
|
||||
|
||||
// get-header-key-duplicate
|
||||
V4SignerSuiteCase{
|
||||
label: "get-header-key-duplicate",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"DATE:Mon, 09 Sep 2011 23:36:00 GMT", "ZOO:zoobar", "zoo:foobar", "zoo:zoobar"},
|
||||
},
|
||||
canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\nzoo:foobar,zoobar,zoobar\n\ndate;host;zoo\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n3c52f0eaae2b61329c0a332e3fa15842a37bc5812cf4d80eb64784308850e313",
|
||||
signature: "54afcaaf45b331f81cd2edb974f7b824ff4dd594cbbaa945ed636b48477368ed",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;zoo, Signature=54afcaaf45b331f81cd2edb974f7b824ff4dd594cbbaa945ed636b48477368ed",
|
||||
},
|
||||
|
||||
// get-header-value-order
|
||||
V4SignerSuiteCase{
|
||||
label: "get-header-value-order",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"DATE:Mon, 09 Sep 2011 23:36:00 GMT", "p:z", "p:a", "p:p", "p:a"},
|
||||
},
|
||||
canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\np:a,a,p,z\n\ndate;host;p\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n94c0389fefe0988cbbedc8606f0ca0b485b48da010d09fc844b45b697c8924fe",
|
||||
signature: "d2973954263943b11624a11d1c963ca81fb274169c7868b2858c04f083199e3d",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;p, Signature=d2973954263943b11624a11d1c963ca81fb274169c7868b2858c04f083199e3d",
|
||||
},
|
||||
|
||||
// get-header-value-trim
|
||||
V4SignerSuiteCase{
|
||||
label: "get-header-value-trim",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"DATE:Mon, 09 Sep 2011 23:36:00 GMT", "p: phfft "},
|
||||
},
|
||||
canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\np:phfft\n\ndate;host;p\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\ndddd1902add08da1ac94782b05f9278c08dc7468db178a84f8950d93b30b1f35",
|
||||
signature: "debf546796015d6f6ded8626f5ce98597c33b47b9164cf6b17b4642036fcb592",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;p, Signature=debf546796015d6f6ded8626f5ce98597c33b47b9164cf6b17b4642036fcb592",
|
||||
},
|
||||
|
||||
// get-empty
|
||||
V4SignerSuiteCase{
|
||||
label: "get-relative-relative",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1",
|
||||
signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
},
|
||||
|
||||
// get-single-relative
|
||||
V4SignerSuiteCase{
|
||||
label: "get-relative-relative",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/.",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1",
|
||||
signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
},
|
||||
|
||||
// get-multiple-relative
|
||||
V4SignerSuiteCase{
|
||||
label: "get-relative-relative",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/./././",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1",
|
||||
signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
},
|
||||
|
||||
// get-relative-relative
|
||||
V4SignerSuiteCase{
|
||||
label: "get-relative-relative",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/foo/bar/../..",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1",
|
||||
signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
},
|
||||
|
||||
// get-relative
|
||||
V4SignerSuiteCase{
|
||||
label: "get-relative",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/foo/..",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1",
|
||||
signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
},
|
||||
|
||||
// get-slash-dot-slash
|
||||
V4SignerSuiteCase{
|
||||
label: "get-slash-dot-slash",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/./",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1",
|
||||
signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
},
|
||||
|
||||
// get-slash-pointless-dot
|
||||
V4SignerSuiteCase{
|
||||
label: "get-slash-pointless-dot",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/./foo",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/foo\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n8021a97572ee460f87ca67f4e8c0db763216d84715f5424a843a5312a3321e2d",
|
||||
signature: "910e4d6c9abafaf87898e1eb4c929135782ea25bb0279703146455745391e63a",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=910e4d6c9abafaf87898e1eb4c929135782ea25bb0279703146455745391e63a",
|
||||
},
|
||||
|
||||
// get-slash
|
||||
V4SignerSuiteCase{
|
||||
label: "get-slash",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "//",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1",
|
||||
signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
},
|
||||
|
||||
// get-slashes
|
||||
V4SignerSuiteCase{
|
||||
label: "get-slashes",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "//foo//",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/foo/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n6bb4476ee8745730c9cb79f33a0c70baa6d8af29c0077fa12e4e8f1dd17e7098",
|
||||
signature: "b00392262853cfe3201e47ccf945601079e9b8a7f51ee4c3d9ee4f187aa9bf19",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b00392262853cfe3201e47ccf945601079e9b8a7f51ee4c3d9ee4f187aa9bf19",
|
||||
},
|
||||
|
||||
// get-space
|
||||
V4SignerSuiteCase{
|
||||
label: "get-space",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/%20/foo",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/%20/foo\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n69c45fb9fe3fd76442b5086e50b2e9fec8298358da957b293ef26e506fdfb54b",
|
||||
signature: "f309cfbd10197a230c42dd17dbf5cca8a0722564cb40a872d25623cfa758e374",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=f309cfbd10197a230c42dd17dbf5cca8a0722564cb40a872d25623cfa758e374",
|
||||
},
|
||||
|
||||
// get-unreserved
|
||||
V4SignerSuiteCase{
|
||||
label: "get-unreserved",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\ndf63ee3247c0356c696a3b21f8d8490b01fa9cd5bc6550ef5ef5f4636b7b8901",
|
||||
signature: "830cc36d03f0f84e6ee4953fbe701c1c8b71a0372c63af9255aa364dd183281e",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=830cc36d03f0f84e6ee4953fbe701c1c8b71a0372c63af9255aa364dd183281e",
|
||||
},
|
||||
|
||||
// get-utf8
|
||||
V4SignerSuiteCase{
|
||||
label: "get-utf8",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/%E1%88%B4",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/%E1%88%B4\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n27ba31df5dbc6e063d8f87d62eb07143f7f271c5330a917840586ac1c85b6f6b",
|
||||
signature: "8d6634c189aa8c75c2e51e106b6b5121bed103fdb351f7d7d4381c738823af74",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=8d6634c189aa8c75c2e51e106b6b5121bed103fdb351f7d7d4381c738823af74",
|
||||
},
|
||||
|
||||
// get-vanilla-empty-query-key
|
||||
V4SignerSuiteCase{
|
||||
label: "get-vanilla-empty-query-key",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/?foo=bar",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\nfoo=bar\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n0846c2945b0832deb7a463c66af5c4f8bd54ec28c438e67a214445b157c9ddf8",
|
||||
signature: "56c054473fd260c13e4e7393eb203662195f5d4a1fada5314b8b52b23f985e9f",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=56c054473fd260c13e4e7393eb203662195f5d4a1fada5314b8b52b23f985e9f",
|
||||
},
|
||||
|
||||
// get-vanilla-query-order-key-case
|
||||
V4SignerSuiteCase{
|
||||
label: "get-vanilla-query-order-key-case",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/?foo=Zoo&foo=aha",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\nfoo=Zoo&foo=aha\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\ne25f777ba161a0f1baf778a87faf057187cf5987f17953320e3ca399feb5f00d",
|
||||
signature: "be7148d34ebccdc6423b19085378aa0bee970bdc61d144bd1a8c48c33079ab09",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=be7148d34ebccdc6423b19085378aa0bee970bdc61d144bd1a8c48c33079ab09",
|
||||
},
|
||||
|
||||
// get-vanilla-query-order-key
|
||||
V4SignerSuiteCase{
|
||||
label: "get-vanilla-query-order-key",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/?a=foo&b=foo",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\na=foo&b=foo\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n2f23d14fe13caebf6dfda346285c6d9c14f49eaca8f5ec55c627dd7404f7a727",
|
||||
signature: "0dc122f3b28b831ab48ba65cb47300de53fbe91b577fe113edac383730254a3b",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=0dc122f3b28b831ab48ba65cb47300de53fbe91b577fe113edac383730254a3b",
|
||||
},
|
||||
|
||||
// get-vanilla-query-order-value
|
||||
V4SignerSuiteCase{
|
||||
label: "get-vanilla-query-order-value",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/?foo=b&foo=a",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\nfoo=a&foo=b\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n33dffc220e89131f8f6157a35c40903daa658608d9129ff9489e5cf5bbd9b11b",
|
||||
signature: "feb926e49e382bec75c9d7dcb2a1b6dc8aa50ca43c25d2bc51143768c0875acc",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=feb926e49e382bec75c9d7dcb2a1b6dc8aa50ca43c25d2bc51143768c0875acc",
|
||||
},
|
||||
|
||||
// get-vanilla-query-unreserved
|
||||
V4SignerSuiteCase{
|
||||
label: "get-vanilla-query-unreserved",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/?-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\nd2578f3156d4c9d180713d1ff20601d8a3eed0dd35447d24603d7d67414bd6b5",
|
||||
signature: "f1498ddb4d6dae767d97c466fb92f1b59a2c71ca29ac954692663f9db03426fb",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=f1498ddb4d6dae767d97c466fb92f1b59a2c71ca29ac954692663f9db03426fb",
|
||||
},
|
||||
|
||||
// get-vanilla-query
|
||||
V4SignerSuiteCase{
|
||||
label: "get-vanilla-query",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1",
|
||||
signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
},
|
||||
|
||||
// get-vanilla-ut8-query
|
||||
V4SignerSuiteCase{
|
||||
label: "get-vanilla-ut8-query",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/?ሴ=bar",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n%E1%88%B4=bar\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\nde5065ff39c131e6c2e2bd19cd9345a794bf3b561eab20b8d97b2093fc2a979e",
|
||||
signature: "6fb359e9a05394cc7074e0feb42573a2601abc0c869a953e8c5c12e4e01f1a8c",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=6fb359e9a05394cc7074e0feb42573a2601abc0c869a953e8c5c12e4e01f1a8c",
|
||||
},
|
||||
|
||||
// get-vanilla
|
||||
V4SignerSuiteCase{
|
||||
label: "get-vanilla",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "GET",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1",
|
||||
signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470",
|
||||
},
|
||||
|
||||
// post-header-key-case
|
||||
V4SignerSuiteCase{
|
||||
label: "post-header-key-case",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"DATE:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n05da62cee468d24ae84faff3c39f1b85540de60243c1bcaace39c0a2acc7b2c4",
|
||||
signature: "22902d79e148b64e7571c3565769328423fe276eae4b26f83afceda9e767f726",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=22902d79e148b64e7571c3565769328423fe276eae4b26f83afceda9e767f726",
|
||||
},
|
||||
|
||||
// post-header-key-sort
|
||||
V4SignerSuiteCase{
|
||||
label: "post-header-key-sort",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"DATE:Mon, 09 Sep 2011 23:36:00 GMT", "ZOO:zoobar"},
|
||||
},
|
||||
canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\nzoo:zoobar\n\ndate;host;zoo\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n34e1bddeb99e76ee01d63b5e28656111e210529efeec6cdfd46a48e4c734545d",
|
||||
signature: "b7a95a52518abbca0964a999a880429ab734f35ebbf1235bd79a5de87756dc4a",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;zoo, Signature=b7a95a52518abbca0964a999a880429ab734f35ebbf1235bd79a5de87756dc4a",
|
||||
},
|
||||
|
||||
// post-header-value-case
|
||||
V4SignerSuiteCase{
|
||||
label: "post-header-value-case",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"DATE:Mon, 09 Sep 2011 23:36:00 GMT", "zoo:ZOOBAR"},
|
||||
},
|
||||
canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\nzoo:ZOOBAR\n\ndate;host;zoo\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n3aae6d8274b8c03e2cc96fc7d6bda4b9bd7a0a184309344470b2c96953e124aa",
|
||||
signature: "273313af9d0c265c531e11db70bbd653f3ba074c1009239e8559d3987039cad7",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;zoo, Signature=273313af9d0c265c531e11db70bbd653f3ba074c1009239e8559d3987039cad7",
|
||||
},
|
||||
|
||||
// post-vanilla-empty-query-value
|
||||
V4SignerSuiteCase{
|
||||
label: "post-vanilla-empty-query-value",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/?foo=bar",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "POST\n/\nfoo=bar\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\ncd4f39132d8e60bb388831d734230460872b564871c47f5de62e62d1a68dbe1e",
|
||||
signature: "b6e3b79003ce0743a491606ba1035a804593b0efb1e20a11cba83f8c25a57a92",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b6e3b79003ce0743a491606ba1035a804593b0efb1e20a11cba83f8c25a57a92",
|
||||
},
|
||||
|
||||
// post-vanilla-query
|
||||
V4SignerSuiteCase{
|
||||
label: "post-vanilla-query",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/?foo=bar",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "POST\n/\nfoo=bar\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\ncd4f39132d8e60bb388831d734230460872b564871c47f5de62e62d1a68dbe1e",
|
||||
signature: "b6e3b79003ce0743a491606ba1035a804593b0efb1e20a11cba83f8c25a57a92",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b6e3b79003ce0743a491606ba1035a804593b0efb1e20a11cba83f8c25a57a92",
|
||||
},
|
||||
|
||||
// post-vanilla
|
||||
V4SignerSuiteCase{
|
||||
label: "post-vanilla",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
},
|
||||
canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n05da62cee468d24ae84faff3c39f1b85540de60243c1bcaace39c0a2acc7b2c4",
|
||||
signature: "22902d79e148b64e7571c3565769328423fe276eae4b26f83afceda9e767f726",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=22902d79e148b64e7571c3565769328423fe276eae4b26f83afceda9e767f726",
|
||||
},
|
||||
|
||||
// post-x-www-form-urlencoded-parameters
|
||||
V4SignerSuiteCase{
|
||||
label: "post-x-www-form-urlencoded-parameters",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"Content-Type:application/x-www-form-urlencoded; charset=utf8", "Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
body: "foo=bar",
|
||||
},
|
||||
canonicalRequest: "POST\n/\n\ncontent-type:application/x-www-form-urlencoded; charset=utf8\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ncontent-type;date;host\n3ba8907e7a252327488df390ed517c45b96dead033600219bdca7107d1d3f88a",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\nc4115f9e54b5cecf192b1eaa23b8e88ed8dc5391bd4fde7b3fff3d9c9fe0af1f",
|
||||
signature: "b105eb10c6d318d2294de9d49dd8b031b55e3c3fe139f2e637da70511e9e7b71",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=content-type;date;host, Signature=b105eb10c6d318d2294de9d49dd8b031b55e3c3fe139f2e637da70511e9e7b71",
|
||||
},
|
||||
|
||||
// post-x-www-form-urlencoded
|
||||
V4SignerSuiteCase{
|
||||
label: "post-x-www-form-urlencoded",
|
||||
request: V4SignerSuiteCaseRequest{
|
||||
method: "POST",
|
||||
host: "host.foo.com",
|
||||
url: "/",
|
||||
headers: []string{"Content-Type:application/x-www-form-urlencoded", "Date:Mon, 09 Sep 2011 23:36:00 GMT"},
|
||||
body: "foo=bar",
|
||||
},
|
||||
canonicalRequest: "POST\n/\n\ncontent-type:application/x-www-form-urlencoded\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ncontent-type;date;host\n3ba8907e7a252327488df390ed517c45b96dead033600219bdca7107d1d3f88a",
|
||||
stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n4c5c6e4b52fb5fb947a8733982a8a5a61b14f04345cbfe6e739236c76dd48f74",
|
||||
signature: "5a15b22cf462f047318703b92e6f4f38884e4a7ab7b1d6426ca46a8bd1c26cbc",
|
||||
authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=content-type;date;host, Signature=5a15b22cf462f047318703b92e6f4f38884e4a7ab7b1d6426ca46a8bd1c26cbc",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (s *V4SignerSuite) TestCases(c *check.C) {
|
||||
signer := aws.NewV4Signer(s.auth, "host", s.region)
|
||||
|
||||
for _, testCase := range s.cases {
|
||||
|
||||
req, err := http.NewRequest(testCase.request.method, "http://"+testCase.request.host+testCase.request.url, strings.NewReader(testCase.request.body))
|
||||
c.Assert(err, check.IsNil, check.Commentf("Testcase: %s", testCase.label))
|
||||
for _, v := range testCase.request.headers {
|
||||
h := strings.SplitN(v, ":", 2)
|
||||
req.Header.Add(h[0], h[1])
|
||||
}
|
||||
req.Header.Set("host", req.Host)
|
||||
|
||||
t := signer.RequestTime(req)
|
||||
|
||||
canonicalRequest := signer.CanonicalRequest(req)
|
||||
c.Check(canonicalRequest, check.Equals, testCase.canonicalRequest, check.Commentf("Testcase: %s", testCase.label))
|
||||
|
||||
stringToSign := signer.StringToSign(t, canonicalRequest)
|
||||
c.Check(stringToSign, check.Equals, testCase.stringToSign, check.Commentf("Testcase: %s", testCase.label))
|
||||
|
||||
signature := signer.Signature(t, stringToSign)
|
||||
c.Check(signature, check.Equals, testCase.signature, check.Commentf("Testcase: %s", testCase.label))
|
||||
|
||||
authorization := signer.Authorization(req.Header, t, signature)
|
||||
c.Check(authorization, check.Equals, testCase.authorization, check.Commentf("Testcase: %s", testCase.label))
|
||||
|
||||
signer.Sign(req)
|
||||
c.Check(req.Header.Get("Authorization"), check.Equals, testCase.authorization, check.Commentf("Testcase: %s", testCase.label))
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleV4Signer() {
|
||||
// Get auth from env vars
|
||||
auth, err := aws.EnvAuth()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
// Create a signer with the auth, name of the service, and aws region
|
||||
signer := aws.NewV4Signer(auth, "dynamodb", aws.USEast)
|
||||
|
||||
// Create a request
|
||||
req, err := http.NewRequest("POST", aws.USEast.DynamoDBEndpoint, strings.NewReader("sample_request"))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
// Date or x-amz-date header is required to sign a request
|
||||
req.Header.Add("Date", time.Now().UTC().Format(http.TimeFormat))
|
||||
|
||||
// Sign the request
|
||||
signer.Sign(req)
|
||||
|
||||
// Issue signed request
|
||||
http.DefaultClient.Do(req)
|
||||
}
|
||||
27
Godeps/_workspace/src/github.com/crowdmob/goamz/s3/export_test.go
generated
vendored
27
Godeps/_workspace/src/github.com/crowdmob/goamz/s3/export_test.go
generated
vendored
@ -1,27 +0,0 @@
|
||||
package s3
|
||||
|
||||
import (
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/crowdmob/goamz/aws"
|
||||
)
|
||||
|
||||
var originalStrategy = attempts
|
||||
|
||||
func SetAttemptStrategy(s *aws.AttemptStrategy) {
|
||||
if s == nil {
|
||||
attempts = originalStrategy
|
||||
} else {
|
||||
attempts = *s
|
||||
}
|
||||
}
|
||||
|
||||
func Sign(auth aws.Auth, method, path string, params, headers map[string][]string) {
|
||||
sign(auth, method, path, params, headers)
|
||||
}
|
||||
|
||||
func SetListPartsMax(n int) {
|
||||
listPartsMax = n
|
||||
}
|
||||
|
||||
func SetListMultiMax(n int) {
|
||||
listMultiMax = n
|
||||
}
|
||||
202
Godeps/_workspace/src/github.com/crowdmob/goamz/s3/lifecycle.go
generated
vendored
202
Godeps/_workspace/src/github.com/crowdmob/goamz/s3/lifecycle.go
generated
vendored
@ -1,202 +0,0 @@
|
||||
package s3
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"encoding/xml"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Implements an interface for s3 bucket lifecycle configuration
|
||||
// See goo.gl/d0bbDf for details.
|
||||
|
||||
const (
|
||||
LifecycleRuleStatusEnabled = "Enabled"
|
||||
LifecycleRuleStatusDisabled = "Disabled"
|
||||
LifecycleRuleDateFormat = "2006-01-02"
|
||||
StorageClassGlacier = "GLACIER"
|
||||
)
|
||||
|
||||
type Expiration struct {
|
||||
Days *uint `xml:"Days,omitempty"`
|
||||
Date string `xml:"Date,omitempty"`
|
||||
}
|
||||
|
||||
// Returns Date as a time.Time.
|
||||
func (r *Expiration) ParseDate() (time.Time, error) {
|
||||
return time.Parse(LifecycleRuleDateFormat, r.Date)
|
||||
}
|
||||
|
||||
type Transition struct {
|
||||
Days *uint `xml:"Days,omitempty"`
|
||||
Date string `xml:"Date,omitempty"`
|
||||
StorageClass string `xml:"StorageClass"`
|
||||
}
|
||||
|
||||
// Returns Date as a time.Time.
|
||||
func (r *Transition) ParseDate() (time.Time, error) {
|
||||
return time.Parse(LifecycleRuleDateFormat, r.Date)
|
||||
}
|
||||
|
||||
type NoncurrentVersionExpiration struct {
|
||||
Days *uint `xml:"NoncurrentDays,omitempty"`
|
||||
}
|
||||
|
||||
type NoncurrentVersionTransition struct {
|
||||
Days *uint `xml:"NoncurrentDays,omitempty"`
|
||||
StorageClass string `xml:"StorageClass"`
|
||||
}
|
||||
|
||||
type LifecycleRule struct {
|
||||
ID string `xml:"ID"`
|
||||
Prefix string `xml:"Prefix"`
|
||||
Status string `xml:"Status"`
|
||||
NoncurrentVersionTransition *NoncurrentVersionTransition `xml:"NoncurrentVersionTransition,omitempty"`
|
||||
NoncurrentVersionExpiration *NoncurrentVersionExpiration `xml:"NoncurrentVersionExpiration,omitempty"`
|
||||
Transition *Transition `xml:"Transition,omitempty"`
|
||||
Expiration *Expiration `xml:"Expiration,omitempty"`
|
||||
}
|
||||
|
||||
// Create a lifecycle rule with arbitrary identifier id and object name prefix
|
||||
// for which the rules should apply.
|
||||
func NewLifecycleRule(id, prefix string) *LifecycleRule {
|
||||
rule := &LifecycleRule{
|
||||
ID: id,
|
||||
Prefix: prefix,
|
||||
Status: LifecycleRuleStatusEnabled,
|
||||
}
|
||||
return rule
|
||||
}
|
||||
|
||||
// Adds a transition rule in days. Overwrites any previous transition rule.
|
||||
func (r *LifecycleRule) SetTransitionDays(days uint) {
|
||||
r.Transition = &Transition{
|
||||
Days: &days,
|
||||
StorageClass: StorageClassGlacier,
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a transition rule as a date. Overwrites any previous transition rule.
|
||||
func (r *LifecycleRule) SetTransitionDate(date time.Time) {
|
||||
r.Transition = &Transition{
|
||||
Date: date.Format(LifecycleRuleDateFormat),
|
||||
StorageClass: StorageClassGlacier,
|
||||
}
|
||||
}
|
||||
|
||||
// Adds an expiration rule in days. Overwrites any previous expiration rule.
|
||||
// Days must be > 0.
|
||||
func (r *LifecycleRule) SetExpirationDays(days uint) {
|
||||
r.Expiration = &Expiration{
|
||||
Days: &days,
|
||||
}
|
||||
}
|
||||
|
||||
// Adds an expiration rule as a date. Overwrites any previous expiration rule.
|
||||
func (r *LifecycleRule) SetExpirationDate(date time.Time) {
|
||||
r.Expiration = &Expiration{
|
||||
Date: date.Format(LifecycleRuleDateFormat),
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a noncurrent version transition rule. Overwrites any previous
|
||||
// noncurrent version transition rule.
|
||||
func (r *LifecycleRule) SetNoncurrentVersionTransitionDays(days uint) {
|
||||
r.NoncurrentVersionTransition = &NoncurrentVersionTransition{
|
||||
Days: &days,
|
||||
StorageClass: StorageClassGlacier,
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a noncurrent version expiration rule. Days must be > 0. Overwrites
|
||||
// any previous noncurrent version expiration rule.
|
||||
func (r *LifecycleRule) SetNoncurrentVersionExpirationDays(days uint) {
|
||||
r.NoncurrentVersionExpiration = &NoncurrentVersionExpiration{
|
||||
Days: &days,
|
||||
}
|
||||
}
|
||||
|
||||
// Marks the rule as disabled.
|
||||
func (r *LifecycleRule) Disable() {
|
||||
r.Status = LifecycleRuleStatusDisabled
|
||||
}
|
||||
|
||||
// Marks the rule as enabled (default).
|
||||
func (r *LifecycleRule) Enable() {
|
||||
r.Status = LifecycleRuleStatusEnabled
|
||||
}
|
||||
|
||||
type LifecycleConfiguration struct {
|
||||
XMLName xml.Name `xml:"LifecycleConfiguration"`
|
||||
Rules *[]*LifecycleRule `xml:"Rule,omitempty"`
|
||||
}
|
||||
|
||||
// Adds a LifecycleRule to the configuration.
|
||||
func (c *LifecycleConfiguration) AddRule(r *LifecycleRule) {
|
||||
var rules []*LifecycleRule
|
||||
if c.Rules != nil {
|
||||
rules = *c.Rules
|
||||
}
|
||||
rules = append(rules, r)
|
||||
c.Rules = &rules
|
||||
}
|
||||
|
||||
// Sets the bucket's lifecycle configuration.
|
||||
func (b *Bucket) PutLifecycleConfiguration(c *LifecycleConfiguration) error {
|
||||
doc, err := xml.Marshal(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf := makeXmlBuffer(doc)
|
||||
digest := md5.New()
|
||||
size, err := digest.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
headers := map[string][]string{
|
||||
"Content-Length": {strconv.FormatInt(int64(size), 10)},
|
||||
"Content-MD5": {base64.StdEncoding.EncodeToString(digest.Sum(nil))},
|
||||
}
|
||||
|
||||
req := &request{
|
||||
path: "/",
|
||||
method: "PUT",
|
||||
bucket: b.Name,
|
||||
headers: headers,
|
||||
payload: buf,
|
||||
params: url.Values{"lifecycle": {""}},
|
||||
}
|
||||
|
||||
return b.S3.queryV4Sign(req, nil)
|
||||
}
|
||||
|
||||
// Retrieves the lifecycle configuration for the bucket. AWS returns an error
|
||||
// if no lifecycle found.
|
||||
func (b *Bucket) GetLifecycleConfiguration() (*LifecycleConfiguration, error) {
|
||||
req := &request{
|
||||
method: "GET",
|
||||
bucket: b.Name,
|
||||
path: "/",
|
||||
params: url.Values{"lifecycle": {""}},
|
||||
}
|
||||
|
||||
conf := &LifecycleConfiguration{}
|
||||
err := b.S3.queryV4Sign(req, conf)
|
||||
return conf, err
|
||||
}
|
||||
|
||||
// Delete the bucket's lifecycle configuration.
|
||||
func (b *Bucket) DeleteLifecycleConfiguration() error {
|
||||
req := &request{
|
||||
method: "DELETE",
|
||||
bucket: b.Name,
|
||||
path: "/",
|
||||
params: url.Values{"lifecycle": {""}},
|
||||
}
|
||||
|
||||
return b.S3.queryV4Sign(req, nil)
|
||||
}
|
||||
205
Godeps/_workspace/src/github.com/crowdmob/goamz/s3/lifecycle_test.go
generated
vendored
205
Godeps/_workspace/src/github.com/crowdmob/goamz/s3/lifecycle_test.go
generated
vendored
@ -1,205 +0,0 @@
|
||||
package s3_test
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/crowdmob/goamz/s3"
|
||||
"gopkg.in/check.v1"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (s *S) TestLifecycleConfiguration(c *check.C) {
|
||||
date, err := time.Parse(s3.LifecycleRuleDateFormat, "2014-09-10")
|
||||
c.Check(err, check.IsNil)
|
||||
|
||||
conf := &s3.LifecycleConfiguration{}
|
||||
|
||||
rule := s3.NewLifecycleRule("transition-days", "/")
|
||||
rule.SetTransitionDays(7)
|
||||
conf.AddRule(rule)
|
||||
|
||||
rule = s3.NewLifecycleRule("transition-date", "/")
|
||||
rule.SetTransitionDate(date)
|
||||
conf.AddRule(rule)
|
||||
|
||||
rule = s3.NewLifecycleRule("expiration-days", "")
|
||||
rule.SetExpirationDays(1)
|
||||
conf.AddRule(rule)
|
||||
|
||||
rule = s3.NewLifecycleRule("expiration-date", "")
|
||||
rule.SetExpirationDate(date)
|
||||
conf.AddRule(rule)
|
||||
|
||||
rule = s3.NewLifecycleRule("noncurrent-transition", "")
|
||||
rule.SetNoncurrentVersionTransitionDays(11)
|
||||
conf.AddRule(rule)
|
||||
|
||||
rule = s3.NewLifecycleRule("noncurrent-expiration", "")
|
||||
rule.SetNoncurrentVersionExpirationDays(1011)
|
||||
|
||||
// Test Disable() and Enable() toggling
|
||||
c.Check(rule.Status, check.Equals, s3.LifecycleRuleStatusEnabled)
|
||||
rule.Disable()
|
||||
c.Check(rule.Status, check.Equals, s3.LifecycleRuleStatusDisabled)
|
||||
rule.Enable()
|
||||
c.Check(rule.Status, check.Equals, s3.LifecycleRuleStatusEnabled)
|
||||
rule.Disable()
|
||||
c.Check(rule.Status, check.Equals, s3.LifecycleRuleStatusDisabled)
|
||||
|
||||
conf.AddRule(rule)
|
||||
|
||||
doc, err := xml.MarshalIndent(conf, "", " ")
|
||||
c.Check(err, check.IsNil)
|
||||
|
||||
expectedDoc := `<LifecycleConfiguration>
|
||||
<Rule>
|
||||
<ID>transition-days</ID>
|
||||
<Prefix>/</Prefix>
|
||||
<Status>Enabled</Status>
|
||||
<Transition>
|
||||
<Days>7</Days>
|
||||
<StorageClass>GLACIER</StorageClass>
|
||||
</Transition>
|
||||
</Rule>
|
||||
<Rule>
|
||||
<ID>transition-date</ID>
|
||||
<Prefix>/</Prefix>
|
||||
<Status>Enabled</Status>
|
||||
<Transition>
|
||||
<Date>2014-09-10</Date>
|
||||
<StorageClass>GLACIER</StorageClass>
|
||||
</Transition>
|
||||
</Rule>
|
||||
<Rule>
|
||||
<ID>expiration-days</ID>
|
||||
<Prefix></Prefix>
|
||||
<Status>Enabled</Status>
|
||||
<Expiration>
|
||||
<Days>1</Days>
|
||||
</Expiration>
|
||||
</Rule>
|
||||
<Rule>
|
||||
<ID>expiration-date</ID>
|
||||
<Prefix></Prefix>
|
||||
<Status>Enabled</Status>
|
||||
<Expiration>
|
||||
<Date>2014-09-10</Date>
|
||||
</Expiration>
|
||||
</Rule>
|
||||
<Rule>
|
||||
<ID>noncurrent-transition</ID>
|
||||
<Prefix></Prefix>
|
||||
<Status>Enabled</Status>
|
||||
<NoncurrentVersionTransition>
|
||||
<NoncurrentDays>11</NoncurrentDays>
|
||||
<StorageClass>GLACIER</StorageClass>
|
||||
</NoncurrentVersionTransition>
|
||||
</Rule>
|
||||
<Rule>
|
||||
<ID>noncurrent-expiration</ID>
|
||||
<Prefix></Prefix>
|
||||
<Status>Disabled</Status>
|
||||
<NoncurrentVersionExpiration>
|
||||
<NoncurrentDays>1011</NoncurrentDays>
|
||||
</NoncurrentVersionExpiration>
|
||||
</Rule>
|
||||
</LifecycleConfiguration>`
|
||||
|
||||
c.Check(string(doc), check.Equals, expectedDoc)
|
||||
|
||||
// Unmarshalling test
|
||||
conf2 := &s3.LifecycleConfiguration{}
|
||||
err = xml.Unmarshal(doc, conf2)
|
||||
c.Check(err, check.IsNil)
|
||||
s.checkLifecycleConfigurationEqual(c, conf, conf2)
|
||||
}
|
||||
|
||||
func (s *S) checkLifecycleConfigurationEqual(c *check.C, conf, conf2 *s3.LifecycleConfiguration) {
|
||||
c.Check(len(*conf2.Rules), check.Equals, len(*conf.Rules))
|
||||
for i, rule := range *conf2.Rules {
|
||||
confRules := *conf.Rules
|
||||
c.Check(rule, check.DeepEquals, confRules[i])
|
||||
}
|
||||
}
|
||||
|
||||
func (s *S) checkLifecycleRequest(c *check.C, req *http.Request) {
|
||||
// ?lifecycle= is the only query param
|
||||
v, ok := req.Form["lifecycle"]
|
||||
c.Assert(ok, check.Equals, true)
|
||||
c.Assert(v, check.HasLen, 1)
|
||||
c.Assert(v[0], check.Equals, "")
|
||||
|
||||
c.Assert(req.Header["X-Amz-Date"], check.HasLen, 1)
|
||||
c.Assert(req.Header["X-Amz-Date"][0], check.Not(check.Equals), "")
|
||||
|
||||
// Lifecycle methods require V4 auth
|
||||
usesV4 := strings.HasPrefix(req.Header["Authorization"][0], "AWS4-HMAC-SHA256")
|
||||
c.Assert(usesV4, check.Equals, true)
|
||||
}
|
||||
|
||||
func (s *S) TestPutLifecycleConfiguration(c *check.C) {
|
||||
testServer.Response(200, nil, "")
|
||||
|
||||
conf := &s3.LifecycleConfiguration{}
|
||||
rule := s3.NewLifecycleRule("id", "")
|
||||
rule.SetTransitionDays(7)
|
||||
conf.AddRule(rule)
|
||||
|
||||
doc, err := xml.Marshal(conf)
|
||||
c.Check(err, check.IsNil)
|
||||
|
||||
b := s.s3.Bucket("bucket")
|
||||
err = b.PutLifecycleConfiguration(conf)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "PUT")
|
||||
c.Assert(req.URL.Path, check.Equals, "/bucket/")
|
||||
c.Assert(req.Header["Content-Md5"], check.HasLen, 1)
|
||||
c.Assert(req.Header["Content-Md5"][0], check.Not(check.Equals), "")
|
||||
s.checkLifecycleRequest(c, req)
|
||||
|
||||
// Check we sent the correct xml serialization
|
||||
data, err := ioutil.ReadAll(req.Body)
|
||||
req.Body.Close()
|
||||
c.Assert(err, check.IsNil)
|
||||
header := "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
c.Assert(string(data), check.Equals, header+string(doc))
|
||||
}
|
||||
|
||||
func (s *S) TestGetLifecycleConfiguration(c *check.C) {
|
||||
conf := &s3.LifecycleConfiguration{}
|
||||
rule := s3.NewLifecycleRule("id", "")
|
||||
rule.SetTransitionDays(7)
|
||||
conf.AddRule(rule)
|
||||
|
||||
doc, err := xml.Marshal(conf)
|
||||
c.Check(err, check.IsNil)
|
||||
|
||||
testServer.Response(200, nil, string(doc))
|
||||
|
||||
b := s.s3.Bucket("bucket")
|
||||
conf2, err := b.GetLifecycleConfiguration()
|
||||
c.Check(err, check.IsNil)
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "GET")
|
||||
c.Assert(req.URL.Path, check.Equals, "/bucket/")
|
||||
s.checkLifecycleRequest(c, req)
|
||||
s.checkLifecycleConfigurationEqual(c, conf, conf2)
|
||||
}
|
||||
|
||||
func (s *S) TestDeleteLifecycleConfiguration(c *check.C) {
|
||||
testServer.Response(200, nil, "")
|
||||
|
||||
b := s.s3.Bucket("bucket")
|
||||
err := b.DeleteLifecycleConfiguration()
|
||||
c.Check(err, check.IsNil)
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "DELETE")
|
||||
c.Assert(req.URL.Path, check.Equals, "/bucket/")
|
||||
s.checkLifecycleRequest(c, req)
|
||||
}
|
||||
464
Godeps/_workspace/src/github.com/crowdmob/goamz/s3/multi.go
generated
vendored
464
Godeps/_workspace/src/github.com/crowdmob/goamz/s3/multi.go
generated
vendored
@ -1,464 +0,0 @@
|
||||
package s3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"io"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Multi represents an unfinished multipart upload.
|
||||
//
|
||||
// Multipart uploads allow sending big objects in smaller chunks.
|
||||
// After all parts have been sent, the upload must be explicitly
|
||||
// completed by calling Complete with the list of parts.
|
||||
//
|
||||
// See http://goo.gl/vJfTG for an overview of multipart uploads.
|
||||
type Multi struct {
|
||||
Bucket *Bucket
|
||||
Key string
|
||||
UploadId string
|
||||
}
|
||||
|
||||
// That's the default. Here just for testing.
|
||||
var listMultiMax = 1000
|
||||
|
||||
type listMultiResp struct {
|
||||
NextKeyMarker string
|
||||
NextUploadIdMarker string
|
||||
IsTruncated bool
|
||||
Upload []Multi
|
||||
CommonPrefixes []string `xml:"CommonPrefixes>Prefix"`
|
||||
}
|
||||
|
||||
// ListMulti returns the list of unfinished multipart uploads in b.
|
||||
//
|
||||
// The prefix parameter limits the response to keys that begin with the
|
||||
// specified prefix. You can use prefixes to separate a bucket into different
|
||||
// groupings of keys (to get the feeling of folders, for example).
|
||||
//
|
||||
// The delim parameter causes the response to group all of the keys that
|
||||
// share a common prefix up to the next delimiter in a single entry within
|
||||
// the CommonPrefixes field. You can use delimiters to separate a bucket
|
||||
// into different groupings of keys, similar to how folders would work.
|
||||
//
|
||||
// See http://goo.gl/ePioY for details.
|
||||
func (b *Bucket) ListMulti(prefix, delim string) (multis []*Multi, prefixes []string, err error) {
|
||||
params := map[string][]string{
|
||||
"uploads": {""},
|
||||
"max-uploads": {strconv.FormatInt(int64(listMultiMax), 10)},
|
||||
"prefix": {prefix},
|
||||
"delimiter": {delim},
|
||||
}
|
||||
for attempt := attempts.Start(); attempt.Next(); {
|
||||
req := &request{
|
||||
method: "GET",
|
||||
bucket: b.Name,
|
||||
params: params,
|
||||
}
|
||||
var resp listMultiResp
|
||||
err := b.S3.query(req, &resp)
|
||||
if shouldRetry(err) && attempt.HasNext() {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for i := range resp.Upload {
|
||||
multi := &resp.Upload[i]
|
||||
multi.Bucket = b
|
||||
multis = append(multis, multi)
|
||||
}
|
||||
prefixes = append(prefixes, resp.CommonPrefixes...)
|
||||
if !resp.IsTruncated {
|
||||
return multis, prefixes, nil
|
||||
}
|
||||
params["key-marker"] = []string{resp.NextKeyMarker}
|
||||
params["upload-id-marker"] = []string{resp.NextUploadIdMarker}
|
||||
attempt = attempts.Start() // Last request worked.
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Multi returns a multipart upload handler for the provided key
|
||||
// inside b. If a multipart upload exists for key, it is returned,
|
||||
// otherwise a new multipart upload is initiated with contType and perm.
|
||||
func (b *Bucket) Multi(key, contType string, perm ACL, options Options) (*Multi, error) {
|
||||
multis, _, err := b.ListMulti(key, "")
|
||||
if err != nil && !hasCode(err, "NoSuchUpload") {
|
||||
return nil, err
|
||||
}
|
||||
for _, m := range multis {
|
||||
if m.Key == key {
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
return b.InitMulti(key, contType, perm, options)
|
||||
}
|
||||
|
||||
// InitMulti initializes a new multipart upload at the provided
|
||||
// key inside b and returns a value for manipulating it.
|
||||
//
|
||||
// See http://goo.gl/XP8kL for details.
|
||||
func (b *Bucket) InitMulti(key string, contType string, perm ACL, options Options) (*Multi, error) {
|
||||
headers := map[string][]string{
|
||||
"Content-Type": {contType},
|
||||
"Content-Length": {"0"},
|
||||
"x-amz-acl": {string(perm)},
|
||||
}
|
||||
options.addHeaders(headers)
|
||||
params := map[string][]string{
|
||||
"uploads": {""},
|
||||
}
|
||||
req := &request{
|
||||
method: "POST",
|
||||
bucket: b.Name,
|
||||
path: key,
|
||||
headers: headers,
|
||||
params: params,
|
||||
}
|
||||
var err error
|
||||
var resp struct {
|
||||
UploadId string `xml:"UploadId"`
|
||||
}
|
||||
for attempt := attempts.Start(); attempt.Next(); {
|
||||
err = b.S3.query(req, &resp)
|
||||
if !shouldRetry(err) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Multi{Bucket: b, Key: key, UploadId: resp.UploadId}, nil
|
||||
}
|
||||
|
||||
func (m *Multi) PutPartCopy(n int, options CopyOptions, source string) (*CopyObjectResult, Part, error) {
|
||||
headers := map[string][]string{
|
||||
"x-amz-copy-source": {url.QueryEscape(source)},
|
||||
}
|
||||
options.addHeaders(headers)
|
||||
params := map[string][]string{
|
||||
"uploadId": {m.UploadId},
|
||||
"partNumber": {strconv.FormatInt(int64(n), 10)},
|
||||
}
|
||||
|
||||
sourceBucket := m.Bucket.S3.Bucket(strings.TrimRight(strings.SplitAfterN(source, "/", 2)[0], "/"))
|
||||
sourceMeta, err := sourceBucket.Head(strings.SplitAfterN(source, "/", 2)[1], nil)
|
||||
if err != nil {
|
||||
return nil, Part{}, err
|
||||
}
|
||||
|
||||
for attempt := attempts.Start(); attempt.Next(); {
|
||||
req := &request{
|
||||
method: "PUT",
|
||||
bucket: m.Bucket.Name,
|
||||
path: m.Key,
|
||||
headers: headers,
|
||||
params: params,
|
||||
}
|
||||
resp := &CopyObjectResult{}
|
||||
err = m.Bucket.S3.query(req, resp)
|
||||
if shouldRetry(err) && attempt.HasNext() {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, Part{}, err
|
||||
}
|
||||
if resp.ETag == "" {
|
||||
return nil, Part{}, errors.New("part upload succeeded with no ETag")
|
||||
}
|
||||
return resp, Part{n, resp.ETag, sourceMeta.ContentLength}, nil
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// PutPart sends part n of the multipart upload, reading all the content from r.
|
||||
// Each part, except for the last one, must be at least 5MB in size.
|
||||
//
|
||||
// See http://goo.gl/pqZer for details.
|
||||
func (m *Multi) PutPart(n int, r io.ReadSeeker) (Part, error) {
|
||||
partSize, _, md5b64, err := seekerInfo(r)
|
||||
if err != nil {
|
||||
return Part{}, err
|
||||
}
|
||||
return m.putPart(n, r, partSize, md5b64)
|
||||
}
|
||||
|
||||
func (m *Multi) putPart(n int, r io.ReadSeeker, partSize int64, md5b64 string) (Part, error) {
|
||||
headers := map[string][]string{
|
||||
"Content-Length": {strconv.FormatInt(partSize, 10)},
|
||||
"Content-MD5": {md5b64},
|
||||
}
|
||||
params := map[string][]string{
|
||||
"uploadId": {m.UploadId},
|
||||
"partNumber": {strconv.FormatInt(int64(n), 10)},
|
||||
}
|
||||
for attempt := attempts.Start(); attempt.Next(); {
|
||||
_, err := r.Seek(0, 0)
|
||||
if err != nil {
|
||||
return Part{}, err
|
||||
}
|
||||
req := &request{
|
||||
method: "PUT",
|
||||
bucket: m.Bucket.Name,
|
||||
path: m.Key,
|
||||
headers: headers,
|
||||
params: params,
|
||||
payload: r,
|
||||
}
|
||||
err = m.Bucket.S3.prepare(req)
|
||||
if err != nil {
|
||||
return Part{}, err
|
||||
}
|
||||
resp, err := m.Bucket.S3.run(req, nil)
|
||||
if shouldRetry(err) && attempt.HasNext() {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return Part{}, err
|
||||
}
|
||||
etag := resp.Header.Get("ETag")
|
||||
if etag == "" {
|
||||
return Part{}, errors.New("part upload succeeded with no ETag")
|
||||
}
|
||||
return Part{n, etag, partSize}, nil
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func seekerInfo(r io.ReadSeeker) (size int64, md5hex string, md5b64 string, err error) {
|
||||
_, err = r.Seek(0, 0)
|
||||
if err != nil {
|
||||
return 0, "", "", err
|
||||
}
|
||||
digest := md5.New()
|
||||
size, err = io.Copy(digest, r)
|
||||
if err != nil {
|
||||
return 0, "", "", err
|
||||
}
|
||||
sum := digest.Sum(nil)
|
||||
md5hex = hex.EncodeToString(sum)
|
||||
md5b64 = base64.StdEncoding.EncodeToString(sum)
|
||||
return size, md5hex, md5b64, nil
|
||||
}
|
||||
|
||||
type Part struct {
|
||||
N int `xml:"PartNumber"`
|
||||
ETag string
|
||||
Size int64
|
||||
}
|
||||
|
||||
type partSlice []Part
|
||||
|
||||
func (s partSlice) Len() int { return len(s) }
|
||||
func (s partSlice) Less(i, j int) bool { return s[i].N < s[j].N }
|
||||
func (s partSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
type listPartsResp struct {
|
||||
NextPartNumberMarker string
|
||||
IsTruncated bool
|
||||
Part []Part
|
||||
}
|
||||
|
||||
// That's the default. Here just for testing.
|
||||
var listPartsMax = 1000
|
||||
|
||||
// Kept for backcompatability. See the documentation for ListPartsFull
|
||||
func (m *Multi) ListParts() ([]Part, error) {
|
||||
return m.ListPartsFull(0, listPartsMax)
|
||||
}
|
||||
|
||||
// ListParts returns the list of previously uploaded parts in m,
|
||||
// ordered by part number (Only parts with higher part numbers than
|
||||
// partNumberMarker will be listed). Only up to maxParts parts will be
|
||||
// returned.
|
||||
//
|
||||
// See http://goo.gl/ePioY for details.
|
||||
func (m *Multi) ListPartsFull(partNumberMarker int, maxParts int) ([]Part, error) {
|
||||
if maxParts > listPartsMax {
|
||||
maxParts = listPartsMax
|
||||
}
|
||||
|
||||
params := map[string][]string{
|
||||
"uploadId": {m.UploadId},
|
||||
"max-parts": {strconv.FormatInt(int64(maxParts), 10)},
|
||||
"part-number-marker": {strconv.FormatInt(int64(partNumberMarker), 10)},
|
||||
}
|
||||
var parts partSlice
|
||||
for attempt := attempts.Start(); attempt.Next(); {
|
||||
req := &request{
|
||||
method: "GET",
|
||||
bucket: m.Bucket.Name,
|
||||
path: m.Key,
|
||||
params: params,
|
||||
}
|
||||
var resp listPartsResp
|
||||
err := m.Bucket.S3.query(req, &resp)
|
||||
if shouldRetry(err) && attempt.HasNext() {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parts = append(parts, resp.Part...)
|
||||
if !resp.IsTruncated {
|
||||
sort.Sort(parts)
|
||||
return parts, nil
|
||||
}
|
||||
params["part-number-marker"] = []string{resp.NextPartNumberMarker}
|
||||
attempt = attempts.Start() // Last request worked.
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
type ReaderAtSeeker interface {
|
||||
io.ReaderAt
|
||||
io.ReadSeeker
|
||||
}
|
||||
|
||||
// PutAll sends all of r via a multipart upload with parts no larger
|
||||
// than partSize bytes, which must be set to at least 5MB.
|
||||
// Parts previously uploaded are either reused if their checksum
|
||||
// and size match the new part, or otherwise overwritten with the
|
||||
// new content.
|
||||
// PutAll returns all the parts of m (reused or not).
|
||||
func (m *Multi) PutAll(r ReaderAtSeeker, partSize int64) ([]Part, error) {
|
||||
old, err := m.ListParts()
|
||||
if err != nil && !hasCode(err, "NoSuchUpload") {
|
||||
return nil, err
|
||||
}
|
||||
reuse := 0 // Index of next old part to consider reusing.
|
||||
current := 1 // Part number of latest good part handled.
|
||||
totalSize, err := r.Seek(0, 2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
first := true // Must send at least one empty part if the file is empty.
|
||||
var result []Part
|
||||
NextSection:
|
||||
for offset := int64(0); offset < totalSize || first; offset += partSize {
|
||||
first = false
|
||||
if offset+partSize > totalSize {
|
||||
partSize = totalSize - offset
|
||||
}
|
||||
section := io.NewSectionReader(r, offset, partSize)
|
||||
_, md5hex, md5b64, err := seekerInfo(section)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for reuse < len(old) && old[reuse].N <= current {
|
||||
// Looks like this part was already sent.
|
||||
part := &old[reuse]
|
||||
etag := `"` + md5hex + `"`
|
||||
if part.N == current && part.Size == partSize && part.ETag == etag {
|
||||
// Checksum matches. Reuse the old part.
|
||||
result = append(result, *part)
|
||||
current++
|
||||
continue NextSection
|
||||
}
|
||||
reuse++
|
||||
}
|
||||
|
||||
// Part wasn't found or doesn't match. Send it.
|
||||
part, err := m.putPart(current, section, partSize, md5b64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, part)
|
||||
current++
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type completeUpload struct {
|
||||
XMLName xml.Name `xml:"CompleteMultipartUpload"`
|
||||
Parts completeParts `xml:"Part"`
|
||||
}
|
||||
|
||||
type completePart struct {
|
||||
PartNumber int
|
||||
ETag string
|
||||
}
|
||||
|
||||
type completeParts []completePart
|
||||
|
||||
func (p completeParts) Len() int { return len(p) }
|
||||
func (p completeParts) Less(i, j int) bool { return p[i].PartNumber < p[j].PartNumber }
|
||||
func (p completeParts) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
// Complete assembles the given previously uploaded parts into the
|
||||
// final object. This operation may take several minutes.
|
||||
//
|
||||
// See http://goo.gl/2Z7Tw for details.
|
||||
func (m *Multi) Complete(parts []Part) error {
|
||||
params := map[string][]string{
|
||||
"uploadId": {m.UploadId},
|
||||
}
|
||||
c := completeUpload{}
|
||||
for _, p := range parts {
|
||||
c.Parts = append(c.Parts, completePart{p.N, p.ETag})
|
||||
}
|
||||
sort.Sort(c.Parts)
|
||||
data, err := xml.Marshal(&c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for attempt := attempts.Start(); attempt.Next(); {
|
||||
req := &request{
|
||||
method: "POST",
|
||||
bucket: m.Bucket.Name,
|
||||
path: m.Key,
|
||||
params: params,
|
||||
payload: bytes.NewReader(data),
|
||||
}
|
||||
err := m.Bucket.S3.query(req, nil)
|
||||
if shouldRetry(err) && attempt.HasNext() {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Abort deletes an unifinished multipart upload and any previously
|
||||
// uploaded parts for it.
|
||||
//
|
||||
// After a multipart upload is aborted, no additional parts can be
|
||||
// uploaded using it. However, if any part uploads are currently in
|
||||
// progress, those part uploads might or might not succeed. As a result,
|
||||
// it might be necessary to abort a given multipart upload multiple
|
||||
// times in order to completely free all storage consumed by all parts.
|
||||
//
|
||||
// NOTE: If the described scenario happens to you, please report back to
|
||||
// the goamz authors with details. In the future such retrying should be
|
||||
// handled internally, but it's not clear what happens precisely (Is an
|
||||
// error returned? Is the issue completely undetectable?).
|
||||
//
|
||||
// See http://goo.gl/dnyJw for details.
|
||||
func (m *Multi) Abort() error {
|
||||
params := map[string][]string{
|
||||
"uploadId": {m.UploadId},
|
||||
}
|
||||
for attempt := attempts.Start(); attempt.Next(); {
|
||||
req := &request{
|
||||
method: "DELETE",
|
||||
bucket: m.Bucket.Name,
|
||||
path: m.Key,
|
||||
params: params,
|
||||
}
|
||||
err := m.Bucket.S3.query(req, nil)
|
||||
if shouldRetry(err) && attempt.HasNext() {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
425
Godeps/_workspace/src/github.com/crowdmob/goamz/s3/multi_test.go
generated
vendored
425
Godeps/_workspace/src/github.com/crowdmob/goamz/s3/multi_test.go
generated
vendored
@ -1,425 +0,0 @@
|
||||
package s3_test
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/crowdmob/goamz/s3"
|
||||
"gopkg.in/check.v1"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (s *S) TestInitMulti(c *check.C) {
|
||||
testServer.Response(200, nil, InitMultiResultDump)
|
||||
b := s.s3.Bucket("sample")
|
||||
|
||||
metadata := make(map[string][]string)
|
||||
metadata["key1"] = []string{"value1"}
|
||||
metadata["key2"] = []string{"value2"}
|
||||
options := s3.Options{
|
||||
SSE: true,
|
||||
Meta: metadata,
|
||||
ContentEncoding: "text/utf8",
|
||||
CacheControl: "no-cache",
|
||||
RedirectLocation: "http://github.com/crowdmob/goamz",
|
||||
ContentMD5: "0000000000000000",
|
||||
}
|
||||
|
||||
multi, err := b.InitMulti("multi", "text/plain", s3.Private, options)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "POST")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/multi")
|
||||
c.Assert(req.Header["Content-Type"], check.DeepEquals, []string{"text/plain"})
|
||||
c.Assert(req.Header["X-Amz-Acl"], check.DeepEquals, []string{"private"})
|
||||
c.Assert(req.Form["uploads"], check.DeepEquals, []string{""})
|
||||
|
||||
c.Assert(req.Header["X-Amz-Server-Side-Encryption"], check.DeepEquals, []string{"AES256"})
|
||||
c.Assert(req.Header["Content-Encoding"], check.DeepEquals, []string{"text/utf8"})
|
||||
c.Assert(req.Header["Cache-Control"], check.DeepEquals, []string{"no-cache"})
|
||||
c.Assert(req.Header["Content-Md5"], check.DeepEquals, []string{"0000000000000000"})
|
||||
c.Assert(req.Header["X-Amz-Website-Redirect-Location"], check.DeepEquals, []string{"http://github.com/crowdmob/goamz"})
|
||||
c.Assert(req.Header["X-Amz-Meta-Key1"], check.DeepEquals, []string{"value1"})
|
||||
c.Assert(req.Header["X-Amz-Meta-Key2"], check.DeepEquals, []string{"value2"})
|
||||
|
||||
c.Assert(multi.UploadId, check.Matches, "JNbR_[A-Za-z0-9.]+QQ--")
|
||||
}
|
||||
|
||||
func (s *S) TestMultiNoPreviousUpload(c *check.C) {
|
||||
// Don't retry the NoSuchUpload error.
|
||||
s.DisableRetries()
|
||||
|
||||
testServer.Response(404, nil, NoSuchUploadErrorDump)
|
||||
testServer.Response(200, nil, InitMultiResultDump)
|
||||
|
||||
b := s.s3.Bucket("sample")
|
||||
|
||||
multi, err := b.Multi("multi", "text/plain", s3.Private, s3.Options{})
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "GET")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/")
|
||||
c.Assert(req.Form["uploads"], check.DeepEquals, []string{""})
|
||||
c.Assert(req.Form["prefix"], check.DeepEquals, []string{"multi"})
|
||||
|
||||
req = testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "POST")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/multi")
|
||||
c.Assert(req.Form["uploads"], check.DeepEquals, []string{""})
|
||||
|
||||
c.Assert(multi.UploadId, check.Matches, "JNbR_[A-Za-z0-9.]+QQ--")
|
||||
}
|
||||
|
||||
func (s *S) TestMultiReturnOld(c *check.C) {
|
||||
testServer.Response(200, nil, ListMultiResultDump)
|
||||
|
||||
b := s.s3.Bucket("sample")
|
||||
|
||||
multi, err := b.Multi("multi1", "text/plain", s3.Private, s3.Options{})
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(multi.Key, check.Equals, "multi1")
|
||||
c.Assert(multi.UploadId, check.Equals, "iUVug89pPvSswrikD")
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "GET")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/")
|
||||
c.Assert(req.Form["uploads"], check.DeepEquals, []string{""})
|
||||
c.Assert(req.Form["prefix"], check.DeepEquals, []string{"multi1"})
|
||||
}
|
||||
|
||||
func (s *S) TestListParts(c *check.C) {
|
||||
testServer.Response(200, nil, InitMultiResultDump)
|
||||
testServer.Response(200, nil, ListPartsResultDump1)
|
||||
testServer.Response(404, nil, NoSuchUploadErrorDump) // :-(
|
||||
testServer.Response(200, nil, ListPartsResultDump2)
|
||||
|
||||
b := s.s3.Bucket("sample")
|
||||
|
||||
multi, err := b.InitMulti("multi", "text/plain", s3.Private, s3.Options{})
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
parts, err := multi.ListParts()
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(parts, check.HasLen, 3)
|
||||
c.Assert(parts[0].N, check.Equals, 1)
|
||||
c.Assert(parts[0].Size, check.Equals, int64(5))
|
||||
c.Assert(parts[0].ETag, check.Equals, `"ffc88b4ca90a355f8ddba6b2c3b2af5c"`)
|
||||
c.Assert(parts[1].N, check.Equals, 2)
|
||||
c.Assert(parts[1].Size, check.Equals, int64(5))
|
||||
c.Assert(parts[1].ETag, check.Equals, `"d067a0fa9dc61a6e7195ca99696b5a89"`)
|
||||
c.Assert(parts[2].N, check.Equals, 3)
|
||||
c.Assert(parts[2].Size, check.Equals, int64(5))
|
||||
c.Assert(parts[2].ETag, check.Equals, `"49dcd91231f801159e893fb5c6674985"`)
|
||||
testServer.WaitRequest()
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "GET")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/multi")
|
||||
c.Assert(req.Form.Get("uploadId"), check.Matches, "JNbR_[A-Za-z0-9.]+QQ--")
|
||||
c.Assert(req.Form["max-parts"], check.DeepEquals, []string{"1000"})
|
||||
|
||||
testServer.WaitRequest() // The internal error.
|
||||
req = testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "GET")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/multi")
|
||||
c.Assert(req.Form.Get("uploadId"), check.Matches, "JNbR_[A-Za-z0-9.]+QQ--")
|
||||
c.Assert(req.Form["max-parts"], check.DeepEquals, []string{"1000"})
|
||||
c.Assert(req.Form["part-number-marker"], check.DeepEquals, []string{"2"})
|
||||
}
|
||||
|
||||
func (s *S) TestPutPart(c *check.C) {
|
||||
headers := map[string]string{
|
||||
"ETag": `"26f90efd10d614f100252ff56d88dad8"`,
|
||||
}
|
||||
testServer.Response(200, nil, InitMultiResultDump)
|
||||
testServer.Response(200, headers, "")
|
||||
|
||||
b := s.s3.Bucket("sample")
|
||||
|
||||
multi, err := b.InitMulti("multi", "text/plain", s3.Private, s3.Options{})
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
part, err := multi.PutPart(1, strings.NewReader("<part 1>"))
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(part.N, check.Equals, 1)
|
||||
c.Assert(part.Size, check.Equals, int64(8))
|
||||
c.Assert(part.ETag, check.Equals, headers["ETag"])
|
||||
|
||||
testServer.WaitRequest()
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "PUT")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/multi")
|
||||
c.Assert(req.Form.Get("uploadId"), check.Matches, "JNbR_[A-Za-z0-9.]+QQ--")
|
||||
c.Assert(req.Form["partNumber"], check.DeepEquals, []string{"1"})
|
||||
c.Assert(req.Header["Content-Length"], check.DeepEquals, []string{"8"})
|
||||
c.Assert(req.Header["Content-Md5"], check.DeepEquals, []string{"JvkO/RDWFPEAJS/1bYja2A=="})
|
||||
}
|
||||
|
||||
func (s *S) TestPutPartCopy(c *check.C) {
|
||||
testServer.Response(200, nil, InitMultiResultDump)
|
||||
// PutPartCopy makes a Head request internally to verify access to the source object
|
||||
// and obtain its size
|
||||
testServer.Response(200, nil, "content")
|
||||
testServer.Response(200, nil, PutCopyResultDump)
|
||||
|
||||
b := s.s3.Bucket("sample")
|
||||
|
||||
multi, err := b.InitMulti("multi", "text/plain", s3.Private, s3.Options{})
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
res, part, err := multi.PutPartCopy(1, s3.CopyOptions{}, "source-bucket/\u00FCber-fil\u00E9.jpg")
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(part.N, check.Equals, 1)
|
||||
c.Assert(part.Size, check.Equals, int64(7))
|
||||
c.Assert(res, check.DeepEquals, &s3.CopyObjectResult{
|
||||
ETag: `"9b2cf535f27731c974343645a3985328"`,
|
||||
LastModified: `2009-10-28T22:32:00`})
|
||||
|
||||
// Verify the Head request
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "POST")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/multi")
|
||||
c.Assert(req.Header["Date"], check.Not(check.Equals), "")
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
testServer.WaitRequest()
|
||||
req = testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "PUT")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/multi")
|
||||
c.Assert(req.Form.Get("uploadId"), check.Matches, "JNbR_[A-Za-z0-9.]+QQ--")
|
||||
c.Assert(req.Form["partNumber"], check.DeepEquals, []string{"1"})
|
||||
c.Assert(req.Header["X-Amz-Copy-Source"], check.DeepEquals, []string{`source-bucket%2F%C3%BCber-fil%C3%A9.jpg`})
|
||||
}
|
||||
|
||||
func readAll(r io.Reader) string {
|
||||
data, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func (s *S) TestPutAllNoPreviousUpload(c *check.C) {
|
||||
// Don't retry the NoSuchUpload error.
|
||||
s.DisableRetries()
|
||||
|
||||
etag1 := map[string]string{"ETag": `"etag1"`}
|
||||
etag2 := map[string]string{"ETag": `"etag2"`}
|
||||
etag3 := map[string]string{"ETag": `"etag3"`}
|
||||
testServer.Response(200, nil, InitMultiResultDump)
|
||||
testServer.Response(404, nil, NoSuchUploadErrorDump)
|
||||
testServer.Response(200, etag1, "")
|
||||
testServer.Response(200, etag2, "")
|
||||
testServer.Response(200, etag3, "")
|
||||
|
||||
b := s.s3.Bucket("sample")
|
||||
|
||||
multi, err := b.InitMulti("multi", "text/plain", s3.Private, s3.Options{})
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
parts, err := multi.PutAll(strings.NewReader("part1part2last"), 5)
|
||||
c.Assert(parts, check.HasLen, 3)
|
||||
c.Assert(parts[0].ETag, check.Equals, `"etag1"`)
|
||||
c.Assert(parts[1].ETag, check.Equals, `"etag2"`)
|
||||
c.Assert(parts[2].ETag, check.Equals, `"etag3"`)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// Init
|
||||
testServer.WaitRequest()
|
||||
|
||||
// List old parts. Won't find anything.
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "GET")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/multi")
|
||||
|
||||
// Send part 1.
|
||||
req = testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "PUT")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/multi")
|
||||
c.Assert(req.Form["partNumber"], check.DeepEquals, []string{"1"})
|
||||
c.Assert(req.Header["Content-Length"], check.DeepEquals, []string{"5"})
|
||||
c.Assert(readAll(req.Body), check.Equals, "part1")
|
||||
|
||||
// Send part 2.
|
||||
req = testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "PUT")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/multi")
|
||||
c.Assert(req.Form["partNumber"], check.DeepEquals, []string{"2"})
|
||||
c.Assert(req.Header["Content-Length"], check.DeepEquals, []string{"5"})
|
||||
c.Assert(readAll(req.Body), check.Equals, "part2")
|
||||
|
||||
// Send part 3 with shorter body.
|
||||
req = testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "PUT")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/multi")
|
||||
c.Assert(req.Form["partNumber"], check.DeepEquals, []string{"3"})
|
||||
c.Assert(req.Header["Content-Length"], check.DeepEquals, []string{"4"})
|
||||
c.Assert(readAll(req.Body), check.Equals, "last")
|
||||
}
|
||||
|
||||
func (s *S) TestPutAllZeroSizeFile(c *check.C) {
|
||||
// Don't retry the NoSuchUpload error.
|
||||
s.DisableRetries()
|
||||
|
||||
etag1 := map[string]string{"ETag": `"etag1"`}
|
||||
testServer.Response(200, nil, InitMultiResultDump)
|
||||
testServer.Response(404, nil, NoSuchUploadErrorDump)
|
||||
testServer.Response(200, etag1, "")
|
||||
|
||||
b := s.s3.Bucket("sample")
|
||||
|
||||
multi, err := b.InitMulti("multi", "text/plain", s3.Private, s3.Options{})
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// Must send at least one part, so that completing it will work.
|
||||
parts, err := multi.PutAll(strings.NewReader(""), 5)
|
||||
c.Assert(parts, check.HasLen, 1)
|
||||
c.Assert(parts[0].ETag, check.Equals, `"etag1"`)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// Init
|
||||
testServer.WaitRequest()
|
||||
|
||||
// List old parts. Won't find anything.
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "GET")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/multi")
|
||||
|
||||
// Send empty part.
|
||||
req = testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "PUT")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/multi")
|
||||
c.Assert(req.Form["partNumber"], check.DeepEquals, []string{"1"})
|
||||
c.Assert(req.Header["Content-Length"], check.DeepEquals, []string{"0"})
|
||||
c.Assert(readAll(req.Body), check.Equals, "")
|
||||
}
|
||||
|
||||
func (s *S) TestPutAllResume(c *check.C) {
|
||||
etag2 := map[string]string{"ETag": `"etag2"`}
|
||||
testServer.Response(200, nil, InitMultiResultDump)
|
||||
testServer.Response(200, nil, ListPartsResultDump1)
|
||||
testServer.Response(200, nil, ListPartsResultDump2)
|
||||
testServer.Response(200, etag2, "")
|
||||
|
||||
b := s.s3.Bucket("sample")
|
||||
|
||||
multi, err := b.InitMulti("multi", "text/plain", s3.Private, s3.Options{})
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// "part1" and "part3" match the checksums in ResultDump1.
|
||||
// The middle one is a mismatch (it refers to "part2").
|
||||
parts, err := multi.PutAll(strings.NewReader("part1partXpart3"), 5)
|
||||
c.Assert(parts, check.HasLen, 3)
|
||||
c.Assert(parts[0].N, check.Equals, 1)
|
||||
c.Assert(parts[0].Size, check.Equals, int64(5))
|
||||
c.Assert(parts[0].ETag, check.Equals, `"ffc88b4ca90a355f8ddba6b2c3b2af5c"`)
|
||||
c.Assert(parts[1].N, check.Equals, 2)
|
||||
c.Assert(parts[1].Size, check.Equals, int64(5))
|
||||
c.Assert(parts[1].ETag, check.Equals, `"etag2"`)
|
||||
c.Assert(parts[2].N, check.Equals, 3)
|
||||
c.Assert(parts[2].Size, check.Equals, int64(5))
|
||||
c.Assert(parts[2].ETag, check.Equals, `"49dcd91231f801159e893fb5c6674985"`)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// Init
|
||||
testServer.WaitRequest()
|
||||
|
||||
// List old parts, broken in two requests.
|
||||
for i := 0; i < 2; i++ {
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "GET")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/multi")
|
||||
}
|
||||
|
||||
// Send part 2, as it didn't match the checksum.
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "PUT")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/multi")
|
||||
c.Assert(req.Form["partNumber"], check.DeepEquals, []string{"2"})
|
||||
c.Assert(req.Header["Content-Length"], check.DeepEquals, []string{"5"})
|
||||
c.Assert(readAll(req.Body), check.Equals, "partX")
|
||||
}
|
||||
|
||||
func (s *S) TestMultiComplete(c *check.C) {
|
||||
testServer.Response(200, nil, InitMultiResultDump)
|
||||
// Note the 200 response. Completing will hold the connection on some
|
||||
// kind of long poll, and may return a late error even after a 200.
|
||||
testServer.Response(200, nil, InternalErrorDump)
|
||||
testServer.Response(200, nil, "")
|
||||
|
||||
b := s.s3.Bucket("sample")
|
||||
|
||||
multi, err := b.InitMulti("multi", "text/plain", s3.Private, s3.Options{})
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
err = multi.Complete([]s3.Part{{2, `"ETag2"`, 32}, {1, `"ETag1"`, 64}})
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
testServer.WaitRequest()
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "POST")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/multi")
|
||||
c.Assert(req.Form.Get("uploadId"), check.Matches, "JNbR_[A-Za-z0-9.]+QQ--")
|
||||
|
||||
var payload struct {
|
||||
XMLName xml.Name
|
||||
Part []struct {
|
||||
PartNumber int
|
||||
ETag string
|
||||
}
|
||||
}
|
||||
|
||||
dec := xml.NewDecoder(req.Body)
|
||||
err = dec.Decode(&payload)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
c.Assert(payload.XMLName.Local, check.Equals, "CompleteMultipartUpload")
|
||||
c.Assert(len(payload.Part), check.Equals, 2)
|
||||
c.Assert(payload.Part[0].PartNumber, check.Equals, 1)
|
||||
c.Assert(payload.Part[0].ETag, check.Equals, `"ETag1"`)
|
||||
c.Assert(payload.Part[1].PartNumber, check.Equals, 2)
|
||||
c.Assert(payload.Part[1].ETag, check.Equals, `"ETag2"`)
|
||||
}
|
||||
|
||||
func (s *S) TestMultiAbort(c *check.C) {
|
||||
testServer.Response(200, nil, InitMultiResultDump)
|
||||
testServer.Response(200, nil, "")
|
||||
|
||||
b := s.s3.Bucket("sample")
|
||||
|
||||
multi, err := b.InitMulti("multi", "text/plain", s3.Private, s3.Options{})
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
err = multi.Abort()
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
testServer.WaitRequest()
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "DELETE")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/multi")
|
||||
c.Assert(req.Form.Get("uploadId"), check.Matches, "JNbR_[A-Za-z0-9.]+QQ--")
|
||||
}
|
||||
|
||||
func (s *S) TestListMulti(c *check.C) {
|
||||
testServer.Response(200, nil, ListMultiResultDump)
|
||||
|
||||
b := s.s3.Bucket("sample")
|
||||
|
||||
multis, prefixes, err := b.ListMulti("", "/")
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(prefixes, check.DeepEquals, []string{"a/", "b/"})
|
||||
c.Assert(multis, check.HasLen, 2)
|
||||
c.Assert(multis[0].Key, check.Equals, "multi1")
|
||||
c.Assert(multis[0].UploadId, check.Equals, "iUVug89pPvSswrikD")
|
||||
c.Assert(multis[1].Key, check.Equals, "multi2")
|
||||
c.Assert(multis[1].UploadId, check.Equals, "DkirwsSvPp98guVUi")
|
||||
|
||||
req := testServer.WaitRequest()
|
||||
c.Assert(req.Method, check.Equals, "GET")
|
||||
c.Assert(req.URL.Path, check.Equals, "/sample/")
|
||||
c.Assert(req.Form["uploads"], check.DeepEquals, []string{""})
|
||||
c.Assert(req.Form["prefix"], check.DeepEquals, []string{""})
|
||||
c.Assert(req.Form["delimiter"], check.DeepEquals, []string{"/"})
|
||||
c.Assert(req.Form["max-uploads"], check.DeepEquals, []string{"1000"})
|
||||
}
|
||||
239
Godeps/_workspace/src/github.com/crowdmob/goamz/s3/responses_test.go
generated
vendored
239
Godeps/_workspace/src/github.com/crowdmob/goamz/s3/responses_test.go
generated
vendored
@ -1,239 +0,0 @@
|
||||
package s3_test
|
||||
|
||||
var PutCopyResultDump = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<CopyObjectResult>
|
||||
<LastModified>2009-10-28T22:32:00</LastModified>
|
||||
<ETag>"9b2cf535f27731c974343645a3985328"</ETag>
|
||||
</CopyObjectResult>
|
||||
`
|
||||
|
||||
var GetObjectErrorDump = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Error><Code>NoSuchBucket</Code><Message>The specified bucket does not exist</Message>
|
||||
<BucketName>non-existent-bucket</BucketName><RequestId>3F1B667FAD71C3D8</RequestId>
|
||||
<HostId>L4ee/zrm1irFXY5F45fKXIRdOf9ktsKY/8TDVawuMK2jWRb1RF84i1uBzkdNqS5D</HostId></Error>
|
||||
`
|
||||
|
||||
var GetListResultDump1 = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01">
|
||||
<Name>quotes</Name>
|
||||
<Prefix>N</Prefix>
|
||||
<IsTruncated>false</IsTruncated>
|
||||
<Contents>
|
||||
<Key>Nelson</Key>
|
||||
<LastModified>2006-01-01T12:00:00.000Z</LastModified>
|
||||
<ETag>"828ef3fdfa96f00ad9f27c383fc9ac7f"</ETag>
|
||||
<Size>5</Size>
|
||||
<StorageClass>STANDARD</StorageClass>
|
||||
<Owner>
|
||||
<ID>bcaf161ca5fb16fd081034f</ID>
|
||||
<DisplayName>webfile</DisplayName>
|
||||
</Owner>
|
||||
</Contents>
|
||||
<Contents>
|
||||
<Key>Neo</Key>
|
||||
<LastModified>2006-01-01T12:00:00.000Z</LastModified>
|
||||
<ETag>"828ef3fdfa96f00ad9f27c383fc9ac7f"</ETag>
|
||||
<Size>4</Size>
|
||||
<StorageClass>STANDARD</StorageClass>
|
||||
<Owner>
|
||||
<ID>bcaf1ffd86a5fb16fd081034f</ID>
|
||||
<DisplayName>webfile</DisplayName>
|
||||
</Owner>
|
||||
</Contents>
|
||||
</ListBucketResult>
|
||||
`
|
||||
|
||||
var GetListResultDump2 = `
|
||||
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
<Name>example-bucket</Name>
|
||||
<Prefix>photos/2006/</Prefix>
|
||||
<Marker>some-marker</Marker>
|
||||
<MaxKeys>1000</MaxKeys>
|
||||
<Delimiter>/</Delimiter>
|
||||
<IsTruncated>false</IsTruncated>
|
||||
|
||||
<CommonPrefixes>
|
||||
<Prefix>photos/2006/feb/</Prefix>
|
||||
</CommonPrefixes>
|
||||
<CommonPrefixes>
|
||||
<Prefix>photos/2006/jan/</Prefix>
|
||||
</CommonPrefixes>
|
||||
</ListBucketResult>
|
||||
`
|
||||
|
||||
var InitMultiResultDump = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<InitiateMultipartUploadResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
<Bucket>sample</Bucket>
|
||||
<Key>multi</Key>
|
||||
<UploadId>JNbR_cMdwnGiD12jKAd6WK2PUkfj2VxA7i4nCwjE6t71nI9Tl3eVDPFlU0nOixhftH7I17ZPGkV3QA.l7ZD.QQ--</UploadId>
|
||||
</InitiateMultipartUploadResult>
|
||||
`
|
||||
|
||||
var ListPartsResultDump1 = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ListPartsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
<Bucket>sample</Bucket>
|
||||
<Key>multi</Key>
|
||||
<UploadId>JNbR_cMdwnGiD12jKAd6WK2PUkfj2VxA7i4nCwjE6t71nI9Tl3eVDPFlU0nOixhftH7I17ZPGkV3QA.l7ZD.QQ--</UploadId>
|
||||
<Initiator>
|
||||
<ID>bb5c0f63b0b25f2d099c</ID>
|
||||
<DisplayName>joe</DisplayName>
|
||||
</Initiator>
|
||||
<Owner>
|
||||
<ID>bb5c0f63b0b25f2d099c</ID>
|
||||
<DisplayName>joe</DisplayName>
|
||||
</Owner>
|
||||
<StorageClass>STANDARD</StorageClass>
|
||||
<PartNumberMarker>0</PartNumberMarker>
|
||||
<NextPartNumberMarker>2</NextPartNumberMarker>
|
||||
<MaxParts>2</MaxParts>
|
||||
<IsTruncated>true</IsTruncated>
|
||||
<Part>
|
||||
<PartNumber>1</PartNumber>
|
||||
<LastModified>2013-01-30T13:45:51.000Z</LastModified>
|
||||
<ETag>"ffc88b4ca90a355f8ddba6b2c3b2af5c"</ETag>
|
||||
<Size>5</Size>
|
||||
</Part>
|
||||
<Part>
|
||||
<PartNumber>2</PartNumber>
|
||||
<LastModified>2013-01-30T13:45:52.000Z</LastModified>
|
||||
<ETag>"d067a0fa9dc61a6e7195ca99696b5a89"</ETag>
|
||||
<Size>5</Size>
|
||||
</Part>
|
||||
</ListPartsResult>
|
||||
`
|
||||
|
||||
var ListPartsResultDump2 = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ListPartsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
<Bucket>sample</Bucket>
|
||||
<Key>multi</Key>
|
||||
<UploadId>JNbR_cMdwnGiD12jKAd6WK2PUkfj2VxA7i4nCwjE6t71nI9Tl3eVDPFlU0nOixhftH7I17ZPGkV3QA.l7ZD.QQ--</UploadId>
|
||||
<Initiator>
|
||||
<ID>bb5c0f63b0b25f2d099c</ID>
|
||||
<DisplayName>joe</DisplayName>
|
||||
</Initiator>
|
||||
<Owner>
|
||||
<ID>bb5c0f63b0b25f2d099c</ID>
|
||||
<DisplayName>joe</DisplayName>
|
||||
</Owner>
|
||||
<StorageClass>STANDARD</StorageClass>
|
||||
<PartNumberMarker>2</PartNumberMarker>
|
||||
<NextPartNumberMarker>3</NextPartNumberMarker>
|
||||
<MaxParts>2</MaxParts>
|
||||
<IsTruncated>false</IsTruncated>
|
||||
<Part>
|
||||
<PartNumber>3</PartNumber>
|
||||
<LastModified>2013-01-30T13:46:50.000Z</LastModified>
|
||||
<ETag>"49dcd91231f801159e893fb5c6674985"</ETag>
|
||||
<Size>5</Size>
|
||||
</Part>
|
||||
</ListPartsResult>
|
||||
`
|
||||
|
||||
var ListMultiResultDump = `
|
||||
<?xml version="1.0"?>
|
||||
<ListMultipartUploadsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
<Bucket>goamz-test-bucket-us-east-1-akiajk3wyewhctyqbf7a</Bucket>
|
||||
<KeyMarker/>
|
||||
<UploadIdMarker/>
|
||||
<NextKeyMarker>multi1</NextKeyMarker>
|
||||
<NextUploadIdMarker>iUVug89pPvSswrikD72p8uO62EzhNtpDxRmwC5WSiWDdK9SfzmDqe3xpP1kMWimyimSnz4uzFc3waVM5ufrKYQ--</NextUploadIdMarker>
|
||||
<Delimiter>/</Delimiter>
|
||||
<MaxUploads>1000</MaxUploads>
|
||||
<IsTruncated>false</IsTruncated>
|
||||
<Upload>
|
||||
<Key>multi1</Key>
|
||||
<UploadId>iUVug89pPvSswrikD</UploadId>
|
||||
<Initiator>
|
||||
<ID>bb5c0f63b0b25f2d0</ID>
|
||||
<DisplayName>gustavoniemeyer</DisplayName>
|
||||
</Initiator>
|
||||
<Owner>
|
||||
<ID>bb5c0f63b0b25f2d0</ID>
|
||||
<DisplayName>gustavoniemeyer</DisplayName>
|
||||
</Owner>
|
||||
<StorageClass>STANDARD</StorageClass>
|
||||
<Initiated>2013-01-30T18:15:47.000Z</Initiated>
|
||||
</Upload>
|
||||
<Upload>
|
||||
<Key>multi2</Key>
|
||||
<UploadId>DkirwsSvPp98guVUi</UploadId>
|
||||
<Initiator>
|
||||
<ID>bb5c0f63b0b25f2d0</ID>
|
||||
<DisplayName>joe</DisplayName>
|
||||
</Initiator>
|
||||
<Owner>
|
||||
<ID>bb5c0f63b0b25f2d0</ID>
|
||||
<DisplayName>joe</DisplayName>
|
||||
</Owner>
|
||||
<StorageClass>STANDARD</StorageClass>
|
||||
<Initiated>2013-01-30T18:15:47.000Z</Initiated>
|
||||
</Upload>
|
||||
<CommonPrefixes>
|
||||
<Prefix>a/</Prefix>
|
||||
</CommonPrefixes>
|
||||
<CommonPrefixes>
|
||||
<Prefix>b/</Prefix>
|
||||
</CommonPrefixes>
|
||||
</ListMultipartUploadsResult>
|
||||
`
|
||||
|
||||
var NoSuchUploadErrorDump = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Error>
|
||||
<Code>NoSuchUpload</Code>
|
||||
<Message>Not relevant</Message>
|
||||
<BucketName>sample</BucketName>
|
||||
<RequestId>3F1B667FAD71C3D8</RequestId>
|
||||
<HostId>kjhwqk</HostId>
|
||||
</Error>
|
||||
`
|
||||
|
||||
var InternalErrorDump = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Error>
|
||||
<Code>InternalError</Code>
|
||||
<Message>Not relevant</Message>
|
||||
<BucketName>sample</BucketName>
|
||||
<RequestId>3F1B667FAD71C3D8</RequestId>
|
||||
<HostId>kjhwqk</HostId>
|
||||
</Error>
|
||||
`
|
||||
|
||||
var GetServiceDump = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ListAllMyBucketsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01">
|
||||
<Owner>
|
||||
<ID>bcaf1ffd86f461ca5fb16fd081034f</ID>
|
||||
<DisplayName>webfile</DisplayName>
|
||||
</Owner>
|
||||
<Buckets>
|
||||
<Bucket>
|
||||
<Name>quotes</Name>
|
||||
<CreationDate>2006-02-03T16:45:09.000Z</CreationDate>
|
||||
</Bucket>
|
||||
<Bucket>
|
||||
<Name>samples</Name>
|
||||
<CreationDate>2006-02-03T16:41:58.000Z</CreationDate>
|
||||
</Bucket>
|
||||
</Buckets>
|
||||
</ListAllMyBucketsResult>
|
||||
`
|
||||
|
||||
var GetLocationUsStandard = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<LocationConstraint xmlns="http://s3.amazonaws.com/doc/2006-03-01/"/>
|
||||
`
|
||||
|
||||
var GetLocationUsWest1 = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<LocationConstraint xmlns="http://s3.amazonaws.com/doc/2006-03-01/">us-west-1</LocationConstraint>
|
||||
`
|
||||
|
||||
var BucketWebsiteConfigurationDump = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<WebsiteConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><RedirectAllRequestsTo><HostName>example.com</HostName></RedirectAllRequestsTo></WebsiteConfiguration>`
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user