mirror of
https://github.com/Luzifer/s3sync.git
synced 2024-12-20 19:41:15 +00:00
Vendor libraries
This commit is contained in:
parent
2094d61be4
commit
c9bb60b6aa
136 changed files with 33359 additions and 1 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1 @@
|
||||||
s3sync
|
s3sync
|
||||||
Godeps/_workspace/
|
|
||||||
|
|
2
Godeps/_workspace/.gitignore
generated
vendored
Normal file
2
Godeps/_workspace/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/pkg
|
||||||
|
/bin
|
105
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awserr/error.go
generated
vendored
Normal file
105
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awserr/error.go
generated
vendored
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
// Package awserr represents API error interface accessors for the SDK.
|
||||||
|
package awserr
|
||||||
|
|
||||||
|
// An Error wraps lower level errors with code, message and an original error.
|
||||||
|
// The underlying concrete error type may also satisfy other interfaces which
|
||||||
|
// can be to used to obtain more specific information about the error.
|
||||||
|
//
|
||||||
|
// Calling Error() or String() will always include the full information about
|
||||||
|
// an error based on its underlying type.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// output, err := s3manage.Upload(svc, input, opts)
|
||||||
|
// if err != nil {
|
||||||
|
// if awsErr, ok := err.(awserr.Error); ok {
|
||||||
|
// // Get error details
|
||||||
|
// log.Println("Error:", err.Code(), err.Message())
|
||||||
|
//
|
||||||
|
// // Prints out full error message, including original error if there was one.
|
||||||
|
// log.Println("Error:", err.Error())
|
||||||
|
//
|
||||||
|
// // Get original error
|
||||||
|
// if origErr := err.Err(); origErr != nil {
|
||||||
|
// // operate on original error.
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// fmt.Println(err.Error())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
type Error interface {
|
||||||
|
// Satisfy the generic error interface.
|
||||||
|
error
|
||||||
|
|
||||||
|
// Returns the short phrase depicting the classification of the error.
|
||||||
|
Code() string
|
||||||
|
|
||||||
|
// Returns the error details message.
|
||||||
|
Message() string
|
||||||
|
|
||||||
|
// Returns the original error if one was set. Nil is returned if not set.
|
||||||
|
OrigErr() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns an Error object described by the code, message, and origErr.
|
||||||
|
//
|
||||||
|
// If origErr satisfies the Error interface it will not be wrapped within a new
|
||||||
|
// Error object and will instead be returned.
|
||||||
|
func New(code, message string, origErr error) Error {
|
||||||
|
if e, ok := origErr.(Error); ok && e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return newBaseError(code, message, origErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A RequestFailure is an interface to extract request failure information from
|
||||||
|
// an Error such as the request ID of the failed request returned by a service.
|
||||||
|
// RequestFailures may not always have a requestID value if the request failed
|
||||||
|
// prior to reaching the service such as a connection error.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// output, err := s3manage.Upload(svc, input, opts)
|
||||||
|
// if err != nil {
|
||||||
|
// if reqerr, ok := err.(RequestFailure); ok {
|
||||||
|
// log.Printf("Request failed", reqerr.Code(), reqerr.Message(), reqerr.RequestID())
|
||||||
|
// } else {
|
||||||
|
// log.Printf("Error:", err.Error()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Combined with awserr.Error:
|
||||||
|
//
|
||||||
|
// output, err := s3manage.Upload(svc, input, opts)
|
||||||
|
// if err != nil {
|
||||||
|
// if awsErr, ok := err.(awserr.Error); ok {
|
||||||
|
// // Generic AWS Error with Code, Message, and original error (if any)
|
||||||
|
// fmt.Println(awsErr.Code(), awsErr.Message(), awsErr.OrigErr())
|
||||||
|
//
|
||||||
|
// if reqErr, ok := err.(awserr.RequestFailure); ok {
|
||||||
|
// // A service error occurred
|
||||||
|
// fmt.Println(reqErr.StatusCode(), reqErr.RequestID())
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// fmt.Println(err.Error())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
type RequestFailure interface {
|
||||||
|
Error
|
||||||
|
|
||||||
|
// The status code of the HTTP response.
|
||||||
|
StatusCode() int
|
||||||
|
|
||||||
|
// The request ID returned by the service for a request failure. This will
|
||||||
|
// be empty if no request ID is available such as the request failed due
|
||||||
|
// to a connection error.
|
||||||
|
RequestID() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRequestFailure returns a new request error wrapper for the given Error
|
||||||
|
// provided.
|
||||||
|
func NewRequestFailure(err Error, statusCode int, reqID string) RequestFailure {
|
||||||
|
return newRequestError(err, statusCode, reqID)
|
||||||
|
}
|
135
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awserr/types.go
generated
vendored
Normal file
135
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awserr/types.go
generated
vendored
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
package awserr
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// SprintError returns a string of the formatted error code.
|
||||||
|
//
|
||||||
|
// Both extra and origErr are optional. If they are included their lines
|
||||||
|
// will be added, but if they are not included their lines will be ignored.
|
||||||
|
func SprintError(code, message, extra string, origErr error) string {
|
||||||
|
msg := fmt.Sprintf("%s: %s", code, message)
|
||||||
|
if extra != "" {
|
||||||
|
msg = fmt.Sprintf("%s\n\t%s", msg, extra)
|
||||||
|
}
|
||||||
|
if origErr != nil {
|
||||||
|
msg = fmt.Sprintf("%s\ncaused by: %s", msg, origErr.Error())
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// A baseError wraps the code and message which defines an error. It also
|
||||||
|
// can be used to wrap an original error object.
|
||||||
|
//
|
||||||
|
// Should be used as the root for errors satisfying the awserr.Error. Also
|
||||||
|
// for any error which does not fit into a specific error wrapper type.
|
||||||
|
type baseError struct {
|
||||||
|
// Classification of error
|
||||||
|
code string
|
||||||
|
|
||||||
|
// Detailed information about error
|
||||||
|
message string
|
||||||
|
|
||||||
|
// Optional original error this error is based off of. Allows building
|
||||||
|
// chained errors.
|
||||||
|
origErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
// newBaseError returns an error object for the code, message, and err.
|
||||||
|
//
|
||||||
|
// code is a short no whitespace phrase depicting the classification of
|
||||||
|
// the error that is being created.
|
||||||
|
//
|
||||||
|
// message is the free flow string containing detailed information about the error.
|
||||||
|
//
|
||||||
|
// origErr is the error object which will be nested under the new error to be returned.
|
||||||
|
func newBaseError(code, message string, origErr error) *baseError {
|
||||||
|
return &baseError{
|
||||||
|
code: code,
|
||||||
|
message: message,
|
||||||
|
origErr: origErr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the string representation of the error.
|
||||||
|
//
|
||||||
|
// See ErrorWithExtra for formatting.
|
||||||
|
//
|
||||||
|
// Satisfies the error interface.
|
||||||
|
func (b baseError) Error() string {
|
||||||
|
return SprintError(b.code, b.message, "", b.origErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the error.
|
||||||
|
// Alias for Error to satisfy the stringer interface.
|
||||||
|
func (b baseError) String() string {
|
||||||
|
return b.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code returns the short phrase depicting the classification of the error.
|
||||||
|
func (b baseError) Code() string {
|
||||||
|
return b.code
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message returns the error details message.
|
||||||
|
func (b baseError) Message() string {
|
||||||
|
return b.message
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrigErr returns the original error if one was set. Nil is returned if no error
|
||||||
|
// was set.
|
||||||
|
func (b baseError) OrigErr() error {
|
||||||
|
return b.origErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// So that the Error interface type can be included as an anonymous field
|
||||||
|
// in the requestError struct and not conflict with the error.Error() method.
|
||||||
|
type awsError Error
|
||||||
|
|
||||||
|
// A requestError wraps a request or service error.
|
||||||
|
//
|
||||||
|
// Composed of baseError for code, message, and original error.
|
||||||
|
type requestError struct {
|
||||||
|
awsError
|
||||||
|
statusCode int
|
||||||
|
requestID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// newRequestError returns a wrapped error with additional information for request
|
||||||
|
// status code, and service requestID.
|
||||||
|
//
|
||||||
|
// Should be used to wrap all request which involve service requests. Even if
|
||||||
|
// the request failed without a service response, but had an HTTP status code
|
||||||
|
// that may be meaningful.
|
||||||
|
//
|
||||||
|
// Also wraps original errors via the baseError.
|
||||||
|
func newRequestError(err Error, statusCode int, requestID string) *requestError {
|
||||||
|
return &requestError{
|
||||||
|
awsError: err,
|
||||||
|
statusCode: statusCode,
|
||||||
|
requestID: requestID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the string representation of the error.
|
||||||
|
// Satisfies the error interface.
|
||||||
|
func (r requestError) Error() string {
|
||||||
|
extra := fmt.Sprintf("status code: %d, request id: [%s]",
|
||||||
|
r.statusCode, r.requestID)
|
||||||
|
return SprintError(r.Code(), r.Message(), extra, r.OrigErr())
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the error.
|
||||||
|
// Alias for Error to satisfy the stringer interface.
|
||||||
|
func (r requestError) String() string {
|
||||||
|
return r.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatusCode returns the wrapped status code for the error
|
||||||
|
func (r requestError) StatusCode() int {
|
||||||
|
return r.statusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestID returns the wrapped requestID
|
||||||
|
func (r requestError) RequestID() string {
|
||||||
|
return r.requestID
|
||||||
|
}
|
103
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awsutil/copy.go
generated
vendored
Normal file
103
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awsutil/copy.go
generated
vendored
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package awsutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Copy deeply copies a src structure to dst. Useful for copying request and
|
||||||
|
// response structures.
|
||||||
|
//
|
||||||
|
// Can copy between structs of different type, but will only copy fields which
|
||||||
|
// are assignable, and exist in both structs. Fields which are not assignable,
|
||||||
|
// or do not exist in both structs are ignored.
|
||||||
|
func Copy(dst, src interface{}) {
|
||||||
|
dstval := reflect.ValueOf(dst)
|
||||||
|
if !dstval.IsValid() {
|
||||||
|
panic("Copy dst cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
rcopy(dstval, reflect.ValueOf(src), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyOf returns a copy of src while also allocating the memory for dst.
|
||||||
|
// src must be a pointer type or this operation will fail.
|
||||||
|
func CopyOf(src interface{}) (dst interface{}) {
|
||||||
|
dsti := reflect.New(reflect.TypeOf(src).Elem())
|
||||||
|
dst = dsti.Interface()
|
||||||
|
rcopy(dsti, reflect.ValueOf(src), true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// rcopy performs a recursive copy of values from the source to destination.
|
||||||
|
//
|
||||||
|
// root is used to skip certain aspects of the copy which are not valid
|
||||||
|
// for the root node of a object.
|
||||||
|
func rcopy(dst, src reflect.Value, root bool) {
|
||||||
|
if !src.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch src.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
if _, ok := src.Interface().(io.Reader); ok {
|
||||||
|
if dst.Kind() == reflect.Ptr && dst.Elem().CanSet() {
|
||||||
|
dst.Elem().Set(src)
|
||||||
|
} else if dst.CanSet() {
|
||||||
|
dst.Set(src)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
e := src.Type().Elem()
|
||||||
|
if dst.CanSet() && !src.IsNil() {
|
||||||
|
dst.Set(reflect.New(e))
|
||||||
|
}
|
||||||
|
if src.Elem().IsValid() {
|
||||||
|
// Keep the current root state since the depth hasn't changed
|
||||||
|
rcopy(dst.Elem(), src.Elem(), root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
if !root {
|
||||||
|
dst.Set(reflect.New(src.Type()).Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
t := dst.Type()
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
name := t.Field(i).Name
|
||||||
|
srcval := src.FieldByName(name)
|
||||||
|
if srcval.IsValid() {
|
||||||
|
rcopy(dst.FieldByName(name), srcval, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
if src.IsNil() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
s := reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
|
||||||
|
dst.Set(s)
|
||||||
|
for i := 0; i < src.Len(); i++ {
|
||||||
|
rcopy(dst.Index(i), src.Index(i), false)
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
if src.IsNil() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
s := reflect.MakeMap(src.Type())
|
||||||
|
dst.Set(s)
|
||||||
|
for _, k := range src.MapKeys() {
|
||||||
|
v := src.MapIndex(k)
|
||||||
|
v2 := reflect.New(v.Type()).Elem()
|
||||||
|
rcopy(v2, v, false)
|
||||||
|
dst.SetMapIndex(k, v2)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Assign the value if possible. If its not assignable, the value would
|
||||||
|
// need to be converted and the impact of that may be unexpected, or is
|
||||||
|
// not compatible with the dst type.
|
||||||
|
if src.Type().AssignableTo(dst.Type()) {
|
||||||
|
dst.Set(src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
201
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awsutil/copy_test.go
generated
vendored
Normal file
201
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awsutil/copy_test.go
generated
vendored
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
package awsutil_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleCopy() {
|
||||||
|
type Foo struct {
|
||||||
|
A int
|
||||||
|
B []*string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the initial value
|
||||||
|
str1 := "hello"
|
||||||
|
str2 := "bye bye"
|
||||||
|
f1 := &Foo{A: 1, B: []*string{&str1, &str2}}
|
||||||
|
|
||||||
|
// Do the copy
|
||||||
|
var f2 Foo
|
||||||
|
awsutil.Copy(&f2, f1)
|
||||||
|
|
||||||
|
// Print the result
|
||||||
|
fmt.Println(awsutil.Prettify(f2))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// {
|
||||||
|
// A: 1,
|
||||||
|
// B: ["hello","bye bye"]
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopy(t *testing.T) {
|
||||||
|
type Foo struct {
|
||||||
|
A int
|
||||||
|
B []*string
|
||||||
|
C map[string]*int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the initial value
|
||||||
|
str1 := "hello"
|
||||||
|
str2 := "bye bye"
|
||||||
|
int1 := 1
|
||||||
|
int2 := 2
|
||||||
|
f1 := &Foo{
|
||||||
|
A: 1,
|
||||||
|
B: []*string{&str1, &str2},
|
||||||
|
C: map[string]*int{
|
||||||
|
"A": &int1,
|
||||||
|
"B": &int2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the copy
|
||||||
|
var f2 Foo
|
||||||
|
awsutil.Copy(&f2, f1)
|
||||||
|
|
||||||
|
// Values are equal
|
||||||
|
assert.Equal(t, f2.A, f1.A)
|
||||||
|
assert.Equal(t, f2.B, f1.B)
|
||||||
|
assert.Equal(t, f2.C, f1.C)
|
||||||
|
|
||||||
|
// But pointers are not!
|
||||||
|
str3 := "nothello"
|
||||||
|
int3 := 57
|
||||||
|
f2.A = 100
|
||||||
|
f2.B[0] = &str3
|
||||||
|
f2.C["B"] = &int3
|
||||||
|
assert.NotEqual(t, f2.A, f1.A)
|
||||||
|
assert.NotEqual(t, f2.B, f1.B)
|
||||||
|
assert.NotEqual(t, f2.C, f1.C)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyIgnoreNilMembers(t *testing.T) {
|
||||||
|
type Foo struct {
|
||||||
|
A *string
|
||||||
|
B []string
|
||||||
|
C map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
f := &Foo{}
|
||||||
|
assert.Nil(t, f.A)
|
||||||
|
assert.Nil(t, f.B)
|
||||||
|
assert.Nil(t, f.C)
|
||||||
|
|
||||||
|
var f2 Foo
|
||||||
|
awsutil.Copy(&f2, f)
|
||||||
|
assert.Nil(t, f2.A)
|
||||||
|
assert.Nil(t, f2.B)
|
||||||
|
assert.Nil(t, f2.C)
|
||||||
|
|
||||||
|
fcopy := awsutil.CopyOf(f)
|
||||||
|
f3 := fcopy.(*Foo)
|
||||||
|
assert.Nil(t, f3.A)
|
||||||
|
assert.Nil(t, f3.B)
|
||||||
|
assert.Nil(t, f3.C)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyPrimitive(t *testing.T) {
|
||||||
|
str := "hello"
|
||||||
|
var s string
|
||||||
|
awsutil.Copy(&s, &str)
|
||||||
|
assert.Equal(t, "hello", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyNil(t *testing.T) {
|
||||||
|
var s string
|
||||||
|
awsutil.Copy(&s, nil)
|
||||||
|
assert.Equal(t, "", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyReader(t *testing.T) {
|
||||||
|
var buf io.Reader = bytes.NewReader([]byte("hello world"))
|
||||||
|
var r io.Reader
|
||||||
|
awsutil.Copy(&r, buf)
|
||||||
|
b, err := ioutil.ReadAll(r)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []byte("hello world"), b)
|
||||||
|
|
||||||
|
// empty bytes because this is not a deep copy
|
||||||
|
b, err = ioutil.ReadAll(buf)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []byte(""), b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyDifferentStructs(t *testing.T) {
|
||||||
|
type SrcFoo struct {
|
||||||
|
A int
|
||||||
|
B []*string
|
||||||
|
C map[string]*int
|
||||||
|
SrcUnique string
|
||||||
|
SameNameDiffType int
|
||||||
|
}
|
||||||
|
type DstFoo struct {
|
||||||
|
A int
|
||||||
|
B []*string
|
||||||
|
C map[string]*int
|
||||||
|
DstUnique int
|
||||||
|
SameNameDiffType string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the initial value
|
||||||
|
str1 := "hello"
|
||||||
|
str2 := "bye bye"
|
||||||
|
int1 := 1
|
||||||
|
int2 := 2
|
||||||
|
f1 := &SrcFoo{
|
||||||
|
A: 1,
|
||||||
|
B: []*string{&str1, &str2},
|
||||||
|
C: map[string]*int{
|
||||||
|
"A": &int1,
|
||||||
|
"B": &int2,
|
||||||
|
},
|
||||||
|
SrcUnique: "unique",
|
||||||
|
SameNameDiffType: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the copy
|
||||||
|
var f2 DstFoo
|
||||||
|
awsutil.Copy(&f2, f1)
|
||||||
|
|
||||||
|
// Values are equal
|
||||||
|
assert.Equal(t, f2.A, f1.A)
|
||||||
|
assert.Equal(t, f2.B, f1.B)
|
||||||
|
assert.Equal(t, f2.C, f1.C)
|
||||||
|
assert.Equal(t, "unique", f1.SrcUnique)
|
||||||
|
assert.Equal(t, 1, f1.SameNameDiffType)
|
||||||
|
assert.Equal(t, 0, f2.DstUnique)
|
||||||
|
assert.Equal(t, "", f2.SameNameDiffType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleCopyOf() {
|
||||||
|
type Foo struct {
|
||||||
|
A int
|
||||||
|
B []*string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the initial value
|
||||||
|
str1 := "hello"
|
||||||
|
str2 := "bye bye"
|
||||||
|
f1 := &Foo{A: 1, B: []*string{&str1, &str2}}
|
||||||
|
|
||||||
|
// Do the copy
|
||||||
|
v := awsutil.CopyOf(f1)
|
||||||
|
var f2 *Foo = v.(*Foo)
|
||||||
|
|
||||||
|
// Print the result
|
||||||
|
fmt.Println(awsutil.Prettify(f2))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// {
|
||||||
|
// A: 1,
|
||||||
|
// B: ["hello","bye bye"]
|
||||||
|
// }
|
||||||
|
}
|
187
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awsutil/path_value.go
generated
vendored
Normal file
187
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awsutil/path_value.go
generated
vendored
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
package awsutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var indexRe = regexp.MustCompile(`(.+)\[(-?\d+)?\]$`)
|
||||||
|
|
||||||
|
// rValuesAtPath returns a slice of values found in value v. The values
|
||||||
|
// in v are explored recursively so all nested values are collected.
|
||||||
|
func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool) []reflect.Value {
|
||||||
|
pathparts := strings.Split(path, "||")
|
||||||
|
if len(pathparts) > 1 {
|
||||||
|
for _, pathpart := range pathparts {
|
||||||
|
vals := rValuesAtPath(v, pathpart, create, caseSensitive)
|
||||||
|
if vals != nil && len(vals) > 0 {
|
||||||
|
return vals
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
values := []reflect.Value{reflect.Indirect(reflect.ValueOf(v))}
|
||||||
|
components := strings.Split(path, ".")
|
||||||
|
for len(values) > 0 && len(components) > 0 {
|
||||||
|
var index *int64
|
||||||
|
var indexStar bool
|
||||||
|
c := strings.TrimSpace(components[0])
|
||||||
|
if c == "" { // no actual component, illegal syntax
|
||||||
|
return nil
|
||||||
|
} else if caseSensitive && c != "*" && strings.ToLower(c[0:1]) == c[0:1] {
|
||||||
|
// TODO normalize case for user
|
||||||
|
return nil // don't support unexported fields
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse this component
|
||||||
|
if m := indexRe.FindStringSubmatch(c); m != nil {
|
||||||
|
c = m[1]
|
||||||
|
if m[2] == "" {
|
||||||
|
index = nil
|
||||||
|
indexStar = true
|
||||||
|
} else {
|
||||||
|
i, _ := strconv.ParseInt(m[2], 10, 32)
|
||||||
|
index = &i
|
||||||
|
indexStar = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextvals := []reflect.Value{}
|
||||||
|
for _, value := range values {
|
||||||
|
// pull component name out of struct member
|
||||||
|
if value.Kind() != reflect.Struct {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if c == "*" { // pull all members
|
||||||
|
for i := 0; i < value.NumField(); i++ {
|
||||||
|
if f := reflect.Indirect(value.Field(i)); f.IsValid() {
|
||||||
|
nextvals = append(nextvals, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
value = value.FieldByNameFunc(func(name string) bool {
|
||||||
|
if c == name {
|
||||||
|
return true
|
||||||
|
} else if !caseSensitive && strings.ToLower(name) == strings.ToLower(c) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
if create && value.Kind() == reflect.Ptr && value.IsNil() {
|
||||||
|
value.Set(reflect.New(value.Type().Elem()))
|
||||||
|
value = value.Elem()
|
||||||
|
} else {
|
||||||
|
value = reflect.Indirect(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.Kind() == reflect.Slice || value.Kind() == reflect.Map {
|
||||||
|
if !create && value.IsNil() {
|
||||||
|
value = reflect.ValueOf(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.IsValid() {
|
||||||
|
nextvals = append(nextvals, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
values = nextvals
|
||||||
|
|
||||||
|
if indexStar || index != nil {
|
||||||
|
nextvals = []reflect.Value{}
|
||||||
|
for _, value := range values {
|
||||||
|
value := reflect.Indirect(value)
|
||||||
|
if value.Kind() != reflect.Slice {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if indexStar { // grab all indices
|
||||||
|
for i := 0; i < value.Len(); i++ {
|
||||||
|
idx := reflect.Indirect(value.Index(i))
|
||||||
|
if idx.IsValid() {
|
||||||
|
nextvals = append(nextvals, idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// pull out index
|
||||||
|
i := int(*index)
|
||||||
|
if i >= value.Len() { // check out of bounds
|
||||||
|
if create {
|
||||||
|
// TODO resize slice
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else if i < 0 { // support negative indexing
|
||||||
|
i = value.Len() + i
|
||||||
|
}
|
||||||
|
value = reflect.Indirect(value.Index(i))
|
||||||
|
|
||||||
|
if value.Kind() == reflect.Slice || value.Kind() == reflect.Map {
|
||||||
|
if !create && value.IsNil() {
|
||||||
|
value = reflect.ValueOf(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.IsValid() {
|
||||||
|
nextvals = append(nextvals, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
values = nextvals
|
||||||
|
}
|
||||||
|
|
||||||
|
components = components[1:]
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValuesAtPath returns a list of objects at the lexical path inside of a structure
|
||||||
|
func ValuesAtPath(i interface{}, path string) []interface{} {
|
||||||
|
if rvals := rValuesAtPath(i, path, false, true); rvals != nil {
|
||||||
|
vals := make([]interface{}, len(rvals))
|
||||||
|
for i, rval := range rvals {
|
||||||
|
vals[i] = rval.Interface()
|
||||||
|
}
|
||||||
|
return vals
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValuesAtAnyPath returns a list of objects at the case-insensitive lexical
|
||||||
|
// path inside of a structure
|
||||||
|
func ValuesAtAnyPath(i interface{}, path string) []interface{} {
|
||||||
|
if rvals := rValuesAtPath(i, path, false, false); rvals != nil {
|
||||||
|
vals := make([]interface{}, len(rvals))
|
||||||
|
for i, rval := range rvals {
|
||||||
|
vals[i] = rval.Interface()
|
||||||
|
}
|
||||||
|
return vals
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetValueAtPath sets an object at the lexical path inside of a structure
|
||||||
|
func SetValueAtPath(i interface{}, path string, v interface{}) {
|
||||||
|
if rvals := rValuesAtPath(i, path, true, true); rvals != nil {
|
||||||
|
for _, rval := range rvals {
|
||||||
|
rval.Set(reflect.ValueOf(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetValueAtAnyPath sets an object at the case insensitive lexical path inside
|
||||||
|
// of a structure
|
||||||
|
func SetValueAtAnyPath(i interface{}, path string, v interface{}) {
|
||||||
|
if rvals := rValuesAtPath(i, path, true, false); rvals != nil {
|
||||||
|
for _, rval := range rvals {
|
||||||
|
rval.Set(reflect.ValueOf(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awsutil/path_value_test.go
generated
vendored
Normal file
68
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awsutil/path_value_test.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
package awsutil_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Struct struct {
|
||||||
|
A []Struct
|
||||||
|
z []Struct
|
||||||
|
B *Struct
|
||||||
|
D *Struct
|
||||||
|
C string
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = Struct{
|
||||||
|
A: []Struct{{C: "value1"}, {C: "value2"}, {C: "value3"}},
|
||||||
|
z: []Struct{{C: "value1"}, {C: "value2"}, {C: "value3"}},
|
||||||
|
B: &Struct{B: &Struct{C: "terminal"}, D: &Struct{C: "terminal2"}},
|
||||||
|
C: "initial",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValueAtPathSuccess(t *testing.T) {
|
||||||
|
assert.Equal(t, []interface{}{"initial"}, awsutil.ValuesAtPath(data, "C"))
|
||||||
|
assert.Equal(t, []interface{}{"value1"}, awsutil.ValuesAtPath(data, "A[0].C"))
|
||||||
|
assert.Equal(t, []interface{}{"value2"}, awsutil.ValuesAtPath(data, "A[1].C"))
|
||||||
|
assert.Equal(t, []interface{}{"value3"}, awsutil.ValuesAtPath(data, "A[2].C"))
|
||||||
|
assert.Equal(t, []interface{}{"value3"}, awsutil.ValuesAtAnyPath(data, "a[2].c"))
|
||||||
|
assert.Equal(t, []interface{}{"value3"}, awsutil.ValuesAtPath(data, "A[-1].C"))
|
||||||
|
assert.Equal(t, []interface{}{"value1", "value2", "value3"}, awsutil.ValuesAtPath(data, "A[].C"))
|
||||||
|
assert.Equal(t, []interface{}{"terminal"}, awsutil.ValuesAtPath(data, "B . B . C"))
|
||||||
|
assert.Equal(t, []interface{}{"terminal", "terminal2"}, awsutil.ValuesAtPath(data, "B.*.C"))
|
||||||
|
assert.Equal(t, []interface{}{"initial"}, awsutil.ValuesAtPath(data, "A.D.X || C"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValueAtPathFailure(t *testing.T) {
|
||||||
|
assert.Equal(t, []interface{}(nil), awsutil.ValuesAtPath(data, "C.x"))
|
||||||
|
assert.Equal(t, []interface{}(nil), awsutil.ValuesAtPath(data, ".x"))
|
||||||
|
assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(data, "X.Y.Z"))
|
||||||
|
assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(data, "A[100].C"))
|
||||||
|
assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(data, "A[3].C"))
|
||||||
|
assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(data, "B.B.C.Z"))
|
||||||
|
assert.Equal(t, []interface{}(nil), awsutil.ValuesAtPath(data, "z[-1].C"))
|
||||||
|
assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(nil, "A.B.C"))
|
||||||
|
assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(Struct{}, "A"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetValueAtPathSuccess(t *testing.T) {
|
||||||
|
var s Struct
|
||||||
|
awsutil.SetValueAtPath(&s, "C", "test1")
|
||||||
|
awsutil.SetValueAtPath(&s, "B.B.C", "test2")
|
||||||
|
awsutil.SetValueAtPath(&s, "B.D.C", "test3")
|
||||||
|
assert.Equal(t, "test1", s.C)
|
||||||
|
assert.Equal(t, "test2", s.B.B.C)
|
||||||
|
assert.Equal(t, "test3", s.B.D.C)
|
||||||
|
|
||||||
|
awsutil.SetValueAtPath(&s, "B.*.C", "test0")
|
||||||
|
assert.Equal(t, "test0", s.B.B.C)
|
||||||
|
assert.Equal(t, "test0", s.B.D.C)
|
||||||
|
|
||||||
|
var s2 Struct
|
||||||
|
awsutil.SetValueAtAnyPath(&s2, "b.b.c", "test0")
|
||||||
|
assert.Equal(t, "test0", s2.B.B.C)
|
||||||
|
awsutil.SetValueAtAnyPath(&s2, "A", []Struct{{}})
|
||||||
|
assert.Equal(t, []Struct{{}}, s2.A)
|
||||||
|
}
|
103
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awsutil/prettify.go
generated
vendored
Normal file
103
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awsutil/prettify.go
generated
vendored
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package awsutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Prettify returns the string representation of a value.
|
||||||
|
func Prettify(i interface{}) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
prettify(reflect.ValueOf(i), 0, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// prettify will recursively walk value v to build a textual
|
||||||
|
// representation of the value.
|
||||||
|
func prettify(v reflect.Value, indent int, buf *bytes.Buffer) {
|
||||||
|
for v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
strtype := v.Type().String()
|
||||||
|
if strtype == "time.Time" {
|
||||||
|
fmt.Fprintf(buf, "%s", v.Interface())
|
||||||
|
break
|
||||||
|
} else if strings.HasPrefix(strtype, "io.") {
|
||||||
|
buf.WriteString("<buffer>")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString("{\n")
|
||||||
|
|
||||||
|
names := []string{}
|
||||||
|
for i := 0; i < v.Type().NumField(); i++ {
|
||||||
|
name := v.Type().Field(i).Name
|
||||||
|
f := v.Field(i)
|
||||||
|
if name[0:1] == strings.ToLower(name[0:1]) {
|
||||||
|
continue // ignore unexported fields
|
||||||
|
}
|
||||||
|
if (f.Kind() == reflect.Ptr || f.Kind() == reflect.Slice || f.Kind() == reflect.Map) && f.IsNil() {
|
||||||
|
continue // ignore unset fields
|
||||||
|
}
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, n := range names {
|
||||||
|
val := v.FieldByName(n)
|
||||||
|
buf.WriteString(strings.Repeat(" ", indent+2))
|
||||||
|
buf.WriteString(n + ": ")
|
||||||
|
prettify(val, indent+2, buf)
|
||||||
|
|
||||||
|
if i < len(names)-1 {
|
||||||
|
buf.WriteString(",\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString("\n" + strings.Repeat(" ", indent) + "}")
|
||||||
|
case reflect.Slice:
|
||||||
|
nl, id, id2 := "", "", ""
|
||||||
|
if v.Len() > 3 {
|
||||||
|
nl, id, id2 = "\n", strings.Repeat(" ", indent), strings.Repeat(" ", indent+2)
|
||||||
|
}
|
||||||
|
buf.WriteString("[" + nl)
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
buf.WriteString(id2)
|
||||||
|
prettify(v.Index(i), indent+2, buf)
|
||||||
|
|
||||||
|
if i < v.Len()-1 {
|
||||||
|
buf.WriteString("," + nl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString(nl + id + "]")
|
||||||
|
case reflect.Map:
|
||||||
|
buf.WriteString("{\n")
|
||||||
|
|
||||||
|
for i, k := range v.MapKeys() {
|
||||||
|
buf.WriteString(strings.Repeat(" ", indent+2))
|
||||||
|
buf.WriteString(k.String() + ": ")
|
||||||
|
prettify(v.MapIndex(k), indent+2, buf)
|
||||||
|
|
||||||
|
if i < v.Len()-1 {
|
||||||
|
buf.WriteString(",\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString("\n" + strings.Repeat(" ", indent) + "}")
|
||||||
|
default:
|
||||||
|
format := "%v"
|
||||||
|
switch v.Interface().(type) {
|
||||||
|
case string:
|
||||||
|
format = "%q"
|
||||||
|
case io.ReadSeeker, io.Reader:
|
||||||
|
format = "buffer(%p)"
|
||||||
|
}
|
||||||
|
fmt.Fprintf(buf, format, v.Interface())
|
||||||
|
}
|
||||||
|
}
|
254
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/config.go
generated
vendored
Normal file
254
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/config.go
generated
vendored
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultChainCredentials is a Credentials which will find the first available
|
||||||
|
// credentials Value from the list of Providers.
|
||||||
|
//
|
||||||
|
// This should be used in the default case. Once the type of credentials are
|
||||||
|
// known switching to the specific Credentials will be more efficient.
|
||||||
|
var DefaultChainCredentials = credentials.NewChainCredentials(
|
||||||
|
[]credentials.Provider{
|
||||||
|
&credentials.EnvProvider{},
|
||||||
|
&credentials.SharedCredentialsProvider{Filename: "", Profile: ""},
|
||||||
|
&credentials.EC2RoleProvider{ExpiryWindow: 5 * time.Minute},
|
||||||
|
})
|
||||||
|
|
||||||
|
// The default number of retries for a service. The value of -1 indicates that
|
||||||
|
// the service specific retry default will be used.
|
||||||
|
const DefaultRetries = -1
|
||||||
|
|
||||||
|
// DefaultConfig is the default all service configuration will be based off of.
|
||||||
|
// By default, all clients use this structure for initialization options unless
|
||||||
|
// a custom configuration object is passed in.
|
||||||
|
//
|
||||||
|
// You may modify this global structure to change all default configuration
|
||||||
|
// in the SDK. Note that configuration options are copied by value, so any
|
||||||
|
// modifications must happen before constructing a client.
|
||||||
|
var DefaultConfig = NewConfig().
|
||||||
|
WithCredentials(DefaultChainCredentials).
|
||||||
|
WithRegion(os.Getenv("AWS_REGION")).
|
||||||
|
WithHTTPClient(http.DefaultClient).
|
||||||
|
WithMaxRetries(DefaultRetries).
|
||||||
|
WithLogger(NewDefaultLogger()).
|
||||||
|
WithLogLevel(LogOff)
|
||||||
|
|
||||||
|
// A Config provides service configuration for service clients. By default,
|
||||||
|
// all clients will use the {DefaultConfig} structure.
|
||||||
|
type Config struct {
|
||||||
|
// The credentials object to use when signing requests. Defaults to
|
||||||
|
// {DefaultChainCredentials}.
|
||||||
|
Credentials *credentials.Credentials
|
||||||
|
|
||||||
|
// An optional endpoint URL (hostname only or fully qualified URI)
|
||||||
|
// that overrides the default generated endpoint for a client. Set this
|
||||||
|
// to `""` to use the default generated endpoint.
|
||||||
|
//
|
||||||
|
// @note You must still provide a `Region` value when specifying an
|
||||||
|
// endpoint for a client.
|
||||||
|
Endpoint *string
|
||||||
|
|
||||||
|
// The region to send requests to. This parameter is required and must
|
||||||
|
// be configured globally or on a per-client basis unless otherwise
|
||||||
|
// noted. A full list of regions is found in the "Regions and Endpoints"
|
||||||
|
// document.
|
||||||
|
//
|
||||||
|
// @see http://docs.aws.amazon.com/general/latest/gr/rande.html
|
||||||
|
// AWS Regions and Endpoints
|
||||||
|
Region *string
|
||||||
|
|
||||||
|
// Set this to `true` to disable SSL when sending requests. Defaults
|
||||||
|
// to `false`.
|
||||||
|
DisableSSL *bool
|
||||||
|
|
||||||
|
// The HTTP client to use when sending requests. Defaults to
|
||||||
|
// `http.DefaultClient`.
|
||||||
|
HTTPClient *http.Client
|
||||||
|
|
||||||
|
// An integer value representing the logging level. The default log level
|
||||||
|
// is zero (LogOff), which represents no logging. To enable logging set
|
||||||
|
// to a LogLevel Value.
|
||||||
|
LogLevel *LogLevelType
|
||||||
|
|
||||||
|
// The logger writer interface to write logging messages to. Defaults to
|
||||||
|
// standard out.
|
||||||
|
Logger Logger
|
||||||
|
|
||||||
|
// The maximum number of times that a request will be retried for failures.
|
||||||
|
// Defaults to -1, which defers the max retry setting to the service specific
|
||||||
|
// configuration.
|
||||||
|
MaxRetries *int
|
||||||
|
|
||||||
|
// Disables semantic parameter validation, which validates input for missing
|
||||||
|
// required fields and/or other semantic request input errors.
|
||||||
|
DisableParamValidation *bool
|
||||||
|
|
||||||
|
// Disables the computation of request and response checksums, e.g.,
|
||||||
|
// CRC32 checksums in Amazon DynamoDB.
|
||||||
|
DisableComputeChecksums *bool
|
||||||
|
|
||||||
|
// Set this to `true` to force the request to use path-style addressing,
|
||||||
|
// i.e., `http://s3.amazonaws.com/BUCKET/KEY`. By default, the S3 client will
|
||||||
|
// use virtual hosted bucket addressing when possible
|
||||||
|
// (`http://BUCKET.s3.amazonaws.com/KEY`).
|
||||||
|
//
|
||||||
|
// @note This configuration option is specific to the Amazon S3 service.
|
||||||
|
// @see http://docs.aws.amazon.com/AmazonS3/latest/dev/VirtualHosting.html
|
||||||
|
// Amazon S3: Virtual Hosting of Buckets
|
||||||
|
S3ForcePathStyle *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConfig returns a new Config pointer that can be chained with builder methods to
|
||||||
|
// set multiple configuration values inline without using pointers.
|
||||||
|
//
|
||||||
|
// svc := s3.New(aws.NewConfig().WithRegion("us-west-2").WithMaxRetries(10))
|
||||||
|
//
|
||||||
|
func NewConfig() *Config {
|
||||||
|
return &Config{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCredentials sets a config Credentials value returning a Config pointer
|
||||||
|
// for chaining.
|
||||||
|
func (c *Config) WithCredentials(creds *credentials.Credentials) *Config {
|
||||||
|
c.Credentials = creds
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithEndpoint sets a config Endpoint value returning a Config pointer for
|
||||||
|
// chaining.
|
||||||
|
func (c *Config) WithEndpoint(endpoint string) *Config {
|
||||||
|
c.Endpoint = &endpoint
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithRegion sets a config Region value returning a Config pointer for
|
||||||
|
// chaining.
|
||||||
|
func (c *Config) WithRegion(region string) *Config {
|
||||||
|
c.Region = ®ion
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDisableSSL sets a config DisableSSL value returning a Config pointer
|
||||||
|
// for chaining.
|
||||||
|
func (c *Config) WithDisableSSL(disable bool) *Config {
|
||||||
|
c.DisableSSL = &disable
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithHTTPClient sets a config HTTPClient value returning a Config pointer
|
||||||
|
// for chaining.
|
||||||
|
func (c *Config) WithHTTPClient(client *http.Client) *Config {
|
||||||
|
c.HTTPClient = client
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMaxRetries sets a config MaxRetries value returning a Config pointer
|
||||||
|
// for chaining.
|
||||||
|
func (c *Config) WithMaxRetries(max int) *Config {
|
||||||
|
c.MaxRetries = &max
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDisableParamValidation sets a config DisableParamValidation value
|
||||||
|
// returning a Config pointer for chaining.
|
||||||
|
func (c *Config) WithDisableParamValidation(disable bool) *Config {
|
||||||
|
c.DisableParamValidation = &disable
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDisableComputeChecksums sets a config DisableComputeChecksums value
|
||||||
|
// returning a Config pointer for chaining.
|
||||||
|
func (c *Config) WithDisableComputeChecksums(disable bool) *Config {
|
||||||
|
c.DisableComputeChecksums = &disable
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLogLevel sets a config LogLevel value returning a Config pointer for
|
||||||
|
// chaining.
|
||||||
|
func (c *Config) WithLogLevel(level LogLevelType) *Config {
|
||||||
|
c.LogLevel = &level
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLogger sets a config Logger value returning a Config pointer for
|
||||||
|
// chaining.
|
||||||
|
func (c *Config) WithLogger(logger Logger) *Config {
|
||||||
|
c.Logger = logger
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithS3ForcePathStyle sets a config S3ForcePathStyle value returning a Config
|
||||||
|
// pointer for chaining.
|
||||||
|
func (c *Config) WithS3ForcePathStyle(force bool) *Config {
|
||||||
|
c.S3ForcePathStyle = &force
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge returns a new Config with the other Config's attribute values merged into
|
||||||
|
// this Config. If the other Config's attribute is nil it will not be merged into
|
||||||
|
// the new Config to be returned.
|
||||||
|
func (c Config) Merge(other *Config) *Config {
|
||||||
|
if other == nil {
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
dst := c
|
||||||
|
|
||||||
|
if other.Credentials != nil {
|
||||||
|
dst.Credentials = other.Credentials
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.Endpoint != nil {
|
||||||
|
dst.Endpoint = other.Endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.Region != nil {
|
||||||
|
dst.Region = other.Region
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.DisableSSL != nil {
|
||||||
|
dst.DisableSSL = other.DisableSSL
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.HTTPClient != nil {
|
||||||
|
dst.HTTPClient = other.HTTPClient
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.LogLevel != nil {
|
||||||
|
dst.LogLevel = other.LogLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.Logger != nil {
|
||||||
|
dst.Logger = other.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.MaxRetries != nil {
|
||||||
|
dst.MaxRetries = other.MaxRetries
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.DisableParamValidation != nil {
|
||||||
|
dst.DisableParamValidation = other.DisableParamValidation
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.DisableComputeChecksums != nil {
|
||||||
|
dst.DisableComputeChecksums = other.DisableComputeChecksums
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.S3ForcePathStyle != nil {
|
||||||
|
dst.S3ForcePathStyle = other.S3ForcePathStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
return &dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy will return a shallow copy of the Config object.
|
||||||
|
func (c Config) Copy() *Config {
|
||||||
|
dst := c
|
||||||
|
return &dst
|
||||||
|
}
|
87
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/config_test.go
generated
vendored
Normal file
87
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/config_test.go
generated
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testCredentials = credentials.NewChainCredentials([]credentials.Provider{
|
||||||
|
&credentials.EnvProvider{},
|
||||||
|
&credentials.SharedCredentialsProvider{
|
||||||
|
Filename: "TestFilename",
|
||||||
|
Profile: "TestProfile"},
|
||||||
|
&credentials.EC2RoleProvider{ExpiryWindow: 5 * time.Minute},
|
||||||
|
})
|
||||||
|
|
||||||
|
var copyTestConfig = Config{
|
||||||
|
Credentials: testCredentials,
|
||||||
|
Endpoint: String("CopyTestEndpoint"),
|
||||||
|
Region: String("COPY_TEST_AWS_REGION"),
|
||||||
|
DisableSSL: Bool(true),
|
||||||
|
HTTPClient: http.DefaultClient,
|
||||||
|
LogLevel: LogLevel(LogDebug),
|
||||||
|
Logger: NewDefaultLogger(),
|
||||||
|
MaxRetries: Int(DefaultRetries),
|
||||||
|
DisableParamValidation: Bool(true),
|
||||||
|
DisableComputeChecksums: Bool(true),
|
||||||
|
S3ForcePathStyle: Bool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopy(t *testing.T) {
|
||||||
|
want := copyTestConfig
|
||||||
|
got := copyTestConfig.Copy()
|
||||||
|
if !reflect.DeepEqual(*got, want) {
|
||||||
|
t.Errorf("Copy() = %+v", got)
|
||||||
|
t.Errorf(" want %+v", want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyReturnsNewInstance(t *testing.T) {
|
||||||
|
want := copyTestConfig
|
||||||
|
got := copyTestConfig.Copy()
|
||||||
|
if got == &want {
|
||||||
|
t.Errorf("Copy() = %p; want different instance as source %p", got, &want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var mergeTestZeroValueConfig = Config{}
|
||||||
|
|
||||||
|
var mergeTestConfig = Config{
|
||||||
|
Credentials: testCredentials,
|
||||||
|
Endpoint: String("MergeTestEndpoint"),
|
||||||
|
Region: String("MERGE_TEST_AWS_REGION"),
|
||||||
|
DisableSSL: Bool(true),
|
||||||
|
HTTPClient: http.DefaultClient,
|
||||||
|
LogLevel: LogLevel(LogDebug),
|
||||||
|
Logger: NewDefaultLogger(),
|
||||||
|
MaxRetries: Int(10),
|
||||||
|
DisableParamValidation: Bool(true),
|
||||||
|
DisableComputeChecksums: Bool(true),
|
||||||
|
S3ForcePathStyle: Bool(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
var mergeTests = []struct {
|
||||||
|
cfg *Config
|
||||||
|
in *Config
|
||||||
|
want *Config
|
||||||
|
}{
|
||||||
|
{&Config{}, nil, &Config{}},
|
||||||
|
{&Config{}, &mergeTestZeroValueConfig, &Config{}},
|
||||||
|
{&Config{}, &mergeTestConfig, &mergeTestConfig},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMerge(t *testing.T) {
|
||||||
|
for i, tt := range mergeTests {
|
||||||
|
got := tt.cfg.Merge(tt.in)
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("Config %d %+v", i, tt.cfg)
|
||||||
|
t.Errorf(" Merge(%+v)", tt.in)
|
||||||
|
t.Errorf(" got %+v", got)
|
||||||
|
t.Errorf(" want %+v", tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
357
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/convutil.go
generated
vendored
Normal file
357
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/convutil.go
generated
vendored
Normal file
|
@ -0,0 +1,357 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// String returns a pointer to of the string value passed in.
|
||||||
|
func String(v string) *string {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringValue returns the value of the string pointer passed in or
|
||||||
|
// "" if the pointer is nil.
|
||||||
|
func StringValue(v *string) string {
|
||||||
|
if v != nil {
|
||||||
|
return *v
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSlice converts a slice of string values into a slice of
|
||||||
|
// string pointers
|
||||||
|
func StringSlice(src []string) []*string {
|
||||||
|
dst := make([]*string, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
dst[i] = &(src[i])
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringValueSlice converts a slice of string pointers into a slice of
|
||||||
|
// string values
|
||||||
|
func StringValueSlice(src []*string) []string {
|
||||||
|
dst := make([]string, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
if src[i] != nil {
|
||||||
|
dst[i] = *(src[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringMap converts a string map of string values into a string
|
||||||
|
// map of string pointers
|
||||||
|
func StringMap(src map[string]string) map[string]*string {
|
||||||
|
dst := make(map[string]*string)
|
||||||
|
for k, val := range src {
|
||||||
|
v := val
|
||||||
|
dst[k] = &v
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringValueMap converts a string map of string pointers into a string
|
||||||
|
// map of string values
|
||||||
|
func StringValueMap(src map[string]*string) map[string]string {
|
||||||
|
dst := make(map[string]string)
|
||||||
|
for k, val := range src {
|
||||||
|
if val != nil {
|
||||||
|
dst[k] = *val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool returns a pointer to of the bool value passed in.
|
||||||
|
func Bool(v bool) *bool {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolValue returns the value of the bool pointer passed in or
|
||||||
|
// false if the pointer is nil.
|
||||||
|
func BoolValue(v *bool) bool {
|
||||||
|
if v != nil {
|
||||||
|
return *v
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolSlice converts a slice of bool values into a slice of
|
||||||
|
// bool pointers
|
||||||
|
func BoolSlice(src []bool) []*bool {
|
||||||
|
dst := make([]*bool, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
dst[i] = &(src[i])
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolValueSlice converts a slice of bool pointers into a slice of
|
||||||
|
// bool values
|
||||||
|
func BoolValueSlice(src []*bool) []bool {
|
||||||
|
dst := make([]bool, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
if src[i] != nil {
|
||||||
|
dst[i] = *(src[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolMap converts a string map of bool values into a string
|
||||||
|
// map of bool pointers
|
||||||
|
func BoolMap(src map[string]bool) map[string]*bool {
|
||||||
|
dst := make(map[string]*bool)
|
||||||
|
for k, val := range src {
|
||||||
|
v := val
|
||||||
|
dst[k] = &v
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolValueMap converts a string map of bool pointers into a string
|
||||||
|
// map of bool values
|
||||||
|
func BoolValueMap(src map[string]*bool) map[string]bool {
|
||||||
|
dst := make(map[string]bool)
|
||||||
|
for k, val := range src {
|
||||||
|
if val != nil {
|
||||||
|
dst[k] = *val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int returns a pointer to of the int value passed in.
|
||||||
|
func Int(v int) *int {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntValue returns the value of the int pointer passed in or
|
||||||
|
// 0 if the pointer is nil.
|
||||||
|
func IntValue(v *int) int {
|
||||||
|
if v != nil {
|
||||||
|
return *v
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntSlice converts a slice of int values into a slice of
|
||||||
|
// int pointers
|
||||||
|
func IntSlice(src []int) []*int {
|
||||||
|
dst := make([]*int, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
dst[i] = &(src[i])
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntValueSlice converts a slice of int pointers into a slice of
|
||||||
|
// int values
|
||||||
|
func IntValueSlice(src []*int) []int {
|
||||||
|
dst := make([]int, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
if src[i] != nil {
|
||||||
|
dst[i] = *(src[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntMap converts a string map of int values into a string
|
||||||
|
// map of int pointers
|
||||||
|
func IntMap(src map[string]int) map[string]*int {
|
||||||
|
dst := make(map[string]*int)
|
||||||
|
for k, val := range src {
|
||||||
|
v := val
|
||||||
|
dst[k] = &v
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntValueMap converts a string map of int pointers into a string
|
||||||
|
// map of int values
|
||||||
|
func IntValueMap(src map[string]*int) map[string]int {
|
||||||
|
dst := make(map[string]int)
|
||||||
|
for k, val := range src {
|
||||||
|
if val != nil {
|
||||||
|
dst[k] = *val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 returns a pointer to of the int64 value passed in.
|
||||||
|
func Int64(v int64) *int64 {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Value returns the value of the int64 pointer passed in or
|
||||||
|
// 0 if the pointer is nil.
|
||||||
|
func Int64Value(v *int64) int64 {
|
||||||
|
if v != nil {
|
||||||
|
return *v
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Slice converts a slice of int64 values into a slice of
|
||||||
|
// int64 pointers
|
||||||
|
func Int64Slice(src []int64) []*int64 {
|
||||||
|
dst := make([]*int64, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
dst[i] = &(src[i])
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64ValueSlice converts a slice of int64 pointers into a slice of
|
||||||
|
// int64 values
|
||||||
|
func Int64ValueSlice(src []*int64) []int64 {
|
||||||
|
dst := make([]int64, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
if src[i] != nil {
|
||||||
|
dst[i] = *(src[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Map converts a string map of int64 values into a string
|
||||||
|
// map of int64 pointers
|
||||||
|
func Int64Map(src map[string]int64) map[string]*int64 {
|
||||||
|
dst := make(map[string]*int64)
|
||||||
|
for k, val := range src {
|
||||||
|
v := val
|
||||||
|
dst[k] = &v
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64ValueMap converts a string map of int64 pointers into a string
|
||||||
|
// map of int64 values
|
||||||
|
func Int64ValueMap(src map[string]*int64) map[string]int64 {
|
||||||
|
dst := make(map[string]int64)
|
||||||
|
for k, val := range src {
|
||||||
|
if val != nil {
|
||||||
|
dst[k] = *val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 returns a pointer to of the float64 value passed in.
|
||||||
|
func Float64(v float64) *float64 {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Value returns the value of the float64 pointer passed in or
|
||||||
|
// 0 if the pointer is nil.
|
||||||
|
func Float64Value(v *float64) float64 {
|
||||||
|
if v != nil {
|
||||||
|
return *v
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Slice converts a slice of float64 values into a slice of
|
||||||
|
// float64 pointers
|
||||||
|
func Float64Slice(src []float64) []*float64 {
|
||||||
|
dst := make([]*float64, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
dst[i] = &(src[i])
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64ValueSlice converts a slice of float64 pointers into a slice of
|
||||||
|
// float64 values
|
||||||
|
func Float64ValueSlice(src []*float64) []float64 {
|
||||||
|
dst := make([]float64, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
if src[i] != nil {
|
||||||
|
dst[i] = *(src[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Map converts a string map of float64 values into a string
|
||||||
|
// map of float64 pointers
|
||||||
|
func Float64Map(src map[string]float64) map[string]*float64 {
|
||||||
|
dst := make(map[string]*float64)
|
||||||
|
for k, val := range src {
|
||||||
|
v := val
|
||||||
|
dst[k] = &v
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64ValueMap converts a string map of float64 pointers into a string
|
||||||
|
// map of float64 values
|
||||||
|
func Float64ValueMap(src map[string]*float64) map[string]float64 {
|
||||||
|
dst := make(map[string]float64)
|
||||||
|
for k, val := range src {
|
||||||
|
if val != nil {
|
||||||
|
dst[k] = *val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time returns a pointer to of the time.Time value passed in.
|
||||||
|
func Time(v time.Time) *time.Time {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeValue returns the value of the time.Time pointer passed in or
|
||||||
|
// time.Time{} if the pointer is nil.
|
||||||
|
func TimeValue(v *time.Time) time.Time {
|
||||||
|
if v != nil {
|
||||||
|
return *v
|
||||||
|
}
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeSlice converts a slice of time.Time values into a slice of
|
||||||
|
// time.Time pointers
|
||||||
|
func TimeSlice(src []time.Time) []*time.Time {
|
||||||
|
dst := make([]*time.Time, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
dst[i] = &(src[i])
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeValueSlice converts a slice of time.Time pointers into a slice of
|
||||||
|
// time.Time values
|
||||||
|
func TimeValueSlice(src []*time.Time) []time.Time {
|
||||||
|
dst := make([]time.Time, len(src))
|
||||||
|
for i := 0; i < len(src); i++ {
|
||||||
|
if src[i] != nil {
|
||||||
|
dst[i] = *(src[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeMap converts a string map of time.Time values into a string
|
||||||
|
// map of time.Time pointers
|
||||||
|
func TimeMap(src map[string]time.Time) map[string]*time.Time {
|
||||||
|
dst := make(map[string]*time.Time)
|
||||||
|
for k, val := range src {
|
||||||
|
v := val
|
||||||
|
dst[k] = &v
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeValueMap converts a string map of time.Time pointers into a string
|
||||||
|
// map of time.Time values
|
||||||
|
func TimeValueMap(src map[string]*time.Time) map[string]time.Time {
|
||||||
|
dst := make(map[string]time.Time)
|
||||||
|
for k, val := range src {
|
||||||
|
if val != nil {
|
||||||
|
dst[k] = *val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
438
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/convutil_test.go
generated
vendored
Normal file
438
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/convutil_test.go
generated
vendored
Normal file
|
@ -0,0 +1,438 @@
|
||||||
|
package aws_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testCasesStringSlice = [][]string{
|
||||||
|
{"a", "b", "c", "d", "e"},
|
||||||
|
{"a", "b", "", "", "e"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringSlice(t *testing.T) {
|
||||||
|
for idx, in := range testCasesStringSlice {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := aws.StringSlice(in)
|
||||||
|
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out {
|
||||||
|
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := aws.StringValueSlice(out)
|
||||||
|
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCasesStringValueSlice = [][]*string{
|
||||||
|
{aws.String("a"), aws.String("b"), nil, aws.String("c")},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringValueSlice(t *testing.T) {
|
||||||
|
for idx, in := range testCasesStringValueSlice {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := aws.StringValueSlice(in)
|
||||||
|
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out {
|
||||||
|
if in[i] == nil {
|
||||||
|
assert.Empty(t, out[i], "Unexpected value at idx %d", idx)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := aws.StringSlice(out)
|
||||||
|
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out2 {
|
||||||
|
if in[i] == nil {
|
||||||
|
assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCasesStringMap = []map[string]string{
|
||||||
|
{"a": "1", "b": "2", "c": "3"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringMap(t *testing.T) {
|
||||||
|
for idx, in := range testCasesStringMap {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := aws.StringMap(in)
|
||||||
|
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out {
|
||||||
|
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := aws.StringValueMap(out)
|
||||||
|
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCasesBoolSlice = [][]bool{
|
||||||
|
{true, true, false, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBoolSlice(t *testing.T) {
|
||||||
|
for idx, in := range testCasesBoolSlice {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := aws.BoolSlice(in)
|
||||||
|
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out {
|
||||||
|
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := aws.BoolValueSlice(out)
|
||||||
|
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCasesBoolValueSlice = [][]*bool{}
|
||||||
|
|
||||||
|
func TestBoolValueSlice(t *testing.T) {
|
||||||
|
for idx, in := range testCasesBoolValueSlice {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := aws.BoolValueSlice(in)
|
||||||
|
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out {
|
||||||
|
if in[i] == nil {
|
||||||
|
assert.Empty(t, out[i], "Unexpected value at idx %d", idx)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := aws.BoolSlice(out)
|
||||||
|
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out2 {
|
||||||
|
if in[i] == nil {
|
||||||
|
assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCasesBoolMap = []map[string]bool{
|
||||||
|
{"a": true, "b": false, "c": true},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBoolMap(t *testing.T) {
|
||||||
|
for idx, in := range testCasesBoolMap {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := aws.BoolMap(in)
|
||||||
|
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out {
|
||||||
|
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := aws.BoolValueMap(out)
|
||||||
|
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCasesIntSlice = [][]int{
|
||||||
|
{1, 2, 3, 4},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIntSlice(t *testing.T) {
|
||||||
|
for idx, in := range testCasesIntSlice {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := aws.IntSlice(in)
|
||||||
|
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out {
|
||||||
|
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := aws.IntValueSlice(out)
|
||||||
|
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCasesIntValueSlice = [][]*int{}
|
||||||
|
|
||||||
|
func TestIntValueSlice(t *testing.T) {
|
||||||
|
for idx, in := range testCasesIntValueSlice {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := aws.IntValueSlice(in)
|
||||||
|
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out {
|
||||||
|
if in[i] == nil {
|
||||||
|
assert.Empty(t, out[i], "Unexpected value at idx %d", idx)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := aws.IntSlice(out)
|
||||||
|
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out2 {
|
||||||
|
if in[i] == nil {
|
||||||
|
assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCasesIntMap = []map[string]int{
|
||||||
|
{"a": 3, "b": 2, "c": 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIntMap(t *testing.T) {
|
||||||
|
for idx, in := range testCasesIntMap {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := aws.IntMap(in)
|
||||||
|
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out {
|
||||||
|
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := aws.IntValueMap(out)
|
||||||
|
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCasesInt64Slice = [][]int64{
|
||||||
|
{1, 2, 3, 4},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInt64Slice(t *testing.T) {
|
||||||
|
for idx, in := range testCasesInt64Slice {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := aws.Int64Slice(in)
|
||||||
|
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out {
|
||||||
|
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := aws.Int64ValueSlice(out)
|
||||||
|
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCasesInt64ValueSlice = [][]*int64{}
|
||||||
|
|
||||||
|
func TestInt64ValueSlice(t *testing.T) {
|
||||||
|
for idx, in := range testCasesInt64ValueSlice {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := aws.Int64ValueSlice(in)
|
||||||
|
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out {
|
||||||
|
if in[i] == nil {
|
||||||
|
assert.Empty(t, out[i], "Unexpected value at idx %d", idx)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := aws.Int64Slice(out)
|
||||||
|
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out2 {
|
||||||
|
if in[i] == nil {
|
||||||
|
assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCasesInt64Map = []map[string]int64{
|
||||||
|
{"a": 3, "b": 2, "c": 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInt64Map(t *testing.T) {
|
||||||
|
for idx, in := range testCasesInt64Map {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := aws.Int64Map(in)
|
||||||
|
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out {
|
||||||
|
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := aws.Int64ValueMap(out)
|
||||||
|
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCasesFloat64Slice = [][]float64{
|
||||||
|
{1, 2, 3, 4},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFloat64Slice(t *testing.T) {
|
||||||
|
for idx, in := range testCasesFloat64Slice {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := aws.Float64Slice(in)
|
||||||
|
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out {
|
||||||
|
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := aws.Float64ValueSlice(out)
|
||||||
|
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCasesFloat64ValueSlice = [][]*float64{}
|
||||||
|
|
||||||
|
func TestFloat64ValueSlice(t *testing.T) {
|
||||||
|
for idx, in := range testCasesFloat64ValueSlice {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := aws.Float64ValueSlice(in)
|
||||||
|
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out {
|
||||||
|
if in[i] == nil {
|
||||||
|
assert.Empty(t, out[i], "Unexpected value at idx %d", idx)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := aws.Float64Slice(out)
|
||||||
|
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out2 {
|
||||||
|
if in[i] == nil {
|
||||||
|
assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCasesFloat64Map = []map[string]float64{
|
||||||
|
{"a": 3, "b": 2, "c": 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFloat64Map(t *testing.T) {
|
||||||
|
for idx, in := range testCasesFloat64Map {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := aws.Float64Map(in)
|
||||||
|
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out {
|
||||||
|
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := aws.Float64ValueMap(out)
|
||||||
|
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCasesTimeSlice = [][]time.Time{
|
||||||
|
{time.Now(), time.Now().AddDate(100, 0, 0)},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimeSlice(t *testing.T) {
|
||||||
|
for idx, in := range testCasesTimeSlice {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := aws.TimeSlice(in)
|
||||||
|
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out {
|
||||||
|
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := aws.TimeValueSlice(out)
|
||||||
|
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCasesTimeValueSlice = [][]*time.Time{}
|
||||||
|
|
||||||
|
func TestTimeValueSlice(t *testing.T) {
|
||||||
|
for idx, in := range testCasesTimeValueSlice {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := aws.TimeValueSlice(in)
|
||||||
|
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out {
|
||||||
|
if in[i] == nil {
|
||||||
|
assert.Empty(t, out[i], "Unexpected value at idx %d", idx)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := aws.TimeSlice(out)
|
||||||
|
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out2 {
|
||||||
|
if in[i] == nil {
|
||||||
|
assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCasesTimeMap = []map[string]time.Time{
|
||||||
|
{"a": time.Now().AddDate(-100, 0, 0), "b": time.Now()},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimeMap(t *testing.T) {
|
||||||
|
for idx, in := range testCasesTimeMap {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := aws.TimeMap(in)
|
||||||
|
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
for i := range out {
|
||||||
|
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := aws.TimeValueMap(out)
|
||||||
|
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||||
|
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||||
|
}
|
||||||
|
}
|
85
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/chain_provider.go
generated
vendored
Normal file
85
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/chain_provider.go
generated
vendored
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrNoValidProvidersFoundInChain Is returned when there are no valid
|
||||||
|
// providers in the ChainProvider.
|
||||||
|
//
|
||||||
|
// @readonly
|
||||||
|
ErrNoValidProvidersFoundInChain = awserr.New("NoCredentialProviders", "no valid providers in chain", nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
// A ChainProvider will search for a provider which returns credentials
|
||||||
|
// and cache that provider until Retrieve is called again.
|
||||||
|
//
|
||||||
|
// The ChainProvider provides a way of chaining multiple providers together
|
||||||
|
// which will pick the first available using priority order of the Providers
|
||||||
|
// in the list.
|
||||||
|
//
|
||||||
|
// If none of the Providers retrieve valid credentials Value, ChainProvider's
|
||||||
|
// Retrieve() will return the error ErrNoValidProvidersFoundInChain.
|
||||||
|
//
|
||||||
|
// If a Provider is found which returns valid credentials Value ChainProvider
|
||||||
|
// will cache that Provider for all calls to IsExpired(), until Retrieve is
|
||||||
|
// called again.
|
||||||
|
//
|
||||||
|
// Example of ChainProvider to be used with an EnvProvider and EC2RoleProvider.
|
||||||
|
// In this example EnvProvider will first check if any credentials are available
|
||||||
|
// vai the environment variables. If there are none ChainProvider will check
|
||||||
|
// the next Provider in the list, EC2RoleProvider in this case. If EC2RoleProvider
|
||||||
|
// does not return any credentials ChainProvider will return the error
|
||||||
|
// ErrNoValidProvidersFoundInChain
|
||||||
|
//
|
||||||
|
// creds := NewChainCredentials(
|
||||||
|
// []Provider{
|
||||||
|
// &EnvProvider{},
|
||||||
|
// &EC2RoleProvider{},
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// // Usage of ChainCredentials with aws.Config
|
||||||
|
// svc := ec2.New(&aws.Config{Credentials: creds})
|
||||||
|
//
|
||||||
|
type ChainProvider struct {
|
||||||
|
Providers []Provider
|
||||||
|
curr Provider
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChainCredentials returns a pointer to a new Credentials object
|
||||||
|
// wrapping a chain of providers.
|
||||||
|
func NewChainCredentials(providers []Provider) *Credentials {
|
||||||
|
return NewCredentials(&ChainProvider{
|
||||||
|
Providers: append([]Provider{}, providers...),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve returns the credentials value or error if no provider returned
|
||||||
|
// without error.
|
||||||
|
//
|
||||||
|
// If a provider is found it will be cached and any calls to IsExpired()
|
||||||
|
// will return the expired state of the cached provider.
|
||||||
|
func (c *ChainProvider) Retrieve() (Value, error) {
|
||||||
|
for _, p := range c.Providers {
|
||||||
|
if creds, err := p.Retrieve(); err == nil {
|
||||||
|
c.curr = p
|
||||||
|
return creds, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.curr = nil
|
||||||
|
|
||||||
|
// TODO better error reporting. maybe report error for each failed retrieve?
|
||||||
|
|
||||||
|
return Value{}, ErrNoValidProvidersFoundInChain
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExpired will returned the expired state of the currently cached provider
|
||||||
|
// if there is one. If there is no current provider, true will be returned.
|
||||||
|
func (c *ChainProvider) IsExpired() bool {
|
||||||
|
if c.curr != nil {
|
||||||
|
return c.curr.IsExpired()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
73
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/chain_provider_test.go
generated
vendored
Normal file
73
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/chain_provider_test.go
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestChainProviderGet(t *testing.T) {
|
||||||
|
p := &ChainProvider{
|
||||||
|
Providers: []Provider{
|
||||||
|
&stubProvider{err: awserr.New("FirstError", "first provider error", nil)},
|
||||||
|
&stubProvider{err: awserr.New("SecondError", "second provider error", nil)},
|
||||||
|
&stubProvider{
|
||||||
|
creds: Value{
|
||||||
|
AccessKeyID: "AKID",
|
||||||
|
SecretAccessKey: "SECRET",
|
||||||
|
SessionToken: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
creds, err := p.Retrieve()
|
||||||
|
assert.Nil(t, err, "Expect no error")
|
||||||
|
assert.Equal(t, "AKID", creds.AccessKeyID, "Expect access key ID to match")
|
||||||
|
assert.Equal(t, "SECRET", creds.SecretAccessKey, "Expect secret access key to match")
|
||||||
|
assert.Empty(t, creds.SessionToken, "Expect session token to be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainProviderIsExpired(t *testing.T) {
|
||||||
|
stubProvider := &stubProvider{expired: true}
|
||||||
|
p := &ChainProvider{
|
||||||
|
Providers: []Provider{
|
||||||
|
stubProvider,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.True(t, p.IsExpired(), "Expect expired to be true before any Retrieve")
|
||||||
|
_, err := p.Retrieve()
|
||||||
|
assert.Nil(t, err, "Expect no error")
|
||||||
|
assert.False(t, p.IsExpired(), "Expect not expired after retrieve")
|
||||||
|
|
||||||
|
stubProvider.expired = true
|
||||||
|
assert.True(t, p.IsExpired(), "Expect return of expired provider")
|
||||||
|
|
||||||
|
_, err = p.Retrieve()
|
||||||
|
assert.False(t, p.IsExpired(), "Expect not expired after retrieve")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainProviderWithNoProvider(t *testing.T) {
|
||||||
|
p := &ChainProvider{
|
||||||
|
Providers: []Provider{},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.True(t, p.IsExpired(), "Expect expired with no providers")
|
||||||
|
_, err := p.Retrieve()
|
||||||
|
assert.Equal(t, ErrNoValidProvidersFoundInChain, err, "Expect no providers error returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainProviderWithNoValidProvider(t *testing.T) {
|
||||||
|
p := &ChainProvider{
|
||||||
|
Providers: []Provider{
|
||||||
|
&stubProvider{err: awserr.New("FirstError", "first provider error", nil)},
|
||||||
|
&stubProvider{err: awserr.New("SecondError", "second provider error", nil)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.True(t, p.IsExpired(), "Expect expired with no providers")
|
||||||
|
_, err := p.Retrieve()
|
||||||
|
assert.Equal(t, ErrNoValidProvidersFoundInChain, err, "Expect no providers error returned")
|
||||||
|
}
|
220
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/credentials.go
generated
vendored
Normal file
220
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/credentials.go
generated
vendored
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
// Package credentials provides credential retrieval and management
|
||||||
|
//
|
||||||
|
// The Credentials is the primary method of getting access to and managing
|
||||||
|
// credentials Values. Using dependency injection retrieval of the credential
|
||||||
|
// values is handled by a object which satisfies the Provider interface.
|
||||||
|
//
|
||||||
|
// By default the Credentials.Get() will cache the successful result of a
|
||||||
|
// Provider's Retrieve() until Provider.IsExpired() returns true. At which
|
||||||
|
// point Credentials will call Provider's Retrieve() to get new credential Value.
|
||||||
|
//
|
||||||
|
// The Provider is responsible for determining when credentials Value have expired.
|
||||||
|
// It is also important to note that Credentials will always call Retrieve the
|
||||||
|
// first time Credentials.Get() is called.
|
||||||
|
//
|
||||||
|
// Example of using the environment variable credentials.
|
||||||
|
//
|
||||||
|
// creds := NewEnvCredentials()
|
||||||
|
//
|
||||||
|
// // Retrieve the credentials value
|
||||||
|
// credValue, err := creds.Get()
|
||||||
|
// if err != nil {
|
||||||
|
// // handle error
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Example of forcing credentials to expire and be refreshed on the next Get().
|
||||||
|
// This may be helpful to proactively expire credentials and refresh them sooner
|
||||||
|
// than they would naturally expire on their own.
|
||||||
|
//
|
||||||
|
// creds := NewCredentials(&EC2RoleProvider{})
|
||||||
|
// creds.Expire()
|
||||||
|
// credsValue, err := creds.Get()
|
||||||
|
// // New credentials will be retrieved instead of from cache.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Custom Provider
|
||||||
|
//
|
||||||
|
// Each Provider built into this package also provides a helper method to generate
|
||||||
|
// a Credentials pointer setup with the provider. To use a custom Provider just
|
||||||
|
// create a type which satisfies the Provider interface and pass it to the
|
||||||
|
// NewCredentials method.
|
||||||
|
//
|
||||||
|
// type MyProvider struct{}
|
||||||
|
// func (m *MyProvider) Retrieve() (Value, error) {...}
|
||||||
|
// func (m *MyProvider) IsExpired() bool {...}
|
||||||
|
//
|
||||||
|
// creds := NewCredentials(&MyProvider{})
|
||||||
|
// credValue, err := creds.Get()
|
||||||
|
//
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create an empty Credential object that can be used as dummy placeholder
|
||||||
|
// credentials for requests that do not need signed.
|
||||||
|
//
|
||||||
|
// This Credentials can be used to configure a service to not sign requests
|
||||||
|
// when making service API calls. For example, when accessing public
|
||||||
|
// s3 buckets.
|
||||||
|
//
|
||||||
|
// svc := s3.New(&aws.Config{Credentials: AnonymousCredentials})
|
||||||
|
// // Access public S3 buckets.
|
||||||
|
//
|
||||||
|
// @readonly
|
||||||
|
var AnonymousCredentials = NewStaticCredentials("", "", "")
|
||||||
|
|
||||||
|
// A Value is the AWS credentials value for individual credential fields.
|
||||||
|
type Value struct {
|
||||||
|
// AWS Access key ID
|
||||||
|
AccessKeyID string
|
||||||
|
|
||||||
|
// AWS Secret Access Key
|
||||||
|
SecretAccessKey string
|
||||||
|
|
||||||
|
// AWS Session Token
|
||||||
|
SessionToken string
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Provider is the interface for any component which will provide credentials
|
||||||
|
// Value. A provider is required to manage its own Expired state, and what to
|
||||||
|
// be expired means.
|
||||||
|
//
|
||||||
|
// The Provider should not need to implement its own mutexes, because
|
||||||
|
// that will be managed by Credentials.
|
||||||
|
type Provider interface {
|
||||||
|
// Refresh returns nil if it successfully retrieved the value.
|
||||||
|
// Error is returned if the value were not obtainable, or empty.
|
||||||
|
Retrieve() (Value, error)
|
||||||
|
|
||||||
|
// IsExpired returns if the credentials are no longer valid, and need
|
||||||
|
// to be retrieved.
|
||||||
|
IsExpired() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Expiry provides shared expiration logic to be used by credentials
|
||||||
|
// providers to implement expiry functionality.
|
||||||
|
//
|
||||||
|
// The best method to use this struct is as an anonymous field within the
|
||||||
|
// provider's struct.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// type EC2RoleProvider struct {
|
||||||
|
// Expiry
|
||||||
|
// ...
|
||||||
|
// }
|
||||||
|
type Expiry struct {
|
||||||
|
// The date/time when to expire on
|
||||||
|
expiration time.Time
|
||||||
|
|
||||||
|
// If set will be used by IsExpired to determine the current time.
|
||||||
|
// Defaults to time.Now if CurrentTime is not set. Available for testing
|
||||||
|
// to be able to mock out the current time.
|
||||||
|
CurrentTime func() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetExpiration sets the expiration IsExpired will check when called.
|
||||||
|
//
|
||||||
|
// If window is greater than 0 the expiration time will be reduced by the
|
||||||
|
// window value.
|
||||||
|
//
|
||||||
|
// Using a window is helpful to trigger credentials to expire sooner than
|
||||||
|
// the expiration time given to ensure no requests are made with expired
|
||||||
|
// tokens.
|
||||||
|
func (e *Expiry) SetExpiration(expiration time.Time, window time.Duration) {
|
||||||
|
e.expiration = expiration
|
||||||
|
if window > 0 {
|
||||||
|
e.expiration = e.expiration.Add(-window)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExpired returns if the credentials are expired.
|
||||||
|
func (e *Expiry) IsExpired() bool {
|
||||||
|
if e.CurrentTime == nil {
|
||||||
|
e.CurrentTime = time.Now
|
||||||
|
}
|
||||||
|
return e.expiration.Before(e.CurrentTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Credentials provides synchronous safe retrieval of AWS credentials Value.
|
||||||
|
// Credentials will cache the credentials value until they expire. Once the value
|
||||||
|
// expires the next Get will attempt to retrieve valid credentials.
|
||||||
|
//
|
||||||
|
// Credentials is safe to use across multiple goroutines and will manage the
|
||||||
|
// synchronous state so the Providers do not need to implement their own
|
||||||
|
// synchronization.
|
||||||
|
//
|
||||||
|
// The first Credentials.Get() will always call Provider.Retrieve() to get the
|
||||||
|
// first instance of the credentials Value. All calls to Get() after that
|
||||||
|
// will return the cached credentials Value until IsExpired() returns true.
|
||||||
|
type Credentials struct {
|
||||||
|
creds Value
|
||||||
|
forceRefresh bool
|
||||||
|
m sync.Mutex
|
||||||
|
|
||||||
|
provider Provider
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCredentials returns a pointer to a new Credentials with the provider set.
|
||||||
|
func NewCredentials(provider Provider) *Credentials {
|
||||||
|
return &Credentials{
|
||||||
|
provider: provider,
|
||||||
|
forceRefresh: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the credentials value, or error if the credentials Value failed
|
||||||
|
// to be retrieved.
|
||||||
|
//
|
||||||
|
// Will return the cached credentials Value if it has not expired. If the
|
||||||
|
// credentials Value has expired the Provider's Retrieve() will be called
|
||||||
|
// to refresh the credentials.
|
||||||
|
//
|
||||||
|
// If Credentials.Expire() was called the credentials Value will be force
|
||||||
|
// expired, and the next call to Get() will cause them to be refreshed.
|
||||||
|
func (c *Credentials) Get() (Value, error) {
|
||||||
|
c.m.Lock()
|
||||||
|
defer c.m.Unlock()
|
||||||
|
|
||||||
|
if c.isExpired() {
|
||||||
|
creds, err := c.provider.Retrieve()
|
||||||
|
if err != nil {
|
||||||
|
return Value{}, err
|
||||||
|
}
|
||||||
|
c.creds = creds
|
||||||
|
c.forceRefresh = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.creds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expire expires the credentials and forces them to be retrieved on the
|
||||||
|
// next call to Get().
|
||||||
|
//
|
||||||
|
// This will override the Provider's expired state, and force Credentials
|
||||||
|
// to call the Provider's Retrieve().
|
||||||
|
func (c *Credentials) Expire() {
|
||||||
|
c.m.Lock()
|
||||||
|
defer c.m.Unlock()
|
||||||
|
|
||||||
|
c.forceRefresh = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExpired returns if the credentials are no longer valid, and need
|
||||||
|
// to be retrieved.
|
||||||
|
//
|
||||||
|
// If the Credentials were forced to be expired with Expire() this will
|
||||||
|
// reflect that override.
|
||||||
|
func (c *Credentials) IsExpired() bool {
|
||||||
|
c.m.Lock()
|
||||||
|
defer c.m.Unlock()
|
||||||
|
|
||||||
|
return c.isExpired()
|
||||||
|
}
|
||||||
|
|
||||||
|
// isExpired helper method wrapping the definition of expired credentials.
|
||||||
|
func (c *Credentials) isExpired() bool {
|
||||||
|
return c.forceRefresh || c.provider.IsExpired()
|
||||||
|
}
|
62
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/credentials_test.go
generated
vendored
Normal file
62
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/credentials_test.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stubProvider struct {
|
||||||
|
creds Value
|
||||||
|
expired bool
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stubProvider) Retrieve() (Value, error) {
|
||||||
|
s.expired = false
|
||||||
|
return s.creds, s.err
|
||||||
|
}
|
||||||
|
func (s *stubProvider) IsExpired() bool {
|
||||||
|
return s.expired
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCredentialsGet(t *testing.T) {
|
||||||
|
c := NewCredentials(&stubProvider{
|
||||||
|
creds: Value{
|
||||||
|
AccessKeyID: "AKID",
|
||||||
|
SecretAccessKey: "SECRET",
|
||||||
|
SessionToken: "",
|
||||||
|
},
|
||||||
|
expired: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
creds, err := c.Get()
|
||||||
|
assert.Nil(t, err, "Expected no error")
|
||||||
|
assert.Equal(t, "AKID", creds.AccessKeyID, "Expect access key ID to match")
|
||||||
|
assert.Equal(t, "SECRET", creds.SecretAccessKey, "Expect secret access key to match")
|
||||||
|
assert.Empty(t, creds.SessionToken, "Expect session token to be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCredentialsGetWithError(t *testing.T) {
|
||||||
|
c := NewCredentials(&stubProvider{err: awserr.New("provider error", "", nil), expired: true})
|
||||||
|
|
||||||
|
_, err := c.Get()
|
||||||
|
assert.Equal(t, "provider error", err.(awserr.Error).Code(), "Expected provider error")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCredentialsExpire(t *testing.T) {
|
||||||
|
stub := &stubProvider{}
|
||||||
|
c := NewCredentials(stub)
|
||||||
|
|
||||||
|
stub.expired = false
|
||||||
|
assert.True(t, c.IsExpired(), "Expected to start out expired")
|
||||||
|
c.Expire()
|
||||||
|
assert.True(t, c.IsExpired(), "Expected to be expired")
|
||||||
|
|
||||||
|
c.forceRefresh = false
|
||||||
|
assert.False(t, c.IsExpired(), "Expected not to be expired")
|
||||||
|
|
||||||
|
stub.expired = true
|
||||||
|
assert.True(t, c.IsExpired(), "Expected to be expired")
|
||||||
|
}
|
162
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/ec2_role_provider.go
generated
vendored
Normal file
162
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/ec2_role_provider.go
generated
vendored
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
const metadataCredentialsEndpoint = "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
|
||||||
|
|
||||||
|
// A EC2RoleProvider retrieves credentials from the EC2 service, and keeps track if
|
||||||
|
// those credentials are expired.
|
||||||
|
//
|
||||||
|
// Example how to configure the EC2RoleProvider with custom http Client, Endpoint
|
||||||
|
// or ExpiryWindow
|
||||||
|
//
|
||||||
|
// p := &credentials.EC2RoleProvider{
|
||||||
|
// // Pass in a custom timeout to be used when requesting
|
||||||
|
// // IAM EC2 Role credentials.
|
||||||
|
// Client: &http.Client{
|
||||||
|
// Timeout: 10 * time.Second,
|
||||||
|
// },
|
||||||
|
// // Use default EC2 Role metadata endpoint, Alternate endpoints can be
|
||||||
|
// // specified setting Endpoint to something else.
|
||||||
|
// Endpoint: "",
|
||||||
|
// // Do not use early expiry of credentials. If a non zero value is
|
||||||
|
// // specified the credentials will be expired early
|
||||||
|
// ExpiryWindow: 0,
|
||||||
|
// }
|
||||||
|
type EC2RoleProvider struct {
|
||||||
|
Expiry
|
||||||
|
|
||||||
|
// Endpoint must be fully quantified URL
|
||||||
|
Endpoint string
|
||||||
|
|
||||||
|
// HTTP client to use when connecting to EC2 service
|
||||||
|
Client *http.Client
|
||||||
|
|
||||||
|
// ExpiryWindow will allow the credentials to trigger refreshing prior to
|
||||||
|
// the credentials actually expiring. This is beneficial so race conditions
|
||||||
|
// with expiring credentials do not cause request to fail unexpectedly
|
||||||
|
// due to ExpiredTokenException exceptions.
|
||||||
|
//
|
||||||
|
// So a ExpiryWindow of 10s would cause calls to IsExpired() to return true
|
||||||
|
// 10 seconds before the credentials are actually expired.
|
||||||
|
//
|
||||||
|
// If ExpiryWindow is 0 or less it will be ignored.
|
||||||
|
ExpiryWindow time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEC2RoleCredentials returns a pointer to a new Credentials object
|
||||||
|
// wrapping the EC2RoleProvider.
|
||||||
|
//
|
||||||
|
// Takes a custom http.Client which can be configured for custom handling of
|
||||||
|
// things such as timeout.
|
||||||
|
//
|
||||||
|
// Endpoint is the URL that the EC2RoleProvider will connect to when retrieving
|
||||||
|
// role and credentials.
|
||||||
|
//
|
||||||
|
// Window is the expiry window that will be subtracted from the expiry returned
|
||||||
|
// by the role credential request. This is done so that the credentials will
|
||||||
|
// expire sooner than their actual lifespan.
|
||||||
|
func NewEC2RoleCredentials(client *http.Client, endpoint string, window time.Duration) *Credentials {
|
||||||
|
return NewCredentials(&EC2RoleProvider{
|
||||||
|
Endpoint: endpoint,
|
||||||
|
Client: client,
|
||||||
|
ExpiryWindow: window,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve retrieves credentials from the EC2 service.
|
||||||
|
// Error will be returned if the request fails, or unable to extract
|
||||||
|
// the desired credentials.
|
||||||
|
func (m *EC2RoleProvider) Retrieve() (Value, error) {
|
||||||
|
if m.Client == nil {
|
||||||
|
m.Client = http.DefaultClient
|
||||||
|
}
|
||||||
|
if m.Endpoint == "" {
|
||||||
|
m.Endpoint = metadataCredentialsEndpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
credsList, err := requestCredList(m.Client, m.Endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return Value{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(credsList) == 0 {
|
||||||
|
return Value{}, awserr.New("EmptyEC2RoleList", "empty EC2 Role list", nil)
|
||||||
|
}
|
||||||
|
credsName := credsList[0]
|
||||||
|
|
||||||
|
roleCreds, err := requestCred(m.Client, m.Endpoint, credsName)
|
||||||
|
if err != nil {
|
||||||
|
return Value{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m.SetExpiration(roleCreds.Expiration, m.ExpiryWindow)
|
||||||
|
|
||||||
|
return Value{
|
||||||
|
AccessKeyID: roleCreds.AccessKeyID,
|
||||||
|
SecretAccessKey: roleCreds.SecretAccessKey,
|
||||||
|
SessionToken: roleCreds.Token,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// A ec2RoleCredRespBody provides the shape for deserializing credential
|
||||||
|
// request responses.
|
||||||
|
type ec2RoleCredRespBody struct {
|
||||||
|
Expiration time.Time
|
||||||
|
AccessKeyID string
|
||||||
|
SecretAccessKey string
|
||||||
|
Token string
|
||||||
|
}
|
||||||
|
|
||||||
|
// requestCredList requests a list of credentials from the EC2 service.
|
||||||
|
// If there are no credentials, or there is an error making or receiving the request
|
||||||
|
func requestCredList(client *http.Client, endpoint string) ([]string, error) {
|
||||||
|
resp, err := client.Get(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, awserr.New("ListEC2Role", "failed to list EC2 Roles", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
credsList := []string{}
|
||||||
|
s := bufio.NewScanner(resp.Body)
|
||||||
|
for s.Scan() {
|
||||||
|
credsList = append(credsList, s.Text())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
return nil, awserr.New("ReadEC2Role", "failed to read list of EC2 Roles", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return credsList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// requestCred requests the credentials for a specific credentials from the EC2 service.
|
||||||
|
//
|
||||||
|
// If the credentials cannot be found, or there is an error reading the response
|
||||||
|
// and error will be returned.
|
||||||
|
func requestCred(client *http.Client, endpoint, credsName string) (*ec2RoleCredRespBody, error) {
|
||||||
|
resp, err := client.Get(endpoint + credsName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, awserr.New("GetEC2RoleCredentials",
|
||||||
|
fmt.Sprintf("failed to get %s EC2 Role credentials", credsName),
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
respCreds := &ec2RoleCredRespBody{}
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(respCreds); err != nil {
|
||||||
|
return nil, awserr.New("DecodeEC2RoleCredentials",
|
||||||
|
fmt.Sprintf("failed to decode %s EC2 Role credentials", credsName),
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return respCreds, nil
|
||||||
|
}
|
108
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/ec2_role_provider_test.go
generated
vendored
Normal file
108
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/ec2_role_provider_test.go
generated
vendored
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initTestServer(expireOn string) *httptest.Server {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.RequestURI == "/" {
|
||||||
|
fmt.Fprintln(w, "/creds")
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, `{
|
||||||
|
"AccessKeyId" : "accessKey",
|
||||||
|
"SecretAccessKey" : "secret",
|
||||||
|
"Token" : "token",
|
||||||
|
"Expiration" : "%s"
|
||||||
|
}`, expireOn)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEC2RoleProvider(t *testing.T) {
|
||||||
|
server := initTestServer("2014-12-16T01:51:37Z")
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
p := &EC2RoleProvider{Client: http.DefaultClient, Endpoint: server.URL}
|
||||||
|
|
||||||
|
creds, err := p.Retrieve()
|
||||||
|
assert.Nil(t, err, "Expect no error")
|
||||||
|
|
||||||
|
assert.Equal(t, "accessKey", creds.AccessKeyID, "Expect access key ID to match")
|
||||||
|
assert.Equal(t, "secret", creds.SecretAccessKey, "Expect secret access key to match")
|
||||||
|
assert.Equal(t, "token", creds.SessionToken, "Expect session token to match")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEC2RoleProviderIsExpired(t *testing.T) {
|
||||||
|
server := initTestServer("2014-12-16T01:51:37Z")
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
p := &EC2RoleProvider{Client: http.DefaultClient, Endpoint: server.URL}
|
||||||
|
p.CurrentTime = func() time.Time {
|
||||||
|
return time.Date(2014, 12, 15, 21, 26, 0, 0, time.UTC)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.True(t, p.IsExpired(), "Expect creds to be expired before retrieve.")
|
||||||
|
|
||||||
|
_, err := p.Retrieve()
|
||||||
|
assert.Nil(t, err, "Expect no error")
|
||||||
|
|
||||||
|
assert.False(t, p.IsExpired(), "Expect creds to not be expired after retrieve.")
|
||||||
|
|
||||||
|
p.CurrentTime = func() time.Time {
|
||||||
|
return time.Date(3014, 12, 15, 21, 26, 0, 0, time.UTC)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.True(t, p.IsExpired(), "Expect creds to be expired.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEC2RoleProviderExpiryWindowIsExpired(t *testing.T) {
|
||||||
|
server := initTestServer("2014-12-16T01:51:37Z")
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
p := &EC2RoleProvider{Client: http.DefaultClient, Endpoint: server.URL, ExpiryWindow: time.Hour * 1}
|
||||||
|
p.CurrentTime = func() time.Time {
|
||||||
|
return time.Date(2014, 12, 15, 0, 51, 37, 0, time.UTC)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.True(t, p.IsExpired(), "Expect creds to be expired before retrieve.")
|
||||||
|
|
||||||
|
_, err := p.Retrieve()
|
||||||
|
assert.Nil(t, err, "Expect no error")
|
||||||
|
|
||||||
|
assert.False(t, p.IsExpired(), "Expect creds to not be expired after retrieve.")
|
||||||
|
|
||||||
|
p.CurrentTime = func() time.Time {
|
||||||
|
return time.Date(2014, 12, 16, 0, 55, 37, 0, time.UTC)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.True(t, p.IsExpired(), "Expect creds to be expired.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEC2RoleProvider(b *testing.B) {
|
||||||
|
server := initTestServer("2014-12-16T01:51:37Z")
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
p := &EC2RoleProvider{Client: http.DefaultClient, Endpoint: server.URL}
|
||||||
|
_, err := p.Retrieve()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
_, err := p.Retrieve()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
73
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/env_provider.go
generated
vendored
Normal file
73
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/env_provider.go
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrAccessKeyIDNotFound is returned when the AWS Access Key ID can't be
|
||||||
|
// found in the process's environment.
|
||||||
|
//
|
||||||
|
// @readonly
|
||||||
|
ErrAccessKeyIDNotFound = awserr.New("EnvAccessKeyNotFound", "AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY not found in environment", nil)
|
||||||
|
|
||||||
|
// ErrSecretAccessKeyNotFound is returned when the AWS Secret Access Key
|
||||||
|
// can't be found in the process's environment.
|
||||||
|
//
|
||||||
|
// @readonly
|
||||||
|
ErrSecretAccessKeyNotFound = awserr.New("EnvSecretNotFound", "AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY not found in environment", nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
// A EnvProvider retrieves credentials from the environment variables of the
|
||||||
|
// running process. Environment credentials never expire.
|
||||||
|
//
|
||||||
|
// Environment variables used:
|
||||||
|
//
|
||||||
|
// * Access Key ID: AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY
|
||||||
|
// * Secret Access Key: AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY
|
||||||
|
type EnvProvider struct {
|
||||||
|
retrieved bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEnvCredentials returns a pointer to a new Credentials object
|
||||||
|
// wrapping the environment variable provider.
|
||||||
|
func NewEnvCredentials() *Credentials {
|
||||||
|
return NewCredentials(&EnvProvider{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve retrieves the keys from the environment.
|
||||||
|
func (e *EnvProvider) Retrieve() (Value, error) {
|
||||||
|
e.retrieved = false
|
||||||
|
|
||||||
|
id := os.Getenv("AWS_ACCESS_KEY_ID")
|
||||||
|
if id == "" {
|
||||||
|
id = os.Getenv("AWS_ACCESS_KEY")
|
||||||
|
}
|
||||||
|
|
||||||
|
secret := os.Getenv("AWS_SECRET_ACCESS_KEY")
|
||||||
|
if secret == "" {
|
||||||
|
secret = os.Getenv("AWS_SECRET_KEY")
|
||||||
|
}
|
||||||
|
|
||||||
|
if id == "" {
|
||||||
|
return Value{}, ErrAccessKeyIDNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if secret == "" {
|
||||||
|
return Value{}, ErrSecretAccessKeyNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
e.retrieved = true
|
||||||
|
return Value{
|
||||||
|
AccessKeyID: id,
|
||||||
|
SecretAccessKey: secret,
|
||||||
|
SessionToken: os.Getenv("AWS_SESSION_TOKEN"),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExpired returns if the credentials have been retrieved.
|
||||||
|
func (e *EnvProvider) IsExpired() bool {
|
||||||
|
return !e.retrieved
|
||||||
|
}
|
70
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/env_provider_test.go
generated
vendored
Normal file
70
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/env_provider_test.go
generated
vendored
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEnvProviderRetrieve(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("AWS_ACCESS_KEY_ID", "access")
|
||||||
|
os.Setenv("AWS_SECRET_ACCESS_KEY", "secret")
|
||||||
|
os.Setenv("AWS_SESSION_TOKEN", "token")
|
||||||
|
|
||||||
|
e := EnvProvider{}
|
||||||
|
creds, err := e.Retrieve()
|
||||||
|
assert.Nil(t, err, "Expect no error")
|
||||||
|
|
||||||
|
assert.Equal(t, "access", creds.AccessKeyID, "Expect access key ID to match")
|
||||||
|
assert.Equal(t, "secret", creds.SecretAccessKey, "Expect secret access key to match")
|
||||||
|
assert.Equal(t, "token", creds.SessionToken, "Expect session token to match")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnvProviderIsExpired(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("AWS_ACCESS_KEY_ID", "access")
|
||||||
|
os.Setenv("AWS_SECRET_ACCESS_KEY", "secret")
|
||||||
|
os.Setenv("AWS_SESSION_TOKEN", "token")
|
||||||
|
|
||||||
|
e := EnvProvider{}
|
||||||
|
|
||||||
|
assert.True(t, e.IsExpired(), "Expect creds to be expired before retrieve.")
|
||||||
|
|
||||||
|
_, err := e.Retrieve()
|
||||||
|
assert.Nil(t, err, "Expect no error")
|
||||||
|
|
||||||
|
assert.False(t, e.IsExpired(), "Expect creds to not be expired after retrieve.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnvProviderNoAccessKeyID(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("AWS_SECRET_ACCESS_KEY", "secret")
|
||||||
|
|
||||||
|
e := EnvProvider{}
|
||||||
|
creds, err := e.Retrieve()
|
||||||
|
assert.Equal(t, ErrAccessKeyIDNotFound, err, "ErrAccessKeyIDNotFound expected, but was %#v error: %#v", creds, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnvProviderNoSecretAccessKey(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("AWS_ACCESS_KEY_ID", "access")
|
||||||
|
|
||||||
|
e := EnvProvider{}
|
||||||
|
creds, err := e.Retrieve()
|
||||||
|
assert.Equal(t, ErrSecretAccessKeyNotFound, err, "ErrSecretAccessKeyNotFound expected, but was %#v error: %#v", creds, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnvProviderAlternateNames(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("AWS_ACCESS_KEY", "access")
|
||||||
|
os.Setenv("AWS_SECRET_KEY", "secret")
|
||||||
|
|
||||||
|
e := EnvProvider{}
|
||||||
|
creds, err := e.Retrieve()
|
||||||
|
assert.Nil(t, err, "Expect no error")
|
||||||
|
|
||||||
|
assert.Equal(t, "access", creds.AccessKeyID, "Expected access key ID")
|
||||||
|
assert.Equal(t, "secret", creds.SecretAccessKey, "Expected secret access key")
|
||||||
|
assert.Empty(t, creds.SessionToken, "Expected no token")
|
||||||
|
}
|
8
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/example.ini
generated
vendored
Normal file
8
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/example.ini
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[default]
|
||||||
|
aws_access_key_id = accessKey
|
||||||
|
aws_secret_access_key = secret
|
||||||
|
aws_session_token = token
|
||||||
|
|
||||||
|
[no_token]
|
||||||
|
aws_access_key_id = accessKey
|
||||||
|
aws_secret_access_key = secret
|
135
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/shared_credentials_provider.go
generated
vendored
Normal file
135
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/shared_credentials_provider.go
generated
vendored
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/vaughan0/go-ini"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrSharedCredentialsHomeNotFound is emitted when the user directory cannot be found.
|
||||||
|
//
|
||||||
|
// @readonly
|
||||||
|
ErrSharedCredentialsHomeNotFound = awserr.New("UserHomeNotFound", "user home directory not found.", nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
// A SharedCredentialsProvider retrieves credentials from the current user's home
|
||||||
|
// directory, and keeps track if those credentials are expired.
|
||||||
|
//
|
||||||
|
// Profile ini file example: $HOME/.aws/credentials
|
||||||
|
type SharedCredentialsProvider struct {
|
||||||
|
// Path to the shared credentials file. If empty will default to current user's
|
||||||
|
// home directory.
|
||||||
|
Filename string
|
||||||
|
|
||||||
|
// AWS Profile to extract credentials from the shared credentials file. If empty
|
||||||
|
// will default to environment variable "AWS_PROFILE" or "default" if
|
||||||
|
// environment variable is also not set.
|
||||||
|
Profile string
|
||||||
|
|
||||||
|
// retrieved states if the credentials have been successfully retrieved.
|
||||||
|
retrieved bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSharedCredentials returns a pointer to a new Credentials object
|
||||||
|
// wrapping the Profile file provider.
|
||||||
|
func NewSharedCredentials(filename, profile string) *Credentials {
|
||||||
|
return NewCredentials(&SharedCredentialsProvider{
|
||||||
|
Filename: filename,
|
||||||
|
Profile: profile,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve reads and extracts the shared credentials from the current
|
||||||
|
// users home directory.
|
||||||
|
func (p *SharedCredentialsProvider) Retrieve() (Value, error) {
|
||||||
|
p.retrieved = false
|
||||||
|
|
||||||
|
filename, err := p.filename()
|
||||||
|
if err != nil {
|
||||||
|
return Value{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
creds, err := loadProfile(filename, p.profile())
|
||||||
|
if err != nil {
|
||||||
|
return Value{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.retrieved = true
|
||||||
|
return creds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExpired returns if the shared credentials have expired.
|
||||||
|
func (p *SharedCredentialsProvider) IsExpired() bool {
|
||||||
|
return !p.retrieved
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadProfiles loads from the file pointed to by shared credentials filename for profile.
|
||||||
|
// The credentials retrieved from the profile will be returned or error. Error will be
|
||||||
|
// returned if it fails to read from the file, or the data is invalid.
|
||||||
|
func loadProfile(filename, profile string) (Value, error) {
|
||||||
|
config, err := ini.LoadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return Value{}, awserr.New("SharedCredsLoad", "failed to load shared credentials file", err)
|
||||||
|
}
|
||||||
|
iniProfile := config.Section(profile)
|
||||||
|
|
||||||
|
id, ok := iniProfile["aws_access_key_id"]
|
||||||
|
if !ok {
|
||||||
|
return Value{}, awserr.New("SharedCredsAccessKey",
|
||||||
|
fmt.Sprintf("shared credentials %s in %s did not contain aws_access_key_id", profile, filename),
|
||||||
|
nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
secret, ok := iniProfile["aws_secret_access_key"]
|
||||||
|
if !ok {
|
||||||
|
return Value{}, awserr.New("SharedCredsSecret",
|
||||||
|
fmt.Sprintf("shared credentials %s in %s did not contain aws_secret_access_key", profile, filename),
|
||||||
|
nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
token := iniProfile["aws_session_token"]
|
||||||
|
|
||||||
|
return Value{
|
||||||
|
AccessKeyID: id,
|
||||||
|
SecretAccessKey: secret,
|
||||||
|
SessionToken: token,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// filename returns the filename to use to read AWS shared credentials.
|
||||||
|
//
|
||||||
|
// Will return an error if the user's home directory path cannot be found.
|
||||||
|
func (p *SharedCredentialsProvider) filename() (string, error) {
|
||||||
|
if p.Filename == "" {
|
||||||
|
homeDir := os.Getenv("HOME") // *nix
|
||||||
|
if homeDir == "" { // Windows
|
||||||
|
homeDir = os.Getenv("USERPROFILE")
|
||||||
|
}
|
||||||
|
if homeDir == "" {
|
||||||
|
return "", ErrSharedCredentialsHomeNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Filename = filepath.Join(homeDir, ".aws", "credentials")
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.Filename, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// profile returns the AWS shared credentials profile. If empty will read
|
||||||
|
// environment variable "AWS_PROFILE". If that is not set profile will
|
||||||
|
// return "default".
|
||||||
|
func (p *SharedCredentialsProvider) profile() string {
|
||||||
|
if p.Profile == "" {
|
||||||
|
p.Profile = os.Getenv("AWS_PROFILE")
|
||||||
|
}
|
||||||
|
if p.Profile == "" {
|
||||||
|
p.Profile = "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.Profile
|
||||||
|
}
|
77
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/shared_credentials_provider_test.go
generated
vendored
Normal file
77
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/shared_credentials_provider_test.go
generated
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSharedCredentialsProvider(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
|
||||||
|
p := SharedCredentialsProvider{Filename: "example.ini", Profile: ""}
|
||||||
|
creds, err := p.Retrieve()
|
||||||
|
assert.Nil(t, err, "Expect no error")
|
||||||
|
|
||||||
|
assert.Equal(t, "accessKey", creds.AccessKeyID, "Expect access key ID to match")
|
||||||
|
assert.Equal(t, "secret", creds.SecretAccessKey, "Expect secret access key to match")
|
||||||
|
assert.Equal(t, "token", creds.SessionToken, "Expect session token to match")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSharedCredentialsProviderIsExpired(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
|
||||||
|
p := SharedCredentialsProvider{Filename: "example.ini", Profile: ""}
|
||||||
|
|
||||||
|
assert.True(t, p.IsExpired(), "Expect creds to be expired before retrieve")
|
||||||
|
|
||||||
|
_, err := p.Retrieve()
|
||||||
|
assert.Nil(t, err, "Expect no error")
|
||||||
|
|
||||||
|
assert.False(t, p.IsExpired(), "Expect creds to not be expired after retrieve")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSharedCredentialsProviderWithAWS_PROFILE(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("AWS_PROFILE", "no_token")
|
||||||
|
|
||||||
|
p := SharedCredentialsProvider{Filename: "example.ini", Profile: ""}
|
||||||
|
creds, err := p.Retrieve()
|
||||||
|
assert.Nil(t, err, "Expect no error")
|
||||||
|
|
||||||
|
assert.Equal(t, "accessKey", creds.AccessKeyID, "Expect access key ID to match")
|
||||||
|
assert.Equal(t, "secret", creds.SecretAccessKey, "Expect secret access key to match")
|
||||||
|
assert.Empty(t, creds.SessionToken, "Expect no token")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSharedCredentialsProviderWithoutTokenFromProfile(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
|
||||||
|
p := SharedCredentialsProvider{Filename: "example.ini", Profile: "no_token"}
|
||||||
|
creds, err := p.Retrieve()
|
||||||
|
assert.Nil(t, err, "Expect no error")
|
||||||
|
|
||||||
|
assert.Equal(t, "accessKey", creds.AccessKeyID, "Expect access key ID to match")
|
||||||
|
assert.Equal(t, "secret", creds.SecretAccessKey, "Expect secret access key to match")
|
||||||
|
assert.Empty(t, creds.SessionToken, "Expect no token")
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSharedCredentialsProvider(b *testing.B) {
|
||||||
|
os.Clearenv()
|
||||||
|
|
||||||
|
p := SharedCredentialsProvider{Filename: "example.ini", Profile: ""}
|
||||||
|
_, err := p.Retrieve()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
_, err := p.Retrieve()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
44
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/static_provider.go
generated
vendored
Normal file
44
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/static_provider.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrStaticCredentialsEmpty is emitted when static credentials are empty.
|
||||||
|
//
|
||||||
|
// @readonly
|
||||||
|
ErrStaticCredentialsEmpty = awserr.New("EmptyStaticCreds", "static credentials are empty", nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
// A StaticProvider is a set of credentials which are set pragmatically,
|
||||||
|
// and will never expire.
|
||||||
|
type StaticProvider struct {
|
||||||
|
Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStaticCredentials returns a pointer to a new Credentials object
|
||||||
|
// wrapping a static credentials value provider.
|
||||||
|
func NewStaticCredentials(id, secret, token string) *Credentials {
|
||||||
|
return NewCredentials(&StaticProvider{Value: Value{
|
||||||
|
AccessKeyID: id,
|
||||||
|
SecretAccessKey: secret,
|
||||||
|
SessionToken: token,
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve returns the credentials or error if the credentials are invalid.
|
||||||
|
func (s *StaticProvider) Retrieve() (Value, error) {
|
||||||
|
if s.AccessKeyID == "" || s.SecretAccessKey == "" {
|
||||||
|
return Value{}, ErrStaticCredentialsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExpired returns if the credentials are expired.
|
||||||
|
//
|
||||||
|
// For StaticProvider, the credentials never expired.
|
||||||
|
func (s *StaticProvider) IsExpired() bool {
|
||||||
|
return false
|
||||||
|
}
|
34
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/static_provider_test.go
generated
vendored
Normal file
34
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/static_provider_test.go
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStaticProviderGet(t *testing.T) {
|
||||||
|
s := StaticProvider{
|
||||||
|
Value: Value{
|
||||||
|
AccessKeyID: "AKID",
|
||||||
|
SecretAccessKey: "SECRET",
|
||||||
|
SessionToken: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
creds, err := s.Retrieve()
|
||||||
|
assert.Nil(t, err, "Expect no error")
|
||||||
|
assert.Equal(t, "AKID", creds.AccessKeyID, "Expect access key ID to match")
|
||||||
|
assert.Equal(t, "SECRET", creds.SecretAccessKey, "Expect secret access key to match")
|
||||||
|
assert.Empty(t, creds.SessionToken, "Expect no session token")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStaticProviderIsExpired(t *testing.T) {
|
||||||
|
s := StaticProvider{
|
||||||
|
Value: Value{
|
||||||
|
AccessKeyID: "AKID",
|
||||||
|
SecretAccessKey: "SECRET",
|
||||||
|
SessionToken: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.False(t, s.IsExpired(), "Expect static credentials to never expire")
|
||||||
|
}
|
120
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/stscreds/assume_role_provider.go
generated
vendored
Normal file
120
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/stscreds/assume_role_provider.go
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
// Package stscreds are credential Providers to retrieve STS AWS credentials.
|
||||||
|
//
|
||||||
|
// STS provides multiple ways to retrieve credentials which can be used when making
|
||||||
|
// future AWS service API operation calls.
|
||||||
|
package stscreds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go/service/sts"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AssumeRoler represents the minimal subset of the STS client API used by this provider.
|
||||||
|
type AssumeRoler interface {
|
||||||
|
AssumeRole(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssumeRoleProvider retrieves temporary credentials from the STS service, and
|
||||||
|
// keeps track of their expiration time. This provider must be used explicitly,
|
||||||
|
// as it is not included in the credentials chain.
|
||||||
|
//
|
||||||
|
// Example how to configure a service to use this provider:
|
||||||
|
//
|
||||||
|
// config := &aws.Config{
|
||||||
|
// Credentials: stscreds.NewCredentials(nil, "arn-of-the-role-to-assume", 10*time.Second),
|
||||||
|
// })
|
||||||
|
// // Use config for creating your AWS service.
|
||||||
|
//
|
||||||
|
// Example how to obtain customised credentials:
|
||||||
|
//
|
||||||
|
// provider := &stscreds.Provider{
|
||||||
|
// // Extend the duration to 1 hour.
|
||||||
|
// Duration: time.Hour,
|
||||||
|
// // Custom role name.
|
||||||
|
// RoleSessionName: "custom-session-name",
|
||||||
|
// }
|
||||||
|
// creds := credentials.NewCredentials(provider)
|
||||||
|
//
|
||||||
|
type AssumeRoleProvider struct {
|
||||||
|
credentials.Expiry
|
||||||
|
|
||||||
|
// Custom STS client. If not set the default STS client will be used.
|
||||||
|
Client AssumeRoler
|
||||||
|
|
||||||
|
// Role to be assumed.
|
||||||
|
RoleARN string
|
||||||
|
|
||||||
|
// Session name, if you wish to reuse the credentials elsewhere.
|
||||||
|
RoleSessionName string
|
||||||
|
|
||||||
|
// Expiry duration of the STS credentials. Defaults to 15 minutes if not set.
|
||||||
|
Duration time.Duration
|
||||||
|
|
||||||
|
// ExpiryWindow will allow the credentials to trigger refreshing prior to
|
||||||
|
// the credentials actually expiring. This is beneficial so race conditions
|
||||||
|
// with expiring credentials do not cause request to fail unexpectedly
|
||||||
|
// due to ExpiredTokenException exceptions.
|
||||||
|
//
|
||||||
|
// So a ExpiryWindow of 10s would cause calls to IsExpired() to return true
|
||||||
|
// 10 seconds before the credentials are actually expired.
|
||||||
|
//
|
||||||
|
// If ExpiryWindow is 0 or less it will be ignored.
|
||||||
|
ExpiryWindow time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCredentials returns a pointer to a new Credentials object wrapping the
|
||||||
|
// AssumeRoleProvider. The credentials will expire every 15 minutes and the
|
||||||
|
// role will be named after a nanosecond timestamp of this operation.
|
||||||
|
//
|
||||||
|
// The sts and roleARN parameters are used for building the "AssumeRole" call.
|
||||||
|
// Pass nil as sts to use the default client.
|
||||||
|
//
|
||||||
|
// Window is the expiry window that will be subtracted from the expiry returned
|
||||||
|
// by the role credential request. This is done so that the credentials will
|
||||||
|
// expire sooner than their actual lifespan.
|
||||||
|
func NewCredentials(client AssumeRoler, roleARN string, window time.Duration) *credentials.Credentials {
|
||||||
|
return credentials.NewCredentials(&AssumeRoleProvider{
|
||||||
|
Client: client,
|
||||||
|
RoleARN: roleARN,
|
||||||
|
ExpiryWindow: window,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve generates a new set of temporary credentials using STS.
|
||||||
|
func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) {
|
||||||
|
|
||||||
|
// Apply defaults where parameters are not set.
|
||||||
|
if p.Client == nil {
|
||||||
|
p.Client = sts.New(nil)
|
||||||
|
}
|
||||||
|
if p.RoleSessionName == "" {
|
||||||
|
// Try to work out a role name that will hopefully end up unique.
|
||||||
|
p.RoleSessionName = fmt.Sprintf("%d", time.Now().UTC().UnixNano())
|
||||||
|
}
|
||||||
|
if p.Duration == 0 {
|
||||||
|
// Expire as often as AWS permits.
|
||||||
|
p.Duration = 15 * time.Minute
|
||||||
|
}
|
||||||
|
|
||||||
|
roleOutput, err := p.Client.AssumeRole(&sts.AssumeRoleInput{
|
||||||
|
DurationSeconds: aws.Int64(int64(p.Duration / time.Second)),
|
||||||
|
RoleARN: aws.String(p.RoleARN),
|
||||||
|
RoleSessionName: aws.String(p.RoleSessionName),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return credentials.Value{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We will proactively generate new credentials before they expire.
|
||||||
|
p.SetExpiration(*roleOutput.Credentials.Expiration, p.ExpiryWindow)
|
||||||
|
|
||||||
|
return credentials.Value{
|
||||||
|
AccessKeyID: *roleOutput.Credentials.AccessKeyID,
|
||||||
|
SecretAccessKey: *roleOutput.Credentials.SecretAccessKey,
|
||||||
|
SessionToken: *roleOutput.Credentials.SessionToken,
|
||||||
|
}, nil
|
||||||
|
}
|
58
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/stscreds/assume_role_provider_test.go
generated
vendored
Normal file
58
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/stscreds/assume_role_provider_test.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package stscreds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/sts"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stubSTS struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stubSTS) AssumeRole(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) {
|
||||||
|
expiry := time.Now().Add(60 * time.Minute)
|
||||||
|
return &sts.AssumeRoleOutput{
|
||||||
|
Credentials: &sts.Credentials{
|
||||||
|
// Just reflect the role arn to the provider.
|
||||||
|
AccessKeyID: input.RoleARN,
|
||||||
|
SecretAccessKey: aws.String("assumedSecretAccessKey"),
|
||||||
|
SessionToken: aws.String("assumedSessionToken"),
|
||||||
|
Expiration: &expiry,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAssumeRoleProvider(t *testing.T) {
|
||||||
|
stub := &stubSTS{}
|
||||||
|
p := &AssumeRoleProvider{
|
||||||
|
Client: stub,
|
||||||
|
RoleARN: "roleARN",
|
||||||
|
}
|
||||||
|
|
||||||
|
creds, err := p.Retrieve()
|
||||||
|
assert.Nil(t, err, "Expect no error")
|
||||||
|
|
||||||
|
assert.Equal(t, "roleARN", creds.AccessKeyID, "Expect access key ID to be reflected role ARN")
|
||||||
|
assert.Equal(t, "assumedSecretAccessKey", creds.SecretAccessKey, "Expect secret access key to match")
|
||||||
|
assert.Equal(t, "assumedSessionToken", creds.SessionToken, "Expect session token to match")
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkAssumeRoleProvider(b *testing.B) {
|
||||||
|
stub := &stubSTS{}
|
||||||
|
p := &AssumeRoleProvider{
|
||||||
|
Client: stub,
|
||||||
|
RoleARN: "roleARN",
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
_, err := p.Retrieve()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
157
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/handler_functions.go
generated
vendored
Normal file
157
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/handler_functions.go
generated
vendored
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sleepDelay = func(delay time.Duration) {
|
||||||
|
time.Sleep(delay)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface for matching types which also have a Len method.
|
||||||
|
type lener interface {
|
||||||
|
Len() int
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildContentLength builds the content length of a request based on the body,
|
||||||
|
// or will use the HTTPRequest.Header's "Content-Length" if defined. If unable
|
||||||
|
// to determine request body length and no "Content-Length" was specified it will panic.
|
||||||
|
func BuildContentLength(r *Request) {
|
||||||
|
if slength := r.HTTPRequest.Header.Get("Content-Length"); slength != "" {
|
||||||
|
length, _ := strconv.ParseInt(slength, 10, 64)
|
||||||
|
r.HTTPRequest.ContentLength = length
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var length int64
|
||||||
|
switch body := r.Body.(type) {
|
||||||
|
case nil:
|
||||||
|
length = 0
|
||||||
|
case lener:
|
||||||
|
length = int64(body.Len())
|
||||||
|
case io.Seeker:
|
||||||
|
r.bodyStart, _ = body.Seek(0, 1)
|
||||||
|
end, _ := body.Seek(0, 2)
|
||||||
|
body.Seek(r.bodyStart, 0) // make sure to seek back to original location
|
||||||
|
length = end - r.bodyStart
|
||||||
|
default:
|
||||||
|
panic("Cannot get length of body, must provide `ContentLength`")
|
||||||
|
}
|
||||||
|
|
||||||
|
r.HTTPRequest.ContentLength = length
|
||||||
|
r.HTTPRequest.Header.Set("Content-Length", fmt.Sprintf("%d", length))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserAgentHandler is a request handler for injecting User agent into requests.
|
||||||
|
func UserAgentHandler(r *Request) {
|
||||||
|
r.HTTPRequest.Header.Set("User-Agent", SDKName+"/"+SDKVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
var reStatusCode = regexp.MustCompile(`^(\d+)`)
|
||||||
|
|
||||||
|
// SendHandler is a request handler to send service request using HTTP client.
|
||||||
|
func SendHandler(r *Request) {
|
||||||
|
var err error
|
||||||
|
r.HTTPResponse, err = r.Service.Config.HTTPClient.Do(r.HTTPRequest)
|
||||||
|
if err != nil {
|
||||||
|
// Capture the case where url.Error is returned for error processing
|
||||||
|
// response. e.g. 301 without location header comes back as string
|
||||||
|
// error and r.HTTPResponse is nil. Other url redirect errors will
|
||||||
|
// comeback in a similar method.
|
||||||
|
if e, ok := err.(*url.Error); ok {
|
||||||
|
if s := reStatusCode.FindStringSubmatch(e.Error()); s != nil {
|
||||||
|
code, _ := strconv.ParseInt(s[1], 10, 64)
|
||||||
|
r.HTTPResponse = &http.Response{
|
||||||
|
StatusCode: int(code),
|
||||||
|
Status: http.StatusText(int(code)),
|
||||||
|
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.HTTPRequest == nil {
|
||||||
|
// Add a dummy request response object to ensure the HTTPResponse
|
||||||
|
// value is consistent.
|
||||||
|
r.HTTPResponse = &http.Response{
|
||||||
|
StatusCode: int(0),
|
||||||
|
Status: http.StatusText(int(0)),
|
||||||
|
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Catch all other request errors.
|
||||||
|
r.Error = awserr.New("RequestError", "send request failed", err)
|
||||||
|
r.Retryable = Bool(true) // network errors are retryable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateResponseHandler is a request handler to validate service response.
|
||||||
|
func ValidateResponseHandler(r *Request) {
|
||||||
|
if r.HTTPResponse.StatusCode == 0 || r.HTTPResponse.StatusCode >= 300 {
|
||||||
|
// this may be replaced by an UnmarshalError handler
|
||||||
|
r.Error = awserr.New("UnknownError", "unknown error", nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterRetryHandler performs final checks to determine if the request should
|
||||||
|
// be retried and how long to delay.
|
||||||
|
func AfterRetryHandler(r *Request) {
|
||||||
|
// If one of the other handlers already set the retry state
|
||||||
|
// we don't want to override it based on the service's state
|
||||||
|
if r.Retryable == nil {
|
||||||
|
r.Retryable = Bool(r.Service.ShouldRetry(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.WillRetry() {
|
||||||
|
r.RetryDelay = r.Service.RetryRules(r)
|
||||||
|
sleepDelay(r.RetryDelay)
|
||||||
|
|
||||||
|
// when the expired token exception occurs the credentials
|
||||||
|
// need to be expired locally so that the next request to
|
||||||
|
// get credentials will trigger a credentials refresh.
|
||||||
|
if r.Error != nil {
|
||||||
|
if err, ok := r.Error.(awserr.Error); ok {
|
||||||
|
if isCodeExpiredCreds(err.Code()) {
|
||||||
|
r.Config.Credentials.Expire()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r.RetryCount++
|
||||||
|
r.Error = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrMissingRegion is an error that is returned if region configuration is
|
||||||
|
// not found.
|
||||||
|
//
|
||||||
|
// @readonly
|
||||||
|
ErrMissingRegion error = awserr.New("MissingRegion", "could not find region configuration", nil)
|
||||||
|
|
||||||
|
// ErrMissingEndpoint is an error that is returned if an endpoint cannot be
|
||||||
|
// resolved for a service.
|
||||||
|
//
|
||||||
|
// @readonly
|
||||||
|
ErrMissingEndpoint error = awserr.New("MissingEndpoint", "'Endpoint' configuration is required for this service", nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValidateEndpointHandler is a request handler to validate a request had the
|
||||||
|
// appropriate Region and Endpoint set. Will set r.Error if the endpoint or
|
||||||
|
// region is not valid.
|
||||||
|
func ValidateEndpointHandler(r *Request) {
|
||||||
|
if r.Service.SigningRegion == "" && StringValue(r.Service.Config.Region) == "" {
|
||||||
|
r.Error = ErrMissingRegion
|
||||||
|
} else if r.Service.Endpoint == "" {
|
||||||
|
r.Error = ErrMissingEndpoint
|
||||||
|
}
|
||||||
|
}
|
81
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/handler_functions_test.go
generated
vendored
Normal file
81
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/handler_functions_test.go
generated
vendored
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValidateEndpointHandler(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
svc := NewService(NewConfig().WithRegion("us-west-2"))
|
||||||
|
svc.Handlers.Clear()
|
||||||
|
svc.Handlers.Validate.PushBack(ValidateEndpointHandler)
|
||||||
|
|
||||||
|
req := NewRequest(svc, &Operation{Name: "Operation"}, nil, nil)
|
||||||
|
err := req.Build()
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateEndpointHandlerErrorRegion(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
svc := NewService(nil)
|
||||||
|
svc.Handlers.Clear()
|
||||||
|
svc.Handlers.Validate.PushBack(ValidateEndpointHandler)
|
||||||
|
|
||||||
|
req := NewRequest(svc, &Operation{Name: "Operation"}, nil, nil)
|
||||||
|
err := req.Build()
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, ErrMissingRegion, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockCredsProvider struct {
|
||||||
|
expired bool
|
||||||
|
retrieveCalled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockCredsProvider) Retrieve() (credentials.Value, error) {
|
||||||
|
m.retrieveCalled = true
|
||||||
|
return credentials.Value{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockCredsProvider) IsExpired() bool {
|
||||||
|
return m.expired
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAfterRetryRefreshCreds(t *testing.T) {
|
||||||
|
os.Clearenv()
|
||||||
|
credProvider := &mockCredsProvider{}
|
||||||
|
svc := NewService(&Config{Credentials: credentials.NewCredentials(credProvider), MaxRetries: Int(1)})
|
||||||
|
|
||||||
|
svc.Handlers.Clear()
|
||||||
|
svc.Handlers.ValidateResponse.PushBack(func(r *Request) {
|
||||||
|
r.Error = awserr.New("UnknownError", "", nil)
|
||||||
|
r.HTTPResponse = &http.Response{StatusCode: 400}
|
||||||
|
})
|
||||||
|
svc.Handlers.UnmarshalError.PushBack(func(r *Request) {
|
||||||
|
r.Error = awserr.New("ExpiredTokenException", "", nil)
|
||||||
|
})
|
||||||
|
svc.Handlers.AfterRetry.PushBack(func(r *Request) {
|
||||||
|
AfterRetryHandler(r)
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.True(t, svc.Config.Credentials.IsExpired(), "Expect to start out expired")
|
||||||
|
assert.False(t, credProvider.retrieveCalled)
|
||||||
|
|
||||||
|
req := NewRequest(svc, &Operation{Name: "Operation"}, nil, nil)
|
||||||
|
req.Send()
|
||||||
|
|
||||||
|
assert.True(t, svc.Config.Credentials.IsExpired())
|
||||||
|
assert.False(t, credProvider.retrieveCalled)
|
||||||
|
|
||||||
|
_, err := svc.Config.Credentials.Get()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, credProvider.retrieveCalled)
|
||||||
|
}
|
85
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/handlers.go
generated
vendored
Normal file
85
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/handlers.go
generated
vendored
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
// A Handlers provides a collection of request handlers for various
|
||||||
|
// stages of handling requests.
|
||||||
|
type Handlers struct {
|
||||||
|
Validate HandlerList
|
||||||
|
Build HandlerList
|
||||||
|
Sign HandlerList
|
||||||
|
Send HandlerList
|
||||||
|
ValidateResponse HandlerList
|
||||||
|
Unmarshal HandlerList
|
||||||
|
UnmarshalMeta HandlerList
|
||||||
|
UnmarshalError HandlerList
|
||||||
|
Retry HandlerList
|
||||||
|
AfterRetry HandlerList
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy returns of this handler's lists.
|
||||||
|
func (h *Handlers) copy() Handlers {
|
||||||
|
return Handlers{
|
||||||
|
Validate: h.Validate.copy(),
|
||||||
|
Build: h.Build.copy(),
|
||||||
|
Sign: h.Sign.copy(),
|
||||||
|
Send: h.Send.copy(),
|
||||||
|
ValidateResponse: h.ValidateResponse.copy(),
|
||||||
|
Unmarshal: h.Unmarshal.copy(),
|
||||||
|
UnmarshalError: h.UnmarshalError.copy(),
|
||||||
|
UnmarshalMeta: h.UnmarshalMeta.copy(),
|
||||||
|
Retry: h.Retry.copy(),
|
||||||
|
AfterRetry: h.AfterRetry.copy(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear removes callback functions for all handlers
|
||||||
|
func (h *Handlers) Clear() {
|
||||||
|
h.Validate.Clear()
|
||||||
|
h.Build.Clear()
|
||||||
|
h.Send.Clear()
|
||||||
|
h.Sign.Clear()
|
||||||
|
h.Unmarshal.Clear()
|
||||||
|
h.UnmarshalMeta.Clear()
|
||||||
|
h.UnmarshalError.Clear()
|
||||||
|
h.ValidateResponse.Clear()
|
||||||
|
h.Retry.Clear()
|
||||||
|
h.AfterRetry.Clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
// A HandlerList manages zero or more handlers in a list.
|
||||||
|
type HandlerList struct {
|
||||||
|
list []func(*Request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy creates a copy of the handler list.
|
||||||
|
func (l *HandlerList) copy() HandlerList {
|
||||||
|
var n HandlerList
|
||||||
|
n.list = append([]func(*Request){}, l.list...)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear clears the handler list.
|
||||||
|
func (l *HandlerList) Clear() {
|
||||||
|
l.list = []func(*Request){}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of handlers in the list.
|
||||||
|
func (l *HandlerList) Len() int {
|
||||||
|
return len(l.list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushBack pushes handlers f to the back of the handler list.
|
||||||
|
func (l *HandlerList) PushBack(f ...func(*Request)) {
|
||||||
|
l.list = append(l.list, f...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushFront pushes handlers f to the front of the handler list.
|
||||||
|
func (l *HandlerList) PushFront(f ...func(*Request)) {
|
||||||
|
l.list = append(f, l.list...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run executes all handlers in the list with a given request object.
|
||||||
|
func (l *HandlerList) Run(r *Request) {
|
||||||
|
for _, f := range l.list {
|
||||||
|
f(r)
|
||||||
|
}
|
||||||
|
}
|
31
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/handlers_test.go
generated
vendored
Normal file
31
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/handlers_test.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHandlerList(t *testing.T) {
|
||||||
|
s := ""
|
||||||
|
r := &Request{}
|
||||||
|
l := HandlerList{}
|
||||||
|
l.PushBack(func(r *Request) {
|
||||||
|
s += "a"
|
||||||
|
r.Data = s
|
||||||
|
})
|
||||||
|
l.Run(r)
|
||||||
|
assert.Equal(t, "a", s)
|
||||||
|
assert.Equal(t, "a", r.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultipleHandlers(t *testing.T) {
|
||||||
|
r := &Request{}
|
||||||
|
l := HandlerList{}
|
||||||
|
l.PushBack(func(r *Request) { r.Data = nil })
|
||||||
|
l.PushFront(func(r *Request) { r.Data = Bool(true) })
|
||||||
|
l.Run(r)
|
||||||
|
if r.Data != nil {
|
||||||
|
t.Error("Expected handler to execute")
|
||||||
|
}
|
||||||
|
}
|
89
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/logger.go
generated
vendored
Normal file
89
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/logger.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A LogLevelType defines the level logging should be performed at. Used to instruct
|
||||||
|
// the SDK which statements should be logged.
|
||||||
|
type LogLevelType uint
|
||||||
|
|
||||||
|
// LogLevel returns the pointer to a LogLevel. Should be used to workaround
|
||||||
|
// not being able to take the address of a non-composite literal.
|
||||||
|
func LogLevel(l LogLevelType) *LogLevelType {
|
||||||
|
return &l
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the LogLevel value or the default value LogOff if the LogLevel
|
||||||
|
// is nil. Safe to use on nil value LogLevelTypes.
|
||||||
|
func (l *LogLevelType) Value() LogLevelType {
|
||||||
|
if l != nil {
|
||||||
|
return *l
|
||||||
|
}
|
||||||
|
return LogOff
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches returns true if the v LogLevel is enabled by this LogLevel. Should be
|
||||||
|
// used with logging sub levels. Is safe to use on nil value LogLevelTypes. If
|
||||||
|
// LogLevel is nill, will default to LogOff comparison.
|
||||||
|
func (l *LogLevelType) Matches(v LogLevelType) bool {
|
||||||
|
c := l.Value()
|
||||||
|
return c&v == v
|
||||||
|
}
|
||||||
|
|
||||||
|
// AtLeast returns true if this LogLevel is at least high enough to satisfies v.
|
||||||
|
// Is safe to use on nil value LogLevelTypes. If LogLevel is nill, will default
|
||||||
|
// to LogOff comparison.
|
||||||
|
func (l *LogLevelType) AtLeast(v LogLevelType) bool {
|
||||||
|
c := l.Value()
|
||||||
|
return c >= v
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// LogOff states that no logging should be performed by the SDK. This is the
|
||||||
|
// default state of the SDK, and should be use to disable all logging.
|
||||||
|
LogOff LogLevelType = iota * 0x1000
|
||||||
|
|
||||||
|
// LogDebug state that debug output should be logged by the SDK. This should
|
||||||
|
// be used to inspect request made and responses received.
|
||||||
|
LogDebug
|
||||||
|
)
|
||||||
|
|
||||||
|
// Debug Logging Sub Levels
|
||||||
|
const (
|
||||||
|
// LogDebugWithSigning states that the SDK should log request signing and
|
||||||
|
// presigning events. This should be used to log the signing details of
|
||||||
|
// requests for debugging. Will also enable LogDebug.
|
||||||
|
LogDebugWithSigning LogLevelType = LogDebug | (1 << iota)
|
||||||
|
|
||||||
|
// LogDebugWithHTTPBody states the SDK should log HTTP request and response
|
||||||
|
// HTTP bodys in addition to the headers and path. This should be used to
|
||||||
|
// see the body content of requests and responses made while using the SDK
|
||||||
|
// Will also enable LogDebug.
|
||||||
|
LogDebugWithHTTPBody
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Logger is a minimalistic interface for the SDK to log messages to. Should
|
||||||
|
// be used to provide custom logging writers for the SDK to use.
|
||||||
|
type Logger interface {
|
||||||
|
Log(...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultLogger returns a Logger which will write log messages to stdout, and
|
||||||
|
// use same formatting runes as the stdlib log.Logger
|
||||||
|
func NewDefaultLogger() Logger {
|
||||||
|
return &defaultLogger{
|
||||||
|
logger: log.New(os.Stdout, "", log.LstdFlags),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A defaultLogger provides a minimalistic logger satisfying the Logger interface.
|
||||||
|
type defaultLogger struct {
|
||||||
|
logger *log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log logs the parameters to the stdlib logger. See log.Println.
|
||||||
|
func (l defaultLogger) Log(args ...interface{}) {
|
||||||
|
l.logger.Println(args...)
|
||||||
|
}
|
89
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/param_validator.go
generated
vendored
Normal file
89
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/param_validator.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValidateParameters is a request handler to validate the input parameters.
|
||||||
|
// Validating parameters only has meaning if done prior to the request being sent.
|
||||||
|
func ValidateParameters(r *Request) {
|
||||||
|
if r.ParamsFilled() {
|
||||||
|
v := validator{errors: []string{}}
|
||||||
|
v.validateAny(reflect.ValueOf(r.Params), "")
|
||||||
|
|
||||||
|
if count := len(v.errors); count > 0 {
|
||||||
|
format := "%d validation errors:\n- %s"
|
||||||
|
msg := fmt.Sprintf(format, count, strings.Join(v.errors, "\n- "))
|
||||||
|
r.Error = awserr.New("InvalidParameter", msg, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A validator validates values. Collects validations errors which occurs.
|
||||||
|
type validator struct {
|
||||||
|
errors []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateAny will validate any struct, slice or map type. All validations
|
||||||
|
// are also performed recursively for nested types.
|
||||||
|
func (v *validator) validateAny(value reflect.Value, path string) {
|
||||||
|
value = reflect.Indirect(value)
|
||||||
|
if !value.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch value.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
v.validateStruct(value, path)
|
||||||
|
case reflect.Slice:
|
||||||
|
for i := 0; i < value.Len(); i++ {
|
||||||
|
v.validateAny(value.Index(i), path+fmt.Sprintf("[%d]", i))
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
for _, n := range value.MapKeys() {
|
||||||
|
v.validateAny(value.MapIndex(n), path+fmt.Sprintf("[%q]", n.String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateStruct will validate the struct value's fields. If the structure has
|
||||||
|
// nested types those types will be validated also.
|
||||||
|
func (v *validator) validateStruct(value reflect.Value, path string) {
|
||||||
|
prefix := "."
|
||||||
|
if path == "" {
|
||||||
|
prefix = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < value.Type().NumField(); i++ {
|
||||||
|
f := value.Type().Field(i)
|
||||||
|
if strings.ToLower(f.Name[0:1]) == f.Name[0:1] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fvalue := value.FieldByName(f.Name)
|
||||||
|
|
||||||
|
notset := false
|
||||||
|
if f.Tag.Get("required") != "" {
|
||||||
|
switch fvalue.Kind() {
|
||||||
|
case reflect.Ptr, reflect.Slice, reflect.Map:
|
||||||
|
if fvalue.IsNil() {
|
||||||
|
notset = true
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if !fvalue.IsValid() {
|
||||||
|
notset = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if notset {
|
||||||
|
msg := "missing required parameter: " + path + prefix + f.Name
|
||||||
|
v.errors = append(v.errors, msg)
|
||||||
|
} else {
|
||||||
|
v.validateAny(fvalue, path+prefix+f.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
84
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/param_validator_test.go
generated
vendored
Normal file
84
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/param_validator_test.go
generated
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
package aws_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var service = func() *aws.Service {
|
||||||
|
s := &aws.Service{
|
||||||
|
Config: &aws.Config{},
|
||||||
|
ServiceName: "mock-service",
|
||||||
|
APIVersion: "2015-01-01",
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}()
|
||||||
|
|
||||||
|
type StructShape struct {
|
||||||
|
RequiredList []*ConditionalStructShape `required:"true"`
|
||||||
|
RequiredMap map[string]*ConditionalStructShape `required:"true"`
|
||||||
|
RequiredBool *bool `required:"true"`
|
||||||
|
OptionalStruct *ConditionalStructShape
|
||||||
|
|
||||||
|
hiddenParameter *string
|
||||||
|
|
||||||
|
metadataStructureShape
|
||||||
|
}
|
||||||
|
|
||||||
|
type metadataStructureShape struct {
|
||||||
|
SDKShapeTraits bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConditionalStructShape struct {
|
||||||
|
Name *string `required:"true"`
|
||||||
|
SDKShapeTraits bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoErrors(t *testing.T) {
|
||||||
|
input := &StructShape{
|
||||||
|
RequiredList: []*ConditionalStructShape{},
|
||||||
|
RequiredMap: map[string]*ConditionalStructShape{
|
||||||
|
"key1": {Name: aws.String("Name")},
|
||||||
|
"key2": {Name: aws.String("Name")},
|
||||||
|
},
|
||||||
|
RequiredBool: aws.Bool(true),
|
||||||
|
OptionalStruct: &ConditionalStructShape{Name: aws.String("Name")},
|
||||||
|
}
|
||||||
|
|
||||||
|
req := aws.NewRequest(service, &aws.Operation{}, input, nil)
|
||||||
|
aws.ValidateParameters(req)
|
||||||
|
assert.NoError(t, req.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMissingRequiredParameters(t *testing.T) {
|
||||||
|
input := &StructShape{}
|
||||||
|
req := aws.NewRequest(service, &aws.Operation{}, input, nil)
|
||||||
|
aws.ValidateParameters(req)
|
||||||
|
|
||||||
|
assert.Error(t, req.Error)
|
||||||
|
assert.Equal(t, "InvalidParameter", req.Error.(awserr.Error).Code())
|
||||||
|
assert.Equal(t, "3 validation errors:\n- missing required parameter: RequiredList\n- missing required parameter: RequiredMap\n- missing required parameter: RequiredBool", req.Error.(awserr.Error).Message())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNestedMissingRequiredParameters(t *testing.T) {
|
||||||
|
input := &StructShape{
|
||||||
|
RequiredList: []*ConditionalStructShape{{}},
|
||||||
|
RequiredMap: map[string]*ConditionalStructShape{
|
||||||
|
"key1": {Name: aws.String("Name")},
|
||||||
|
"key2": {},
|
||||||
|
},
|
||||||
|
RequiredBool: aws.Bool(true),
|
||||||
|
OptionalStruct: &ConditionalStructShape{},
|
||||||
|
}
|
||||||
|
|
||||||
|
req := aws.NewRequest(service, &aws.Operation{}, input, nil)
|
||||||
|
aws.ValidateParameters(req)
|
||||||
|
|
||||||
|
assert.Error(t, req.Error)
|
||||||
|
assert.Equal(t, "InvalidParameter", req.Error.(awserr.Error).Code())
|
||||||
|
assert.Equal(t, "3 validation errors:\n- missing required parameter: RequiredList[0].Name\n- missing required parameter: RequiredMap[\"key2\"].Name\n- missing required parameter: OptionalStruct.Name", req.Error.(awserr.Error).Message())
|
||||||
|
|
||||||
|
}
|
312
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request.go
generated
vendored
Normal file
312
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request.go
generated
vendored
Normal file
|
@ -0,0 +1,312 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Request is the service request to be made.
|
||||||
|
type Request struct {
|
||||||
|
*Service
|
||||||
|
Handlers Handlers
|
||||||
|
Time time.Time
|
||||||
|
ExpireTime time.Duration
|
||||||
|
Operation *Operation
|
||||||
|
HTTPRequest *http.Request
|
||||||
|
HTTPResponse *http.Response
|
||||||
|
Body io.ReadSeeker
|
||||||
|
bodyStart int64 // offset from beginning of Body that the request body starts
|
||||||
|
Params interface{}
|
||||||
|
Error error
|
||||||
|
Data interface{}
|
||||||
|
RequestID string
|
||||||
|
RetryCount uint
|
||||||
|
Retryable *bool
|
||||||
|
RetryDelay time.Duration
|
||||||
|
|
||||||
|
built bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Operation is the service API operation to be made.
|
||||||
|
type Operation struct {
|
||||||
|
Name string
|
||||||
|
HTTPMethod string
|
||||||
|
HTTPPath string
|
||||||
|
*Paginator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paginator keeps track of pagination configuration for an API operation.
|
||||||
|
type Paginator struct {
|
||||||
|
InputTokens []string
|
||||||
|
OutputTokens []string
|
||||||
|
LimitToken string
|
||||||
|
TruncationToken string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRequest returns a new Request pointer for the service API
|
||||||
|
// operation and parameters.
|
||||||
|
//
|
||||||
|
// Params is any value of input parameters to be the request payload.
|
||||||
|
// Data is pointer value to an object which the request's response
|
||||||
|
// payload will be deserialized to.
|
||||||
|
func NewRequest(service *Service, operation *Operation, params interface{}, data interface{}) *Request {
|
||||||
|
method := operation.HTTPMethod
|
||||||
|
if method == "" {
|
||||||
|
method = "POST"
|
||||||
|
}
|
||||||
|
p := operation.HTTPPath
|
||||||
|
if p == "" {
|
||||||
|
p = "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
httpReq, _ := http.NewRequest(method, "", nil)
|
||||||
|
httpReq.URL, _ = url.Parse(service.Endpoint + p)
|
||||||
|
|
||||||
|
r := &Request{
|
||||||
|
Service: service,
|
||||||
|
Handlers: service.Handlers.copy(),
|
||||||
|
Time: time.Now(),
|
||||||
|
ExpireTime: 0,
|
||||||
|
Operation: operation,
|
||||||
|
HTTPRequest: httpReq,
|
||||||
|
Body: nil,
|
||||||
|
Params: params,
|
||||||
|
Error: nil,
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
r.SetBufferBody([]byte{})
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// WillRetry returns if the request's can be retried.
|
||||||
|
func (r *Request) WillRetry() bool {
|
||||||
|
return r.Error != nil && BoolValue(r.Retryable) && r.RetryCount < r.Service.MaxRetries()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParamsFilled returns if the request's parameters have been populated
|
||||||
|
// and the parameters are valid. False is returned if no parameters are
|
||||||
|
// provided or invalid.
|
||||||
|
func (r *Request) ParamsFilled() bool {
|
||||||
|
return r.Params != nil && reflect.ValueOf(r.Params).Elem().IsValid()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataFilled returns true if the request's data for response deserialization
|
||||||
|
// target has been set and is a valid. False is returned if data is not
|
||||||
|
// set, or is invalid.
|
||||||
|
func (r *Request) DataFilled() bool {
|
||||||
|
return r.Data != nil && reflect.ValueOf(r.Data).Elem().IsValid()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBufferBody will set the request's body bytes that will be sent to
|
||||||
|
// the service API.
|
||||||
|
func (r *Request) SetBufferBody(buf []byte) {
|
||||||
|
r.SetReaderBody(bytes.NewReader(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetStringBody sets the body of the request to be backed by a string.
|
||||||
|
func (r *Request) SetStringBody(s string) {
|
||||||
|
r.SetReaderBody(strings.NewReader(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReaderBody will set the request's body reader.
|
||||||
|
func (r *Request) SetReaderBody(reader io.ReadSeeker) {
|
||||||
|
r.HTTPRequest.Body = ioutil.NopCloser(reader)
|
||||||
|
r.Body = reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// Presign returns the request's signed URL. Error will be returned
|
||||||
|
// if the signing fails.
|
||||||
|
func (r *Request) Presign(expireTime time.Duration) (string, error) {
|
||||||
|
r.ExpireTime = expireTime
|
||||||
|
r.Sign()
|
||||||
|
if r.Error != nil {
|
||||||
|
return "", r.Error
|
||||||
|
}
|
||||||
|
return r.HTTPRequest.URL.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build will build the request's object so it can be signed and sent
|
||||||
|
// to the service. Build will also validate all the request's parameters.
|
||||||
|
// Anny additional build Handlers set on this request will be run
|
||||||
|
// in the order they were set.
|
||||||
|
//
|
||||||
|
// The request will only be built once. Multiple calls to build will have
|
||||||
|
// no effect.
|
||||||
|
//
|
||||||
|
// If any Validate or Build errors occur the build will stop and the error
|
||||||
|
// which occurred will be returned.
|
||||||
|
func (r *Request) Build() error {
|
||||||
|
if !r.built {
|
||||||
|
r.Error = nil
|
||||||
|
r.Handlers.Validate.Run(r)
|
||||||
|
if r.Error != nil {
|
||||||
|
return r.Error
|
||||||
|
}
|
||||||
|
r.Handlers.Build.Run(r)
|
||||||
|
r.built = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign will sign the request retuning error if errors are encountered.
|
||||||
|
//
|
||||||
|
// Send will build the request prior to signing. All Sign Handlers will
|
||||||
|
// be executed in the order they were set.
|
||||||
|
func (r *Request) Sign() error {
|
||||||
|
r.Build()
|
||||||
|
if r.Error != nil {
|
||||||
|
return r.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Handlers.Sign.Run(r)
|
||||||
|
return r.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send will send the request returning error if errors are encountered.
|
||||||
|
//
|
||||||
|
// Send will sign the request prior to sending. All Send Handlers will
|
||||||
|
// be executed in the order they were set.
|
||||||
|
func (r *Request) Send() error {
|
||||||
|
for {
|
||||||
|
r.Sign()
|
||||||
|
if r.Error != nil {
|
||||||
|
return r.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if BoolValue(r.Retryable) {
|
||||||
|
// Re-seek the body back to the original point in for a retry so that
|
||||||
|
// send will send the body's contents again in the upcoming request.
|
||||||
|
r.Body.Seek(r.bodyStart, 0)
|
||||||
|
}
|
||||||
|
r.Retryable = nil
|
||||||
|
|
||||||
|
r.Handlers.Send.Run(r)
|
||||||
|
if r.Error != nil {
|
||||||
|
r.Handlers.Retry.Run(r)
|
||||||
|
r.Handlers.AfterRetry.Run(r)
|
||||||
|
if r.Error != nil {
|
||||||
|
return r.Error
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Handlers.UnmarshalMeta.Run(r)
|
||||||
|
r.Handlers.ValidateResponse.Run(r)
|
||||||
|
if r.Error != nil {
|
||||||
|
r.Handlers.UnmarshalError.Run(r)
|
||||||
|
r.Handlers.Retry.Run(r)
|
||||||
|
r.Handlers.AfterRetry.Run(r)
|
||||||
|
if r.Error != nil {
|
||||||
|
return r.Error
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Handlers.Unmarshal.Run(r)
|
||||||
|
if r.Error != nil {
|
||||||
|
r.Handlers.Retry.Run(r)
|
||||||
|
r.Handlers.AfterRetry.Run(r)
|
||||||
|
if r.Error != nil {
|
||||||
|
return r.Error
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasNextPage returns true if this request has more pages of data available.
|
||||||
|
func (r *Request) HasNextPage() bool {
|
||||||
|
return r.nextPageTokens() != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// nextPageTokens returns the tokens to use when asking for the next page of
|
||||||
|
// data.
|
||||||
|
func (r *Request) nextPageTokens() []interface{} {
|
||||||
|
if r.Operation.Paginator == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Operation.TruncationToken != "" {
|
||||||
|
tr := awsutil.ValuesAtAnyPath(r.Data, r.Operation.TruncationToken)
|
||||||
|
if tr == nil || len(tr) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch v := tr[0].(type) {
|
||||||
|
case bool:
|
||||||
|
if v == false {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
|
tokens := make([]interface{}, len(r.Operation.OutputTokens))
|
||||||
|
|
||||||
|
for i, outtok := range r.Operation.OutputTokens {
|
||||||
|
v := awsutil.ValuesAtAnyPath(r.Data, outtok)
|
||||||
|
if v != nil && len(v) > 0 {
|
||||||
|
found = true
|
||||||
|
tokens[i] = v[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found {
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextPage returns a new Request that can be executed to return the next
|
||||||
|
// page of result data. Call .Send() on this request to execute it.
|
||||||
|
func (r *Request) NextPage() *Request {
|
||||||
|
tokens := r.nextPageTokens()
|
||||||
|
if tokens == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
data := reflect.New(reflect.TypeOf(r.Data).Elem()).Interface()
|
||||||
|
nr := NewRequest(r.Service, r.Operation, awsutil.CopyOf(r.Params), data)
|
||||||
|
for i, intok := range nr.Operation.InputTokens {
|
||||||
|
awsutil.SetValueAtAnyPath(nr.Params, intok, tokens[i])
|
||||||
|
}
|
||||||
|
return nr
|
||||||
|
}
|
||||||
|
|
||||||
|
// EachPage iterates over each page of a paginated request object. The fn
|
||||||
|
// parameter should be a function with the following sample signature:
|
||||||
|
//
|
||||||
|
// func(page *T, lastPage bool) bool {
|
||||||
|
// return true // return false to stop iterating
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Where "T" is the structure type matching the output structure of the given
|
||||||
|
// operation. For example, a request object generated by
|
||||||
|
// DynamoDB.ListTablesRequest() would expect to see dynamodb.ListTablesOutput
|
||||||
|
// as the structure "T". The lastPage value represents whether the page is
|
||||||
|
// the last page of data or not. The return value of this function should
|
||||||
|
// return true to keep iterating or false to stop.
|
||||||
|
func (r *Request) EachPage(fn func(data interface{}, isLastPage bool) (shouldContinue bool)) error {
|
||||||
|
for page := r; page != nil; page = page.NextPage() {
|
||||||
|
page.Send()
|
||||||
|
shouldContinue := fn(page.Data, !page.HasNextPage())
|
||||||
|
if page.Error != nil || !shouldContinue {
|
||||||
|
return page.Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
305
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request_pagination_test.go
generated
vendored
Normal file
305
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request_pagination_test.go
generated
vendored
Normal file
|
@ -0,0 +1,305 @@
|
||||||
|
package aws_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/test/unit"
|
||||||
|
"github.com/aws/aws-sdk-go/service/dynamodb"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = unit.Imported
|
||||||
|
|
||||||
|
// Use DynamoDB methods for simplicity
|
||||||
|
func TestPagination(t *testing.T) {
|
||||||
|
db := dynamodb.New(nil)
|
||||||
|
tokens, pages, numPages, gotToEnd := []string{}, []string{}, 0, false
|
||||||
|
|
||||||
|
reqNum := 0
|
||||||
|
resps := []*dynamodb.ListTablesOutput{
|
||||||
|
{TableNames: []*string{aws.String("Table1"), aws.String("Table2")}, LastEvaluatedTableName: aws.String("Table2")},
|
||||||
|
{TableNames: []*string{aws.String("Table3"), aws.String("Table4")}, LastEvaluatedTableName: aws.String("Table4")},
|
||||||
|
{TableNames: []*string{aws.String("Table5")}},
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Handlers.Send.Clear() // mock sending
|
||||||
|
db.Handlers.Unmarshal.Clear()
|
||||||
|
db.Handlers.UnmarshalMeta.Clear()
|
||||||
|
db.Handlers.ValidateResponse.Clear()
|
||||||
|
db.Handlers.Build.PushBack(func(r *aws.Request) {
|
||||||
|
in := r.Params.(*dynamodb.ListTablesInput)
|
||||||
|
if in == nil {
|
||||||
|
tokens = append(tokens, "")
|
||||||
|
} else if in.ExclusiveStartTableName != nil {
|
||||||
|
tokens = append(tokens, *in.ExclusiveStartTableName)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
db.Handlers.Unmarshal.PushBack(func(r *aws.Request) {
|
||||||
|
r.Data = resps[reqNum]
|
||||||
|
reqNum++
|
||||||
|
})
|
||||||
|
|
||||||
|
params := &dynamodb.ListTablesInput{Limit: aws.Int64(2)}
|
||||||
|
err := db.ListTablesPages(params, func(p *dynamodb.ListTablesOutput, last bool) bool {
|
||||||
|
numPages++
|
||||||
|
for _, t := range p.TableNames {
|
||||||
|
pages = append(pages, *t)
|
||||||
|
}
|
||||||
|
if last {
|
||||||
|
if gotToEnd {
|
||||||
|
assert.Fail(t, "last=true happened twice")
|
||||||
|
}
|
||||||
|
gotToEnd = true
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, []string{"Table2", "Table4"}, tokens)
|
||||||
|
assert.Equal(t, []string{"Table1", "Table2", "Table3", "Table4", "Table5"}, pages)
|
||||||
|
assert.Equal(t, 3, numPages)
|
||||||
|
assert.True(t, gotToEnd)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Nil(t, params.ExclusiveStartTableName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use DynamoDB methods for simplicity
|
||||||
|
func TestPaginationEachPage(t *testing.T) {
|
||||||
|
db := dynamodb.New(nil)
|
||||||
|
tokens, pages, numPages, gotToEnd := []string{}, []string{}, 0, false
|
||||||
|
|
||||||
|
reqNum := 0
|
||||||
|
resps := []*dynamodb.ListTablesOutput{
|
||||||
|
{TableNames: []*string{aws.String("Table1"), aws.String("Table2")}, LastEvaluatedTableName: aws.String("Table2")},
|
||||||
|
{TableNames: []*string{aws.String("Table3"), aws.String("Table4")}, LastEvaluatedTableName: aws.String("Table4")},
|
||||||
|
{TableNames: []*string{aws.String("Table5")}},
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Handlers.Send.Clear() // mock sending
|
||||||
|
db.Handlers.Unmarshal.Clear()
|
||||||
|
db.Handlers.UnmarshalMeta.Clear()
|
||||||
|
db.Handlers.ValidateResponse.Clear()
|
||||||
|
db.Handlers.Build.PushBack(func(r *aws.Request) {
|
||||||
|
in := r.Params.(*dynamodb.ListTablesInput)
|
||||||
|
if in == nil {
|
||||||
|
tokens = append(tokens, "")
|
||||||
|
} else if in.ExclusiveStartTableName != nil {
|
||||||
|
tokens = append(tokens, *in.ExclusiveStartTableName)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
db.Handlers.Unmarshal.PushBack(func(r *aws.Request) {
|
||||||
|
r.Data = resps[reqNum]
|
||||||
|
reqNum++
|
||||||
|
})
|
||||||
|
|
||||||
|
params := &dynamodb.ListTablesInput{Limit: aws.Int64(2)}
|
||||||
|
req, _ := db.ListTablesRequest(params)
|
||||||
|
err := req.EachPage(func(p interface{}, last bool) bool {
|
||||||
|
numPages++
|
||||||
|
for _, t := range p.(*dynamodb.ListTablesOutput).TableNames {
|
||||||
|
pages = append(pages, *t)
|
||||||
|
}
|
||||||
|
if last {
|
||||||
|
if gotToEnd {
|
||||||
|
assert.Fail(t, "last=true happened twice")
|
||||||
|
}
|
||||||
|
gotToEnd = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, []string{"Table2", "Table4"}, tokens)
|
||||||
|
assert.Equal(t, []string{"Table1", "Table2", "Table3", "Table4", "Table5"}, pages)
|
||||||
|
assert.Equal(t, 3, numPages)
|
||||||
|
assert.True(t, gotToEnd)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use DynamoDB methods for simplicity
|
||||||
|
func TestPaginationEarlyExit(t *testing.T) {
|
||||||
|
db := dynamodb.New(nil)
|
||||||
|
numPages, gotToEnd := 0, false
|
||||||
|
|
||||||
|
reqNum := 0
|
||||||
|
resps := []*dynamodb.ListTablesOutput{
|
||||||
|
{TableNames: []*string{aws.String("Table1"), aws.String("Table2")}, LastEvaluatedTableName: aws.String("Table2")},
|
||||||
|
{TableNames: []*string{aws.String("Table3"), aws.String("Table4")}, LastEvaluatedTableName: aws.String("Table4")},
|
||||||
|
{TableNames: []*string{aws.String("Table5")}},
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Handlers.Send.Clear() // mock sending
|
||||||
|
db.Handlers.Unmarshal.Clear()
|
||||||
|
db.Handlers.UnmarshalMeta.Clear()
|
||||||
|
db.Handlers.ValidateResponse.Clear()
|
||||||
|
db.Handlers.Unmarshal.PushBack(func(r *aws.Request) {
|
||||||
|
r.Data = resps[reqNum]
|
||||||
|
reqNum++
|
||||||
|
})
|
||||||
|
|
||||||
|
params := &dynamodb.ListTablesInput{Limit: aws.Int64(2)}
|
||||||
|
err := db.ListTablesPages(params, func(p *dynamodb.ListTablesOutput, last bool) bool {
|
||||||
|
numPages++
|
||||||
|
if numPages == 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if last {
|
||||||
|
if gotToEnd {
|
||||||
|
assert.Fail(t, "last=true happened twice")
|
||||||
|
}
|
||||||
|
gotToEnd = true
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, 2, numPages)
|
||||||
|
assert.False(t, gotToEnd)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSkipPagination(t *testing.T) {
|
||||||
|
client := s3.New(nil)
|
||||||
|
client.Handlers.Send.Clear() // mock sending
|
||||||
|
client.Handlers.Unmarshal.Clear()
|
||||||
|
client.Handlers.UnmarshalMeta.Clear()
|
||||||
|
client.Handlers.ValidateResponse.Clear()
|
||||||
|
client.Handlers.Unmarshal.PushBack(func(r *aws.Request) {
|
||||||
|
r.Data = &s3.HeadBucketOutput{}
|
||||||
|
})
|
||||||
|
|
||||||
|
req, _ := client.HeadBucketRequest(&s3.HeadBucketInput{Bucket: aws.String("bucket")})
|
||||||
|
|
||||||
|
numPages, gotToEnd := 0, false
|
||||||
|
req.EachPage(func(p interface{}, last bool) bool {
|
||||||
|
numPages++
|
||||||
|
if last {
|
||||||
|
gotToEnd = true
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
assert.Equal(t, 1, numPages)
|
||||||
|
assert.True(t, gotToEnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use S3 for simplicity
|
||||||
|
func TestPaginationTruncation(t *testing.T) {
|
||||||
|
count := 0
|
||||||
|
client := s3.New(nil)
|
||||||
|
|
||||||
|
reqNum := &count
|
||||||
|
resps := []*s3.ListObjectsOutput{
|
||||||
|
{IsTruncated: aws.Bool(true), Contents: []*s3.Object{{Key: aws.String("Key1")}}},
|
||||||
|
{IsTruncated: aws.Bool(true), Contents: []*s3.Object{{Key: aws.String("Key2")}}},
|
||||||
|
{IsTruncated: aws.Bool(false), Contents: []*s3.Object{{Key: aws.String("Key3")}}},
|
||||||
|
{IsTruncated: aws.Bool(true), Contents: []*s3.Object{{Key: aws.String("Key4")}}},
|
||||||
|
}
|
||||||
|
|
||||||
|
client.Handlers.Send.Clear() // mock sending
|
||||||
|
client.Handlers.Unmarshal.Clear()
|
||||||
|
client.Handlers.UnmarshalMeta.Clear()
|
||||||
|
client.Handlers.ValidateResponse.Clear()
|
||||||
|
client.Handlers.Unmarshal.PushBack(func(r *aws.Request) {
|
||||||
|
r.Data = resps[*reqNum]
|
||||||
|
*reqNum++
|
||||||
|
})
|
||||||
|
|
||||||
|
params := &s3.ListObjectsInput{Bucket: aws.String("bucket")}
|
||||||
|
|
||||||
|
results := []string{}
|
||||||
|
err := client.ListObjectsPages(params, func(p *s3.ListObjectsOutput, last bool) bool {
|
||||||
|
results = append(results, *p.Contents[0].Key)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, []string{"Key1", "Key2", "Key3"}, results)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
// Try again without truncation token at all
|
||||||
|
count = 0
|
||||||
|
resps[1].IsTruncated = nil
|
||||||
|
resps[2].IsTruncated = aws.Bool(true)
|
||||||
|
results = []string{}
|
||||||
|
err = client.ListObjectsPages(params, func(p *s3.ListObjectsOutput, last bool) bool {
|
||||||
|
results = append(results, *p.Contents[0].Key)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, []string{"Key1", "Key2"}, results)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmarks
|
||||||
|
var benchResps = []*dynamodb.ListTablesOutput{
|
||||||
|
{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
|
||||||
|
{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
|
||||||
|
{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
|
||||||
|
{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
|
||||||
|
{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
|
||||||
|
{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
|
||||||
|
{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
|
||||||
|
{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
|
||||||
|
{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
|
||||||
|
{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
|
||||||
|
{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
|
||||||
|
{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
|
||||||
|
{TableNames: []*string{aws.String("TABLE"), aws.String("NXT")}, LastEvaluatedTableName: aws.String("NXT")},
|
||||||
|
{TableNames: []*string{aws.String("TABLE")}},
|
||||||
|
}
|
||||||
|
|
||||||
|
var benchDb = func() *dynamodb.DynamoDB {
|
||||||
|
db := dynamodb.New(nil)
|
||||||
|
db.Handlers.Send.Clear() // mock sending
|
||||||
|
db.Handlers.Unmarshal.Clear()
|
||||||
|
db.Handlers.UnmarshalMeta.Clear()
|
||||||
|
db.Handlers.ValidateResponse.Clear()
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkCodegenIterator(b *testing.B) {
|
||||||
|
reqNum := 0
|
||||||
|
db := benchDb()
|
||||||
|
db.Handlers.Unmarshal.PushBack(func(r *aws.Request) {
|
||||||
|
r.Data = benchResps[reqNum]
|
||||||
|
reqNum++
|
||||||
|
})
|
||||||
|
|
||||||
|
input := &dynamodb.ListTablesInput{Limit: aws.Int64(2)}
|
||||||
|
iter := func(fn func(*dynamodb.ListTablesOutput, bool) bool) error {
|
||||||
|
page, _ := db.ListTablesRequest(input)
|
||||||
|
for ; page != nil; page = page.NextPage() {
|
||||||
|
page.Send()
|
||||||
|
out := page.Data.(*dynamodb.ListTablesOutput)
|
||||||
|
if result := fn(out, !page.HasNextPage()); page.Error != nil || !result {
|
||||||
|
return page.Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
reqNum = 0
|
||||||
|
iter(func(p *dynamodb.ListTablesOutput, last bool) bool {
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEachPageIterator(b *testing.B) {
|
||||||
|
reqNum := 0
|
||||||
|
db := benchDb()
|
||||||
|
db.Handlers.Unmarshal.PushBack(func(r *aws.Request) {
|
||||||
|
r.Data = benchResps[reqNum]
|
||||||
|
reqNum++
|
||||||
|
})
|
||||||
|
|
||||||
|
input := &dynamodb.ListTablesInput{Limit: aws.Int64(2)}
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
reqNum = 0
|
||||||
|
req, _ := db.ListTablesRequest(input)
|
||||||
|
req.EachPage(func(p interface{}, last bool) bool {
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
225
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request_test.go
generated
vendored
Normal file
225
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request_test.go
generated
vendored
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testData struct {
|
||||||
|
Data string
|
||||||
|
}
|
||||||
|
|
||||||
|
func body(str string) io.ReadCloser {
|
||||||
|
return ioutil.NopCloser(bytes.NewReader([]byte(str)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshal(req *Request) {
|
||||||
|
defer req.HTTPResponse.Body.Close()
|
||||||
|
if req.Data != nil {
|
||||||
|
json.NewDecoder(req.HTTPResponse.Body).Decode(req.Data)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalError(req *Request) {
|
||||||
|
bodyBytes, err := ioutil.ReadAll(req.HTTPResponse.Body)
|
||||||
|
if err != nil {
|
||||||
|
req.Error = awserr.New("UnmarshaleError", req.HTTPResponse.Status, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(bodyBytes) == 0 {
|
||||||
|
req.Error = awserr.NewRequestFailure(
|
||||||
|
awserr.New("UnmarshaleError", req.HTTPResponse.Status, fmt.Errorf("empty body")),
|
||||||
|
req.HTTPResponse.StatusCode,
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var jsonErr jsonErrorResponse
|
||||||
|
if err := json.Unmarshal(bodyBytes, &jsonErr); err != nil {
|
||||||
|
req.Error = awserr.New("UnmarshaleError", "JSON unmarshal", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Error = awserr.NewRequestFailure(
|
||||||
|
awserr.New(jsonErr.Code, jsonErr.Message, nil),
|
||||||
|
req.HTTPResponse.StatusCode,
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type jsonErrorResponse struct {
|
||||||
|
Code string `json:"__type"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// test that retries occur for 5xx status codes
|
||||||
|
func TestRequestRecoverRetry5xx(t *testing.T) {
|
||||||
|
reqNum := 0
|
||||||
|
reqs := []http.Response{
|
||||||
|
{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)},
|
||||||
|
{StatusCode: 501, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)},
|
||||||
|
{StatusCode: 200, Body: body(`{"data":"valid"}`)},
|
||||||
|
}
|
||||||
|
|
||||||
|
s := NewService(NewConfig().WithMaxRetries(10))
|
||||||
|
s.Handlers.Validate.Clear()
|
||||||
|
s.Handlers.Unmarshal.PushBack(unmarshal)
|
||||||
|
s.Handlers.UnmarshalError.PushBack(unmarshalError)
|
||||||
|
s.Handlers.Send.Clear() // mock sending
|
||||||
|
s.Handlers.Send.PushBack(func(r *Request) {
|
||||||
|
r.HTTPResponse = &reqs[reqNum]
|
||||||
|
reqNum++
|
||||||
|
})
|
||||||
|
out := &testData{}
|
||||||
|
r := NewRequest(s, &Operation{Name: "Operation"}, nil, out)
|
||||||
|
err := r.Send()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 2, int(r.RetryCount))
|
||||||
|
assert.Equal(t, "valid", out.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test that retries occur for 4xx status codes with a response type that can be retried - see `shouldRetry`
|
||||||
|
func TestRequestRecoverRetry4xxRetryable(t *testing.T) {
|
||||||
|
reqNum := 0
|
||||||
|
reqs := []http.Response{
|
||||||
|
{StatusCode: 400, Body: body(`{"__type":"Throttling","message":"Rate exceeded."}`)},
|
||||||
|
{StatusCode: 429, Body: body(`{"__type":"ProvisionedThroughputExceededException","message":"Rate exceeded."}`)},
|
||||||
|
{StatusCode: 200, Body: body(`{"data":"valid"}`)},
|
||||||
|
}
|
||||||
|
|
||||||
|
s := NewService(NewConfig().WithMaxRetries(10))
|
||||||
|
s.Handlers.Validate.Clear()
|
||||||
|
s.Handlers.Unmarshal.PushBack(unmarshal)
|
||||||
|
s.Handlers.UnmarshalError.PushBack(unmarshalError)
|
||||||
|
s.Handlers.Send.Clear() // mock sending
|
||||||
|
s.Handlers.Send.PushBack(func(r *Request) {
|
||||||
|
r.HTTPResponse = &reqs[reqNum]
|
||||||
|
reqNum++
|
||||||
|
})
|
||||||
|
out := &testData{}
|
||||||
|
r := NewRequest(s, &Operation{Name: "Operation"}, nil, out)
|
||||||
|
err := r.Send()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 2, int(r.RetryCount))
|
||||||
|
assert.Equal(t, "valid", out.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test that retries don't occur for 4xx status codes with a response type that can't be retried
|
||||||
|
func TestRequest4xxUnretryable(t *testing.T) {
|
||||||
|
s := NewService(NewConfig().WithMaxRetries(10))
|
||||||
|
s.Handlers.Validate.Clear()
|
||||||
|
s.Handlers.Unmarshal.PushBack(unmarshal)
|
||||||
|
s.Handlers.UnmarshalError.PushBack(unmarshalError)
|
||||||
|
s.Handlers.Send.Clear() // mock sending
|
||||||
|
s.Handlers.Send.PushBack(func(r *Request) {
|
||||||
|
r.HTTPResponse = &http.Response{StatusCode: 401, Body: body(`{"__type":"SignatureDoesNotMatch","message":"Signature does not match."}`)}
|
||||||
|
})
|
||||||
|
out := &testData{}
|
||||||
|
r := NewRequest(s, &Operation{Name: "Operation"}, nil, out)
|
||||||
|
err := r.Send()
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
if e, ok := err.(awserr.RequestFailure); ok {
|
||||||
|
assert.Equal(t, 401, e.StatusCode())
|
||||||
|
} else {
|
||||||
|
assert.Fail(t, "Expected error to be a service failure")
|
||||||
|
}
|
||||||
|
assert.Equal(t, "SignatureDoesNotMatch", err.(awserr.Error).Code())
|
||||||
|
assert.Equal(t, "Signature does not match.", err.(awserr.Error).Message())
|
||||||
|
assert.Equal(t, 0, int(r.RetryCount))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestExhaustRetries(t *testing.T) {
|
||||||
|
delays := []time.Duration{}
|
||||||
|
sleepDelay = func(delay time.Duration) {
|
||||||
|
delays = append(delays, delay)
|
||||||
|
}
|
||||||
|
|
||||||
|
reqNum := 0
|
||||||
|
reqs := []http.Response{
|
||||||
|
{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)},
|
||||||
|
{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)},
|
||||||
|
{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)},
|
||||||
|
{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)},
|
||||||
|
}
|
||||||
|
|
||||||
|
s := NewService(NewConfig().WithMaxRetries(DefaultRetries))
|
||||||
|
s.Handlers.Validate.Clear()
|
||||||
|
s.Handlers.Unmarshal.PushBack(unmarshal)
|
||||||
|
s.Handlers.UnmarshalError.PushBack(unmarshalError)
|
||||||
|
s.Handlers.Send.Clear() // mock sending
|
||||||
|
s.Handlers.Send.PushBack(func(r *Request) {
|
||||||
|
r.HTTPResponse = &reqs[reqNum]
|
||||||
|
reqNum++
|
||||||
|
})
|
||||||
|
r := NewRequest(s, &Operation{Name: "Operation"}, nil, nil)
|
||||||
|
err := r.Send()
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
if e, ok := err.(awserr.RequestFailure); ok {
|
||||||
|
assert.Equal(t, 500, e.StatusCode())
|
||||||
|
} else {
|
||||||
|
assert.Fail(t, "Expected error to be a service failure")
|
||||||
|
}
|
||||||
|
assert.Equal(t, "UnknownError", err.(awserr.Error).Code())
|
||||||
|
assert.Equal(t, "An error occurred.", err.(awserr.Error).Message())
|
||||||
|
assert.Equal(t, 3, int(r.RetryCount))
|
||||||
|
|
||||||
|
expectDelays := []struct{ min, max time.Duration }{{30, 59}, {60, 118}, {120, 236}}
|
||||||
|
for i, v := range delays {
|
||||||
|
min := expectDelays[i].min * time.Millisecond
|
||||||
|
max := expectDelays[i].max * time.Millisecond
|
||||||
|
assert.True(t, min <= v && v <= max,
|
||||||
|
"Expect delay to be within range, i:%d, v:%s, min:%s, max:%s", i, v, min, max)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// test that the request is retried after the credentials are expired.
|
||||||
|
func TestRequestRecoverExpiredCreds(t *testing.T) {
|
||||||
|
reqNum := 0
|
||||||
|
reqs := []http.Response{
|
||||||
|
{StatusCode: 400, Body: body(`{"__type":"ExpiredTokenException","message":"expired token"}`)},
|
||||||
|
{StatusCode: 200, Body: body(`{"data":"valid"}`)},
|
||||||
|
}
|
||||||
|
|
||||||
|
s := NewService(&Config{MaxRetries: Int(10), Credentials: credentials.NewStaticCredentials("AKID", "SECRET", "")})
|
||||||
|
s.Handlers.Validate.Clear()
|
||||||
|
s.Handlers.Unmarshal.PushBack(unmarshal)
|
||||||
|
s.Handlers.UnmarshalError.PushBack(unmarshalError)
|
||||||
|
|
||||||
|
credExpiredBeforeRetry := false
|
||||||
|
credExpiredAfterRetry := false
|
||||||
|
|
||||||
|
s.Handlers.AfterRetry.PushBack(func(r *Request) {
|
||||||
|
credExpiredAfterRetry = r.Config.Credentials.IsExpired()
|
||||||
|
})
|
||||||
|
|
||||||
|
s.Handlers.Sign.Clear()
|
||||||
|
s.Handlers.Sign.PushBack(func(r *Request) {
|
||||||
|
r.Config.Credentials.Get()
|
||||||
|
})
|
||||||
|
s.Handlers.Send.Clear() // mock sending
|
||||||
|
s.Handlers.Send.PushBack(func(r *Request) {
|
||||||
|
r.HTTPResponse = &reqs[reqNum]
|
||||||
|
reqNum++
|
||||||
|
})
|
||||||
|
out := &testData{}
|
||||||
|
r := NewRequest(s, &Operation{Name: "Operation"}, nil, out)
|
||||||
|
err := r.Send()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.False(t, credExpiredBeforeRetry, "Expect valid creds before retry check")
|
||||||
|
assert.True(t, credExpiredAfterRetry, "Expect expired creds after retry check")
|
||||||
|
assert.False(t, s.Config.Credentials.IsExpired(), "Expect valid creds after cred expired recovery")
|
||||||
|
|
||||||
|
assert.Equal(t, 1, int(r.RetryCount))
|
||||||
|
assert.Equal(t, "valid", out.Data)
|
||||||
|
}
|
194
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/service.go
generated
vendored
Normal file
194
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/service.go
generated
vendored
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/endpoints"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Service implements the base service request and response handling
|
||||||
|
// used by all services.
|
||||||
|
type Service struct {
|
||||||
|
Config *Config
|
||||||
|
Handlers Handlers
|
||||||
|
ServiceName string
|
||||||
|
APIVersion string
|
||||||
|
Endpoint string
|
||||||
|
SigningName string
|
||||||
|
SigningRegion string
|
||||||
|
JSONVersion string
|
||||||
|
TargetPrefix string
|
||||||
|
RetryRules func(*Request) time.Duration
|
||||||
|
ShouldRetry func(*Request) bool
|
||||||
|
DefaultMaxRetries uint
|
||||||
|
}
|
||||||
|
|
||||||
|
var schemeRE = regexp.MustCompile("^([^:]+)://")
|
||||||
|
|
||||||
|
// NewService will return a pointer to a new Server object initialized.
|
||||||
|
func NewService(config *Config) *Service {
|
||||||
|
svc := &Service{Config: config}
|
||||||
|
svc.Initialize()
|
||||||
|
return svc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize initializes the service.
|
||||||
|
func (s *Service) Initialize() {
|
||||||
|
if s.Config == nil {
|
||||||
|
s.Config = &Config{}
|
||||||
|
}
|
||||||
|
if s.Config.HTTPClient == nil {
|
||||||
|
s.Config.HTTPClient = http.DefaultClient
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.RetryRules == nil {
|
||||||
|
s.RetryRules = retryRules
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.ShouldRetry == nil {
|
||||||
|
s.ShouldRetry = shouldRetry
|
||||||
|
}
|
||||||
|
|
||||||
|
s.DefaultMaxRetries = 3
|
||||||
|
s.Handlers.Validate.PushBack(ValidateEndpointHandler)
|
||||||
|
s.Handlers.Build.PushBack(UserAgentHandler)
|
||||||
|
s.Handlers.Sign.PushBack(BuildContentLength)
|
||||||
|
s.Handlers.Send.PushBack(SendHandler)
|
||||||
|
s.Handlers.AfterRetry.PushBack(AfterRetryHandler)
|
||||||
|
s.Handlers.ValidateResponse.PushBack(ValidateResponseHandler)
|
||||||
|
s.AddDebugHandlers()
|
||||||
|
s.buildEndpoint()
|
||||||
|
|
||||||
|
if !BoolValue(s.Config.DisableParamValidation) {
|
||||||
|
s.Handlers.Validate.PushBack(ValidateParameters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildEndpoint builds the endpoint values the service will use to make requests with.
|
||||||
|
func (s *Service) buildEndpoint() {
|
||||||
|
if StringValue(s.Config.Endpoint) != "" {
|
||||||
|
s.Endpoint = *s.Config.Endpoint
|
||||||
|
} else {
|
||||||
|
s.Endpoint, s.SigningRegion =
|
||||||
|
endpoints.EndpointForRegion(s.ServiceName, StringValue(s.Config.Region))
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Endpoint != "" && !schemeRE.MatchString(s.Endpoint) {
|
||||||
|
scheme := "https"
|
||||||
|
if BoolValue(s.Config.DisableSSL) {
|
||||||
|
scheme = "http"
|
||||||
|
}
|
||||||
|
s.Endpoint = scheme + "://" + s.Endpoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddDebugHandlers injects debug logging handlers into the service to log request
|
||||||
|
// debug information.
|
||||||
|
func (s *Service) AddDebugHandlers() {
|
||||||
|
if !s.Config.LogLevel.AtLeast(LogDebug) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Handlers.Send.PushFront(logRequest)
|
||||||
|
s.Handlers.Send.PushBack(logResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
const logReqMsg = `DEBUG: Request %s/%s Details:
|
||||||
|
---[ REQUEST POST-SIGN ]-----------------------------
|
||||||
|
%s
|
||||||
|
-----------------------------------------------------`
|
||||||
|
|
||||||
|
func logRequest(r *Request) {
|
||||||
|
logBody := r.Config.LogLevel.Matches(LogDebugWithHTTPBody)
|
||||||
|
dumpedBody, _ := httputil.DumpRequestOut(r.HTTPRequest, logBody)
|
||||||
|
|
||||||
|
r.Config.Logger.Log(fmt.Sprintf(logReqMsg, r.ServiceName, r.Operation.Name, string(dumpedBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
const logRespMsg = `DEBUG: Response %s/%s Details:
|
||||||
|
---[ RESPONSE ]--------------------------------------
|
||||||
|
%s
|
||||||
|
-----------------------------------------------------`
|
||||||
|
|
||||||
|
func logResponse(r *Request) {
|
||||||
|
var msg = "no reponse data"
|
||||||
|
if r.HTTPResponse != nil {
|
||||||
|
logBody := r.Config.LogLevel.Matches(LogDebugWithHTTPBody)
|
||||||
|
dumpedBody, _ := httputil.DumpResponse(r.HTTPResponse, logBody)
|
||||||
|
msg = string(dumpedBody)
|
||||||
|
} else if r.Error != nil {
|
||||||
|
msg = r.Error.Error()
|
||||||
|
}
|
||||||
|
r.Config.Logger.Log(fmt.Sprintf(logRespMsg, r.ServiceName, r.Operation.Name, msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxRetries returns the number of maximum returns the service will use to make
|
||||||
|
// an individual API request.
|
||||||
|
func (s *Service) MaxRetries() uint {
|
||||||
|
if IntValue(s.Config.MaxRetries) < 0 {
|
||||||
|
return s.DefaultMaxRetries
|
||||||
|
}
|
||||||
|
return uint(IntValue(s.Config.MaxRetries))
|
||||||
|
}
|
||||||
|
|
||||||
|
var seededRand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
|
||||||
|
// retryRules returns the delay duration before retrying this request again
|
||||||
|
func retryRules(r *Request) time.Duration {
|
||||||
|
|
||||||
|
delay := int(math.Pow(2, float64(r.RetryCount))) * (seededRand.Intn(30) + 30)
|
||||||
|
return time.Duration(delay) * time.Millisecond
|
||||||
|
}
|
||||||
|
|
||||||
|
// retryableCodes is a collection of service response codes which are retry-able
|
||||||
|
// without any further action.
|
||||||
|
var retryableCodes = map[string]struct{}{
|
||||||
|
"RequestError": {},
|
||||||
|
"ProvisionedThroughputExceededException": {},
|
||||||
|
"Throttling": {},
|
||||||
|
"ThrottlingException": {},
|
||||||
|
"RequestLimitExceeded": {},
|
||||||
|
"RequestThrottled": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
// credsExpiredCodes is a collection of error codes which signify the credentials
|
||||||
|
// need to be refreshed. Expired tokens require refreshing of credentials, and
|
||||||
|
// resigning before the request can be retried.
|
||||||
|
var credsExpiredCodes = map[string]struct{}{
|
||||||
|
"ExpiredToken": {},
|
||||||
|
"ExpiredTokenException": {},
|
||||||
|
"RequestExpired": {}, // EC2 Only
|
||||||
|
}
|
||||||
|
|
||||||
|
func isCodeRetryable(code string) bool {
|
||||||
|
if _, ok := retryableCodes[code]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return isCodeExpiredCreds(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isCodeExpiredCreds(code string) bool {
|
||||||
|
_, ok := credsExpiredCodes[code]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// shouldRetry returns if the request should be retried.
|
||||||
|
func shouldRetry(r *Request) bool {
|
||||||
|
if r.HTTPResponse.StatusCode >= 500 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if r.Error != nil {
|
||||||
|
if err, ok := r.Error.(awserr.Error); ok {
|
||||||
|
return isCodeRetryable(err.Code())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
55
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/types.go
generated
vendored
Normal file
55
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/types.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReadSeekCloser wraps a io.Reader returning a ReaderSeakerCloser
|
||||||
|
func ReadSeekCloser(r io.Reader) ReaderSeekerCloser {
|
||||||
|
return ReaderSeekerCloser{r}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReaderSeekerCloser represents a reader that can also delegate io.Seeker and
|
||||||
|
// io.Closer interfaces to the underlying object if they are available.
|
||||||
|
type ReaderSeekerCloser struct {
|
||||||
|
r io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads from the reader up to size of p. The number of bytes read, and
|
||||||
|
// error if it occurred will be returned.
|
||||||
|
//
|
||||||
|
// If the reader is not an io.Reader zero bytes read, and nil error will be returned.
|
||||||
|
//
|
||||||
|
// Performs the same functionality as io.Reader Read
|
||||||
|
func (r ReaderSeekerCloser) Read(p []byte) (int, error) {
|
||||||
|
switch t := r.r.(type) {
|
||||||
|
case io.Reader:
|
||||||
|
return t.Read(p)
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek sets the offset for the next Read to offset, interpreted according to
|
||||||
|
// whence: 0 means relative to the origin of the file, 1 means relative to the
|
||||||
|
// current offset, and 2 means relative to the end. Seek returns the new offset
|
||||||
|
// and an error, if any.
|
||||||
|
//
|
||||||
|
// If the ReaderSeekerCloser is not an io.Seeker nothing will be done.
|
||||||
|
func (r ReaderSeekerCloser) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
switch t := r.r.(type) {
|
||||||
|
case io.Seeker:
|
||||||
|
return t.Seek(offset, whence)
|
||||||
|
}
|
||||||
|
return int64(0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the ReaderSeekerCloser.
|
||||||
|
//
|
||||||
|
// If the ReaderSeekerCloser is not an io.Closer nothing will be done.
|
||||||
|
func (r ReaderSeekerCloser) Close() error {
|
||||||
|
switch t := r.r.(type) {
|
||||||
|
case io.Closer:
|
||||||
|
return t.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
8
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/version.go
generated
vendored
Normal file
8
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/version.go
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// Package aws provides core functionality for making requests to AWS services.
|
||||||
|
package aws
|
||||||
|
|
||||||
|
// SDKName is the name of this AWS SDK
|
||||||
|
const SDKName = "aws-sdk-go"
|
||||||
|
|
||||||
|
// SDKVersion is the version of this SDK
|
||||||
|
const SDKVersion = "0.7.1"
|
31
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/endpoints/endpoints.go
generated
vendored
Normal file
31
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/endpoints/endpoints.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// Package endpoints validates regional endpoints for services.
|
||||||
|
package endpoints
|
||||||
|
|
||||||
|
//go:generate go run ../model/cli/gen-endpoints/main.go endpoints.json endpoints_map.go
|
||||||
|
//go:generate gofmt -s -w endpoints_map.go
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// EndpointForRegion returns an endpoint and its signing region for a service and region.
|
||||||
|
// if the service and region pair are not found endpoint and signingRegion will be empty.
|
||||||
|
func EndpointForRegion(svcName, region string) (endpoint, signingRegion string) {
|
||||||
|
derivedKeys := []string{
|
||||||
|
region + "/" + svcName,
|
||||||
|
region + "/*",
|
||||||
|
"*/" + svcName,
|
||||||
|
"*/*",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range derivedKeys {
|
||||||
|
if val, ok := endpointsMap.Endpoints[key]; ok {
|
||||||
|
ep := val.Endpoint
|
||||||
|
ep = strings.Replace(ep, "{region}", region, -1)
|
||||||
|
ep = strings.Replace(ep, "{service}", svcName, -1)
|
||||||
|
|
||||||
|
endpoint = ep
|
||||||
|
signingRegion = val.SigningRegion
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
77
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/endpoints/endpoints.json
generated
vendored
Normal file
77
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/endpoints/endpoints.json
generated
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"endpoints": {
|
||||||
|
"*/*": {
|
||||||
|
"endpoint": "{service}.{region}.amazonaws.com"
|
||||||
|
},
|
||||||
|
"cn-north-1/*": {
|
||||||
|
"endpoint": "{service}.{region}.amazonaws.com.cn",
|
||||||
|
"signatureVersion": "v4"
|
||||||
|
},
|
||||||
|
"us-gov-west-1/iam": {
|
||||||
|
"endpoint": "iam.us-gov.amazonaws.com"
|
||||||
|
},
|
||||||
|
"us-gov-west-1/sts": {
|
||||||
|
"endpoint": "sts.us-gov-west-1.amazonaws.com"
|
||||||
|
},
|
||||||
|
"us-gov-west-1/s3": {
|
||||||
|
"endpoint": "s3-{region}.amazonaws.com"
|
||||||
|
},
|
||||||
|
"*/cloudfront": {
|
||||||
|
"endpoint": "cloudfront.amazonaws.com",
|
||||||
|
"signingRegion": "us-east-1"
|
||||||
|
},
|
||||||
|
"*/cloudsearchdomain": {
|
||||||
|
"endpoint": "",
|
||||||
|
"signingRegion": "us-east-1"
|
||||||
|
},
|
||||||
|
"*/iam": {
|
||||||
|
"endpoint": "iam.amazonaws.com",
|
||||||
|
"signingRegion": "us-east-1"
|
||||||
|
},
|
||||||
|
"*/importexport": {
|
||||||
|
"endpoint": "importexport.amazonaws.com",
|
||||||
|
"signingRegion": "us-east-1"
|
||||||
|
},
|
||||||
|
"*/route53": {
|
||||||
|
"endpoint": "route53.amazonaws.com",
|
||||||
|
"signingRegion": "us-east-1"
|
||||||
|
},
|
||||||
|
"*/sts": {
|
||||||
|
"endpoint": "sts.amazonaws.com",
|
||||||
|
"signingRegion": "us-east-1"
|
||||||
|
},
|
||||||
|
"us-east-1/sdb": {
|
||||||
|
"endpoint": "sdb.amazonaws.com",
|
||||||
|
"signingRegion": "us-east-1"
|
||||||
|
},
|
||||||
|
"us-east-1/s3": {
|
||||||
|
"endpoint": "s3.amazonaws.com"
|
||||||
|
},
|
||||||
|
"us-west-1/s3": {
|
||||||
|
"endpoint": "s3-{region}.amazonaws.com"
|
||||||
|
},
|
||||||
|
"us-west-2/s3": {
|
||||||
|
"endpoint": "s3-{region}.amazonaws.com"
|
||||||
|
},
|
||||||
|
"eu-west-1/s3": {
|
||||||
|
"endpoint": "s3-{region}.amazonaws.com"
|
||||||
|
},
|
||||||
|
"ap-southeast-1/s3": {
|
||||||
|
"endpoint": "s3-{region}.amazonaws.com"
|
||||||
|
},
|
||||||
|
"ap-southeast-2/s3": {
|
||||||
|
"endpoint": "s3-{region}.amazonaws.com"
|
||||||
|
},
|
||||||
|
"ap-northeast-1/s3": {
|
||||||
|
"endpoint": "s3-{region}.amazonaws.com"
|
||||||
|
},
|
||||||
|
"sa-east-1/s3": {
|
||||||
|
"endpoint": "s3-{region}.amazonaws.com"
|
||||||
|
},
|
||||||
|
"eu-central-1/s3": {
|
||||||
|
"endpoint": "{service}.{region}.amazonaws.com",
|
||||||
|
"signatureVersion": "v4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
89
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/endpoints/endpoints_map.go
generated
vendored
Normal file
89
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/endpoints/endpoints_map.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package endpoints
|
||||||
|
|
||||||
|
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
|
||||||
|
|
||||||
|
type endpointStruct struct {
|
||||||
|
Version int
|
||||||
|
Endpoints map[string]endpointEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
type endpointEntry struct {
|
||||||
|
Endpoint string
|
||||||
|
SigningRegion string
|
||||||
|
}
|
||||||
|
|
||||||
|
var endpointsMap = endpointStruct{
|
||||||
|
Version: 2,
|
||||||
|
Endpoints: map[string]endpointEntry{
|
||||||
|
"*/*": {
|
||||||
|
Endpoint: "{service}.{region}.amazonaws.com",
|
||||||
|
},
|
||||||
|
"*/cloudfront": {
|
||||||
|
Endpoint: "cloudfront.amazonaws.com",
|
||||||
|
SigningRegion: "us-east-1",
|
||||||
|
},
|
||||||
|
"*/cloudsearchdomain": {
|
||||||
|
Endpoint: "",
|
||||||
|
SigningRegion: "us-east-1",
|
||||||
|
},
|
||||||
|
"*/iam": {
|
||||||
|
Endpoint: "iam.amazonaws.com",
|
||||||
|
SigningRegion: "us-east-1",
|
||||||
|
},
|
||||||
|
"*/importexport": {
|
||||||
|
Endpoint: "importexport.amazonaws.com",
|
||||||
|
SigningRegion: "us-east-1",
|
||||||
|
},
|
||||||
|
"*/route53": {
|
||||||
|
Endpoint: "route53.amazonaws.com",
|
||||||
|
SigningRegion: "us-east-1",
|
||||||
|
},
|
||||||
|
"*/sts": {
|
||||||
|
Endpoint: "sts.amazonaws.com",
|
||||||
|
SigningRegion: "us-east-1",
|
||||||
|
},
|
||||||
|
"ap-northeast-1/s3": {
|
||||||
|
Endpoint: "s3-{region}.amazonaws.com",
|
||||||
|
},
|
||||||
|
"ap-southeast-1/s3": {
|
||||||
|
Endpoint: "s3-{region}.amazonaws.com",
|
||||||
|
},
|
||||||
|
"ap-southeast-2/s3": {
|
||||||
|
Endpoint: "s3-{region}.amazonaws.com",
|
||||||
|
},
|
||||||
|
"cn-north-1/*": {
|
||||||
|
Endpoint: "{service}.{region}.amazonaws.com.cn",
|
||||||
|
},
|
||||||
|
"eu-central-1/s3": {
|
||||||
|
Endpoint: "{service}.{region}.amazonaws.com",
|
||||||
|
},
|
||||||
|
"eu-west-1/s3": {
|
||||||
|
Endpoint: "s3-{region}.amazonaws.com",
|
||||||
|
},
|
||||||
|
"sa-east-1/s3": {
|
||||||
|
Endpoint: "s3-{region}.amazonaws.com",
|
||||||
|
},
|
||||||
|
"us-east-1/s3": {
|
||||||
|
Endpoint: "s3.amazonaws.com",
|
||||||
|
},
|
||||||
|
"us-east-1/sdb": {
|
||||||
|
Endpoint: "sdb.amazonaws.com",
|
||||||
|
SigningRegion: "us-east-1",
|
||||||
|
},
|
||||||
|
"us-gov-west-1/iam": {
|
||||||
|
Endpoint: "iam.us-gov.amazonaws.com",
|
||||||
|
},
|
||||||
|
"us-gov-west-1/s3": {
|
||||||
|
Endpoint: "s3-{region}.amazonaws.com",
|
||||||
|
},
|
||||||
|
"us-gov-west-1/sts": {
|
||||||
|
Endpoint: "sts.us-gov-west-1.amazonaws.com",
|
||||||
|
},
|
||||||
|
"us-west-1/s3": {
|
||||||
|
Endpoint: "s3-{region}.amazonaws.com",
|
||||||
|
},
|
||||||
|
"us-west-2/s3": {
|
||||||
|
Endpoint: "s3-{region}.amazonaws.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
28
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/endpoints/endpoints_test.go
generated
vendored
Normal file
28
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/endpoints/endpoints_test.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package endpoints
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGlobalEndpoints(t *testing.T) {
|
||||||
|
region := "mock-region-1"
|
||||||
|
svcs := []string{"cloudfront", "iam", "importexport", "route53", "sts"}
|
||||||
|
|
||||||
|
for _, name := range svcs {
|
||||||
|
ep, sr := EndpointForRegion(name, region)
|
||||||
|
assert.Equal(t, name+".amazonaws.com", ep)
|
||||||
|
assert.Equal(t, "us-east-1", sr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServicesInCN(t *testing.T) {
|
||||||
|
region := "cn-north-1"
|
||||||
|
svcs := []string{"cloudfront", "iam", "importexport", "route53", "sts", "s3"}
|
||||||
|
|
||||||
|
for _, name := range svcs {
|
||||||
|
ep, _ := EndpointForRegion(name, region)
|
||||||
|
assert.Equal(t, name+"."+region+".amazonaws.com.cn", ep)
|
||||||
|
}
|
||||||
|
}
|
33
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/query/build.go
generated
vendored
Normal file
33
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/query/build.go
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// Package query provides serialisation of AWS query requests, and responses.
|
||||||
|
package query
|
||||||
|
|
||||||
|
//go:generate go run ../../fixtures/protocol/generate.go ../../fixtures/protocol/input/query.json build_test.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/protocol/query/queryutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Build builds a request for an AWS Query service.
|
||||||
|
func Build(r *aws.Request) {
|
||||||
|
body := url.Values{
|
||||||
|
"Action": {r.Operation.Name},
|
||||||
|
"Version": {r.Service.APIVersion},
|
||||||
|
}
|
||||||
|
if err := queryutil.Parse(body, r.Params, false); err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed encoding Query request", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.ExpireTime == 0 {
|
||||||
|
r.HTTPRequest.Method = "POST"
|
||||||
|
r.HTTPRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
|
||||||
|
r.SetBufferBody([]byte(body.Encode()))
|
||||||
|
} else { // This is a pre-signed request
|
||||||
|
r.HTTPRequest.Method = "GET"
|
||||||
|
r.HTTPRequest.URL.RawQuery = body.Encode()
|
||||||
|
}
|
||||||
|
}
|
1482
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/query/build_test.go
generated
vendored
Normal file
1482
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/query/build_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
223
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/query/queryutil/queryutil.go
generated
vendored
Normal file
223
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/query/queryutil/queryutil.go
generated
vendored
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
package queryutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parse parses an object i and fills a url.Values object. The isEC2 flag
|
||||||
|
// indicates if this is the EC2 Query sub-protocol.
|
||||||
|
func Parse(body url.Values, i interface{}, isEC2 bool) error {
|
||||||
|
q := queryParser{isEC2: isEC2}
|
||||||
|
return q.parseValue(body, reflect.ValueOf(i), "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func elemOf(value reflect.Value) reflect.Value {
|
||||||
|
for value.Kind() == reflect.Ptr {
|
||||||
|
value = value.Elem()
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
type queryParser struct {
|
||||||
|
isEC2 bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *queryParser) parseValue(v url.Values, value reflect.Value, prefix string, tag reflect.StructTag) error {
|
||||||
|
value = elemOf(value)
|
||||||
|
|
||||||
|
// no need to handle zero values
|
||||||
|
if !value.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
t := tag.Get("type")
|
||||||
|
if t == "" {
|
||||||
|
switch value.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
t = "structure"
|
||||||
|
case reflect.Slice:
|
||||||
|
t = "list"
|
||||||
|
case reflect.Map:
|
||||||
|
t = "map"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t {
|
||||||
|
case "structure":
|
||||||
|
return q.parseStruct(v, value, prefix)
|
||||||
|
case "list":
|
||||||
|
return q.parseList(v, value, prefix, tag)
|
||||||
|
case "map":
|
||||||
|
return q.parseMap(v, value, prefix, tag)
|
||||||
|
default:
|
||||||
|
return q.parseScalar(v, value, prefix, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *queryParser) parseStruct(v url.Values, value reflect.Value, prefix string) error {
|
||||||
|
if !value.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
t := value.Type()
|
||||||
|
for i := 0; i < value.NumField(); i++ {
|
||||||
|
if c := t.Field(i).Name[0:1]; strings.ToLower(c) == c {
|
||||||
|
continue // ignore unexported fields
|
||||||
|
}
|
||||||
|
|
||||||
|
value := elemOf(value.Field(i))
|
||||||
|
field := t.Field(i)
|
||||||
|
var name string
|
||||||
|
|
||||||
|
if q.isEC2 {
|
||||||
|
name = field.Tag.Get("queryName")
|
||||||
|
}
|
||||||
|
if name == "" {
|
||||||
|
if field.Tag.Get("flattened") != "" && field.Tag.Get("locationNameList") != "" {
|
||||||
|
name = field.Tag.Get("locationNameList")
|
||||||
|
} else if locName := field.Tag.Get("locationName"); locName != "" {
|
||||||
|
name = locName
|
||||||
|
}
|
||||||
|
if name != "" && q.isEC2 {
|
||||||
|
name = strings.ToUpper(name[0:1]) + name[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if name == "" {
|
||||||
|
name = field.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
if prefix != "" {
|
||||||
|
name = prefix + "." + name
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.parseValue(v, value, name, field.Tag); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *queryParser) parseList(v url.Values, value reflect.Value, prefix string, tag reflect.StructTag) error {
|
||||||
|
// If it's empty, generate an empty value
|
||||||
|
if !value.IsNil() && value.Len() == 0 {
|
||||||
|
v.Set(prefix, "")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for unflattened list member
|
||||||
|
if !q.isEC2 && tag.Get("flattened") == "" {
|
||||||
|
prefix += ".member"
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < value.Len(); i++ {
|
||||||
|
slicePrefix := prefix
|
||||||
|
if slicePrefix == "" {
|
||||||
|
slicePrefix = strconv.Itoa(i + 1)
|
||||||
|
} else {
|
||||||
|
slicePrefix = slicePrefix + "." + strconv.Itoa(i+1)
|
||||||
|
}
|
||||||
|
if err := q.parseValue(v, value.Index(i), slicePrefix, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *queryParser) parseMap(v url.Values, value reflect.Value, prefix string, tag reflect.StructTag) error {
|
||||||
|
// If it's empty, generate an empty value
|
||||||
|
if !value.IsNil() && value.Len() == 0 {
|
||||||
|
v.Set(prefix, "")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for unflattened list member
|
||||||
|
if !q.isEC2 && tag.Get("flattened") == "" {
|
||||||
|
prefix += ".entry"
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort keys for improved serialization consistency.
|
||||||
|
// this is not strictly necessary for protocol support.
|
||||||
|
mapKeyValues := value.MapKeys()
|
||||||
|
mapKeys := map[string]reflect.Value{}
|
||||||
|
mapKeyNames := make([]string, len(mapKeyValues))
|
||||||
|
for i, mapKey := range mapKeyValues {
|
||||||
|
name := mapKey.String()
|
||||||
|
mapKeys[name] = mapKey
|
||||||
|
mapKeyNames[i] = name
|
||||||
|
}
|
||||||
|
sort.Strings(mapKeyNames)
|
||||||
|
|
||||||
|
for i, mapKeyName := range mapKeyNames {
|
||||||
|
mapKey := mapKeys[mapKeyName]
|
||||||
|
mapValue := value.MapIndex(mapKey)
|
||||||
|
|
||||||
|
kname := tag.Get("locationNameKey")
|
||||||
|
if kname == "" {
|
||||||
|
kname = "key"
|
||||||
|
}
|
||||||
|
vname := tag.Get("locationNameValue")
|
||||||
|
if vname == "" {
|
||||||
|
vname = "value"
|
||||||
|
}
|
||||||
|
|
||||||
|
// serialize key
|
||||||
|
var keyName string
|
||||||
|
if prefix == "" {
|
||||||
|
keyName = strconv.Itoa(i+1) + "." + kname
|
||||||
|
} else {
|
||||||
|
keyName = prefix + "." + strconv.Itoa(i+1) + "." + kname
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.parseValue(v, mapKey, keyName, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// serialize value
|
||||||
|
var valueName string
|
||||||
|
if prefix == "" {
|
||||||
|
valueName = strconv.Itoa(i+1) + "." + vname
|
||||||
|
} else {
|
||||||
|
valueName = prefix + "." + strconv.Itoa(i+1) + "." + vname
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.parseValue(v, mapValue, valueName, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *queryParser) parseScalar(v url.Values, r reflect.Value, name string, tag reflect.StructTag) error {
|
||||||
|
switch value := r.Interface().(type) {
|
||||||
|
case string:
|
||||||
|
v.Set(name, value)
|
||||||
|
case []byte:
|
||||||
|
if !r.IsNil() {
|
||||||
|
v.Set(name, base64.StdEncoding.EncodeToString(value))
|
||||||
|
}
|
||||||
|
case bool:
|
||||||
|
v.Set(name, strconv.FormatBool(value))
|
||||||
|
case int64:
|
||||||
|
v.Set(name, strconv.FormatInt(value, 10))
|
||||||
|
case int:
|
||||||
|
v.Set(name, strconv.Itoa(value))
|
||||||
|
case float64:
|
||||||
|
v.Set(name, strconv.FormatFloat(value, 'f', -1, 64))
|
||||||
|
case float32:
|
||||||
|
v.Set(name, strconv.FormatFloat(float64(value), 'f', -1, 32))
|
||||||
|
case time.Time:
|
||||||
|
const ISO8601UTC = "2006-01-02T15:04:05Z"
|
||||||
|
v.Set(name, value.UTC().Format(ISO8601UTC))
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported value for param %s: %v (%s)", name, r.Interface(), r.Type().Name())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
29
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/query/unmarshal.go
generated
vendored
Normal file
29
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/query/unmarshal.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package query
|
||||||
|
|
||||||
|
//go:generate go run ../../fixtures/protocol/generate.go ../../fixtures/protocol/output/query.json unmarshal_test.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/protocol/xml/xmlutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Unmarshal unmarshals a response for an AWS Query service.
|
||||||
|
func Unmarshal(r *aws.Request) {
|
||||||
|
defer r.HTTPResponse.Body.Close()
|
||||||
|
if r.DataFilled() {
|
||||||
|
decoder := xml.NewDecoder(r.HTTPResponse.Body)
|
||||||
|
err := xmlutil.UnmarshalXML(r.Data, decoder, r.Operation.Name+"Result")
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed decoding Query response", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalMeta unmarshals header response values for an AWS Query service.
|
||||||
|
func UnmarshalMeta(r *aws.Request) {
|
||||||
|
// TODO implement unmarshaling of request IDs
|
||||||
|
}
|
33
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/query/unmarshal_error.go
generated
vendored
Normal file
33
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/query/unmarshal_error.go
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package query
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
type xmlErrorResponse struct {
|
||||||
|
XMLName xml.Name `xml:"ErrorResponse"`
|
||||||
|
Code string `xml:"Error>Code"`
|
||||||
|
Message string `xml:"Error>Message"`
|
||||||
|
RequestID string `xml:"RequestId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalError unmarshals an error response for an AWS Query service.
|
||||||
|
func UnmarshalError(r *aws.Request) {
|
||||||
|
defer r.HTTPResponse.Body.Close()
|
||||||
|
|
||||||
|
resp := &xmlErrorResponse{}
|
||||||
|
err := xml.NewDecoder(r.HTTPResponse.Body).Decode(resp)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed to decode query XML error response", err)
|
||||||
|
} else {
|
||||||
|
r.Error = awserr.NewRequestFailure(
|
||||||
|
awserr.New(resp.Code, resp.Message, nil),
|
||||||
|
r.HTTPResponse.StatusCode,
|
||||||
|
resp.RequestID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
1418
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/query/unmarshal_test.go
generated
vendored
Normal file
1418
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/query/unmarshal_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
208
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/rest/build.go
generated
vendored
Normal file
208
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/rest/build.go
generated
vendored
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
// Package rest provides RESTful serialisation of AWS requests and responses.
|
||||||
|
package rest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RFC822 returns an RFC822 formatted timestamp for AWS protocols
|
||||||
|
const RFC822 = "Mon, 2 Jan 2006 15:04:05 GMT"
|
||||||
|
|
||||||
|
// Whether the byte value can be sent without escaping in AWS URLs
|
||||||
|
var noEscape [256]bool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for i := 0; i < len(noEscape); i++ {
|
||||||
|
// AWS expects every character except these to be escaped
|
||||||
|
noEscape[i] = (i >= 'A' && i <= 'Z') ||
|
||||||
|
(i >= 'a' && i <= 'z') ||
|
||||||
|
(i >= '0' && i <= '9') ||
|
||||||
|
i == '-' ||
|
||||||
|
i == '.' ||
|
||||||
|
i == '_' ||
|
||||||
|
i == '~'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build builds the REST component of a service request.
|
||||||
|
func Build(r *aws.Request) {
|
||||||
|
if r.ParamsFilled() {
|
||||||
|
v := reflect.ValueOf(r.Params).Elem()
|
||||||
|
buildLocationElements(r, v)
|
||||||
|
buildBody(r, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildLocationElements(r *aws.Request, v reflect.Value) {
|
||||||
|
query := r.HTTPRequest.URL.Query()
|
||||||
|
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
m := v.Field(i)
|
||||||
|
if n := v.Type().Field(i).Name; n[0:1] == strings.ToLower(n[0:1]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.IsValid() {
|
||||||
|
field := v.Type().Field(i)
|
||||||
|
name := field.Tag.Get("locationName")
|
||||||
|
if name == "" {
|
||||||
|
name = field.Name
|
||||||
|
}
|
||||||
|
if m.Kind() == reflect.Ptr {
|
||||||
|
m = m.Elem()
|
||||||
|
}
|
||||||
|
if !m.IsValid() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch field.Tag.Get("location") {
|
||||||
|
case "headers": // header maps
|
||||||
|
buildHeaderMap(r, m, field.Tag.Get("locationName"))
|
||||||
|
case "header":
|
||||||
|
buildHeader(r, m, name)
|
||||||
|
case "uri":
|
||||||
|
buildURI(r, m, name)
|
||||||
|
case "querystring":
|
||||||
|
buildQueryString(r, m, name, query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.Error != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r.HTTPRequest.URL.RawQuery = query.Encode()
|
||||||
|
updatePath(r.HTTPRequest.URL, r.HTTPRequest.URL.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildBody(r *aws.Request, v reflect.Value) {
|
||||||
|
if field, ok := v.Type().FieldByName("SDKShapeTraits"); ok {
|
||||||
|
if payloadName := field.Tag.Get("payload"); payloadName != "" {
|
||||||
|
pfield, _ := v.Type().FieldByName(payloadName)
|
||||||
|
if ptag := pfield.Tag.Get("type"); ptag != "" && ptag != "structure" {
|
||||||
|
payload := reflect.Indirect(v.FieldByName(payloadName))
|
||||||
|
if payload.IsValid() && payload.Interface() != nil {
|
||||||
|
switch reader := payload.Interface().(type) {
|
||||||
|
case io.ReadSeeker:
|
||||||
|
r.SetReaderBody(reader)
|
||||||
|
case []byte:
|
||||||
|
r.SetBufferBody(reader)
|
||||||
|
case string:
|
||||||
|
r.SetStringBody(reader)
|
||||||
|
default:
|
||||||
|
r.Error = awserr.New("SerializationError",
|
||||||
|
"failed to encode REST request",
|
||||||
|
fmt.Errorf("unknown payload type %s", payload.Type()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildHeader(r *aws.Request, v reflect.Value, name string) {
|
||||||
|
str, err := convertType(v)
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed to encode REST request", err)
|
||||||
|
} else if str != nil {
|
||||||
|
r.HTTPRequest.Header.Add(name, *str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildHeaderMap(r *aws.Request, v reflect.Value, prefix string) {
|
||||||
|
for _, key := range v.MapKeys() {
|
||||||
|
str, err := convertType(v.MapIndex(key))
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed to encode REST request", err)
|
||||||
|
} else if str != nil {
|
||||||
|
r.HTTPRequest.Header.Add(prefix+key.String(), *str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildURI(r *aws.Request, v reflect.Value, name string) {
|
||||||
|
value, err := convertType(v)
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed to encode REST request", err)
|
||||||
|
} else if value != nil {
|
||||||
|
uri := r.HTTPRequest.URL.Path
|
||||||
|
uri = strings.Replace(uri, "{"+name+"}", EscapePath(*value, true), -1)
|
||||||
|
uri = strings.Replace(uri, "{"+name+"+}", EscapePath(*value, false), -1)
|
||||||
|
r.HTTPRequest.URL.Path = uri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildQueryString(r *aws.Request, v reflect.Value, name string, query url.Values) {
|
||||||
|
str, err := convertType(v)
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed to encode REST request", err)
|
||||||
|
} else if str != nil {
|
||||||
|
query.Set(name, *str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updatePath(url *url.URL, urlPath string) {
|
||||||
|
scheme, query := url.Scheme, url.RawQuery
|
||||||
|
|
||||||
|
// get formatted URL minus scheme so we can build this into Opaque
|
||||||
|
url.Scheme, url.Path, url.RawQuery = "", "", ""
|
||||||
|
s := url.String()
|
||||||
|
url.Scheme = scheme
|
||||||
|
url.RawQuery = query
|
||||||
|
|
||||||
|
// build opaque URI
|
||||||
|
url.Opaque = s + urlPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// EscapePath escapes part of a URL path in Amazon style
|
||||||
|
func EscapePath(path string, encodeSep bool) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for i := 0; i < len(path); i++ {
|
||||||
|
c := path[i]
|
||||||
|
if noEscape[c] || (c == '/' && !encodeSep) {
|
||||||
|
buf.WriteByte(c)
|
||||||
|
} else {
|
||||||
|
buf.WriteByte('%')
|
||||||
|
buf.WriteString(strings.ToUpper(strconv.FormatUint(uint64(c), 16)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertType(v reflect.Value) (*string, error) {
|
||||||
|
v = reflect.Indirect(v)
|
||||||
|
if !v.IsValid() {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var str string
|
||||||
|
switch value := v.Interface().(type) {
|
||||||
|
case string:
|
||||||
|
str = value
|
||||||
|
case []byte:
|
||||||
|
str = base64.StdEncoding.EncodeToString(value)
|
||||||
|
case bool:
|
||||||
|
str = strconv.FormatBool(value)
|
||||||
|
case int64:
|
||||||
|
str = strconv.FormatInt(value, 10)
|
||||||
|
case float64:
|
||||||
|
str = strconv.FormatFloat(value, 'f', -1, 64)
|
||||||
|
case time.Time:
|
||||||
|
str = value.UTC().Format(RFC822)
|
||||||
|
default:
|
||||||
|
err := fmt.Errorf("Unsupported value for param %v (%s)", v.Interface(), v.Type())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &str, nil
|
||||||
|
}
|
45
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/rest/payload.go
generated
vendored
Normal file
45
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/rest/payload.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package rest
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
|
// PayloadMember returns the payload field member of i if there is one, or nil.
|
||||||
|
func PayloadMember(i interface{}) interface{} {
|
||||||
|
if i == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
v := reflect.ValueOf(i).Elem()
|
||||||
|
if !v.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if field, ok := v.Type().FieldByName("SDKShapeTraits"); ok {
|
||||||
|
if payloadName := field.Tag.Get("payload"); payloadName != "" {
|
||||||
|
field, _ := v.Type().FieldByName(payloadName)
|
||||||
|
if field.Tag.Get("type") != "structure" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := v.FieldByName(payloadName)
|
||||||
|
if payload.IsValid() || (payload.Kind() == reflect.Ptr && !payload.IsNil()) {
|
||||||
|
return payload.Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PayloadType returns the type of a payload field member of i if there is one, or "".
|
||||||
|
func PayloadType(i interface{}) string {
|
||||||
|
v := reflect.Indirect(reflect.ValueOf(i))
|
||||||
|
if !v.IsValid() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if field, ok := v.Type().FieldByName("SDKShapeTraits"); ok {
|
||||||
|
if payloadName := field.Tag.Get("payload"); payloadName != "" {
|
||||||
|
if member, ok := v.Type().FieldByName(payloadName); ok {
|
||||||
|
return member.Tag.Get("type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
174
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/rest/unmarshal.go
generated
vendored
Normal file
174
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/rest/unmarshal.go
generated
vendored
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
package rest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Unmarshal unmarshals the REST component of a response in a REST service.
|
||||||
|
func Unmarshal(r *aws.Request) {
|
||||||
|
if r.DataFilled() {
|
||||||
|
v := reflect.Indirect(reflect.ValueOf(r.Data))
|
||||||
|
unmarshalBody(r, v)
|
||||||
|
unmarshalLocationElements(r, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalBody(r *aws.Request, v reflect.Value) {
|
||||||
|
if field, ok := v.Type().FieldByName("SDKShapeTraits"); ok {
|
||||||
|
if payloadName := field.Tag.Get("payload"); payloadName != "" {
|
||||||
|
pfield, _ := v.Type().FieldByName(payloadName)
|
||||||
|
if ptag := pfield.Tag.Get("type"); ptag != "" && ptag != "structure" {
|
||||||
|
payload := v.FieldByName(payloadName)
|
||||||
|
if payload.IsValid() {
|
||||||
|
switch payload.Interface().(type) {
|
||||||
|
case []byte:
|
||||||
|
b, err := ioutil.ReadAll(r.HTTPResponse.Body)
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed to decode REST response", err)
|
||||||
|
} else {
|
||||||
|
payload.Set(reflect.ValueOf(b))
|
||||||
|
}
|
||||||
|
case *string:
|
||||||
|
b, err := ioutil.ReadAll(r.HTTPResponse.Body)
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed to decode REST response", err)
|
||||||
|
} else {
|
||||||
|
str := string(b)
|
||||||
|
payload.Set(reflect.ValueOf(&str))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
switch payload.Type().String() {
|
||||||
|
case "io.ReadSeeker":
|
||||||
|
payload.Set(reflect.ValueOf(aws.ReadSeekCloser(r.HTTPResponse.Body)))
|
||||||
|
case "aws.ReadSeekCloser", "io.ReadCloser":
|
||||||
|
payload.Set(reflect.ValueOf(r.HTTPResponse.Body))
|
||||||
|
default:
|
||||||
|
r.Error = awserr.New("SerializationError",
|
||||||
|
"failed to decode REST response",
|
||||||
|
fmt.Errorf("unknown payload type %s", payload.Type()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalLocationElements(r *aws.Request, v reflect.Value) {
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
m, field := v.Field(i), v.Type().Field(i)
|
||||||
|
if n := field.Name; n[0:1] == strings.ToLower(n[0:1]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.IsValid() {
|
||||||
|
name := field.Tag.Get("locationName")
|
||||||
|
if name == "" {
|
||||||
|
name = field.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
switch field.Tag.Get("location") {
|
||||||
|
case "statusCode":
|
||||||
|
unmarshalStatusCode(m, r.HTTPResponse.StatusCode)
|
||||||
|
case "header":
|
||||||
|
err := unmarshalHeader(m, r.HTTPResponse.Header.Get(name))
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed to decode REST response", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case "headers":
|
||||||
|
prefix := field.Tag.Get("locationName")
|
||||||
|
err := unmarshalHeaderMap(m, r.HTTPResponse.Header, prefix)
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed to decode REST response", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.Error != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalStatusCode(v reflect.Value, statusCode int) {
|
||||||
|
if !v.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v.Interface().(type) {
|
||||||
|
case *int64:
|
||||||
|
s := int64(statusCode)
|
||||||
|
v.Set(reflect.ValueOf(&s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalHeaderMap(r reflect.Value, headers http.Header, prefix string) error {
|
||||||
|
switch r.Interface().(type) {
|
||||||
|
case map[string]*string: // we only support string map value types
|
||||||
|
out := map[string]*string{}
|
||||||
|
for k, v := range headers {
|
||||||
|
k = http.CanonicalHeaderKey(k)
|
||||||
|
if strings.HasPrefix(strings.ToLower(k), strings.ToLower(prefix)) {
|
||||||
|
out[k[len(prefix):]] = &v[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.Set(reflect.ValueOf(out))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalHeader(v reflect.Value, header string) error {
|
||||||
|
if !v.IsValid() || (header == "" && v.Elem().Kind() != reflect.String) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v.Interface().(type) {
|
||||||
|
case *string:
|
||||||
|
v.Set(reflect.ValueOf(&header))
|
||||||
|
case []byte:
|
||||||
|
b, err := base64.StdEncoding.DecodeString(header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.Set(reflect.ValueOf(&b))
|
||||||
|
case *bool:
|
||||||
|
b, err := strconv.ParseBool(header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.Set(reflect.ValueOf(&b))
|
||||||
|
case *int64:
|
||||||
|
i, err := strconv.ParseInt(header, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.Set(reflect.ValueOf(&i))
|
||||||
|
case *float64:
|
||||||
|
f, err := strconv.ParseFloat(header, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.Set(reflect.ValueOf(&f))
|
||||||
|
case *time.Time:
|
||||||
|
t, err := time.Parse(RFC822, header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.Set(reflect.ValueOf(&t))
|
||||||
|
default:
|
||||||
|
err := fmt.Errorf("Unsupported value for param %v (%s)", v.Interface(), v.Type())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
2717
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/restxml/build_test.go
generated
vendored
Normal file
2717
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/restxml/build_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
55
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/restxml/restxml.go
generated
vendored
Normal file
55
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/restxml/restxml.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
// Package restxml provides RESTful XML serialisation of AWS
|
||||||
|
// requests and responses.
|
||||||
|
package restxml
|
||||||
|
|
||||||
|
//go:generate go run ../../fixtures/protocol/generate.go ../../fixtures/protocol/input/rest-xml.json build_test.go
|
||||||
|
//go:generate go run ../../fixtures/protocol/generate.go ../../fixtures/protocol/output/rest-xml.json unmarshal_test.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/protocol/query"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/protocol/rest"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/protocol/xml/xmlutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Build builds a request payload for the REST XML protocol.
|
||||||
|
func Build(r *aws.Request) {
|
||||||
|
rest.Build(r)
|
||||||
|
|
||||||
|
if t := rest.PayloadType(r.Params); t == "structure" || t == "" {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := xmlutil.BuildXML(r.Params, xml.NewEncoder(&buf))
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed to enode rest XML request", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.SetBufferBody(buf.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal unmarshals a payload response for the REST XML protocol.
|
||||||
|
func Unmarshal(r *aws.Request) {
|
||||||
|
if t := rest.PayloadType(r.Data); t == "structure" || t == "" {
|
||||||
|
defer r.HTTPResponse.Body.Close()
|
||||||
|
decoder := xml.NewDecoder(r.HTTPResponse.Body)
|
||||||
|
err := xmlutil.UnmarshalXML(r.Data, decoder, "")
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed to decode REST XML response", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalMeta unmarshals response headers for the REST XML protocol.
|
||||||
|
func UnmarshalMeta(r *aws.Request) {
|
||||||
|
rest.Unmarshal(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalError unmarshals a response error for the REST XML protocol.
|
||||||
|
func UnmarshalError(r *aws.Request) {
|
||||||
|
query.UnmarshalError(r)
|
||||||
|
}
|
1310
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/restxml/unmarshal_test.go
generated
vendored
Normal file
1310
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/restxml/unmarshal_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
287
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/xml/xmlutil/build.go
generated
vendored
Normal file
287
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/xml/xmlutil/build.go
generated
vendored
Normal file
|
@ -0,0 +1,287 @@
|
||||||
|
// Package xmlutil provides XML serialisation of AWS requests and responses.
|
||||||
|
package xmlutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuildXML will serialize params into an xml.Encoder.
|
||||||
|
// Error will be returned if the serialization of any of the params or nested values fails.
|
||||||
|
func BuildXML(params interface{}, e *xml.Encoder) error {
|
||||||
|
b := xmlBuilder{encoder: e, namespaces: map[string]string{}}
|
||||||
|
root := NewXMLElement(xml.Name{})
|
||||||
|
if err := b.buildValue(reflect.ValueOf(params), root, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, c := range root.Children {
|
||||||
|
for _, v := range c {
|
||||||
|
return StructToXML(e, v, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the reflection element of a value, if it is a pointer.
|
||||||
|
func elemOf(value reflect.Value) reflect.Value {
|
||||||
|
for value.Kind() == reflect.Ptr {
|
||||||
|
value = value.Elem()
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// A xmlBuilder serializes values from Go code to XML
|
||||||
|
type xmlBuilder struct {
|
||||||
|
encoder *xml.Encoder
|
||||||
|
namespaces map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildValue generic XMLNode builder for any type. Will build value for their specific type
|
||||||
|
// struct, list, map, scalar.
|
||||||
|
//
|
||||||
|
// Also takes a "type" tag value to set what type a value should be converted to XMLNode as. If
|
||||||
|
// type is not provided reflect will be used to determine the value's type.
|
||||||
|
func (b *xmlBuilder) buildValue(value reflect.Value, current *XMLNode, tag reflect.StructTag) error {
|
||||||
|
value = elemOf(value)
|
||||||
|
if !value.IsValid() { // no need to handle zero values
|
||||||
|
return nil
|
||||||
|
} else if tag.Get("location") != "" { // don't handle non-body location values
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
t := tag.Get("type")
|
||||||
|
if t == "" {
|
||||||
|
switch value.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
t = "structure"
|
||||||
|
case reflect.Slice:
|
||||||
|
t = "list"
|
||||||
|
case reflect.Map:
|
||||||
|
t = "map"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t {
|
||||||
|
case "structure":
|
||||||
|
if field, ok := value.Type().FieldByName("SDKShapeTraits"); ok {
|
||||||
|
tag = tag + reflect.StructTag(" ") + field.Tag
|
||||||
|
}
|
||||||
|
return b.buildStruct(value, current, tag)
|
||||||
|
case "list":
|
||||||
|
return b.buildList(value, current, tag)
|
||||||
|
case "map":
|
||||||
|
return b.buildMap(value, current, tag)
|
||||||
|
default:
|
||||||
|
return b.buildScalar(value, current, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildStruct adds a struct and its fields to the current XMLNode. All fields any any nested
|
||||||
|
// types are converted to XMLNodes also.
|
||||||
|
func (b *xmlBuilder) buildStruct(value reflect.Value, current *XMLNode, tag reflect.StructTag) error {
|
||||||
|
if !value.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldAdded := false
|
||||||
|
|
||||||
|
// unwrap payloads
|
||||||
|
if payload := tag.Get("payload"); payload != "" {
|
||||||
|
field, _ := value.Type().FieldByName(payload)
|
||||||
|
tag = field.Tag
|
||||||
|
value = elemOf(value.FieldByName(payload))
|
||||||
|
|
||||||
|
if !value.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
child := NewXMLElement(xml.Name{Local: tag.Get("locationName")})
|
||||||
|
|
||||||
|
// there is an xmlNamespace associated with this struct
|
||||||
|
if prefix, uri := tag.Get("xmlPrefix"), tag.Get("xmlURI"); uri != "" {
|
||||||
|
ns := xml.Attr{
|
||||||
|
Name: xml.Name{Local: "xmlns"},
|
||||||
|
Value: uri,
|
||||||
|
}
|
||||||
|
if prefix != "" {
|
||||||
|
b.namespaces[prefix] = uri // register the namespace
|
||||||
|
ns.Name.Local = "xmlns:" + prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
child.Attr = append(child.Attr, ns)
|
||||||
|
}
|
||||||
|
|
||||||
|
t := value.Type()
|
||||||
|
for i := 0; i < value.NumField(); i++ {
|
||||||
|
if c := t.Field(i).Name[0:1]; strings.ToLower(c) == c {
|
||||||
|
continue // ignore unexported fields
|
||||||
|
}
|
||||||
|
|
||||||
|
member := elemOf(value.Field(i))
|
||||||
|
field := t.Field(i)
|
||||||
|
mTag := field.Tag
|
||||||
|
|
||||||
|
if mTag.Get("location") != "" { // skip non-body members
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
memberName := mTag.Get("locationName")
|
||||||
|
if memberName == "" {
|
||||||
|
memberName = field.Name
|
||||||
|
mTag = reflect.StructTag(string(mTag) + ` locationName:"` + memberName + `"`)
|
||||||
|
}
|
||||||
|
if err := b.buildValue(member, child, mTag); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldAdded = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if fieldAdded { // only append this child if we have one ore more valid members
|
||||||
|
current.AddChild(child)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildList adds the value's list items to the current XMLNode as children nodes. All
|
||||||
|
// nested values in the list are converted to XMLNodes also.
|
||||||
|
func (b *xmlBuilder) buildList(value reflect.Value, current *XMLNode, tag reflect.StructTag) error {
|
||||||
|
if value.IsNil() { // don't build omitted lists
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for unflattened list member
|
||||||
|
flattened := tag.Get("flattened") != ""
|
||||||
|
|
||||||
|
xname := xml.Name{Local: tag.Get("locationName")}
|
||||||
|
if flattened {
|
||||||
|
for i := 0; i < value.Len(); i++ {
|
||||||
|
child := NewXMLElement(xname)
|
||||||
|
current.AddChild(child)
|
||||||
|
if err := b.buildValue(value.Index(i), child, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
list := NewXMLElement(xname)
|
||||||
|
current.AddChild(list)
|
||||||
|
|
||||||
|
for i := 0; i < value.Len(); i++ {
|
||||||
|
iname := tag.Get("locationNameList")
|
||||||
|
if iname == "" {
|
||||||
|
iname = "member"
|
||||||
|
}
|
||||||
|
|
||||||
|
child := NewXMLElement(xml.Name{Local: iname})
|
||||||
|
list.AddChild(child)
|
||||||
|
if err := b.buildValue(value.Index(i), child, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildMap adds the value's key/value pairs to the current XMLNode as children nodes. All
|
||||||
|
// nested values in the map are converted to XMLNodes also.
|
||||||
|
//
|
||||||
|
// Error will be returned if it is unable to build the map's values into XMLNodes
|
||||||
|
func (b *xmlBuilder) buildMap(value reflect.Value, current *XMLNode, tag reflect.StructTag) error {
|
||||||
|
if value.IsNil() { // don't build omitted maps
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
maproot := NewXMLElement(xml.Name{Local: tag.Get("locationName")})
|
||||||
|
current.AddChild(maproot)
|
||||||
|
current = maproot
|
||||||
|
|
||||||
|
kname, vname := "key", "value"
|
||||||
|
if n := tag.Get("locationNameKey"); n != "" {
|
||||||
|
kname = n
|
||||||
|
}
|
||||||
|
if n := tag.Get("locationNameValue"); n != "" {
|
||||||
|
vname = n
|
||||||
|
}
|
||||||
|
|
||||||
|
// sorting is not required for compliance, but it makes testing easier
|
||||||
|
keys := make([]string, value.Len())
|
||||||
|
for i, k := range value.MapKeys() {
|
||||||
|
keys[i] = k.String()
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
for _, k := range keys {
|
||||||
|
v := value.MapIndex(reflect.ValueOf(k))
|
||||||
|
|
||||||
|
mapcur := current
|
||||||
|
if tag.Get("flattened") == "" { // add "entry" tag to non-flat maps
|
||||||
|
child := NewXMLElement(xml.Name{Local: "entry"})
|
||||||
|
mapcur.AddChild(child)
|
||||||
|
mapcur = child
|
||||||
|
}
|
||||||
|
|
||||||
|
kchild := NewXMLElement(xml.Name{Local: kname})
|
||||||
|
kchild.Text = k
|
||||||
|
vchild := NewXMLElement(xml.Name{Local: vname})
|
||||||
|
mapcur.AddChild(kchild)
|
||||||
|
mapcur.AddChild(vchild)
|
||||||
|
|
||||||
|
if err := b.buildValue(v, vchild, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildScalar will convert the value into a string and append it as a attribute or child
|
||||||
|
// of the current XMLNode.
|
||||||
|
//
|
||||||
|
// The value will be added as an attribute if tag contains a "xmlAttribute" attribute value.
|
||||||
|
//
|
||||||
|
// Error will be returned if the value type is unsupported.
|
||||||
|
func (b *xmlBuilder) buildScalar(value reflect.Value, current *XMLNode, tag reflect.StructTag) error {
|
||||||
|
var str string
|
||||||
|
switch converted := value.Interface().(type) {
|
||||||
|
case string:
|
||||||
|
str = converted
|
||||||
|
case []byte:
|
||||||
|
if !value.IsNil() {
|
||||||
|
str = base64.StdEncoding.EncodeToString(converted)
|
||||||
|
}
|
||||||
|
case bool:
|
||||||
|
str = strconv.FormatBool(converted)
|
||||||
|
case int64:
|
||||||
|
str = strconv.FormatInt(converted, 10)
|
||||||
|
case int:
|
||||||
|
str = strconv.Itoa(converted)
|
||||||
|
case float64:
|
||||||
|
str = strconv.FormatFloat(converted, 'f', -1, 64)
|
||||||
|
case float32:
|
||||||
|
str = strconv.FormatFloat(float64(converted), 'f', -1, 32)
|
||||||
|
case time.Time:
|
||||||
|
const ISO8601UTC = "2006-01-02T15:04:05Z"
|
||||||
|
str = converted.UTC().Format(ISO8601UTC)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported value for param %s: %v (%s)",
|
||||||
|
tag.Get("locationName"), value.Interface(), value.Type().Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
xname := xml.Name{Local: tag.Get("locationName")}
|
||||||
|
if tag.Get("xmlAttribute") != "" { // put into current node's attribute list
|
||||||
|
attr := xml.Attr{Name: xname, Value: str}
|
||||||
|
current.Attr = append(current.Attr, attr)
|
||||||
|
} else { // regular text node
|
||||||
|
current.AddChild(&XMLNode{Name: xname, Text: str})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
260
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/xml/xmlutil/unmarshal.go
generated
vendored
Normal file
260
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/xml/xmlutil/unmarshal.go
generated
vendored
Normal file
|
@ -0,0 +1,260 @@
|
||||||
|
package xmlutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnmarshalXML deserializes an xml.Decoder into the container v. V
|
||||||
|
// needs to match the shape of the XML expected to be decoded.
|
||||||
|
// If the shape doesn't match unmarshaling will fail.
|
||||||
|
func UnmarshalXML(v interface{}, d *xml.Decoder, wrapper string) error {
|
||||||
|
n, _ := XMLToStruct(d, nil)
|
||||||
|
if n.Children != nil {
|
||||||
|
for _, root := range n.Children {
|
||||||
|
for _, c := range root {
|
||||||
|
if wrappedChild, ok := c.Children[wrapper]; ok {
|
||||||
|
c = wrappedChild[0] // pull out wrapped element
|
||||||
|
}
|
||||||
|
|
||||||
|
err := parse(reflect.ValueOf(v), c, "")
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse deserializes any value from the XMLNode. The type tag is used to infer the type, or reflect
|
||||||
|
// will be used to determine the type from r.
|
||||||
|
func parse(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
||||||
|
rtype := r.Type()
|
||||||
|
if rtype.Kind() == reflect.Ptr {
|
||||||
|
rtype = rtype.Elem() // check kind of actual element type
|
||||||
|
}
|
||||||
|
|
||||||
|
t := tag.Get("type")
|
||||||
|
if t == "" {
|
||||||
|
switch rtype.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
t = "structure"
|
||||||
|
case reflect.Slice:
|
||||||
|
t = "list"
|
||||||
|
case reflect.Map:
|
||||||
|
t = "map"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t {
|
||||||
|
case "structure":
|
||||||
|
if field, ok := rtype.FieldByName("SDKShapeTraits"); ok {
|
||||||
|
tag = field.Tag
|
||||||
|
}
|
||||||
|
return parseStruct(r, node, tag)
|
||||||
|
case "list":
|
||||||
|
return parseList(r, node, tag)
|
||||||
|
case "map":
|
||||||
|
return parseMap(r, node, tag)
|
||||||
|
default:
|
||||||
|
return parseScalar(r, node, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseStruct deserializes a structure and its fields from an XMLNode. Any nested
|
||||||
|
// types in the structure will also be deserialized.
|
||||||
|
func parseStruct(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
||||||
|
t := r.Type()
|
||||||
|
if r.Kind() == reflect.Ptr {
|
||||||
|
if r.IsNil() { // create the structure if it's nil
|
||||||
|
s := reflect.New(r.Type().Elem())
|
||||||
|
r.Set(s)
|
||||||
|
r = s
|
||||||
|
}
|
||||||
|
|
||||||
|
r = r.Elem()
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
// unwrap any payloads
|
||||||
|
if payload := tag.Get("payload"); payload != "" {
|
||||||
|
field, _ := t.FieldByName(payload)
|
||||||
|
return parseStruct(r.FieldByName(payload), node, field.Tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
if c := field.Name[0:1]; strings.ToLower(c) == c {
|
||||||
|
continue // ignore unexported fields
|
||||||
|
}
|
||||||
|
|
||||||
|
// figure out what this field is called
|
||||||
|
name := field.Name
|
||||||
|
if field.Tag.Get("flattened") != "" && field.Tag.Get("locationNameList") != "" {
|
||||||
|
name = field.Tag.Get("locationNameList")
|
||||||
|
} else if locName := field.Tag.Get("locationName"); locName != "" {
|
||||||
|
name = locName
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to find the field by name in elements
|
||||||
|
elems := node.Children[name]
|
||||||
|
|
||||||
|
if elems == nil { // try to find the field in attributes
|
||||||
|
for _, a := range node.Attr {
|
||||||
|
if name == a.Name.Local {
|
||||||
|
// turn this into a text node for de-serializing
|
||||||
|
elems = []*XMLNode{{Text: a.Value}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
member := r.FieldByName(field.Name)
|
||||||
|
for _, elem := range elems {
|
||||||
|
err := parse(member, elem, field.Tag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseList deserializes a list of values from an XML node. Each list entry
|
||||||
|
// will also be deserialized.
|
||||||
|
func parseList(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
||||||
|
t := r.Type()
|
||||||
|
|
||||||
|
if tag.Get("flattened") == "" { // look at all item entries
|
||||||
|
mname := "member"
|
||||||
|
if name := tag.Get("locationNameList"); name != "" {
|
||||||
|
mname = name
|
||||||
|
}
|
||||||
|
|
||||||
|
if Children, ok := node.Children[mname]; ok {
|
||||||
|
if r.IsNil() {
|
||||||
|
r.Set(reflect.MakeSlice(t, len(Children), len(Children)))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range Children {
|
||||||
|
err := parse(r.Index(i), c, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // flattened list means this is a single element
|
||||||
|
if r.IsNil() {
|
||||||
|
r.Set(reflect.MakeSlice(t, 0, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
childR := reflect.Zero(t.Elem())
|
||||||
|
r.Set(reflect.Append(r, childR))
|
||||||
|
err := parse(r.Index(r.Len()-1), node, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseMap deserializes a map from an XMLNode. The direct children of the XMLNode
|
||||||
|
// will also be deserialized as map entries.
|
||||||
|
func parseMap(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
||||||
|
if r.IsNil() {
|
||||||
|
r.Set(reflect.MakeMap(r.Type()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if tag.Get("flattened") == "" { // look at all child entries
|
||||||
|
for _, entry := range node.Children["entry"] {
|
||||||
|
parseMapEntry(r, entry, tag)
|
||||||
|
}
|
||||||
|
} else { // this element is itself an entry
|
||||||
|
parseMapEntry(r, node, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseMapEntry deserializes a map entry from a XML node.
|
||||||
|
func parseMapEntry(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
||||||
|
kname, vname := "key", "value"
|
||||||
|
if n := tag.Get("locationNameKey"); n != "" {
|
||||||
|
kname = n
|
||||||
|
}
|
||||||
|
if n := tag.Get("locationNameValue"); n != "" {
|
||||||
|
vname = n
|
||||||
|
}
|
||||||
|
|
||||||
|
keys, ok := node.Children[kname]
|
||||||
|
values := node.Children[vname]
|
||||||
|
if ok {
|
||||||
|
for i, key := range keys {
|
||||||
|
keyR := reflect.ValueOf(key.Text)
|
||||||
|
value := values[i]
|
||||||
|
valueR := reflect.New(r.Type().Elem()).Elem()
|
||||||
|
|
||||||
|
parse(valueR, value, "")
|
||||||
|
r.SetMapIndex(keyR, valueR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseScaller deserializes an XMLNode value into a concrete type based on the
|
||||||
|
// interface type of r.
|
||||||
|
//
|
||||||
|
// Error is returned if the deserialization fails due to invalid type conversion,
|
||||||
|
// or unsupported interface type.
|
||||||
|
func parseScalar(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
||||||
|
switch r.Interface().(type) {
|
||||||
|
case *string:
|
||||||
|
r.Set(reflect.ValueOf(&node.Text))
|
||||||
|
return nil
|
||||||
|
case []byte:
|
||||||
|
b, err := base64.StdEncoding.DecodeString(node.Text)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.Set(reflect.ValueOf(b))
|
||||||
|
case *bool:
|
||||||
|
v, err := strconv.ParseBool(node.Text)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.Set(reflect.ValueOf(&v))
|
||||||
|
case *int64:
|
||||||
|
v, err := strconv.ParseInt(node.Text, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.Set(reflect.ValueOf(&v))
|
||||||
|
case *float64:
|
||||||
|
v, err := strconv.ParseFloat(node.Text, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.Set(reflect.ValueOf(&v))
|
||||||
|
case *time.Time:
|
||||||
|
const ISO8601UTC = "2006-01-02T15:04:05Z"
|
||||||
|
t, err := time.Parse(ISO8601UTC, node.Text)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.Set(reflect.ValueOf(&t))
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported value: %v (%s)", r.Interface(), r.Type())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
105
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/xml/xmlutil/xml_to_struct.go
generated
vendored
Normal file
105
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/protocol/xml/xmlutil/xml_to_struct.go
generated
vendored
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
package xmlutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"io"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A XMLNode contains the values to be encoded or decoded.
|
||||||
|
type XMLNode struct {
|
||||||
|
Name xml.Name `json:",omitempty"`
|
||||||
|
Children map[string][]*XMLNode `json:",omitempty"`
|
||||||
|
Text string `json:",omitempty"`
|
||||||
|
Attr []xml.Attr `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewXMLElement returns a pointer to a new XMLNode initialized to default values.
|
||||||
|
func NewXMLElement(name xml.Name) *XMLNode {
|
||||||
|
return &XMLNode{
|
||||||
|
Name: name,
|
||||||
|
Children: map[string][]*XMLNode{},
|
||||||
|
Attr: []xml.Attr{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddChild adds child to the XMLNode.
|
||||||
|
func (n *XMLNode) AddChild(child *XMLNode) {
|
||||||
|
if _, ok := n.Children[child.Name.Local]; !ok {
|
||||||
|
n.Children[child.Name.Local] = []*XMLNode{}
|
||||||
|
}
|
||||||
|
n.Children[child.Name.Local] = append(n.Children[child.Name.Local], child)
|
||||||
|
}
|
||||||
|
|
||||||
|
// XMLToStruct converts a xml.Decoder stream to XMLNode with nested values.
|
||||||
|
func XMLToStruct(d *xml.Decoder, s *xml.StartElement) (*XMLNode, error) {
|
||||||
|
out := &XMLNode{}
|
||||||
|
for {
|
||||||
|
tok, err := d.Token()
|
||||||
|
if tok == nil || err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typed := tok.(type) {
|
||||||
|
case xml.CharData:
|
||||||
|
out.Text = string(typed.Copy())
|
||||||
|
case xml.StartElement:
|
||||||
|
el := typed.Copy()
|
||||||
|
out.Attr = el.Attr
|
||||||
|
if out.Children == nil {
|
||||||
|
out.Children = map[string][]*XMLNode{}
|
||||||
|
}
|
||||||
|
|
||||||
|
name := typed.Name.Local
|
||||||
|
slice := out.Children[name]
|
||||||
|
if slice == nil {
|
||||||
|
slice = []*XMLNode{}
|
||||||
|
}
|
||||||
|
node, e := XMLToStruct(d, &el)
|
||||||
|
if e != nil {
|
||||||
|
return out, e
|
||||||
|
}
|
||||||
|
node.Name = typed.Name
|
||||||
|
slice = append(slice, node)
|
||||||
|
out.Children[name] = slice
|
||||||
|
case xml.EndElement:
|
||||||
|
if s != nil && s.Name.Local == typed.Name.Local { // matching end token
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StructToXML writes an XMLNode to a xml.Encoder as tokens.
|
||||||
|
func StructToXML(e *xml.Encoder, node *XMLNode, sorted bool) error {
|
||||||
|
e.EncodeToken(xml.StartElement{Name: node.Name, Attr: node.Attr})
|
||||||
|
|
||||||
|
if node.Text != "" {
|
||||||
|
e.EncodeToken(xml.CharData([]byte(node.Text)))
|
||||||
|
} else if sorted {
|
||||||
|
sortedNames := []string{}
|
||||||
|
for k := range node.Children {
|
||||||
|
sortedNames = append(sortedNames, k)
|
||||||
|
}
|
||||||
|
sort.Strings(sortedNames)
|
||||||
|
|
||||||
|
for _, k := range sortedNames {
|
||||||
|
for _, v := range node.Children[k] {
|
||||||
|
StructToXML(e, v, sorted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, c := range node.Children {
|
||||||
|
for _, v := range c {
|
||||||
|
StructToXML(e, v, sorted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
e.EncodeToken(xml.EndElement{Name: node.Name})
|
||||||
|
return e.Flush()
|
||||||
|
}
|
43
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/signer/v4/functional_test.go
generated
vendored
Normal file
43
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/signer/v4/functional_test.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package v4_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/test/unit"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = unit.Imported
|
||||||
|
|
||||||
|
func TestPresignHandler(t *testing.T) {
|
||||||
|
svc := s3.New(nil)
|
||||||
|
req, _ := svc.PutObjectRequest(&s3.PutObjectInput{
|
||||||
|
Bucket: aws.String("bucket"),
|
||||||
|
Key: aws.String("key"),
|
||||||
|
ContentDisposition: aws.String("a+b c$d"),
|
||||||
|
ACL: aws.String("public-read"),
|
||||||
|
})
|
||||||
|
req.Time = time.Unix(0, 0)
|
||||||
|
urlstr, err := req.Presign(5 * time.Minute)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
expectedDate := "19700101T000000Z"
|
||||||
|
expectedHeaders := "host;x-amz-acl"
|
||||||
|
expectedSig := "7edcb4e3a1bf12f4989018d75acbe3a7f03df24bd6f3112602d59fc551f0e4e2"
|
||||||
|
expectedCred := "AKID/19700101/mock-region/s3/aws4_request"
|
||||||
|
|
||||||
|
u, _ := url.Parse(urlstr)
|
||||||
|
urlQ := u.Query()
|
||||||
|
assert.Equal(t, expectedSig, urlQ.Get("X-Amz-Signature"))
|
||||||
|
assert.Equal(t, expectedCred, urlQ.Get("X-Amz-Credential"))
|
||||||
|
assert.Equal(t, expectedHeaders, urlQ.Get("X-Amz-SignedHeaders"))
|
||||||
|
assert.Equal(t, expectedDate, urlQ.Get("X-Amz-Date"))
|
||||||
|
assert.Equal(t, "300", urlQ.Get("X-Amz-Expires"))
|
||||||
|
|
||||||
|
assert.NotContains(t, urlstr, "+") // + encoded as %20
|
||||||
|
}
|
364
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/signer/v4/v4.go
generated
vendored
Normal file
364
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/signer/v4/v4.go
generated
vendored
Normal file
|
@ -0,0 +1,364 @@
|
||||||
|
// Package v4 implements signing for AWS V4 signer
|
||||||
|
package v4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/protocol/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
authHeaderPrefix = "AWS4-HMAC-SHA256"
|
||||||
|
timeFormat = "20060102T150405Z"
|
||||||
|
shortTimeFormat = "20060102"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ignoredHeaders = map[string]bool{
|
||||||
|
"Authorization": true,
|
||||||
|
"Content-Type": true,
|
||||||
|
"Content-Length": true,
|
||||||
|
"User-Agent": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
type signer struct {
|
||||||
|
Request *http.Request
|
||||||
|
Time time.Time
|
||||||
|
ExpireTime time.Duration
|
||||||
|
ServiceName string
|
||||||
|
Region string
|
||||||
|
CredValues credentials.Value
|
||||||
|
Credentials *credentials.Credentials
|
||||||
|
Query url.Values
|
||||||
|
Body io.ReadSeeker
|
||||||
|
Debug aws.LogLevelType
|
||||||
|
Logger aws.Logger
|
||||||
|
|
||||||
|
isPresign bool
|
||||||
|
formattedTime string
|
||||||
|
formattedShortTime string
|
||||||
|
|
||||||
|
signedHeaders string
|
||||||
|
canonicalHeaders string
|
||||||
|
canonicalString string
|
||||||
|
credentialString string
|
||||||
|
stringToSign string
|
||||||
|
signature string
|
||||||
|
authorization string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign requests with signature version 4.
|
||||||
|
//
|
||||||
|
// Will sign the requests with the service config's Credentials object
|
||||||
|
// Signing is skipped if the credentials is the credentials.AnonymousCredentials
|
||||||
|
// object.
|
||||||
|
func Sign(req *aws.Request) {
|
||||||
|
// If the request does not need to be signed ignore the signing of the
|
||||||
|
// request if the AnonymousCredentials object is used.
|
||||||
|
if req.Service.Config.Credentials == credentials.AnonymousCredentials {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
region := req.Service.SigningRegion
|
||||||
|
if region == "" {
|
||||||
|
region = aws.StringValue(req.Service.Config.Region)
|
||||||
|
}
|
||||||
|
|
||||||
|
name := req.Service.SigningName
|
||||||
|
if name == "" {
|
||||||
|
name = req.Service.ServiceName
|
||||||
|
}
|
||||||
|
|
||||||
|
s := signer{
|
||||||
|
Request: req.HTTPRequest,
|
||||||
|
Time: req.Time,
|
||||||
|
ExpireTime: req.ExpireTime,
|
||||||
|
Query: req.HTTPRequest.URL.Query(),
|
||||||
|
Body: req.Body,
|
||||||
|
ServiceName: name,
|
||||||
|
Region: region,
|
||||||
|
Credentials: req.Service.Config.Credentials,
|
||||||
|
Debug: req.Service.Config.LogLevel.Value(),
|
||||||
|
Logger: req.Service.Config.Logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Error = s.sign()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v4 *signer) sign() error {
|
||||||
|
if v4.ExpireTime != 0 {
|
||||||
|
v4.isPresign = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if v4.isRequestSigned() {
|
||||||
|
if !v4.Credentials.IsExpired() {
|
||||||
|
// If the request is already signed, and the credentials have not
|
||||||
|
// expired yet ignore the signing request.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The credentials have expired for this request. The current signing
|
||||||
|
// is invalid, and needs to be request because the request will fail.
|
||||||
|
if v4.isPresign {
|
||||||
|
v4.removePresign()
|
||||||
|
// Update the request's query string to ensure the values stays in
|
||||||
|
// sync in the case retrieving the new credentials fails.
|
||||||
|
v4.Request.URL.RawQuery = v4.Query.Encode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
v4.CredValues, err = v4.Credentials.Get()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if v4.isPresign {
|
||||||
|
v4.Query.Set("X-Amz-Algorithm", authHeaderPrefix)
|
||||||
|
if v4.CredValues.SessionToken != "" {
|
||||||
|
v4.Query.Set("X-Amz-Security-Token", v4.CredValues.SessionToken)
|
||||||
|
} else {
|
||||||
|
v4.Query.Del("X-Amz-Security-Token")
|
||||||
|
}
|
||||||
|
} else if v4.CredValues.SessionToken != "" {
|
||||||
|
v4.Request.Header.Set("X-Amz-Security-Token", v4.CredValues.SessionToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
v4.build()
|
||||||
|
|
||||||
|
if v4.Debug.Matches(aws.LogDebugWithSigning) {
|
||||||
|
v4.logSigningInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const logSignInfoMsg = `DEBUG: Request Signiture:
|
||||||
|
---[ CANONICAL STRING ]-----------------------------
|
||||||
|
%s
|
||||||
|
---[ STRING TO SIGN ]--------------------------------
|
||||||
|
%s%s
|
||||||
|
-----------------------------------------------------`
|
||||||
|
const logSignedURLMsg = `
|
||||||
|
---[ SIGNED URL ]------------------------------------
|
||||||
|
%s`
|
||||||
|
|
||||||
|
func (v4 *signer) logSigningInfo() {
|
||||||
|
signedURLMsg := ""
|
||||||
|
if v4.isPresign {
|
||||||
|
signedURLMsg = fmt.Sprintf(logSignedURLMsg, v4.Request.URL.String())
|
||||||
|
}
|
||||||
|
msg := fmt.Sprintf(logSignInfoMsg, v4.canonicalString, v4.stringToSign, signedURLMsg)
|
||||||
|
v4.Logger.Log(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v4 *signer) build() {
|
||||||
|
v4.buildTime() // no depends
|
||||||
|
v4.buildCredentialString() // no depends
|
||||||
|
if v4.isPresign {
|
||||||
|
v4.buildQuery() // no depends
|
||||||
|
}
|
||||||
|
v4.buildCanonicalHeaders() // depends on cred string
|
||||||
|
v4.buildCanonicalString() // depends on canon headers / signed headers
|
||||||
|
v4.buildStringToSign() // depends on canon string
|
||||||
|
v4.buildSignature() // depends on string to sign
|
||||||
|
|
||||||
|
if v4.isPresign {
|
||||||
|
v4.Request.URL.RawQuery += "&X-Amz-Signature=" + v4.signature
|
||||||
|
} else {
|
||||||
|
parts := []string{
|
||||||
|
authHeaderPrefix + " Credential=" + v4.CredValues.AccessKeyID + "/" + v4.credentialString,
|
||||||
|
"SignedHeaders=" + v4.signedHeaders,
|
||||||
|
"Signature=" + v4.signature,
|
||||||
|
}
|
||||||
|
v4.Request.Header.Set("Authorization", strings.Join(parts, ", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v4 *signer) buildTime() {
|
||||||
|
v4.formattedTime = v4.Time.UTC().Format(timeFormat)
|
||||||
|
v4.formattedShortTime = v4.Time.UTC().Format(shortTimeFormat)
|
||||||
|
|
||||||
|
if v4.isPresign {
|
||||||
|
duration := int64(v4.ExpireTime / time.Second)
|
||||||
|
v4.Query.Set("X-Amz-Date", v4.formattedTime)
|
||||||
|
v4.Query.Set("X-Amz-Expires", strconv.FormatInt(duration, 10))
|
||||||
|
} else {
|
||||||
|
v4.Request.Header.Set("X-Amz-Date", v4.formattedTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v4 *signer) buildCredentialString() {
|
||||||
|
v4.credentialString = strings.Join([]string{
|
||||||
|
v4.formattedShortTime,
|
||||||
|
v4.Region,
|
||||||
|
v4.ServiceName,
|
||||||
|
"aws4_request",
|
||||||
|
}, "/")
|
||||||
|
|
||||||
|
if v4.isPresign {
|
||||||
|
v4.Query.Set("X-Amz-Credential", v4.CredValues.AccessKeyID+"/"+v4.credentialString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v4 *signer) buildQuery() {
|
||||||
|
for k, h := range v4.Request.Header {
|
||||||
|
if strings.HasPrefix(http.CanonicalHeaderKey(k), "X-Amz-") {
|
||||||
|
continue // never hoist x-amz-* headers, they must be signed
|
||||||
|
}
|
||||||
|
if _, ok := ignoredHeaders[http.CanonicalHeaderKey(k)]; ok {
|
||||||
|
continue // never hoist ignored headers
|
||||||
|
}
|
||||||
|
|
||||||
|
v4.Request.Header.Del(k)
|
||||||
|
v4.Query.Del(k)
|
||||||
|
for _, v := range h {
|
||||||
|
v4.Query.Add(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v4 *signer) buildCanonicalHeaders() {
|
||||||
|
var headers []string
|
||||||
|
headers = append(headers, "host")
|
||||||
|
for k := range v4.Request.Header {
|
||||||
|
if _, ok := ignoredHeaders[http.CanonicalHeaderKey(k)]; ok {
|
||||||
|
continue // ignored header
|
||||||
|
}
|
||||||
|
headers = append(headers, strings.ToLower(k))
|
||||||
|
}
|
||||||
|
sort.Strings(headers)
|
||||||
|
|
||||||
|
v4.signedHeaders = strings.Join(headers, ";")
|
||||||
|
|
||||||
|
if v4.isPresign {
|
||||||
|
v4.Query.Set("X-Amz-SignedHeaders", v4.signedHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
headerValues := make([]string, len(headers))
|
||||||
|
for i, k := range headers {
|
||||||
|
if k == "host" {
|
||||||
|
headerValues[i] = "host:" + v4.Request.URL.Host
|
||||||
|
} else {
|
||||||
|
headerValues[i] = k + ":" +
|
||||||
|
strings.Join(v4.Request.Header[http.CanonicalHeaderKey(k)], ",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v4.canonicalHeaders = strings.Join(headerValues, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v4 *signer) buildCanonicalString() {
|
||||||
|
v4.Request.URL.RawQuery = strings.Replace(v4.Query.Encode(), "+", "%20", -1)
|
||||||
|
uri := v4.Request.URL.Opaque
|
||||||
|
if uri != "" {
|
||||||
|
uri = "/" + strings.Join(strings.Split(uri, "/")[3:], "/")
|
||||||
|
} else {
|
||||||
|
uri = v4.Request.URL.Path
|
||||||
|
}
|
||||||
|
if uri == "" {
|
||||||
|
uri = "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
if v4.ServiceName != "s3" {
|
||||||
|
uri = rest.EscapePath(uri, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
v4.canonicalString = strings.Join([]string{
|
||||||
|
v4.Request.Method,
|
||||||
|
uri,
|
||||||
|
v4.Request.URL.RawQuery,
|
||||||
|
v4.canonicalHeaders + "\n",
|
||||||
|
v4.signedHeaders,
|
||||||
|
v4.bodyDigest(),
|
||||||
|
}, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v4 *signer) buildStringToSign() {
|
||||||
|
v4.stringToSign = strings.Join([]string{
|
||||||
|
authHeaderPrefix,
|
||||||
|
v4.formattedTime,
|
||||||
|
v4.credentialString,
|
||||||
|
hex.EncodeToString(makeSha256([]byte(v4.canonicalString))),
|
||||||
|
}, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v4 *signer) buildSignature() {
|
||||||
|
secret := v4.CredValues.SecretAccessKey
|
||||||
|
date := makeHmac([]byte("AWS4"+secret), []byte(v4.formattedShortTime))
|
||||||
|
region := makeHmac(date, []byte(v4.Region))
|
||||||
|
service := makeHmac(region, []byte(v4.ServiceName))
|
||||||
|
credentials := makeHmac(service, []byte("aws4_request"))
|
||||||
|
signature := makeHmac(credentials, []byte(v4.stringToSign))
|
||||||
|
v4.signature = hex.EncodeToString(signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v4 *signer) bodyDigest() string {
|
||||||
|
hash := v4.Request.Header.Get("X-Amz-Content-Sha256")
|
||||||
|
if hash == "" {
|
||||||
|
if v4.isPresign && v4.ServiceName == "s3" {
|
||||||
|
hash = "UNSIGNED-PAYLOAD"
|
||||||
|
} else if v4.Body == nil {
|
||||||
|
hash = hex.EncodeToString(makeSha256([]byte{}))
|
||||||
|
} else {
|
||||||
|
hash = hex.EncodeToString(makeSha256Reader(v4.Body))
|
||||||
|
}
|
||||||
|
v4.Request.Header.Add("X-Amz-Content-Sha256", hash)
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// isRequestSigned returns if the request is currently signed or presigned
|
||||||
|
func (v4 *signer) isRequestSigned() bool {
|
||||||
|
if v4.isPresign && v4.Query.Get("X-Amz-Signature") != "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if v4.Request.Header.Get("Authorization") != "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsign removes signing flags for both signed and presigned requests.
|
||||||
|
func (v4 *signer) removePresign() {
|
||||||
|
v4.Query.Del("X-Amz-Algorithm")
|
||||||
|
v4.Query.Del("X-Amz-Signature")
|
||||||
|
v4.Query.Del("X-Amz-Security-Token")
|
||||||
|
v4.Query.Del("X-Amz-Date")
|
||||||
|
v4.Query.Del("X-Amz-Expires")
|
||||||
|
v4.Query.Del("X-Amz-Credential")
|
||||||
|
v4.Query.Del("X-Amz-SignedHeaders")
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeHmac(key []byte, data []byte) []byte {
|
||||||
|
hash := hmac.New(sha256.New, key)
|
||||||
|
hash.Write(data)
|
||||||
|
return hash.Sum(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeSha256(data []byte) []byte {
|
||||||
|
hash := sha256.New()
|
||||||
|
hash.Write(data)
|
||||||
|
return hash.Sum(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeSha256Reader(reader io.ReadSeeker) []byte {
|
||||||
|
hash := sha256.New()
|
||||||
|
start, _ := reader.Seek(0, 1)
|
||||||
|
defer reader.Seek(start, 0)
|
||||||
|
|
||||||
|
io.Copy(hash, reader)
|
||||||
|
return hash.Sum(nil)
|
||||||
|
}
|
245
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/signer/v4/v4_test.go
generated
vendored
Normal file
245
Godeps/_workspace/src/github.com/aws/aws-sdk-go/internal/signer/v4/v4_test.go
generated
vendored
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
package v4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func buildSigner(serviceName string, region string, signTime time.Time, expireTime time.Duration, body string) signer {
|
||||||
|
endpoint := "https://" + serviceName + "." + region + ".amazonaws.com"
|
||||||
|
reader := strings.NewReader(body)
|
||||||
|
req, _ := http.NewRequest("POST", endpoint, reader)
|
||||||
|
req.URL.Opaque = "//example.org/bucket/key-._~,!@#$%^&*()"
|
||||||
|
req.Header.Add("X-Amz-Target", "prefix.Operation")
|
||||||
|
req.Header.Add("Content-Type", "application/x-amz-json-1.0")
|
||||||
|
req.Header.Add("Content-Length", string(len(body)))
|
||||||
|
req.Header.Add("X-Amz-Meta-Other-Header", "some-value=!@#$%^&* (+)")
|
||||||
|
|
||||||
|
return signer{
|
||||||
|
Request: req,
|
||||||
|
Time: signTime,
|
||||||
|
ExpireTime: expireTime,
|
||||||
|
Query: req.URL.Query(),
|
||||||
|
Body: reader,
|
||||||
|
ServiceName: serviceName,
|
||||||
|
Region: region,
|
||||||
|
Credentials: credentials.NewStaticCredentials("AKID", "SECRET", "SESSION"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeWS(text string) string {
|
||||||
|
text = strings.Replace(text, " ", "", -1)
|
||||||
|
text = strings.Replace(text, "\n", "", -1)
|
||||||
|
text = strings.Replace(text, "\t", "", -1)
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertEqual(t *testing.T, expected, given string) {
|
||||||
|
if removeWS(expected) != removeWS(given) {
|
||||||
|
t.Errorf("\nExpected: %s\nGiven: %s", expected, given)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPresignRequest(t *testing.T) {
|
||||||
|
signer := buildSigner("dynamodb", "us-east-1", time.Unix(0, 0), 300*time.Second, "{}")
|
||||||
|
signer.sign()
|
||||||
|
|
||||||
|
expectedDate := "19700101T000000Z"
|
||||||
|
expectedHeaders := "host;x-amz-meta-other-header;x-amz-target"
|
||||||
|
expectedSig := "5eeedebf6f995145ce56daa02902d10485246d3defb34f97b973c1f40ab82d36"
|
||||||
|
expectedCred := "AKID/19700101/us-east-1/dynamodb/aws4_request"
|
||||||
|
|
||||||
|
q := signer.Request.URL.Query()
|
||||||
|
assert.Equal(t, expectedSig, q.Get("X-Amz-Signature"))
|
||||||
|
assert.Equal(t, expectedCred, q.Get("X-Amz-Credential"))
|
||||||
|
assert.Equal(t, expectedHeaders, q.Get("X-Amz-SignedHeaders"))
|
||||||
|
assert.Equal(t, expectedDate, q.Get("X-Amz-Date"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSignRequest(t *testing.T) {
|
||||||
|
signer := buildSigner("dynamodb", "us-east-1", time.Unix(0, 0), 0, "{}")
|
||||||
|
signer.sign()
|
||||||
|
|
||||||
|
expectedDate := "19700101T000000Z"
|
||||||
|
expectedSig := "AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/dynamodb/aws4_request, SignedHeaders=host;x-amz-date;x-amz-meta-other-header;x-amz-security-token;x-amz-target, Signature=69ada33fec48180dab153576e4dd80c4e04124f80dda3eccfed8a67c2b91ed5e"
|
||||||
|
|
||||||
|
q := signer.Request.Header
|
||||||
|
assert.Equal(t, expectedSig, q.Get("Authorization"))
|
||||||
|
assert.Equal(t, expectedDate, q.Get("X-Amz-Date"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSignEmptyBody(t *testing.T) {
|
||||||
|
signer := buildSigner("dynamodb", "us-east-1", time.Now(), 0, "")
|
||||||
|
signer.Body = nil
|
||||||
|
signer.sign()
|
||||||
|
hash := signer.Request.Header.Get("X-Amz-Content-Sha256")
|
||||||
|
assert.Equal(t, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSignBody(t *testing.T) {
|
||||||
|
signer := buildSigner("dynamodb", "us-east-1", time.Now(), 0, "hello")
|
||||||
|
signer.sign()
|
||||||
|
hash := signer.Request.Header.Get("X-Amz-Content-Sha256")
|
||||||
|
assert.Equal(t, "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSignSeekedBody(t *testing.T) {
|
||||||
|
signer := buildSigner("dynamodb", "us-east-1", time.Now(), 0, " hello")
|
||||||
|
signer.Body.Read(make([]byte, 3)) // consume first 3 bytes so body is now "hello"
|
||||||
|
signer.sign()
|
||||||
|
hash := signer.Request.Header.Get("X-Amz-Content-Sha256")
|
||||||
|
assert.Equal(t, "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", hash)
|
||||||
|
|
||||||
|
start, _ := signer.Body.Seek(0, 1)
|
||||||
|
assert.Equal(t, int64(3), start)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPresignEmptyBodyS3(t *testing.T) {
|
||||||
|
signer := buildSigner("s3", "us-east-1", time.Now(), 5*time.Minute, "hello")
|
||||||
|
signer.sign()
|
||||||
|
hash := signer.Request.Header.Get("X-Amz-Content-Sha256")
|
||||||
|
assert.Equal(t, "UNSIGNED-PAYLOAD", hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSignPrecomputedBodyChecksum(t *testing.T) {
|
||||||
|
signer := buildSigner("dynamodb", "us-east-1", time.Now(), 0, "hello")
|
||||||
|
signer.Request.Header.Set("X-Amz-Content-Sha256", "PRECOMPUTED")
|
||||||
|
signer.sign()
|
||||||
|
hash := signer.Request.Header.Get("X-Amz-Content-Sha256")
|
||||||
|
assert.Equal(t, "PRECOMPUTED", hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnonymousCredentials(t *testing.T) {
|
||||||
|
r := aws.NewRequest(
|
||||||
|
aws.NewService(&aws.Config{Credentials: credentials.AnonymousCredentials}),
|
||||||
|
&aws.Operation{
|
||||||
|
Name: "BatchGetItem",
|
||||||
|
HTTPMethod: "POST",
|
||||||
|
HTTPPath: "/",
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
Sign(r)
|
||||||
|
|
||||||
|
urlQ := r.HTTPRequest.URL.Query()
|
||||||
|
assert.Empty(t, urlQ.Get("X-Amz-Signature"))
|
||||||
|
assert.Empty(t, urlQ.Get("X-Amz-Credential"))
|
||||||
|
assert.Empty(t, urlQ.Get("X-Amz-SignedHeaders"))
|
||||||
|
assert.Empty(t, urlQ.Get("X-Amz-Date"))
|
||||||
|
|
||||||
|
hQ := r.HTTPRequest.Header
|
||||||
|
assert.Empty(t, hQ.Get("Authorization"))
|
||||||
|
assert.Empty(t, hQ.Get("X-Amz-Date"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIgnoreResignRequestWithValidCreds(t *testing.T) {
|
||||||
|
r := aws.NewRequest(
|
||||||
|
aws.NewService(&aws.Config{
|
||||||
|
Credentials: credentials.NewStaticCredentials("AKID", "SECRET", "SESSION"),
|
||||||
|
Region: aws.String("us-west-2"),
|
||||||
|
}),
|
||||||
|
&aws.Operation{
|
||||||
|
Name: "BatchGetItem",
|
||||||
|
HTTPMethod: "POST",
|
||||||
|
HTTPPath: "/",
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
|
||||||
|
Sign(r)
|
||||||
|
sig := r.HTTPRequest.Header.Get("Authorization")
|
||||||
|
|
||||||
|
Sign(r)
|
||||||
|
assert.Equal(t, sig, r.HTTPRequest.Header.Get("Authorization"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIgnorePreResignRequestWithValidCreds(t *testing.T) {
|
||||||
|
r := aws.NewRequest(
|
||||||
|
aws.NewService(&aws.Config{
|
||||||
|
Credentials: credentials.NewStaticCredentials("AKID", "SECRET", "SESSION"),
|
||||||
|
Region: aws.String("us-west-2"),
|
||||||
|
}),
|
||||||
|
&aws.Operation{
|
||||||
|
Name: "BatchGetItem",
|
||||||
|
HTTPMethod: "POST",
|
||||||
|
HTTPPath: "/",
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
r.ExpireTime = time.Minute * 10
|
||||||
|
|
||||||
|
Sign(r)
|
||||||
|
sig := r.HTTPRequest.Header.Get("X-Amz-Signature")
|
||||||
|
|
||||||
|
Sign(r)
|
||||||
|
assert.Equal(t, sig, r.HTTPRequest.Header.Get("X-Amz-Signature"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResignRequestExpiredCreds(t *testing.T) {
|
||||||
|
creds := credentials.NewStaticCredentials("AKID", "SECRET", "SESSION")
|
||||||
|
r := aws.NewRequest(
|
||||||
|
aws.NewService(&aws.Config{Credentials: creds}),
|
||||||
|
&aws.Operation{
|
||||||
|
Name: "BatchGetItem",
|
||||||
|
HTTPMethod: "POST",
|
||||||
|
HTTPPath: "/",
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
Sign(r)
|
||||||
|
querySig := r.HTTPRequest.Header.Get("Authorization")
|
||||||
|
|
||||||
|
creds.Expire()
|
||||||
|
|
||||||
|
Sign(r)
|
||||||
|
assert.NotEqual(t, querySig, r.HTTPRequest.Header.Get("Authorization"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPreResignRequestExpiredCreds(t *testing.T) {
|
||||||
|
provider := &credentials.StaticProvider{credentials.Value{"AKID", "SECRET", "SESSION"}}
|
||||||
|
creds := credentials.NewCredentials(provider)
|
||||||
|
r := aws.NewRequest(
|
||||||
|
aws.NewService(&aws.Config{Credentials: creds}),
|
||||||
|
&aws.Operation{
|
||||||
|
Name: "BatchGetItem",
|
||||||
|
HTTPMethod: "POST",
|
||||||
|
HTTPPath: "/",
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
r.ExpireTime = time.Minute * 10
|
||||||
|
|
||||||
|
Sign(r)
|
||||||
|
querySig := r.HTTPRequest.URL.Query().Get("X-Amz-Signature")
|
||||||
|
|
||||||
|
creds.Expire()
|
||||||
|
r.Time = time.Now().Add(time.Hour * 48)
|
||||||
|
|
||||||
|
Sign(r)
|
||||||
|
assert.NotEqual(t, querySig, r.HTTPRequest.URL.Query().Get("X-Amz-Signature"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkPresignRequest(b *testing.B) {
|
||||||
|
signer := buildSigner("dynamodb", "us-east-1", time.Now(), 300*time.Second, "{}")
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
signer.sign()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSignRequest(b *testing.B) {
|
||||||
|
signer := buildSigner("dynamodb", "us-east-1", time.Now(), 0, "{}")
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
signer.sign()
|
||||||
|
}
|
||||||
|
}
|
6637
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/api.go
generated
vendored
Normal file
6637
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/api.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
42
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/bucket_location.go
generated
vendored
Normal file
42
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/bucket_location.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package s3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var reBucketLocation = regexp.MustCompile(`>([^<>]+)<\/Location`)
|
||||||
|
|
||||||
|
func buildGetBucketLocation(r *aws.Request) {
|
||||||
|
if r.DataFilled() {
|
||||||
|
out := r.Data.(*GetBucketLocationOutput)
|
||||||
|
b, err := ioutil.ReadAll(r.HTTPResponse.Body)
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed reading response body", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
match := reBucketLocation.FindSubmatch(b)
|
||||||
|
if len(match) > 1 {
|
||||||
|
loc := string(match[1])
|
||||||
|
out.LocationConstraint = &loc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func populateLocationConstraint(r *aws.Request) {
|
||||||
|
if r.ParamsFilled() && aws.StringValue(r.Config.Region) != "us-east-1" {
|
||||||
|
in := r.Params.(*CreateBucketInput)
|
||||||
|
if in.CreateBucketConfiguration == nil {
|
||||||
|
r.Params = awsutil.CopyOf(r.Params)
|
||||||
|
in = r.Params.(*CreateBucketInput)
|
||||||
|
in.CreateBucketConfiguration = &CreateBucketConfiguration{
|
||||||
|
LocationConstraint: r.Config.Region,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/bucket_location_test.go
generated
vendored
Normal file
75
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/bucket_location_test.go
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package s3_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/test/unit"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = unit.Imported
|
||||||
|
var s3LocationTests = []struct {
|
||||||
|
body string
|
||||||
|
loc string
|
||||||
|
}{
|
||||||
|
{`<?xml version="1.0" encoding="UTF-8"?><LocationConstraint xmlns="http://s3.amazonaws.com/doc/2006-03-01/"/>`, ``},
|
||||||
|
{`<?xml version="1.0" encoding="UTF-8"?><LocationConstraint xmlns="http://s3.amazonaws.com/doc/2006-03-01/">EU</LocationConstraint>`, `EU`},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetBucketLocation(t *testing.T) {
|
||||||
|
for _, test := range s3LocationTests {
|
||||||
|
s := s3.New(nil)
|
||||||
|
s.Handlers.Send.Clear()
|
||||||
|
s.Handlers.Send.PushBack(func(r *aws.Request) {
|
||||||
|
reader := ioutil.NopCloser(bytes.NewReader([]byte(test.body)))
|
||||||
|
r.HTTPResponse = &http.Response{StatusCode: 200, Body: reader}
|
||||||
|
})
|
||||||
|
|
||||||
|
resp, err := s.GetBucketLocation(&s3.GetBucketLocationInput{Bucket: aws.String("bucket")})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if test.loc == "" {
|
||||||
|
assert.Nil(t, resp.LocationConstraint)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, test.loc, *resp.LocationConstraint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPopulateLocationConstraint(t *testing.T) {
|
||||||
|
s := s3.New(nil)
|
||||||
|
in := &s3.CreateBucketInput{
|
||||||
|
Bucket: aws.String("bucket"),
|
||||||
|
}
|
||||||
|
req, _ := s.CreateBucketRequest(in)
|
||||||
|
err := req.Build()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "mock-region", awsutil.ValuesAtPath(req.Params, "CreateBucketConfiguration.LocationConstraint")[0])
|
||||||
|
assert.Nil(t, in.CreateBucketConfiguration) // don't modify original params
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoPopulateLocationConstraintIfProvided(t *testing.T) {
|
||||||
|
s := s3.New(nil)
|
||||||
|
req, _ := s.CreateBucketRequest(&s3.CreateBucketInput{
|
||||||
|
Bucket: aws.String("bucket"),
|
||||||
|
CreateBucketConfiguration: &s3.CreateBucketConfiguration{},
|
||||||
|
})
|
||||||
|
err := req.Build()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 0, len(awsutil.ValuesAtPath(req.Params, "CreateBucketConfiguration.LocationConstraint")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoPopulateLocationConstraintIfClassic(t *testing.T) {
|
||||||
|
s := s3.New(&aws.Config{Region: aws.String("us-east-1")})
|
||||||
|
req, _ := s.CreateBucketRequest(&s3.CreateBucketInput{
|
||||||
|
Bucket: aws.String("bucket"),
|
||||||
|
})
|
||||||
|
err := req.Build()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 0, len(awsutil.ValuesAtPath(req.Params, "CreateBucketConfiguration.LocationConstraint")))
|
||||||
|
}
|
36
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/content_md5.go
generated
vendored
Normal file
36
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/content_md5.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package s3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/base64"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// contentMD5 computes and sets the HTTP Content-MD5 header for requests that
|
||||||
|
// require it.
|
||||||
|
func contentMD5(r *aws.Request) {
|
||||||
|
h := md5.New()
|
||||||
|
|
||||||
|
// hash the body. seek back to the first position after reading to reset
|
||||||
|
// the body for transmission. copy errors may be assumed to be from the
|
||||||
|
// body.
|
||||||
|
_, err := io.Copy(h, r.Body)
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("ContentMD5", "failed to read body", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = r.Body.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("ContentMD5", "failed to seek body", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode the md5 checksum in base64 and set the request header.
|
||||||
|
sum := h.Sum(nil)
|
||||||
|
sum64 := make([]byte, base64.StdEncoding.EncodedLen(len(sum)))
|
||||||
|
base64.StdEncoding.Encode(sum64, sum)
|
||||||
|
r.HTTPRequest.Header.Set("Content-MD5", string(sum64))
|
||||||
|
}
|
32
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/customizations.go
generated
vendored
Normal file
32
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/customizations.go
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package s3
|
||||||
|
|
||||||
|
import "github.com/aws/aws-sdk-go/aws"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
initService = func(s *aws.Service) {
|
||||||
|
// Support building custom host-style bucket endpoints
|
||||||
|
s.Handlers.Build.PushFront(updateHostWithBucket)
|
||||||
|
|
||||||
|
// Require SSL when using SSE keys
|
||||||
|
s.Handlers.Validate.PushBack(validateSSERequiresSSL)
|
||||||
|
s.Handlers.Build.PushBack(computeSSEKeys)
|
||||||
|
|
||||||
|
// S3 uses custom error unmarshaling logic
|
||||||
|
s.Handlers.UnmarshalError.Clear()
|
||||||
|
s.Handlers.UnmarshalError.PushBack(unmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
initRequest = func(r *aws.Request) {
|
||||||
|
switch r.Operation.Name {
|
||||||
|
case opPutBucketCORS, opPutBucketLifecycle, opPutBucketPolicy, opPutBucketTagging, opDeleteObjects:
|
||||||
|
// These S3 operations require Content-MD5 to be set
|
||||||
|
r.Handlers.Build.PushBack(contentMD5)
|
||||||
|
case opGetBucketLocation:
|
||||||
|
// GetBucketLocation has custom parsing logic
|
||||||
|
r.Handlers.Unmarshal.PushFront(buildGetBucketLocation)
|
||||||
|
case opCreateBucket:
|
||||||
|
// Auto-populate LocationConstraint with current region
|
||||||
|
r.Handlers.Validate.PushFront(populateLocationConstraint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
90
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/customizations_test.go
generated
vendored
Normal file
90
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/customizations_test.go
generated
vendored
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
package s3_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/base64"
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/test/unit"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = unit.Imported
|
||||||
|
|
||||||
|
func assertMD5(t *testing.T, req *aws.Request) {
|
||||||
|
err := req.Build()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
b, _ := ioutil.ReadAll(req.HTTPRequest.Body)
|
||||||
|
out := md5.Sum(b)
|
||||||
|
assert.NotEmpty(t, b)
|
||||||
|
assert.Equal(t, base64.StdEncoding.EncodeToString(out[:]), req.HTTPRequest.Header.Get("Content-MD5"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMD5InPutBucketCORS(t *testing.T) {
|
||||||
|
svc := s3.New(nil)
|
||||||
|
req, _ := svc.PutBucketCORSRequest(&s3.PutBucketCORSInput{
|
||||||
|
Bucket: aws.String("bucketname"),
|
||||||
|
CORSConfiguration: &s3.CORSConfiguration{
|
||||||
|
CORSRules: []*s3.CORSRule{
|
||||||
|
{AllowedMethods: []*string{aws.String("GET")}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assertMD5(t, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMD5InPutBucketLifecycle(t *testing.T) {
|
||||||
|
svc := s3.New(nil)
|
||||||
|
req, _ := svc.PutBucketLifecycleRequest(&s3.PutBucketLifecycleInput{
|
||||||
|
Bucket: aws.String("bucketname"),
|
||||||
|
LifecycleConfiguration: &s3.LifecycleConfiguration{
|
||||||
|
Rules: []*s3.LifecycleRule{
|
||||||
|
{
|
||||||
|
ID: aws.String("ID"),
|
||||||
|
Prefix: aws.String("Prefix"),
|
||||||
|
Status: aws.String("Enabled"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assertMD5(t, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMD5InPutBucketPolicy(t *testing.T) {
|
||||||
|
svc := s3.New(nil)
|
||||||
|
req, _ := svc.PutBucketPolicyRequest(&s3.PutBucketPolicyInput{
|
||||||
|
Bucket: aws.String("bucketname"),
|
||||||
|
Policy: aws.String("{}"),
|
||||||
|
})
|
||||||
|
assertMD5(t, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMD5InPutBucketTagging(t *testing.T) {
|
||||||
|
svc := s3.New(nil)
|
||||||
|
req, _ := svc.PutBucketTaggingRequest(&s3.PutBucketTaggingInput{
|
||||||
|
Bucket: aws.String("bucketname"),
|
||||||
|
Tagging: &s3.Tagging{
|
||||||
|
TagSet: []*s3.Tag{
|
||||||
|
{Key: aws.String("KEY"), Value: aws.String("VALUE")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assertMD5(t, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMD5InDeleteObjects(t *testing.T) {
|
||||||
|
svc := s3.New(nil)
|
||||||
|
req, _ := svc.DeleteObjectsRequest(&s3.DeleteObjectsInput{
|
||||||
|
Bucket: aws.String("bucketname"),
|
||||||
|
Delete: &s3.Delete{
|
||||||
|
Objects: []*s3.ObjectIdentifier{
|
||||||
|
{Key: aws.String("key")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assertMD5(t, req)
|
||||||
|
}
|
1928
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/examples_test.go
generated
vendored
Normal file
1928
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/examples_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
53
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/host_style_bucket.go
generated
vendored
Normal file
53
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/host_style_bucket.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package s3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var reDomain = regexp.MustCompile(`^[a-z0-9][a-z0-9\.\-]{1,61}[a-z0-9]$`)
|
||||||
|
var reIPAddress = regexp.MustCompile(`^(\d+\.){3}\d+$`)
|
||||||
|
|
||||||
|
// dnsCompatibleBucketName returns true if the bucket name is DNS compatible.
|
||||||
|
// Buckets created outside of the classic region MUST be DNS compatible.
|
||||||
|
func dnsCompatibleBucketName(bucket string) bool {
|
||||||
|
return reDomain.MatchString(bucket) &&
|
||||||
|
!reIPAddress.MatchString(bucket) &&
|
||||||
|
!strings.Contains(bucket, "..")
|
||||||
|
}
|
||||||
|
|
||||||
|
// hostStyleBucketName returns true if the request should put the bucket in
|
||||||
|
// the host. This is false if S3ForcePathStyle is explicitly set or if the
|
||||||
|
// bucket is not DNS compatible.
|
||||||
|
func hostStyleBucketName(r *aws.Request, bucket string) bool {
|
||||||
|
if aws.BoolValue(r.Config.S3ForcePathStyle) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bucket might be DNS compatible but dots in the hostname will fail
|
||||||
|
// certificate validation, so do not use host-style.
|
||||||
|
if r.HTTPRequest.URL.Scheme == "https" && strings.Contains(bucket, ".") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use host-style if the bucket is DNS compatible
|
||||||
|
return dnsCompatibleBucketName(bucket)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateHostWithBucket(r *aws.Request) {
|
||||||
|
b := awsutil.ValuesAtPath(r.Params, "Bucket")
|
||||||
|
if len(b) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if bucket := b[0].(string); bucket != "" && hostStyleBucketName(r, bucket) {
|
||||||
|
r.HTTPRequest.URL.Host = bucket + "." + r.HTTPRequest.URL.Host
|
||||||
|
r.HTTPRequest.URL.Path = strings.Replace(r.HTTPRequest.URL.Path, "/{Bucket}", "", -1)
|
||||||
|
if r.HTTPRequest.URL.Path == "" {
|
||||||
|
r.HTTPRequest.URL.Path = "/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/host_style_bucket_test.go
generated
vendored
Normal file
61
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/host_style_bucket_test.go
generated
vendored
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package s3_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/test/unit"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type s3BucketTest struct {
|
||||||
|
bucket string
|
||||||
|
url string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ = unit.Imported
|
||||||
|
|
||||||
|
sslTests = []s3BucketTest{
|
||||||
|
{"abc", "https://abc.s3.mock-region.amazonaws.com/"},
|
||||||
|
{"a$b$c", "https://s3.mock-region.amazonaws.com/a%24b%24c"},
|
||||||
|
{"a.b.c", "https://s3.mock-region.amazonaws.com/a.b.c"},
|
||||||
|
{"a..bc", "https://s3.mock-region.amazonaws.com/a..bc"},
|
||||||
|
}
|
||||||
|
|
||||||
|
nosslTests = []s3BucketTest{
|
||||||
|
{"a.b.c", "http://a.b.c.s3.mock-region.amazonaws.com/"},
|
||||||
|
{"a..bc", "http://s3.mock-region.amazonaws.com/a..bc"},
|
||||||
|
}
|
||||||
|
|
||||||
|
forcepathTests = []s3BucketTest{
|
||||||
|
{"abc", "https://s3.mock-region.amazonaws.com/abc"},
|
||||||
|
{"a$b$c", "https://s3.mock-region.amazonaws.com/a%24b%24c"},
|
||||||
|
{"a.b.c", "https://s3.mock-region.amazonaws.com/a.b.c"},
|
||||||
|
{"a..bc", "https://s3.mock-region.amazonaws.com/a..bc"},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func runTests(t *testing.T, svc *s3.S3, tests []s3BucketTest) {
|
||||||
|
for _, test := range tests {
|
||||||
|
req, _ := svc.ListObjectsRequest(&s3.ListObjectsInput{Bucket: &test.bucket})
|
||||||
|
req.Build()
|
||||||
|
assert.Equal(t, test.url, req.HTTPRequest.URL.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHostStyleBucketBuild(t *testing.T) {
|
||||||
|
s := s3.New(nil)
|
||||||
|
runTests(t, s, sslTests)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHostStyleBucketBuildNoSSL(t *testing.T) {
|
||||||
|
s := s3.New(&aws.Config{DisableSSL: aws.Bool(true)})
|
||||||
|
runTests(t, s, nosslTests)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPathStyleBucketBuild(t *testing.T) {
|
||||||
|
s := s3.New(&aws.Config{S3ForcePathStyle: aws.Bool(true)})
|
||||||
|
runTests(t, s, forcepathTests)
|
||||||
|
}
|
236
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3iface/interface.go
generated
vendored
Normal file
236
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3iface/interface.go
generated
vendored
Normal file
|
@ -0,0 +1,236 @@
|
||||||
|
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
|
||||||
|
|
||||||
|
// Package s3iface provides an interface for the Amazon Simple Storage Service.
|
||||||
|
package s3iface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// S3API is the interface type for s3.S3.
|
||||||
|
type S3API interface {
|
||||||
|
AbortMultipartUploadRequest(*s3.AbortMultipartUploadInput) (*aws.Request, *s3.AbortMultipartUploadOutput)
|
||||||
|
|
||||||
|
AbortMultipartUpload(*s3.AbortMultipartUploadInput) (*s3.AbortMultipartUploadOutput, error)
|
||||||
|
|
||||||
|
CompleteMultipartUploadRequest(*s3.CompleteMultipartUploadInput) (*aws.Request, *s3.CompleteMultipartUploadOutput)
|
||||||
|
|
||||||
|
CompleteMultipartUpload(*s3.CompleteMultipartUploadInput) (*s3.CompleteMultipartUploadOutput, error)
|
||||||
|
|
||||||
|
CopyObjectRequest(*s3.CopyObjectInput) (*aws.Request, *s3.CopyObjectOutput)
|
||||||
|
|
||||||
|
CopyObject(*s3.CopyObjectInput) (*s3.CopyObjectOutput, error)
|
||||||
|
|
||||||
|
CreateBucketRequest(*s3.CreateBucketInput) (*aws.Request, *s3.CreateBucketOutput)
|
||||||
|
|
||||||
|
CreateBucket(*s3.CreateBucketInput) (*s3.CreateBucketOutput, error)
|
||||||
|
|
||||||
|
CreateMultipartUploadRequest(*s3.CreateMultipartUploadInput) (*aws.Request, *s3.CreateMultipartUploadOutput)
|
||||||
|
|
||||||
|
CreateMultipartUpload(*s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error)
|
||||||
|
|
||||||
|
DeleteBucketRequest(*s3.DeleteBucketInput) (*aws.Request, *s3.DeleteBucketOutput)
|
||||||
|
|
||||||
|
DeleteBucket(*s3.DeleteBucketInput) (*s3.DeleteBucketOutput, error)
|
||||||
|
|
||||||
|
DeleteBucketCORSRequest(*s3.DeleteBucketCORSInput) (*aws.Request, *s3.DeleteBucketCORSOutput)
|
||||||
|
|
||||||
|
DeleteBucketCORS(*s3.DeleteBucketCORSInput) (*s3.DeleteBucketCORSOutput, error)
|
||||||
|
|
||||||
|
DeleteBucketLifecycleRequest(*s3.DeleteBucketLifecycleInput) (*aws.Request, *s3.DeleteBucketLifecycleOutput)
|
||||||
|
|
||||||
|
DeleteBucketLifecycle(*s3.DeleteBucketLifecycleInput) (*s3.DeleteBucketLifecycleOutput, error)
|
||||||
|
|
||||||
|
DeleteBucketPolicyRequest(*s3.DeleteBucketPolicyInput) (*aws.Request, *s3.DeleteBucketPolicyOutput)
|
||||||
|
|
||||||
|
DeleteBucketPolicy(*s3.DeleteBucketPolicyInput) (*s3.DeleteBucketPolicyOutput, error)
|
||||||
|
|
||||||
|
DeleteBucketReplicationRequest(*s3.DeleteBucketReplicationInput) (*aws.Request, *s3.DeleteBucketReplicationOutput)
|
||||||
|
|
||||||
|
DeleteBucketReplication(*s3.DeleteBucketReplicationInput) (*s3.DeleteBucketReplicationOutput, error)
|
||||||
|
|
||||||
|
DeleteBucketTaggingRequest(*s3.DeleteBucketTaggingInput) (*aws.Request, *s3.DeleteBucketTaggingOutput)
|
||||||
|
|
||||||
|
DeleteBucketTagging(*s3.DeleteBucketTaggingInput) (*s3.DeleteBucketTaggingOutput, error)
|
||||||
|
|
||||||
|
DeleteBucketWebsiteRequest(*s3.DeleteBucketWebsiteInput) (*aws.Request, *s3.DeleteBucketWebsiteOutput)
|
||||||
|
|
||||||
|
DeleteBucketWebsite(*s3.DeleteBucketWebsiteInput) (*s3.DeleteBucketWebsiteOutput, error)
|
||||||
|
|
||||||
|
DeleteObjectRequest(*s3.DeleteObjectInput) (*aws.Request, *s3.DeleteObjectOutput)
|
||||||
|
|
||||||
|
DeleteObject(*s3.DeleteObjectInput) (*s3.DeleteObjectOutput, error)
|
||||||
|
|
||||||
|
DeleteObjectsRequest(*s3.DeleteObjectsInput) (*aws.Request, *s3.DeleteObjectsOutput)
|
||||||
|
|
||||||
|
DeleteObjects(*s3.DeleteObjectsInput) (*s3.DeleteObjectsOutput, error)
|
||||||
|
|
||||||
|
GetBucketACLRequest(*s3.GetBucketACLInput) (*aws.Request, *s3.GetBucketACLOutput)
|
||||||
|
|
||||||
|
GetBucketACL(*s3.GetBucketACLInput) (*s3.GetBucketACLOutput, error)
|
||||||
|
|
||||||
|
GetBucketCORSRequest(*s3.GetBucketCORSInput) (*aws.Request, *s3.GetBucketCORSOutput)
|
||||||
|
|
||||||
|
GetBucketCORS(*s3.GetBucketCORSInput) (*s3.GetBucketCORSOutput, error)
|
||||||
|
|
||||||
|
GetBucketLifecycleRequest(*s3.GetBucketLifecycleInput) (*aws.Request, *s3.GetBucketLifecycleOutput)
|
||||||
|
|
||||||
|
GetBucketLifecycle(*s3.GetBucketLifecycleInput) (*s3.GetBucketLifecycleOutput, error)
|
||||||
|
|
||||||
|
GetBucketLocationRequest(*s3.GetBucketLocationInput) (*aws.Request, *s3.GetBucketLocationOutput)
|
||||||
|
|
||||||
|
GetBucketLocation(*s3.GetBucketLocationInput) (*s3.GetBucketLocationOutput, error)
|
||||||
|
|
||||||
|
GetBucketLoggingRequest(*s3.GetBucketLoggingInput) (*aws.Request, *s3.GetBucketLoggingOutput)
|
||||||
|
|
||||||
|
GetBucketLogging(*s3.GetBucketLoggingInput) (*s3.GetBucketLoggingOutput, error)
|
||||||
|
|
||||||
|
GetBucketNotificationRequest(*s3.GetBucketNotificationConfigurationRequest) (*aws.Request, *s3.NotificationConfigurationDeprecated)
|
||||||
|
|
||||||
|
GetBucketNotification(*s3.GetBucketNotificationConfigurationRequest) (*s3.NotificationConfigurationDeprecated, error)
|
||||||
|
|
||||||
|
GetBucketNotificationConfigurationRequest(*s3.GetBucketNotificationConfigurationRequest) (*aws.Request, *s3.NotificationConfiguration)
|
||||||
|
|
||||||
|
GetBucketNotificationConfiguration(*s3.GetBucketNotificationConfigurationRequest) (*s3.NotificationConfiguration, error)
|
||||||
|
|
||||||
|
GetBucketPolicyRequest(*s3.GetBucketPolicyInput) (*aws.Request, *s3.GetBucketPolicyOutput)
|
||||||
|
|
||||||
|
GetBucketPolicy(*s3.GetBucketPolicyInput) (*s3.GetBucketPolicyOutput, error)
|
||||||
|
|
||||||
|
GetBucketReplicationRequest(*s3.GetBucketReplicationInput) (*aws.Request, *s3.GetBucketReplicationOutput)
|
||||||
|
|
||||||
|
GetBucketReplication(*s3.GetBucketReplicationInput) (*s3.GetBucketReplicationOutput, error)
|
||||||
|
|
||||||
|
GetBucketRequestPaymentRequest(*s3.GetBucketRequestPaymentInput) (*aws.Request, *s3.GetBucketRequestPaymentOutput)
|
||||||
|
|
||||||
|
GetBucketRequestPayment(*s3.GetBucketRequestPaymentInput) (*s3.GetBucketRequestPaymentOutput, error)
|
||||||
|
|
||||||
|
GetBucketTaggingRequest(*s3.GetBucketTaggingInput) (*aws.Request, *s3.GetBucketTaggingOutput)
|
||||||
|
|
||||||
|
GetBucketTagging(*s3.GetBucketTaggingInput) (*s3.GetBucketTaggingOutput, error)
|
||||||
|
|
||||||
|
GetBucketVersioningRequest(*s3.GetBucketVersioningInput) (*aws.Request, *s3.GetBucketVersioningOutput)
|
||||||
|
|
||||||
|
GetBucketVersioning(*s3.GetBucketVersioningInput) (*s3.GetBucketVersioningOutput, error)
|
||||||
|
|
||||||
|
GetBucketWebsiteRequest(*s3.GetBucketWebsiteInput) (*aws.Request, *s3.GetBucketWebsiteOutput)
|
||||||
|
|
||||||
|
GetBucketWebsite(*s3.GetBucketWebsiteInput) (*s3.GetBucketWebsiteOutput, error)
|
||||||
|
|
||||||
|
GetObjectRequest(*s3.GetObjectInput) (*aws.Request, *s3.GetObjectOutput)
|
||||||
|
|
||||||
|
GetObject(*s3.GetObjectInput) (*s3.GetObjectOutput, error)
|
||||||
|
|
||||||
|
GetObjectACLRequest(*s3.GetObjectACLInput) (*aws.Request, *s3.GetObjectACLOutput)
|
||||||
|
|
||||||
|
GetObjectACL(*s3.GetObjectACLInput) (*s3.GetObjectACLOutput, error)
|
||||||
|
|
||||||
|
GetObjectTorrentRequest(*s3.GetObjectTorrentInput) (*aws.Request, *s3.GetObjectTorrentOutput)
|
||||||
|
|
||||||
|
GetObjectTorrent(*s3.GetObjectTorrentInput) (*s3.GetObjectTorrentOutput, error)
|
||||||
|
|
||||||
|
HeadBucketRequest(*s3.HeadBucketInput) (*aws.Request, *s3.HeadBucketOutput)
|
||||||
|
|
||||||
|
HeadBucket(*s3.HeadBucketInput) (*s3.HeadBucketOutput, error)
|
||||||
|
|
||||||
|
HeadObjectRequest(*s3.HeadObjectInput) (*aws.Request, *s3.HeadObjectOutput)
|
||||||
|
|
||||||
|
HeadObject(*s3.HeadObjectInput) (*s3.HeadObjectOutput, error)
|
||||||
|
|
||||||
|
ListBucketsRequest(*s3.ListBucketsInput) (*aws.Request, *s3.ListBucketsOutput)
|
||||||
|
|
||||||
|
ListBuckets(*s3.ListBucketsInput) (*s3.ListBucketsOutput, error)
|
||||||
|
|
||||||
|
ListMultipartUploadsRequest(*s3.ListMultipartUploadsInput) (*aws.Request, *s3.ListMultipartUploadsOutput)
|
||||||
|
|
||||||
|
ListMultipartUploads(*s3.ListMultipartUploadsInput) (*s3.ListMultipartUploadsOutput, error)
|
||||||
|
|
||||||
|
ListMultipartUploadsPages(*s3.ListMultipartUploadsInput, func(*s3.ListMultipartUploadsOutput, bool) bool) error
|
||||||
|
|
||||||
|
ListObjectVersionsRequest(*s3.ListObjectVersionsInput) (*aws.Request, *s3.ListObjectVersionsOutput)
|
||||||
|
|
||||||
|
ListObjectVersions(*s3.ListObjectVersionsInput) (*s3.ListObjectVersionsOutput, error)
|
||||||
|
|
||||||
|
ListObjectVersionsPages(*s3.ListObjectVersionsInput, func(*s3.ListObjectVersionsOutput, bool) bool) error
|
||||||
|
|
||||||
|
ListObjectsRequest(*s3.ListObjectsInput) (*aws.Request, *s3.ListObjectsOutput)
|
||||||
|
|
||||||
|
ListObjects(*s3.ListObjectsInput) (*s3.ListObjectsOutput, error)
|
||||||
|
|
||||||
|
ListObjectsPages(*s3.ListObjectsInput, func(*s3.ListObjectsOutput, bool) bool) error
|
||||||
|
|
||||||
|
ListPartsRequest(*s3.ListPartsInput) (*aws.Request, *s3.ListPartsOutput)
|
||||||
|
|
||||||
|
ListParts(*s3.ListPartsInput) (*s3.ListPartsOutput, error)
|
||||||
|
|
||||||
|
ListPartsPages(*s3.ListPartsInput, func(*s3.ListPartsOutput, bool) bool) error
|
||||||
|
|
||||||
|
PutBucketACLRequest(*s3.PutBucketACLInput) (*aws.Request, *s3.PutBucketACLOutput)
|
||||||
|
|
||||||
|
PutBucketACL(*s3.PutBucketACLInput) (*s3.PutBucketACLOutput, error)
|
||||||
|
|
||||||
|
PutBucketCORSRequest(*s3.PutBucketCORSInput) (*aws.Request, *s3.PutBucketCORSOutput)
|
||||||
|
|
||||||
|
PutBucketCORS(*s3.PutBucketCORSInput) (*s3.PutBucketCORSOutput, error)
|
||||||
|
|
||||||
|
PutBucketLifecycleRequest(*s3.PutBucketLifecycleInput) (*aws.Request, *s3.PutBucketLifecycleOutput)
|
||||||
|
|
||||||
|
PutBucketLifecycle(*s3.PutBucketLifecycleInput) (*s3.PutBucketLifecycleOutput, error)
|
||||||
|
|
||||||
|
PutBucketLoggingRequest(*s3.PutBucketLoggingInput) (*aws.Request, *s3.PutBucketLoggingOutput)
|
||||||
|
|
||||||
|
PutBucketLogging(*s3.PutBucketLoggingInput) (*s3.PutBucketLoggingOutput, error)
|
||||||
|
|
||||||
|
PutBucketNotificationRequest(*s3.PutBucketNotificationInput) (*aws.Request, *s3.PutBucketNotificationOutput)
|
||||||
|
|
||||||
|
PutBucketNotification(*s3.PutBucketNotificationInput) (*s3.PutBucketNotificationOutput, error)
|
||||||
|
|
||||||
|
PutBucketNotificationConfigurationRequest(*s3.PutBucketNotificationConfigurationInput) (*aws.Request, *s3.PutBucketNotificationConfigurationOutput)
|
||||||
|
|
||||||
|
PutBucketNotificationConfiguration(*s3.PutBucketNotificationConfigurationInput) (*s3.PutBucketNotificationConfigurationOutput, error)
|
||||||
|
|
||||||
|
PutBucketPolicyRequest(*s3.PutBucketPolicyInput) (*aws.Request, *s3.PutBucketPolicyOutput)
|
||||||
|
|
||||||
|
PutBucketPolicy(*s3.PutBucketPolicyInput) (*s3.PutBucketPolicyOutput, error)
|
||||||
|
|
||||||
|
PutBucketReplicationRequest(*s3.PutBucketReplicationInput) (*aws.Request, *s3.PutBucketReplicationOutput)
|
||||||
|
|
||||||
|
PutBucketReplication(*s3.PutBucketReplicationInput) (*s3.PutBucketReplicationOutput, error)
|
||||||
|
|
||||||
|
PutBucketRequestPaymentRequest(*s3.PutBucketRequestPaymentInput) (*aws.Request, *s3.PutBucketRequestPaymentOutput)
|
||||||
|
|
||||||
|
PutBucketRequestPayment(*s3.PutBucketRequestPaymentInput) (*s3.PutBucketRequestPaymentOutput, error)
|
||||||
|
|
||||||
|
PutBucketTaggingRequest(*s3.PutBucketTaggingInput) (*aws.Request, *s3.PutBucketTaggingOutput)
|
||||||
|
|
||||||
|
PutBucketTagging(*s3.PutBucketTaggingInput) (*s3.PutBucketTaggingOutput, error)
|
||||||
|
|
||||||
|
PutBucketVersioningRequest(*s3.PutBucketVersioningInput) (*aws.Request, *s3.PutBucketVersioningOutput)
|
||||||
|
|
||||||
|
PutBucketVersioning(*s3.PutBucketVersioningInput) (*s3.PutBucketVersioningOutput, error)
|
||||||
|
|
||||||
|
PutBucketWebsiteRequest(*s3.PutBucketWebsiteInput) (*aws.Request, *s3.PutBucketWebsiteOutput)
|
||||||
|
|
||||||
|
PutBucketWebsite(*s3.PutBucketWebsiteInput) (*s3.PutBucketWebsiteOutput, error)
|
||||||
|
|
||||||
|
PutObjectRequest(*s3.PutObjectInput) (*aws.Request, *s3.PutObjectOutput)
|
||||||
|
|
||||||
|
PutObject(*s3.PutObjectInput) (*s3.PutObjectOutput, error)
|
||||||
|
|
||||||
|
PutObjectACLRequest(*s3.PutObjectACLInput) (*aws.Request, *s3.PutObjectACLOutput)
|
||||||
|
|
||||||
|
PutObjectACL(*s3.PutObjectACLInput) (*s3.PutObjectACLOutput, error)
|
||||||
|
|
||||||
|
RestoreObjectRequest(*s3.RestoreObjectInput) (*aws.Request, *s3.RestoreObjectOutput)
|
||||||
|
|
||||||
|
RestoreObject(*s3.RestoreObjectInput) (*s3.RestoreObjectOutput, error)
|
||||||
|
|
||||||
|
UploadPartRequest(*s3.UploadPartInput) (*aws.Request, *s3.UploadPartOutput)
|
||||||
|
|
||||||
|
UploadPart(*s3.UploadPartInput) (*s3.UploadPartOutput, error)
|
||||||
|
|
||||||
|
UploadPartCopyRequest(*s3.UploadPartCopyInput) (*aws.Request, *s3.UploadPartCopyOutput)
|
||||||
|
|
||||||
|
UploadPartCopy(*s3.UploadPartCopyInput) (*s3.UploadPartCopyOutput, error)
|
||||||
|
}
|
15
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3iface/interface_test.go
generated
vendored
Normal file
15
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3iface/interface_test.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
|
||||||
|
|
||||||
|
package s3iface_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3/s3iface"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInterface(t *testing.T) {
|
||||||
|
assert.Implements(t, (*s3iface.S3API)(nil), s3.New(nil))
|
||||||
|
}
|
257
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3manager/download.go
generated
vendored
Normal file
257
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3manager/download.go
generated
vendored
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
package s3manager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The default range of bytes to get at a time when using Download().
|
||||||
|
var DefaultDownloadPartSize int64 = 1024 * 1024 * 5
|
||||||
|
|
||||||
|
// The default number of goroutines to spin up when using Download().
|
||||||
|
var DefaultDownloadConcurrency = 5
|
||||||
|
|
||||||
|
// The default set of options used when opts is nil in Download().
|
||||||
|
var DefaultDownloadOptions = &DownloadOptions{
|
||||||
|
PartSize: DefaultDownloadPartSize,
|
||||||
|
Concurrency: DefaultDownloadConcurrency,
|
||||||
|
}
|
||||||
|
|
||||||
|
// DownloadOptions keeps tracks of extra options to pass to an Download() call.
|
||||||
|
type DownloadOptions struct {
|
||||||
|
// The buffer size (in bytes) to use when buffering data into chunks and
|
||||||
|
// sending them as parts to S3. The minimum allowed part size is 5MB, and
|
||||||
|
// if this value is set to zero, the DefaultPartSize value will be used.
|
||||||
|
PartSize int64
|
||||||
|
|
||||||
|
// The number of goroutines to spin up in parallel when sending parts.
|
||||||
|
// If this is set to zero, the DefaultConcurrency value will be used.
|
||||||
|
Concurrency int
|
||||||
|
|
||||||
|
// An S3 client to use when performing downloads. Leave this as nil to use
|
||||||
|
// a default client.
|
||||||
|
S3 *s3.S3
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDownloader creates a new Downloader structure that downloads an object
|
||||||
|
// from S3 in concurrent chunks. Pass in an optional DownloadOptions struct
|
||||||
|
// to customize the downloader behavior.
|
||||||
|
func NewDownloader(opts *DownloadOptions) *Downloader {
|
||||||
|
if opts == nil {
|
||||||
|
opts = DefaultDownloadOptions
|
||||||
|
}
|
||||||
|
return &Downloader{opts: opts}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Downloader structure that calls Download(). It is safe to call Download()
|
||||||
|
// on this structure for multiple objects and across concurrent goroutines.
|
||||||
|
type Downloader struct {
|
||||||
|
opts *DownloadOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download downloads an object in S3 and writes the payload into w using
|
||||||
|
// concurrent GET requests.
|
||||||
|
//
|
||||||
|
// It is safe to call this method for multiple objects and across concurrent
|
||||||
|
// goroutines.
|
||||||
|
func (d *Downloader) Download(w io.WriterAt, input *s3.GetObjectInput) (n int64, err error) {
|
||||||
|
impl := downloader{w: w, in: input, opts: *d.opts}
|
||||||
|
return impl.download()
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloader is the implementation structure used internally by Downloader.
|
||||||
|
type downloader struct {
|
||||||
|
opts DownloadOptions
|
||||||
|
in *s3.GetObjectInput
|
||||||
|
w io.WriterAt
|
||||||
|
|
||||||
|
wg sync.WaitGroup
|
||||||
|
m sync.Mutex
|
||||||
|
|
||||||
|
pos int64
|
||||||
|
totalBytes int64
|
||||||
|
written int64
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes the downloader with default options.
|
||||||
|
func (d *downloader) init() {
|
||||||
|
d.totalBytes = -1
|
||||||
|
|
||||||
|
if d.opts.Concurrency == 0 {
|
||||||
|
d.opts.Concurrency = DefaultDownloadConcurrency
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.opts.PartSize == 0 {
|
||||||
|
d.opts.PartSize = DefaultDownloadPartSize
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.opts.S3 == nil {
|
||||||
|
d.opts.S3 = s3.New(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// download performs the implementation of the object download across ranged
|
||||||
|
// GETs.
|
||||||
|
func (d *downloader) download() (n int64, err error) {
|
||||||
|
d.init()
|
||||||
|
|
||||||
|
// Spin up workers
|
||||||
|
ch := make(chan dlchunk, d.opts.Concurrency)
|
||||||
|
for i := 0; i < d.opts.Concurrency; i++ {
|
||||||
|
d.wg.Add(1)
|
||||||
|
go d.downloadPart(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign work
|
||||||
|
for d.geterr() == nil {
|
||||||
|
if d.pos != 0 {
|
||||||
|
// This is not the first chunk, let's wait until we know the total
|
||||||
|
// size of the payload so we can see if we have read the entire
|
||||||
|
// object.
|
||||||
|
total := d.getTotalBytes()
|
||||||
|
|
||||||
|
if total < 0 {
|
||||||
|
// Total has not yet been set, so sleep and loop around while
|
||||||
|
// waiting for our first worker to resolve this value.
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
continue
|
||||||
|
} else if d.pos >= total {
|
||||||
|
break // We're finished queueing chunks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queue the next range of bytes to read.
|
||||||
|
ch <- dlchunk{w: d.w, start: d.pos, size: d.opts.PartSize}
|
||||||
|
d.pos += d.opts.PartSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for completion
|
||||||
|
close(ch)
|
||||||
|
d.wg.Wait()
|
||||||
|
|
||||||
|
// Return error
|
||||||
|
return d.written, d.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloadPart is an individual goroutine worker reading from the ch channel
|
||||||
|
// and performing a GetObject request on the data with a given byte range.
|
||||||
|
//
|
||||||
|
// If this is the first worker, this operation also resolves the total number
|
||||||
|
// of bytes to be read so that the worker manager knows when it is finished.
|
||||||
|
func (d *downloader) downloadPart(ch chan dlchunk) {
|
||||||
|
defer d.wg.Done()
|
||||||
|
|
||||||
|
for {
|
||||||
|
chunk, ok := <-ch
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.geterr() == nil {
|
||||||
|
// Get the next byte range of data
|
||||||
|
in := &s3.GetObjectInput{}
|
||||||
|
awsutil.Copy(in, d.in)
|
||||||
|
rng := fmt.Sprintf("bytes=%d-%d",
|
||||||
|
chunk.start, chunk.start+chunk.size-1)
|
||||||
|
in.Range = &rng
|
||||||
|
|
||||||
|
resp, err := d.opts.S3.GetObject(in)
|
||||||
|
if err != nil {
|
||||||
|
d.seterr(err)
|
||||||
|
} else {
|
||||||
|
d.setTotalBytes(resp) // Set total if not yet set.
|
||||||
|
|
||||||
|
n, err := io.Copy(&chunk, resp.Body)
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
d.seterr(err)
|
||||||
|
}
|
||||||
|
d.incrwritten(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTotalBytes is a thread-safe getter for retrieving the total byte status.
|
||||||
|
func (d *downloader) getTotalBytes() int64 {
|
||||||
|
d.m.Lock()
|
||||||
|
defer d.m.Unlock()
|
||||||
|
|
||||||
|
return d.totalBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTotalBytes is a thread-safe setter for setting the total byte status.
|
||||||
|
func (d *downloader) setTotalBytes(resp *s3.GetObjectOutput) {
|
||||||
|
d.m.Lock()
|
||||||
|
defer d.m.Unlock()
|
||||||
|
|
||||||
|
if d.totalBytes >= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(*resp.ContentRange, "/")
|
||||||
|
total, err := strconv.ParseInt(parts[len(parts)-1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
d.err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
d.totalBytes = total
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *downloader) incrwritten(n int64) {
|
||||||
|
d.m.Lock()
|
||||||
|
defer d.m.Unlock()
|
||||||
|
|
||||||
|
d.written += n
|
||||||
|
}
|
||||||
|
|
||||||
|
// geterr is a thread-safe getter for the error object
|
||||||
|
func (d *downloader) geterr() error {
|
||||||
|
d.m.Lock()
|
||||||
|
defer d.m.Unlock()
|
||||||
|
|
||||||
|
return d.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// seterr is a thread-safe setter for the error object
|
||||||
|
func (d *downloader) seterr(e error) {
|
||||||
|
d.m.Lock()
|
||||||
|
defer d.m.Unlock()
|
||||||
|
|
||||||
|
d.err = e
|
||||||
|
}
|
||||||
|
|
||||||
|
// dlchunk represents a single chunk of data to write by the worker routine.
|
||||||
|
// This structure also implements an io.SectionReader style interface for
|
||||||
|
// io.WriterAt, effectively making it an io.SectionWriter (which does not
|
||||||
|
// exist).
|
||||||
|
type dlchunk struct {
|
||||||
|
w io.WriterAt
|
||||||
|
start int64
|
||||||
|
size int64
|
||||||
|
cur int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write wraps io.WriterAt for the dlchunk, writing from the dlchunk's start
|
||||||
|
// position to its end (or EOF).
|
||||||
|
func (c *dlchunk) Write(p []byte) (n int, err error) {
|
||||||
|
if c.cur >= c.size {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err = c.w.WriteAt(p, c.start+c.cur)
|
||||||
|
c.cur += int64(n)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
165
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3manager/download_test.go
generated
vendored
Normal file
165
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3manager/download_test.go
generated
vendored
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
package s3manager_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/test/unit"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = unit.Imported
|
||||||
|
|
||||||
|
func dlLoggingSvc(data []byte) (*s3.S3, *[]string, *[]string) {
|
||||||
|
var m sync.Mutex
|
||||||
|
names := []string{}
|
||||||
|
ranges := []string{}
|
||||||
|
|
||||||
|
svc := s3.New(nil)
|
||||||
|
svc.Handlers.Send.Clear()
|
||||||
|
svc.Handlers.Send.PushBack(func(r *aws.Request) {
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
names = append(names, r.Operation.Name)
|
||||||
|
ranges = append(ranges, *r.Params.(*s3.GetObjectInput).Range)
|
||||||
|
|
||||||
|
rerng := regexp.MustCompile(`bytes=(\d+)-(\d+)`)
|
||||||
|
rng := rerng.FindStringSubmatch(r.HTTPRequest.Header.Get("Range"))
|
||||||
|
start, _ := strconv.ParseInt(rng[1], 10, 64)
|
||||||
|
fin, _ := strconv.ParseInt(rng[2], 10, 64)
|
||||||
|
fin++
|
||||||
|
|
||||||
|
if fin > int64(len(data)) {
|
||||||
|
fin = int64(len(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
r.HTTPResponse = &http.Response{
|
||||||
|
StatusCode: 200,
|
||||||
|
Body: ioutil.NopCloser(bytes.NewReader(data[start:fin])),
|
||||||
|
Header: http.Header{},
|
||||||
|
}
|
||||||
|
r.HTTPResponse.Header.Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d",
|
||||||
|
start, fin, len(data)))
|
||||||
|
})
|
||||||
|
|
||||||
|
return svc, &names, &ranges
|
||||||
|
}
|
||||||
|
|
||||||
|
type dlwriter struct {
|
||||||
|
buf []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDLWriter(size int) *dlwriter {
|
||||||
|
return &dlwriter{buf: make([]byte, size)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d dlwriter) WriteAt(p []byte, pos int64) (n int, err error) {
|
||||||
|
if pos > int64(len(d.buf)) {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
written := 0
|
||||||
|
for i, b := range p {
|
||||||
|
if i >= len(d.buf) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
d.buf[pos+int64(i)] = b
|
||||||
|
written++
|
||||||
|
}
|
||||||
|
return written, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDownloadOrder(t *testing.T) {
|
||||||
|
s, names, ranges := dlLoggingSvc(buf12MB)
|
||||||
|
|
||||||
|
opts := &s3manager.DownloadOptions{S3: s, Concurrency: 1}
|
||||||
|
d := s3manager.NewDownloader(opts)
|
||||||
|
w := newDLWriter(len(buf12MB))
|
||||||
|
n, err := d.Download(w, &s3.GetObjectInput{
|
||||||
|
Bucket: aws.String("bucket"),
|
||||||
|
Key: aws.String("key"),
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, int64(len(buf12MB)), n)
|
||||||
|
assert.Equal(t, []string{"GetObject", "GetObject", "GetObject"}, *names)
|
||||||
|
assert.Equal(t, []string{"bytes=0-5242879", "bytes=5242880-10485759", "bytes=10485760-15728639"}, *ranges)
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
for _, b := range w.buf {
|
||||||
|
count += int(b)
|
||||||
|
}
|
||||||
|
assert.Equal(t, 0, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDownloadZero(t *testing.T) {
|
||||||
|
s, names, ranges := dlLoggingSvc([]byte{})
|
||||||
|
|
||||||
|
opts := &s3manager.DownloadOptions{S3: s}
|
||||||
|
d := s3manager.NewDownloader(opts)
|
||||||
|
w := newDLWriter(0)
|
||||||
|
n, err := d.Download(w, &s3.GetObjectInput{
|
||||||
|
Bucket: aws.String("bucket"),
|
||||||
|
Key: aws.String("key"),
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, int64(0), n)
|
||||||
|
assert.Equal(t, []string{"GetObject"}, *names)
|
||||||
|
assert.Equal(t, []string{"bytes=0-5242879"}, *ranges)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDownloadSetPartSize(t *testing.T) {
|
||||||
|
s, names, ranges := dlLoggingSvc([]byte{1, 2, 3})
|
||||||
|
|
||||||
|
opts := &s3manager.DownloadOptions{S3: s, PartSize: 1, Concurrency: 1}
|
||||||
|
d := s3manager.NewDownloader(opts)
|
||||||
|
w := newDLWriter(3)
|
||||||
|
n, err := d.Download(w, &s3.GetObjectInput{
|
||||||
|
Bucket: aws.String("bucket"),
|
||||||
|
Key: aws.String("key"),
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, int64(3), n)
|
||||||
|
assert.Equal(t, []string{"GetObject", "GetObject", "GetObject"}, *names)
|
||||||
|
assert.Equal(t, []string{"bytes=0-0", "bytes=1-1", "bytes=2-2"}, *ranges)
|
||||||
|
assert.Equal(t, []byte{1, 2, 3}, w.buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDownloadError(t *testing.T) {
|
||||||
|
s, names, _ := dlLoggingSvc([]byte{1, 2, 3})
|
||||||
|
opts := &s3manager.DownloadOptions{S3: s, PartSize: 1, Concurrency: 1}
|
||||||
|
|
||||||
|
num := 0
|
||||||
|
s.Handlers.Send.PushBack(func(r *aws.Request) {
|
||||||
|
num++
|
||||||
|
if num > 1 {
|
||||||
|
r.HTTPResponse.StatusCode = 400
|
||||||
|
r.HTTPResponse.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
d := s3manager.NewDownloader(opts)
|
||||||
|
w := newDLWriter(3)
|
||||||
|
n, err := d.Download(w, &s3.GetObjectInput{
|
||||||
|
Bucket: aws.String("bucket"),
|
||||||
|
Key: aws.String("key"),
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
assert.Equal(t, int64(1), n)
|
||||||
|
assert.Equal(t, []string{"GetObject", "GetObject"}, *names)
|
||||||
|
assert.Equal(t, []byte{1, 0, 0}, w.buf)
|
||||||
|
}
|
562
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3manager/upload.go
generated
vendored
Normal file
562
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3manager/upload.go
generated
vendored
Normal file
|
@ -0,0 +1,562 @@
|
||||||
|
package s3manager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The maximum allowed number of parts in a multi-part upload on Amazon S3.
|
||||||
|
var MaxUploadParts = 10000
|
||||||
|
|
||||||
|
// The minimum allowed part size when uploading a part to Amazon S3.
|
||||||
|
var MinUploadPartSize int64 = 1024 * 1024 * 5
|
||||||
|
|
||||||
|
// The default part size to buffer chunks of a payload into.
|
||||||
|
var DefaultUploadPartSize = MinUploadPartSize
|
||||||
|
|
||||||
|
// The default number of goroutines to spin up when using Upload().
|
||||||
|
var DefaultUploadConcurrency = 5
|
||||||
|
|
||||||
|
// The default set of options used when opts is nil in Upload().
|
||||||
|
var DefaultUploadOptions = &UploadOptions{
|
||||||
|
PartSize: DefaultUploadPartSize,
|
||||||
|
Concurrency: DefaultUploadConcurrency,
|
||||||
|
LeavePartsOnError: false,
|
||||||
|
S3: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
// A MultiUploadFailure wraps a failed S3 multipart upload. An error returned
|
||||||
|
// will satisfy this interface when a multi part upload failed to upload all
|
||||||
|
// chucks to S3. In the case of a failure the UploadID is needed to operate on
|
||||||
|
// the chunks, if any, which were uploaded.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// u := s3manager.NewUploader(opts)
|
||||||
|
// output, err := u.upload(input)
|
||||||
|
// if err != nil {
|
||||||
|
// if multierr, ok := err.(MultiUploadFailure); ok {
|
||||||
|
// // Process error and its associated uploadID
|
||||||
|
// fmt.Println("Error:", multierr.Code(), multierr.Message(), multierr.UploadID())
|
||||||
|
// } else {
|
||||||
|
// // Process error generically
|
||||||
|
// fmt.Println("Error:", err.Error())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
type MultiUploadFailure interface {
|
||||||
|
awserr.Error
|
||||||
|
|
||||||
|
// Returns the upload id for the S3 multipart upload that failed.
|
||||||
|
UploadID() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// So that the Error interface type can be included as an anonymous field
|
||||||
|
// in the multiUploadError struct and not conflict with the error.Error() method.
|
||||||
|
type awsError awserr.Error
|
||||||
|
|
||||||
|
// A multiUploadError wraps the upload ID of a failed s3 multipart upload.
|
||||||
|
// Composed of BaseError for code, message, and original error
|
||||||
|
//
|
||||||
|
// Should be used for an error that occurred failing a S3 multipart upload,
|
||||||
|
// and a upload ID is available. If an uploadID is not available a more relevant
|
||||||
|
type multiUploadError struct {
|
||||||
|
awsError
|
||||||
|
|
||||||
|
// ID for multipart upload which failed.
|
||||||
|
uploadID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the string representation of the error.
|
||||||
|
//
|
||||||
|
// See apierr.BaseError ErrorWithExtra for output format
|
||||||
|
//
|
||||||
|
// Satisfies the error interface.
|
||||||
|
func (m multiUploadError) Error() string {
|
||||||
|
extra := fmt.Sprintf("upload id: %s", m.uploadID)
|
||||||
|
return awserr.SprintError(m.Code(), m.Message(), extra, m.OrigErr())
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the error.
|
||||||
|
// Alias for Error to satisfy the stringer interface.
|
||||||
|
func (m multiUploadError) String() string {
|
||||||
|
return m.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadID returns the id of the S3 upload which failed.
|
||||||
|
func (m multiUploadError) UploadID() string {
|
||||||
|
return m.uploadID
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadInput contains all input for upload requests to Amazon S3.
|
||||||
|
type UploadInput struct {
|
||||||
|
// The canned ACL to apply to the object.
|
||||||
|
ACL *string `location:"header" locationName:"x-amz-acl" type:"string"`
|
||||||
|
|
||||||
|
Bucket *string `location:"uri" locationName:"Bucket" type:"string" required:"true"`
|
||||||
|
|
||||||
|
// Specifies caching behavior along the request/reply chain.
|
||||||
|
CacheControl *string `location:"header" locationName:"Cache-Control" type:"string"`
|
||||||
|
|
||||||
|
// Specifies presentational information for the object.
|
||||||
|
ContentDisposition *string `location:"header" locationName:"Content-Disposition" type:"string"`
|
||||||
|
|
||||||
|
// Specifies what content encodings have been applied to the object and thus
|
||||||
|
// what decoding mechanisms must be applied to obtain the media-type referenced
|
||||||
|
// by the Content-Type header field.
|
||||||
|
ContentEncoding *string `location:"header" locationName:"Content-Encoding" type:"string"`
|
||||||
|
|
||||||
|
// The language the content is in.
|
||||||
|
ContentLanguage *string `location:"header" locationName:"Content-Language" type:"string"`
|
||||||
|
|
||||||
|
// A standard MIME type describing the format of the object data.
|
||||||
|
ContentType *string `location:"header" locationName:"Content-Type" type:"string"`
|
||||||
|
|
||||||
|
// The date and time at which the object is no longer cacheable.
|
||||||
|
Expires *time.Time `location:"header" locationName:"Expires" type:"timestamp" timestampFormat:"rfc822"`
|
||||||
|
|
||||||
|
// Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the object.
|
||||||
|
GrantFullControl *string `location:"header" locationName:"x-amz-grant-full-control" type:"string"`
|
||||||
|
|
||||||
|
// Allows grantee to read the object data and its metadata.
|
||||||
|
GrantRead *string `location:"header" locationName:"x-amz-grant-read" type:"string"`
|
||||||
|
|
||||||
|
// Allows grantee to read the object ACL.
|
||||||
|
GrantReadACP *string `location:"header" locationName:"x-amz-grant-read-acp" type:"string"`
|
||||||
|
|
||||||
|
// Allows grantee to write the ACL for the applicable object.
|
||||||
|
GrantWriteACP *string `location:"header" locationName:"x-amz-grant-write-acp" type:"string"`
|
||||||
|
|
||||||
|
Key *string `location:"uri" locationName:"Key" type:"string" required:"true"`
|
||||||
|
|
||||||
|
// A map of metadata to store with the object in S3.
|
||||||
|
Metadata map[string]*string `location:"headers" locationName:"x-amz-meta-" type:"map"`
|
||||||
|
|
||||||
|
// Confirms that the requester knows that she or he will be charged for the
|
||||||
|
// request. Bucket owners need not specify this parameter in their requests.
|
||||||
|
// Documentation on downloading objects from requester pays buckets can be found
|
||||||
|
// at http://docs.aws.amazon.com/AmazonS3/latest/dev/ObjectsinRequesterPaysBuckets.html
|
||||||
|
RequestPayer *string `location:"header" locationName:"x-amz-request-payer" type:"string"`
|
||||||
|
|
||||||
|
// Specifies the algorithm to use to when encrypting the object (e.g., AES256,
|
||||||
|
// aws:kms).
|
||||||
|
SSECustomerAlgorithm *string `location:"header" locationName:"x-amz-server-side-encryption-customer-algorithm" type:"string"`
|
||||||
|
|
||||||
|
// Specifies the customer-provided encryption key for Amazon S3 to use in encrypting
|
||||||
|
// data. This value is used to store the object and then it is discarded; Amazon
|
||||||
|
// does not store the encryption key. The key must be appropriate for use with
|
||||||
|
// the algorithm specified in the x-amz-server-side-encryption-customer-algorithm
|
||||||
|
// header.
|
||||||
|
SSECustomerKey *string `location:"header" locationName:"x-amz-server-side-encryption-customer-key" type:"string"`
|
||||||
|
|
||||||
|
// Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321.
|
||||||
|
// Amazon S3 uses this header for a message integrity check to ensure the encryption
|
||||||
|
// key was transmitted without error.
|
||||||
|
SSECustomerKeyMD5 *string `location:"header" locationName:"x-amz-server-side-encryption-customer-key-MD5" type:"string"`
|
||||||
|
|
||||||
|
// Specifies the AWS KMS key ID to use for object encryption. All GET and PUT
|
||||||
|
// requests for an object protected by AWS KMS will fail if not made via SSL
|
||||||
|
// or using SigV4. Documentation on configuring any of the officially supported
|
||||||
|
// AWS SDKs and CLI can be found at http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html#specify-signature-version
|
||||||
|
SSEKMSKeyID *string `location:"header" locationName:"x-amz-server-side-encryption-aws-kms-key-id" type:"string"`
|
||||||
|
|
||||||
|
// The Server-side encryption algorithm used when storing this object in S3
|
||||||
|
// (e.g., AES256, aws:kms).
|
||||||
|
ServerSideEncryption *string `location:"header" locationName:"x-amz-server-side-encryption" type:"string"`
|
||||||
|
|
||||||
|
// The type of storage to use for the object. Defaults to 'STANDARD'.
|
||||||
|
StorageClass *string `location:"header" locationName:"x-amz-storage-class" type:"string"`
|
||||||
|
|
||||||
|
// If the bucket is configured as a website, redirects requests for this object
|
||||||
|
// to another object in the same bucket or to an external URL. Amazon S3 stores
|
||||||
|
// the value of this header in the object metadata.
|
||||||
|
WebsiteRedirectLocation *string `location:"header" locationName:"x-amz-website-redirect-location" type:"string"`
|
||||||
|
|
||||||
|
// The readable body payload to send to S3.
|
||||||
|
Body io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadOutput represents a response from the Upload() call.
|
||||||
|
type UploadOutput struct {
|
||||||
|
// The URL where the object was uploaded to.
|
||||||
|
Location string
|
||||||
|
|
||||||
|
// The ID for a multipart upload to S3. In the case of an error the error
|
||||||
|
// can be cast to the MultiUploadFailure interface to extract the upload ID.
|
||||||
|
UploadID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadOptions keeps tracks of extra options to pass to an Upload() call.
|
||||||
|
type UploadOptions struct {
|
||||||
|
// The buffer size (in bytes) to use when buffering data into chunks and
|
||||||
|
// sending them as parts to S3. The minimum allowed part size is 5MB, and
|
||||||
|
// if this value is set to zero, the DefaultPartSize value will be used.
|
||||||
|
PartSize int64
|
||||||
|
|
||||||
|
// The number of goroutines to spin up in parallel when sending parts.
|
||||||
|
// If this is set to zero, the DefaultConcurrency value will be used.
|
||||||
|
Concurrency int
|
||||||
|
|
||||||
|
// Setting this value to true will cause the SDK to avoid calling
|
||||||
|
// AbortMultipartUpload on a failure, leaving all successfully uploaded
|
||||||
|
// parts on S3 for manual recovery.
|
||||||
|
//
|
||||||
|
// Note that storing parts of an incomplete multipart upload counts towards
|
||||||
|
// space usage on S3 and will add additional costs if not cleaned up.
|
||||||
|
LeavePartsOnError bool
|
||||||
|
|
||||||
|
// The client to use when uploading to S3. Leave this as nil to use the
|
||||||
|
// default S3 client.
|
||||||
|
S3 *s3.S3
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUploader creates a new Uploader object to upload data to S3. Pass in
|
||||||
|
// an optional opts structure to customize the uploader behavior.
|
||||||
|
func NewUploader(opts *UploadOptions) *Uploader {
|
||||||
|
if opts == nil {
|
||||||
|
opts = DefaultUploadOptions
|
||||||
|
}
|
||||||
|
return &Uploader{opts: opts}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Uploader structure that calls Upload(). It is safe to call Upload()
|
||||||
|
// on this structure for multiple objects and across concurrent goroutines.
|
||||||
|
type Uploader struct {
|
||||||
|
opts *UploadOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload uploads an object to S3, intelligently buffering large files into
|
||||||
|
// smaller chunks and sending them in parallel across multiple goroutines. You
|
||||||
|
// can configure the buffer size and concurrency through the opts parameter.
|
||||||
|
//
|
||||||
|
// If opts is set to nil, DefaultUploadOptions will be used.
|
||||||
|
//
|
||||||
|
// It is safe to call this method for multiple objects and across concurrent
|
||||||
|
// goroutines.
|
||||||
|
func (u *Uploader) Upload(input *UploadInput) (*UploadOutput, error) {
|
||||||
|
i := uploader{in: input, opts: *u.opts}
|
||||||
|
return i.upload()
|
||||||
|
}
|
||||||
|
|
||||||
|
// internal structure to manage an upload to S3.
|
||||||
|
type uploader struct {
|
||||||
|
in *UploadInput
|
||||||
|
opts UploadOptions
|
||||||
|
|
||||||
|
readerPos int64 // current reader position
|
||||||
|
totalSize int64 // set to -1 if the size is not known
|
||||||
|
}
|
||||||
|
|
||||||
|
// internal logic for deciding whether to upload a single part or use a
|
||||||
|
// multipart upload.
|
||||||
|
func (u *uploader) upload() (*UploadOutput, error) {
|
||||||
|
u.init()
|
||||||
|
|
||||||
|
if u.opts.PartSize < MinUploadPartSize {
|
||||||
|
msg := fmt.Sprintf("part size must be at least %d bytes", MinUploadPartSize)
|
||||||
|
return nil, awserr.New("ConfigError", msg, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do one read to determine if we have more than one part
|
||||||
|
buf, err := u.nextReader()
|
||||||
|
if err == io.EOF || err == io.ErrUnexpectedEOF { // single part
|
||||||
|
return u.singlePart(buf)
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, awserr.New("ReadRequestBody", "read upload data failed", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mu := multiuploader{uploader: u}
|
||||||
|
return mu.upload(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// init will initialize all default options.
|
||||||
|
func (u *uploader) init() {
|
||||||
|
if u.opts.S3 == nil {
|
||||||
|
u.opts.S3 = s3.New(nil)
|
||||||
|
}
|
||||||
|
if u.opts.Concurrency == 0 {
|
||||||
|
u.opts.Concurrency = DefaultUploadConcurrency
|
||||||
|
}
|
||||||
|
if u.opts.PartSize == 0 {
|
||||||
|
u.opts.PartSize = DefaultUploadPartSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get the total size for some optimizations
|
||||||
|
u.initSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
// initSize tries to detect the total stream size, setting u.totalSize. If
|
||||||
|
// the size is not known, totalSize is set to -1.
|
||||||
|
func (u *uploader) initSize() {
|
||||||
|
u.totalSize = -1
|
||||||
|
|
||||||
|
switch r := u.in.Body.(type) {
|
||||||
|
case io.Seeker:
|
||||||
|
pos, _ := r.Seek(0, 1)
|
||||||
|
defer r.Seek(pos, 0)
|
||||||
|
|
||||||
|
n, err := r.Seek(0, 2)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u.totalSize = n
|
||||||
|
|
||||||
|
// try to adjust partSize if it is too small
|
||||||
|
if u.totalSize/u.opts.PartSize >= int64(MaxUploadParts) {
|
||||||
|
u.opts.PartSize = u.totalSize / int64(MaxUploadParts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nextReader returns a seekable reader representing the next packet of data.
|
||||||
|
// This operation increases the shared u.readerPos counter, but note that it
|
||||||
|
// does not need to be wrapped in a mutex because nextReader is only called
|
||||||
|
// from the main thread.
|
||||||
|
func (u *uploader) nextReader() (io.ReadSeeker, error) {
|
||||||
|
switch r := u.in.Body.(type) {
|
||||||
|
case io.ReaderAt:
|
||||||
|
var err error
|
||||||
|
|
||||||
|
n := u.opts.PartSize
|
||||||
|
if u.totalSize >= 0 {
|
||||||
|
bytesLeft := u.totalSize - u.readerPos
|
||||||
|
|
||||||
|
if bytesLeft == 0 {
|
||||||
|
err = io.EOF
|
||||||
|
} else if bytesLeft <= u.opts.PartSize {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
n = bytesLeft
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := io.NewSectionReader(r, u.readerPos, n)
|
||||||
|
u.readerPos += n
|
||||||
|
|
||||||
|
return buf, err
|
||||||
|
|
||||||
|
default:
|
||||||
|
packet := make([]byte, u.opts.PartSize)
|
||||||
|
n, err := io.ReadFull(u.in.Body, packet)
|
||||||
|
u.readerPos += int64(n)
|
||||||
|
|
||||||
|
return bytes.NewReader(packet[0:n]), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// singlePart contains upload logic for uploading a single chunk via
|
||||||
|
// a regular PutObject request. Multipart requests require at least two
|
||||||
|
// parts, or at least 5MB of data.
|
||||||
|
func (u *uploader) singlePart(buf io.ReadSeeker) (*UploadOutput, error) {
|
||||||
|
params := &s3.PutObjectInput{}
|
||||||
|
awsutil.Copy(params, u.in)
|
||||||
|
params.Body = buf
|
||||||
|
|
||||||
|
req, _ := u.opts.S3.PutObjectRequest(params)
|
||||||
|
if err := req.Send(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
url := req.HTTPRequest.URL.String()
|
||||||
|
return &UploadOutput{Location: url}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// internal structure to manage a specific multipart upload to S3.
|
||||||
|
type multiuploader struct {
|
||||||
|
*uploader
|
||||||
|
wg sync.WaitGroup
|
||||||
|
m sync.Mutex
|
||||||
|
err error
|
||||||
|
uploadID string
|
||||||
|
parts completedParts
|
||||||
|
}
|
||||||
|
|
||||||
|
// keeps track of a single chunk of data being sent to S3.
|
||||||
|
type chunk struct {
|
||||||
|
buf io.ReadSeeker
|
||||||
|
num int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// completedParts is a wrapper to make parts sortable by their part number,
|
||||||
|
// since S3 required this list to be sent in sorted order.
|
||||||
|
type completedParts []*s3.CompletedPart
|
||||||
|
|
||||||
|
func (a completedParts) Len() int { return len(a) }
|
||||||
|
func (a completedParts) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
func (a completedParts) Less(i, j int) bool { return *a[i].PartNumber < *a[j].PartNumber }
|
||||||
|
|
||||||
|
// upload will perform a multipart upload using the firstBuf buffer containing
|
||||||
|
// the first chunk of data.
|
||||||
|
func (u *multiuploader) upload(firstBuf io.ReadSeeker) (*UploadOutput, error) {
|
||||||
|
params := &s3.CreateMultipartUploadInput{}
|
||||||
|
awsutil.Copy(params, u.in)
|
||||||
|
|
||||||
|
// Create the multipart
|
||||||
|
resp, err := u.opts.S3.CreateMultipartUpload(params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u.uploadID = *resp.UploadID
|
||||||
|
|
||||||
|
// Create the workers
|
||||||
|
ch := make(chan chunk, u.opts.Concurrency)
|
||||||
|
for i := 0; i < u.opts.Concurrency; i++ {
|
||||||
|
u.wg.Add(1)
|
||||||
|
go u.readChunk(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send part 1 to the workers
|
||||||
|
var num int64 = 1
|
||||||
|
ch <- chunk{buf: firstBuf, num: num}
|
||||||
|
|
||||||
|
// Read and queue the rest of the parts
|
||||||
|
for u.geterr() == nil {
|
||||||
|
// This upload exceeded maximum number of supported parts, error now.
|
||||||
|
if num > int64(MaxUploadParts) {
|
||||||
|
msg := fmt.Sprintf("exceeded total allowed parts (%d). "+
|
||||||
|
"Adjust PartSize to fit in this limit", MaxUploadParts)
|
||||||
|
u.seterr(awserr.New("TotalPartsExceeded", msg, nil))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
num++
|
||||||
|
|
||||||
|
buf, err := u.nextReader()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
ch <- chunk{buf: buf, num: num}
|
||||||
|
|
||||||
|
if err != nil && err != io.ErrUnexpectedEOF {
|
||||||
|
u.seterr(awserr.New(
|
||||||
|
"ReadRequestBody",
|
||||||
|
"read multipart upload data failed",
|
||||||
|
err))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the channel, wait for workers, and complete upload
|
||||||
|
close(ch)
|
||||||
|
u.wg.Wait()
|
||||||
|
complete := u.complete()
|
||||||
|
|
||||||
|
if err := u.geterr(); err != nil {
|
||||||
|
return nil, &multiUploadError{
|
||||||
|
awsError: awserr.New(
|
||||||
|
"MultipartUpload",
|
||||||
|
"upload multipart failed",
|
||||||
|
err),
|
||||||
|
uploadID: u.uploadID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &UploadOutput{
|
||||||
|
Location: *complete.Location,
|
||||||
|
UploadID: u.uploadID,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readChunk runs in worker goroutines to pull chunks off of the ch channel
|
||||||
|
// and send() them as UploadPart requests.
|
||||||
|
func (u *multiuploader) readChunk(ch chan chunk) {
|
||||||
|
defer u.wg.Done()
|
||||||
|
for {
|
||||||
|
data, ok := <-ch
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.geterr() == nil {
|
||||||
|
if err := u.send(data); err != nil {
|
||||||
|
u.seterr(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send performs an UploadPart request and keeps track of the completed
|
||||||
|
// part information.
|
||||||
|
func (u *multiuploader) send(c chunk) error {
|
||||||
|
resp, err := u.opts.S3.UploadPart(&s3.UploadPartInput{
|
||||||
|
Bucket: u.in.Bucket,
|
||||||
|
Key: u.in.Key,
|
||||||
|
Body: c.buf,
|
||||||
|
UploadID: &u.uploadID,
|
||||||
|
PartNumber: &c.num,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
n := c.num
|
||||||
|
completed := &s3.CompletedPart{ETag: resp.ETag, PartNumber: &n}
|
||||||
|
|
||||||
|
u.m.Lock()
|
||||||
|
u.parts = append(u.parts, completed)
|
||||||
|
u.m.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// geterr is a thread-safe getter for the error object
|
||||||
|
func (u *multiuploader) geterr() error {
|
||||||
|
u.m.Lock()
|
||||||
|
defer u.m.Unlock()
|
||||||
|
|
||||||
|
return u.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// seterr is a thread-safe setter for the error object
|
||||||
|
func (u *multiuploader) seterr(e error) {
|
||||||
|
u.m.Lock()
|
||||||
|
defer u.m.Unlock()
|
||||||
|
|
||||||
|
u.err = e
|
||||||
|
}
|
||||||
|
|
||||||
|
// fail will abort the multipart unless LeavePartsOnError is set to true.
|
||||||
|
func (u *multiuploader) fail() {
|
||||||
|
if u.opts.LeavePartsOnError {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
u.opts.S3.AbortMultipartUpload(&s3.AbortMultipartUploadInput{
|
||||||
|
Bucket: u.in.Bucket,
|
||||||
|
Key: u.in.Key,
|
||||||
|
UploadID: &u.uploadID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// complete successfully completes a multipart upload and returns the response.
|
||||||
|
func (u *multiuploader) complete() *s3.CompleteMultipartUploadOutput {
|
||||||
|
if u.geterr() != nil {
|
||||||
|
u.fail()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parts must be sorted in PartNumber order.
|
||||||
|
sort.Sort(u.parts)
|
||||||
|
|
||||||
|
resp, err := u.opts.S3.CompleteMultipartUpload(&s3.CompleteMultipartUploadInput{
|
||||||
|
Bucket: u.in.Bucket,
|
||||||
|
Key: u.in.Key,
|
||||||
|
UploadID: &u.uploadID,
|
||||||
|
MultipartUpload: &s3.CompletedMultipartUpload{Parts: u.parts},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
u.seterr(err)
|
||||||
|
u.fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp
|
||||||
|
}
|
438
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3manager/upload_test.go
generated
vendored
Normal file
438
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3manager/upload_test.go
generated
vendored
Normal file
|
@ -0,0 +1,438 @@
|
||||||
|
package s3manager_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/test/unit"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = unit.Imported
|
||||||
|
var buf12MB = make([]byte, 1024*1024*12)
|
||||||
|
var buf2MB = make([]byte, 1024*1024*2)
|
||||||
|
|
||||||
|
var emptyList = []string{}
|
||||||
|
|
||||||
|
func val(i interface{}, s string) interface{} {
|
||||||
|
return awsutil.ValuesAtPath(i, s)[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func contains(src []string, s string) bool {
|
||||||
|
for _, v := range src {
|
||||||
|
if s == v {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func loggingSvc(ignoreOps []string) (*s3.S3, *[]string, *[]interface{}) {
|
||||||
|
var m sync.Mutex
|
||||||
|
partNum := 0
|
||||||
|
names := []string{}
|
||||||
|
params := []interface{}{}
|
||||||
|
svc := s3.New(nil)
|
||||||
|
svc.Handlers.Unmarshal.Clear()
|
||||||
|
svc.Handlers.UnmarshalMeta.Clear()
|
||||||
|
svc.Handlers.UnmarshalError.Clear()
|
||||||
|
svc.Handlers.Send.Clear()
|
||||||
|
svc.Handlers.Send.PushBack(func(r *aws.Request) {
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
if !contains(ignoreOps, r.Operation.Name) {
|
||||||
|
names = append(names, r.Operation.Name)
|
||||||
|
params = append(params, r.Params)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.HTTPResponse = &http.Response{
|
||||||
|
StatusCode: 200,
|
||||||
|
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
|
||||||
|
}
|
||||||
|
|
||||||
|
switch data := r.Data.(type) {
|
||||||
|
case *s3.CreateMultipartUploadOutput:
|
||||||
|
data.UploadID = aws.String("UPLOAD-ID")
|
||||||
|
case *s3.UploadPartOutput:
|
||||||
|
partNum++
|
||||||
|
data.ETag = aws.String(fmt.Sprintf("ETAG%d", partNum))
|
||||||
|
case *s3.CompleteMultipartUploadOutput:
|
||||||
|
data.Location = aws.String("https://location")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return svc, &names, ¶ms
|
||||||
|
}
|
||||||
|
|
||||||
|
func buflen(i interface{}) int {
|
||||||
|
r := i.(io.Reader)
|
||||||
|
b, _ := ioutil.ReadAll(r)
|
||||||
|
return len(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadOrderMulti(t *testing.T) {
|
||||||
|
s, ops, args := loggingSvc(emptyList)
|
||||||
|
mgr := s3manager.NewUploader(&s3manager.UploadOptions{S3: s})
|
||||||
|
resp, err := mgr.Upload(&s3manager.UploadInput{
|
||||||
|
Bucket: aws.String("Bucket"),
|
||||||
|
Key: aws.String("Key"),
|
||||||
|
Body: bytes.NewReader(buf12MB),
|
||||||
|
ServerSideEncryption: aws.String("AES256"),
|
||||||
|
ContentType: aws.String("content/type"),
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "UploadPart", "CompleteMultipartUpload"}, *ops)
|
||||||
|
assert.Equal(t, "https://location", resp.Location)
|
||||||
|
assert.Equal(t, "UPLOAD-ID", resp.UploadID)
|
||||||
|
|
||||||
|
// Validate input values
|
||||||
|
|
||||||
|
// UploadPart
|
||||||
|
assert.Equal(t, "UPLOAD-ID", val((*args)[1], "UploadID"))
|
||||||
|
assert.Equal(t, "UPLOAD-ID", val((*args)[2], "UploadID"))
|
||||||
|
assert.Equal(t, "UPLOAD-ID", val((*args)[3], "UploadID"))
|
||||||
|
|
||||||
|
// CompleteMultipartUpload
|
||||||
|
assert.Equal(t, "UPLOAD-ID", val((*args)[4], "UploadID"))
|
||||||
|
assert.Equal(t, int64(1), val((*args)[4], "MultipartUpload.Parts[0].PartNumber"))
|
||||||
|
assert.Equal(t, int64(2), val((*args)[4], "MultipartUpload.Parts[1].PartNumber"))
|
||||||
|
assert.Equal(t, int64(3), val((*args)[4], "MultipartUpload.Parts[2].PartNumber"))
|
||||||
|
assert.Regexp(t, `^ETAG\d+$`, val((*args)[4], "MultipartUpload.Parts[0].ETag"))
|
||||||
|
assert.Regexp(t, `^ETAG\d+$`, val((*args)[4], "MultipartUpload.Parts[1].ETag"))
|
||||||
|
assert.Regexp(t, `^ETAG\d+$`, val((*args)[4], "MultipartUpload.Parts[2].ETag"))
|
||||||
|
|
||||||
|
// Custom headers
|
||||||
|
assert.Equal(t, "AES256", val((*args)[0], "ServerSideEncryption"))
|
||||||
|
assert.Equal(t, "content/type", val((*args)[0], "ContentType"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadOrderMultiDifferentPartSize(t *testing.T) {
|
||||||
|
s, ops, args := loggingSvc(emptyList)
|
||||||
|
mgr := s3manager.NewUploader(&s3manager.UploadOptions{
|
||||||
|
S3: s,
|
||||||
|
PartSize: 1024 * 1024 * 7,
|
||||||
|
Concurrency: 1,
|
||||||
|
})
|
||||||
|
_, err := mgr.Upload(&s3manager.UploadInput{
|
||||||
|
Bucket: aws.String("Bucket"),
|
||||||
|
Key: aws.String("Key"),
|
||||||
|
Body: bytes.NewReader(buf12MB),
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "CompleteMultipartUpload"}, *ops)
|
||||||
|
|
||||||
|
// Part lengths
|
||||||
|
assert.Equal(t, 1024*1024*7, buflen(val((*args)[1], "Body")))
|
||||||
|
assert.Equal(t, 1024*1024*5, buflen(val((*args)[2], "Body")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadIncreasePartSize(t *testing.T) {
|
||||||
|
s3manager.MaxUploadParts = 2
|
||||||
|
defer func() { s3manager.MaxUploadParts = 10000 }()
|
||||||
|
|
||||||
|
s, ops, args := loggingSvc(emptyList)
|
||||||
|
opts := &s3manager.UploadOptions{S3: s, Concurrency: 1}
|
||||||
|
mgr := s3manager.NewUploader(opts)
|
||||||
|
_, err := mgr.Upload(&s3manager.UploadInput{
|
||||||
|
Bucket: aws.String("Bucket"),
|
||||||
|
Key: aws.String("Key"),
|
||||||
|
Body: bytes.NewReader(buf12MB),
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, int64(0), opts.PartSize) // don't modify orig options
|
||||||
|
assert.Equal(t, []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "CompleteMultipartUpload"}, *ops)
|
||||||
|
|
||||||
|
// Part lengths
|
||||||
|
assert.Equal(t, 1024*1024*6, buflen(val((*args)[1], "Body")))
|
||||||
|
assert.Equal(t, 1024*1024*6, buflen(val((*args)[2], "Body")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadFailIfPartSizeTooSmall(t *testing.T) {
|
||||||
|
opts := &s3manager.UploadOptions{PartSize: 5}
|
||||||
|
mgr := s3manager.NewUploader(opts)
|
||||||
|
resp, err := mgr.Upload(&s3manager.UploadInput{
|
||||||
|
Bucket: aws.String("Bucket"),
|
||||||
|
Key: aws.String("Key"),
|
||||||
|
Body: bytes.NewReader(buf12MB),
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Nil(t, resp)
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
aerr := err.(awserr.Error)
|
||||||
|
assert.Equal(t, "ConfigError", aerr.Code())
|
||||||
|
assert.Contains(t, aerr.Message(), "part size must be at least")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadOrderSingle(t *testing.T) {
|
||||||
|
s, ops, args := loggingSvc(emptyList)
|
||||||
|
mgr := s3manager.NewUploader(&s3manager.UploadOptions{S3: s})
|
||||||
|
resp, err := mgr.Upload(&s3manager.UploadInput{
|
||||||
|
Bucket: aws.String("Bucket"),
|
||||||
|
Key: aws.String("Key"),
|
||||||
|
Body: bytes.NewReader(buf2MB),
|
||||||
|
ServerSideEncryption: aws.String("AES256"),
|
||||||
|
ContentType: aws.String("content/type"),
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []string{"PutObject"}, *ops)
|
||||||
|
assert.NotEqual(t, "", resp.Location)
|
||||||
|
assert.Equal(t, "", resp.UploadID)
|
||||||
|
assert.Equal(t, "AES256", val((*args)[0], "ServerSideEncryption"))
|
||||||
|
assert.Equal(t, "content/type", val((*args)[0], "ContentType"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadOrderSingleFailure(t *testing.T) {
|
||||||
|
s, ops, _ := loggingSvc(emptyList)
|
||||||
|
s.Handlers.Send.PushBack(func(r *aws.Request) {
|
||||||
|
r.HTTPResponse.StatusCode = 400
|
||||||
|
})
|
||||||
|
mgr := s3manager.NewUploader(&s3manager.UploadOptions{S3: s})
|
||||||
|
resp, err := mgr.Upload(&s3manager.UploadInput{
|
||||||
|
Bucket: aws.String("Bucket"),
|
||||||
|
Key: aws.String("Key"),
|
||||||
|
Body: bytes.NewReader(buf2MB),
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, []string{"PutObject"}, *ops)
|
||||||
|
assert.Nil(t, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadOrderZero(t *testing.T) {
|
||||||
|
s, ops, args := loggingSvc(emptyList)
|
||||||
|
mgr := s3manager.NewUploader(&s3manager.UploadOptions{S3: s})
|
||||||
|
resp, err := mgr.Upload(&s3manager.UploadInput{
|
||||||
|
Bucket: aws.String("Bucket"),
|
||||||
|
Key: aws.String("Key"),
|
||||||
|
Body: bytes.NewReader(make([]byte, 0)),
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []string{"PutObject"}, *ops)
|
||||||
|
assert.NotEqual(t, "", resp.Location)
|
||||||
|
assert.Equal(t, "", resp.UploadID)
|
||||||
|
assert.Equal(t, 0, buflen(val((*args)[0], "Body")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadOrderMultiFailure(t *testing.T) {
|
||||||
|
s, ops, _ := loggingSvc(emptyList)
|
||||||
|
s.Handlers.Send.PushBack(func(r *aws.Request) {
|
||||||
|
switch t := r.Data.(type) {
|
||||||
|
case *s3.UploadPartOutput:
|
||||||
|
if *t.ETag == "ETAG2" {
|
||||||
|
r.HTTPResponse.StatusCode = 400
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
mgr := s3manager.NewUploader(&s3manager.UploadOptions{S3: s, Concurrency: 1})
|
||||||
|
_, err := mgr.Upload(&s3manager.UploadInput{
|
||||||
|
Bucket: aws.String("Bucket"),
|
||||||
|
Key: aws.String("Key"),
|
||||||
|
Body: bytes.NewReader(buf12MB),
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "AbortMultipartUpload"}, *ops)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadOrderMultiFailureOnComplete(t *testing.T) {
|
||||||
|
s, ops, _ := loggingSvc(emptyList)
|
||||||
|
s.Handlers.Send.PushBack(func(r *aws.Request) {
|
||||||
|
switch r.Data.(type) {
|
||||||
|
case *s3.CompleteMultipartUploadOutput:
|
||||||
|
r.HTTPResponse.StatusCode = 400
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
mgr := s3manager.NewUploader(&s3manager.UploadOptions{S3: s, Concurrency: 1})
|
||||||
|
_, err := mgr.Upload(&s3manager.UploadInput{
|
||||||
|
Bucket: aws.String("Bucket"),
|
||||||
|
Key: aws.String("Key"),
|
||||||
|
Body: bytes.NewReader(buf12MB),
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, []string{"CreateMultipartUpload", "UploadPart", "UploadPart",
|
||||||
|
"UploadPart", "CompleteMultipartUpload", "AbortMultipartUpload"}, *ops)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadOrderMultiFailureOnCreate(t *testing.T) {
|
||||||
|
s, ops, _ := loggingSvc(emptyList)
|
||||||
|
s.Handlers.Send.PushBack(func(r *aws.Request) {
|
||||||
|
switch r.Data.(type) {
|
||||||
|
case *s3.CreateMultipartUploadOutput:
|
||||||
|
r.HTTPResponse.StatusCode = 400
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
mgr := s3manager.NewUploader(&s3manager.UploadOptions{S3: s})
|
||||||
|
_, err := mgr.Upload(&s3manager.UploadInput{
|
||||||
|
Bucket: aws.String("Bucket"),
|
||||||
|
Key: aws.String("Key"),
|
||||||
|
Body: bytes.NewReader(make([]byte, 1024*1024*12)),
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, []string{"CreateMultipartUpload"}, *ops)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadOrderMultiFailureLeaveParts(t *testing.T) {
|
||||||
|
s, ops, _ := loggingSvc(emptyList)
|
||||||
|
s.Handlers.Send.PushBack(func(r *aws.Request) {
|
||||||
|
switch data := r.Data.(type) {
|
||||||
|
case *s3.UploadPartOutput:
|
||||||
|
if *data.ETag == "ETAG2" {
|
||||||
|
r.HTTPResponse.StatusCode = 400
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
mgr := s3manager.NewUploader(&s3manager.UploadOptions{
|
||||||
|
S3: s,
|
||||||
|
Concurrency: 1,
|
||||||
|
LeavePartsOnError: true,
|
||||||
|
})
|
||||||
|
_, err := mgr.Upload(&s3manager.UploadInput{
|
||||||
|
Bucket: aws.String("Bucket"),
|
||||||
|
Key: aws.String("Key"),
|
||||||
|
Body: bytes.NewReader(make([]byte, 1024*1024*12)),
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, []string{"CreateMultipartUpload", "UploadPart", "UploadPart"}, *ops)
|
||||||
|
}
|
||||||
|
|
||||||
|
type failreader struct {
|
||||||
|
times int
|
||||||
|
failCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *failreader) Read(b []byte) (int, error) {
|
||||||
|
f.failCount++
|
||||||
|
if f.failCount >= f.times {
|
||||||
|
return 0, fmt.Errorf("random failure")
|
||||||
|
}
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadOrderReadFail1(t *testing.T) {
|
||||||
|
s, ops, _ := loggingSvc(emptyList)
|
||||||
|
mgr := s3manager.NewUploader(&s3manager.UploadOptions{S3: s})
|
||||||
|
_, err := mgr.Upload(&s3manager.UploadInput{
|
||||||
|
Bucket: aws.String("Bucket"),
|
||||||
|
Key: aws.String("Key"),
|
||||||
|
Body: &failreader{times: 1},
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, "ReadRequestBody", err.(awserr.Error).Code())
|
||||||
|
assert.EqualError(t, err.(awserr.Error).OrigErr(), "random failure")
|
||||||
|
assert.Equal(t, []string{}, *ops)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadOrderReadFail2(t *testing.T) {
|
||||||
|
s, ops, _ := loggingSvc([]string{"UploadPart"})
|
||||||
|
mgr := s3manager.NewUploader(&s3manager.UploadOptions{S3: s, Concurrency: 1})
|
||||||
|
_, err := mgr.Upload(&s3manager.UploadInput{
|
||||||
|
Bucket: aws.String("Bucket"),
|
||||||
|
Key: aws.String("Key"),
|
||||||
|
Body: &failreader{times: 2},
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, "ReadRequestBody", err.(awserr.Error).Code())
|
||||||
|
assert.EqualError(t, err.(awserr.Error).OrigErr(), "random failure")
|
||||||
|
assert.Equal(t, []string{"CreateMultipartUpload", "AbortMultipartUpload"}, *ops)
|
||||||
|
}
|
||||||
|
|
||||||
|
type sizedReader struct {
|
||||||
|
size int
|
||||||
|
cur int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sizedReader) Read(p []byte) (n int, err error) {
|
||||||
|
if s.cur >= s.size {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
n = len(p)
|
||||||
|
s.cur += len(p)
|
||||||
|
if s.cur > s.size {
|
||||||
|
n -= s.cur - s.size
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadOrderMultiBufferedReader(t *testing.T) {
|
||||||
|
s, ops, args := loggingSvc(emptyList)
|
||||||
|
mgr := s3manager.NewUploader(&s3manager.UploadOptions{S3: s})
|
||||||
|
_, err := mgr.Upload(&s3manager.UploadInput{
|
||||||
|
Bucket: aws.String("Bucket"),
|
||||||
|
Key: aws.String("Key"),
|
||||||
|
Body: &sizedReader{size: 1024 * 1024 * 12},
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "UploadPart", "CompleteMultipartUpload"}, *ops)
|
||||||
|
|
||||||
|
// Part lengths
|
||||||
|
parts := []int{
|
||||||
|
buflen(val((*args)[1], "Body")),
|
||||||
|
buflen(val((*args)[2], "Body")),
|
||||||
|
buflen(val((*args)[3], "Body")),
|
||||||
|
}
|
||||||
|
sort.Ints(parts)
|
||||||
|
assert.Equal(t, []int{1024 * 1024 * 2, 1024 * 1024 * 5, 1024 * 1024 * 5}, parts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadOrderMultiBufferedReaderExceedTotalParts(t *testing.T) {
|
||||||
|
s3manager.MaxUploadParts = 2
|
||||||
|
defer func() { s3manager.MaxUploadParts = 10000 }()
|
||||||
|
s, ops, _ := loggingSvc([]string{"UploadPart"})
|
||||||
|
mgr := s3manager.NewUploader(&s3manager.UploadOptions{S3: s, Concurrency: 1})
|
||||||
|
resp, err := mgr.Upload(&s3manager.UploadInput{
|
||||||
|
Bucket: aws.String("Bucket"),
|
||||||
|
Key: aws.String("Key"),
|
||||||
|
Body: &sizedReader{size: 1024 * 1024 * 12},
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, resp)
|
||||||
|
assert.Equal(t, []string{"CreateMultipartUpload", "AbortMultipartUpload"}, *ops)
|
||||||
|
|
||||||
|
aerr := err.(awserr.Error)
|
||||||
|
assert.Equal(t, "TotalPartsExceeded", aerr.Code())
|
||||||
|
assert.Contains(t, aerr.Message(), "exceeded total allowed parts (2)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadOrderSingleBufferedReader(t *testing.T) {
|
||||||
|
s, ops, _ := loggingSvc(emptyList)
|
||||||
|
mgr := s3manager.NewUploader(&s3manager.UploadOptions{S3: s})
|
||||||
|
resp, err := mgr.Upload(&s3manager.UploadInput{
|
||||||
|
Bucket: aws.String("Bucket"),
|
||||||
|
Key: aws.String("Key"),
|
||||||
|
Body: &sizedReader{size: 1024 * 1024 * 2},
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []string{"PutObject"}, *ops)
|
||||||
|
assert.NotEqual(t, "", resp.Location)
|
||||||
|
assert.Equal(t, "", resp.UploadID)
|
||||||
|
}
|
57
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/service.go
generated
vendored
Normal file
57
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/service.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
|
||||||
|
|
||||||
|
package s3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/protocol/restxml"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/signer/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
// S3 is a client for Amazon S3.
|
||||||
|
type S3 struct {
|
||||||
|
*aws.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used for custom service initialization logic
|
||||||
|
var initService func(*aws.Service)
|
||||||
|
|
||||||
|
// Used for custom request initialization logic
|
||||||
|
var initRequest func(*aws.Request)
|
||||||
|
|
||||||
|
// New returns a new S3 client.
|
||||||
|
func New(config *aws.Config) *S3 {
|
||||||
|
service := &aws.Service{
|
||||||
|
Config: aws.DefaultConfig.Merge(config),
|
||||||
|
ServiceName: "s3",
|
||||||
|
APIVersion: "2006-03-01",
|
||||||
|
}
|
||||||
|
service.Initialize()
|
||||||
|
|
||||||
|
// Handlers
|
||||||
|
service.Handlers.Sign.PushBack(v4.Sign)
|
||||||
|
service.Handlers.Build.PushBack(restxml.Build)
|
||||||
|
service.Handlers.Unmarshal.PushBack(restxml.Unmarshal)
|
||||||
|
service.Handlers.UnmarshalMeta.PushBack(restxml.UnmarshalMeta)
|
||||||
|
service.Handlers.UnmarshalError.PushBack(restxml.UnmarshalError)
|
||||||
|
|
||||||
|
// Run custom service initialization if present
|
||||||
|
if initService != nil {
|
||||||
|
initService(service)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &S3{service}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newRequest creates a new request for a S3 operation and runs any
|
||||||
|
// custom request initialization.
|
||||||
|
func (c *S3) newRequest(op *aws.Operation, params, data interface{}) *aws.Request {
|
||||||
|
req := aws.NewRequest(c.Service, op, params, data)
|
||||||
|
|
||||||
|
// Run custom request initialization if present
|
||||||
|
if initRequest != nil {
|
||||||
|
initRequest(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
return req
|
||||||
|
}
|
44
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/sse.go
generated
vendored
Normal file
44
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/sse.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package s3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/base64"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errSSERequiresSSL = awserr.New("ConfigError", "cannot send SSE keys over HTTP.", nil)
|
||||||
|
|
||||||
|
func validateSSERequiresSSL(r *aws.Request) {
|
||||||
|
if r.HTTPRequest.URL.Scheme != "https" {
|
||||||
|
p := awsutil.ValuesAtPath(r.Params, "SSECustomerKey||CopySourceSSECustomerKey")
|
||||||
|
if len(p) > 0 {
|
||||||
|
r.Error = errSSERequiresSSL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeSSEKeys(r *aws.Request) {
|
||||||
|
headers := []string{
|
||||||
|
"x-amz-server-side-encryption-customer-key",
|
||||||
|
"x-amz-copy-source-server-side-encryption-customer-key",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, h := range headers {
|
||||||
|
md5h := h + "-md5"
|
||||||
|
if key := r.HTTPRequest.Header.Get(h); key != "" {
|
||||||
|
// Base64-encode the value
|
||||||
|
b64v := base64.StdEncoding.EncodeToString([]byte(key))
|
||||||
|
r.HTTPRequest.Header.Set(h, b64v)
|
||||||
|
|
||||||
|
// Add MD5 if it wasn't computed
|
||||||
|
if r.HTTPRequest.Header.Get(md5h) == "" {
|
||||||
|
sum := md5.Sum([]byte(key))
|
||||||
|
b64sum := base64.StdEncoding.EncodeToString(sum[:])
|
||||||
|
r.HTTPRequest.Header.Set(md5h, b64sum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
81
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/sse_test.go
generated
vendored
Normal file
81
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/sse_test.go
generated
vendored
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
package s3_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/test/unit"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = unit.Imported
|
||||||
|
|
||||||
|
func TestSSECustomerKeyOverHTTPError(t *testing.T) {
|
||||||
|
s := s3.New(&aws.Config{DisableSSL: aws.Bool(true)})
|
||||||
|
req, _ := s.CopyObjectRequest(&s3.CopyObjectInput{
|
||||||
|
Bucket: aws.String("bucket"),
|
||||||
|
CopySource: aws.String("bucket/source"),
|
||||||
|
Key: aws.String("dest"),
|
||||||
|
SSECustomerKey: aws.String("key"),
|
||||||
|
})
|
||||||
|
err := req.Build()
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, "ConfigError", err.(awserr.Error).Code())
|
||||||
|
assert.Contains(t, err.(awserr.Error).Message(), "cannot send SSE keys over HTTP")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopySourceSSECustomerKeyOverHTTPError(t *testing.T) {
|
||||||
|
s := s3.New(&aws.Config{DisableSSL: aws.Bool(true)})
|
||||||
|
req, _ := s.CopyObjectRequest(&s3.CopyObjectInput{
|
||||||
|
Bucket: aws.String("bucket"),
|
||||||
|
CopySource: aws.String("bucket/source"),
|
||||||
|
Key: aws.String("dest"),
|
||||||
|
CopySourceSSECustomerKey: aws.String("key"),
|
||||||
|
})
|
||||||
|
err := req.Build()
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, "ConfigError", err.(awserr.Error).Code())
|
||||||
|
assert.Contains(t, err.(awserr.Error).Message(), "cannot send SSE keys over HTTP")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComputeSSEKeys(t *testing.T) {
|
||||||
|
s := s3.New(nil)
|
||||||
|
req, _ := s.CopyObjectRequest(&s3.CopyObjectInput{
|
||||||
|
Bucket: aws.String("bucket"),
|
||||||
|
CopySource: aws.String("bucket/source"),
|
||||||
|
Key: aws.String("dest"),
|
||||||
|
SSECustomerKey: aws.String("key"),
|
||||||
|
CopySourceSSECustomerKey: aws.String("key"),
|
||||||
|
})
|
||||||
|
err := req.Build()
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "a2V5", req.HTTPRequest.Header.Get("x-amz-server-side-encryption-customer-key"))
|
||||||
|
assert.Equal(t, "a2V5", req.HTTPRequest.Header.Get("x-amz-copy-source-server-side-encryption-customer-key"))
|
||||||
|
assert.Equal(t, "PG4LipwVIkqCKLmpjKFTHQ==", req.HTTPRequest.Header.Get("x-amz-server-side-encryption-customer-key-md5"))
|
||||||
|
assert.Equal(t, "PG4LipwVIkqCKLmpjKFTHQ==", req.HTTPRequest.Header.Get("x-amz-copy-source-server-side-encryption-customer-key-md5"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComputeSSEKeysShortcircuit(t *testing.T) {
|
||||||
|
s := s3.New(nil)
|
||||||
|
req, _ := s.CopyObjectRequest(&s3.CopyObjectInput{
|
||||||
|
Bucket: aws.String("bucket"),
|
||||||
|
CopySource: aws.String("bucket/source"),
|
||||||
|
Key: aws.String("dest"),
|
||||||
|
SSECustomerKey: aws.String("key"),
|
||||||
|
CopySourceSSECustomerKey: aws.String("key"),
|
||||||
|
SSECustomerKeyMD5: aws.String("MD5"),
|
||||||
|
CopySourceSSECustomerKeyMD5: aws.String("MD5"),
|
||||||
|
})
|
||||||
|
err := req.Build()
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "a2V5", req.HTTPRequest.Header.Get("x-amz-server-side-encryption-customer-key"))
|
||||||
|
assert.Equal(t, "a2V5", req.HTTPRequest.Header.Get("x-amz-copy-source-server-side-encryption-customer-key"))
|
||||||
|
assert.Equal(t, "MD5", req.HTTPRequest.Header.Get("x-amz-server-side-encryption-customer-key-md5"))
|
||||||
|
assert.Equal(t, "MD5", req.HTTPRequest.Header.Get("x-amz-copy-source-server-side-encryption-customer-key-md5"))
|
||||||
|
}
|
42
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/unmarshal_error.go
generated
vendored
Normal file
42
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/unmarshal_error.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package s3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
type xmlErrorResponse struct {
|
||||||
|
XMLName xml.Name `xml:"Error"`
|
||||||
|
Code string `xml:"Code"`
|
||||||
|
Message string `xml:"Message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalError(r *aws.Request) {
|
||||||
|
defer r.HTTPResponse.Body.Close()
|
||||||
|
|
||||||
|
if r.HTTPResponse.ContentLength == int64(0) {
|
||||||
|
// No body, use status code to generate an awserr.Error
|
||||||
|
r.Error = awserr.NewRequestFailure(
|
||||||
|
awserr.New(strings.Replace(r.HTTPResponse.Status, " ", "", -1), r.HTTPResponse.Status, nil),
|
||||||
|
r.HTTPResponse.StatusCode,
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &xmlErrorResponse{}
|
||||||
|
err := xml.NewDecoder(r.HTTPResponse.Body).Decode(resp)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed to decode S3 XML error response", nil)
|
||||||
|
} else {
|
||||||
|
r.Error = awserr.NewRequestFailure(
|
||||||
|
awserr.New(resp.Code, resp.Message, nil),
|
||||||
|
r.HTTPResponse.StatusCode,
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
53
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/unmarshal_error_test.go
generated
vendored
Normal file
53
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/unmarshal_error_test.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package s3_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/internal/test/unit"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = unit.Imported
|
||||||
|
|
||||||
|
var s3StatusCodeErrorTests = []struct {
|
||||||
|
scode int
|
||||||
|
status string
|
||||||
|
body string
|
||||||
|
code string
|
||||||
|
message string
|
||||||
|
}{
|
||||||
|
{301, "Moved Permanently", "", "MovedPermanently", "Moved Permanently"},
|
||||||
|
{403, "Forbidden", "", "Forbidden", "Forbidden"},
|
||||||
|
{400, "Bad Request", "", "BadRequest", "Bad Request"},
|
||||||
|
{404, "Not Found", "", "NotFound", "Not Found"},
|
||||||
|
{500, "Internal Error", "", "InternalError", "Internal Error"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStatusCodeError(t *testing.T) {
|
||||||
|
for _, test := range s3StatusCodeErrorTests {
|
||||||
|
s := s3.New(nil)
|
||||||
|
s.Handlers.Send.Clear()
|
||||||
|
s.Handlers.Send.PushBack(func(r *aws.Request) {
|
||||||
|
body := ioutil.NopCloser(bytes.NewReader([]byte(test.body)))
|
||||||
|
r.HTTPResponse = &http.Response{
|
||||||
|
ContentLength: int64(len(test.body)),
|
||||||
|
StatusCode: test.scode,
|
||||||
|
Status: test.status,
|
||||||
|
Body: body,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
_, err := s.PutBucketACL(&s3.PutBucketACLInput{
|
||||||
|
Bucket: aws.String("bucket"), ACL: aws.String("public-read"),
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, test.code, err.(awserr.Error).Code())
|
||||||
|
assert.Equal(t, test.message, err.(awserr.Error).Message())
|
||||||
|
}
|
||||||
|
}
|
13
Godeps/_workspace/src/github.com/inconshreveable/mousetrap/LICENSE
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/inconshreveable/mousetrap/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
Copyright 2014 Alan Shreve
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
23
Godeps/_workspace/src/github.com/inconshreveable/mousetrap/README.md
generated
vendored
Normal file
23
Godeps/_workspace/src/github.com/inconshreveable/mousetrap/README.md
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# mousetrap
|
||||||
|
|
||||||
|
mousetrap is a tiny library that answers a single question.
|
||||||
|
|
||||||
|
On a Windows machine, was the process invoked by someone double clicking on
|
||||||
|
the executable file while browsing in explorer?
|
||||||
|
|
||||||
|
### Motivation
|
||||||
|
|
||||||
|
Windows developers unfamiliar with command line tools will often "double-click"
|
||||||
|
the executable for a tool. Because most CLI tools print the help and then exit
|
||||||
|
when invoked without arguments, this is often very frustrating for those users.
|
||||||
|
|
||||||
|
mousetrap provides a way to detect these invocations so that you can provide
|
||||||
|
more helpful behavior and instructions on how to run the CLI tool. To see what
|
||||||
|
this looks like, both from an organizational and a technical perspective, see
|
||||||
|
https://inconshreveable.com/09-09-2014/sweat-the-small-stuff/
|
||||||
|
|
||||||
|
### The interface
|
||||||
|
|
||||||
|
The library exposes a single interface:
|
||||||
|
|
||||||
|
func StartedByExplorer() (bool)
|
15
Godeps/_workspace/src/github.com/inconshreveable/mousetrap/trap_others.go
generated
vendored
Normal file
15
Godeps/_workspace/src/github.com/inconshreveable/mousetrap/trap_others.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package mousetrap
|
||||||
|
|
||||||
|
// StartedByExplorer returns true if the program was invoked by the user
|
||||||
|
// double-clicking on the executable from explorer.exe
|
||||||
|
//
|
||||||
|
// It is conservative and returns false if any of the internal calls fail.
|
||||||
|
// It does not guarantee that the program was run from a terminal. It only can tell you
|
||||||
|
// whether it was launched from explorer.exe
|
||||||
|
//
|
||||||
|
// On non-Windows platforms, it always returns false.
|
||||||
|
func StartedByExplorer() bool {
|
||||||
|
return false
|
||||||
|
}
|
98
Godeps/_workspace/src/github.com/inconshreveable/mousetrap/trap_windows.go
generated
vendored
Normal file
98
Godeps/_workspace/src/github.com/inconshreveable/mousetrap/trap_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
// +build windows
|
||||||
|
// +build !go1.4
|
||||||
|
|
||||||
|
package mousetrap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// defined by the Win32 API
|
||||||
|
th32cs_snapprocess uintptr = 0x2
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
kernel = syscall.MustLoadDLL("kernel32.dll")
|
||||||
|
CreateToolhelp32Snapshot = kernel.MustFindProc("CreateToolhelp32Snapshot")
|
||||||
|
Process32First = kernel.MustFindProc("Process32FirstW")
|
||||||
|
Process32Next = kernel.MustFindProc("Process32NextW")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessEntry32 structure defined by the Win32 API
|
||||||
|
type processEntry32 struct {
|
||||||
|
dwSize uint32
|
||||||
|
cntUsage uint32
|
||||||
|
th32ProcessID uint32
|
||||||
|
th32DefaultHeapID int
|
||||||
|
th32ModuleID uint32
|
||||||
|
cntThreads uint32
|
||||||
|
th32ParentProcessID uint32
|
||||||
|
pcPriClassBase int32
|
||||||
|
dwFlags uint32
|
||||||
|
szExeFile [syscall.MAX_PATH]uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProcessEntry(pid int) (pe *processEntry32, err error) {
|
||||||
|
snapshot, _, e1 := CreateToolhelp32Snapshot.Call(th32cs_snapprocess, uintptr(0))
|
||||||
|
if snapshot == uintptr(syscall.InvalidHandle) {
|
||||||
|
err = fmt.Errorf("CreateToolhelp32Snapshot: %v", e1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer syscall.CloseHandle(syscall.Handle(snapshot))
|
||||||
|
|
||||||
|
var processEntry processEntry32
|
||||||
|
processEntry.dwSize = uint32(unsafe.Sizeof(processEntry))
|
||||||
|
ok, _, e1 := Process32First.Call(snapshot, uintptr(unsafe.Pointer(&processEntry)))
|
||||||
|
if ok == 0 {
|
||||||
|
err = fmt.Errorf("Process32First: %v", e1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
if processEntry.th32ProcessID == uint32(pid) {
|
||||||
|
pe = &processEntry
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, _, e1 = Process32Next.Call(snapshot, uintptr(unsafe.Pointer(&processEntry)))
|
||||||
|
if ok == 0 {
|
||||||
|
err = fmt.Errorf("Process32Next: %v", e1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getppid() (pid int, err error) {
|
||||||
|
pe, err := getProcessEntry(os.Getpid())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pid = int(pe.th32ParentProcessID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartedByExplorer returns true if the program was invoked by the user double-clicking
|
||||||
|
// on the executable from explorer.exe
|
||||||
|
//
|
||||||
|
// It is conservative and returns false if any of the internal calls fail.
|
||||||
|
// It does not guarantee that the program was run from a terminal. It only can tell you
|
||||||
|
// whether it was launched from explorer.exe
|
||||||
|
func StartedByExplorer() bool {
|
||||||
|
ppid, err := getppid()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
pe, err := getProcessEntry(ppid)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
name := syscall.UTF16ToString(pe.szExeFile[:])
|
||||||
|
return name == "explorer.exe"
|
||||||
|
}
|
46
Godeps/_workspace/src/github.com/inconshreveable/mousetrap/trap_windows_1.4.go
generated
vendored
Normal file
46
Godeps/_workspace/src/github.com/inconshreveable/mousetrap/trap_windows_1.4.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
// +build windows
|
||||||
|
// +build go1.4
|
||||||
|
|
||||||
|
package mousetrap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getProcessEntry(pid int) (*syscall.ProcessEntry32, error) {
|
||||||
|
snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer syscall.CloseHandle(snapshot)
|
||||||
|
var procEntry syscall.ProcessEntry32
|
||||||
|
procEntry.Size = uint32(unsafe.Sizeof(procEntry))
|
||||||
|
if err = syscall.Process32First(snapshot, &procEntry); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
if procEntry.ProcessID == uint32(pid) {
|
||||||
|
return &procEntry, nil
|
||||||
|
}
|
||||||
|
err = syscall.Process32Next(snapshot, &procEntry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartedByExplorer returns true if the program was invoked by the user double-clicking
|
||||||
|
// on the executable from explorer.exe
|
||||||
|
//
|
||||||
|
// It is conservative and returns false if any of the internal calls fail.
|
||||||
|
// It does not guarantee that the program was run from a terminal. It only can tell you
|
||||||
|
// whether it was launched from explorer.exe
|
||||||
|
func StartedByExplorer() bool {
|
||||||
|
pe, err := getProcessEntry(os.Getppid())
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return "explorer.exe" == syscall.UTF16ToString(pe.ExeFile[:])
|
||||||
|
}
|
24
Godeps/_workspace/src/github.com/spf13/cobra/.gitignore
generated
vendored
Normal file
24
Godeps/_workspace/src/github.com/spf13/cobra/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
|
||||||
|
cobra.test
|
8
Godeps/_workspace/src/github.com/spf13/cobra/.travis.yml
generated
vendored
Normal file
8
Godeps/_workspace/src/github.com/spf13/cobra/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.3
|
||||||
|
- 1.4.2
|
||||||
|
- tip
|
||||||
|
script:
|
||||||
|
- go test ./...
|
||||||
|
- go build
|
174
Godeps/_workspace/src/github.com/spf13/cobra/LICENSE.txt
generated
vendored
Normal file
174
Godeps/_workspace/src/github.com/spf13/cobra/LICENSE.txt
generated
vendored
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
485
Godeps/_workspace/src/github.com/spf13/cobra/README.md
generated
vendored
Normal file
485
Godeps/_workspace/src/github.com/spf13/cobra/README.md
generated
vendored
Normal file
|
@ -0,0 +1,485 @@
|
||||||
|
# Cobra
|
||||||
|
|
||||||
|
A Commander for modern go CLI interactions
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/spf13/cobra.svg)](https://travis-ci.org/spf13/cobra)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Cobra is a commander providing a simple interface to create powerful modern CLI
|
||||||
|
interfaces similar to git & go tools. In addition to providing an interface, Cobra
|
||||||
|
simultaneously provides a controller to organize your application code.
|
||||||
|
|
||||||
|
Inspired by go, go-Commander, gh and subcommand, Cobra improves on these by
|
||||||
|
providing **fully posix compliant flags** (including short & long versions),
|
||||||
|
**nesting commands**, and the ability to **define your own help and usage** for any or
|
||||||
|
all commands.
|
||||||
|
|
||||||
|
Cobra has an exceptionally clean interface and simple design without needless
|
||||||
|
constructors or initialization methods.
|
||||||
|
|
||||||
|
Applications built with Cobra commands are designed to be as user friendly as
|
||||||
|
possible. Flags can be placed before or after the command (as long as a
|
||||||
|
confusing space isn’t provided). Both short and long flags can be used. A
|
||||||
|
command need not even be fully typed. The shortest unambiguous string will
|
||||||
|
suffice. Help is automatically generated and available for the application or
|
||||||
|
for a specific command using either the help command or the --help flag.
|
||||||
|
|
||||||
|
## Concepts
|
||||||
|
|
||||||
|
Cobra is built on a structure of commands & flags.
|
||||||
|
|
||||||
|
**Commands** represent actions and **Flags** are modifiers for those actions.
|
||||||
|
|
||||||
|
In the following example 'server' is a command and 'port' is a flag.
|
||||||
|
|
||||||
|
hugo server --port=1313
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
Command is the central point of the application. Each interaction that
|
||||||
|
the application supports will be contained in a Command. A command can
|
||||||
|
have children commands and optionally run an action.
|
||||||
|
|
||||||
|
In the example above 'server' is the command
|
||||||
|
|
||||||
|
A Command has the following structure:
|
||||||
|
|
||||||
|
type Command struct {
|
||||||
|
Use string // The one-line usage message.
|
||||||
|
Short string // The short description shown in the 'help' output.
|
||||||
|
Long string // The long message shown in the 'help <this-command>' output.
|
||||||
|
Run func(cmd *Command, args []string) // Run runs the command.
|
||||||
|
}
|
||||||
|
|
||||||
|
### Flags
|
||||||
|
|
||||||
|
A Flag is a way to modify the behavior of an command. Cobra supports
|
||||||
|
fully posix compliant flags as well as the go flag package.
|
||||||
|
A Cobra command can define flags that persist through to children commands
|
||||||
|
and flags that are only available to that command.
|
||||||
|
|
||||||
|
In the example above 'port' is the flag.
|
||||||
|
|
||||||
|
Flag functionality is provided by the [pflag
|
||||||
|
library](https://github.com/ogier/pflag), a fork of the flag standard library
|
||||||
|
which maintains the same interface while adding posix compliance.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Cobra works by creating a set of commands and then organizing them into a tree.
|
||||||
|
The tree defines the structure of the application.
|
||||||
|
|
||||||
|
Once each command is defined with it's corresponding flags, then the
|
||||||
|
tree is assigned to the commander which is finally executed.
|
||||||
|
|
||||||
|
### Installing
|
||||||
|
Using Cobra is easy. First use go get to install the latest version
|
||||||
|
of the library.
|
||||||
|
|
||||||
|
$ go get github.com/spf13/cobra
|
||||||
|
|
||||||
|
Next include cobra in your application.
|
||||||
|
|
||||||
|
import "github.com/spf13/cobra"
|
||||||
|
|
||||||
|
### Create the root command
|
||||||
|
|
||||||
|
The root command represents your binary itself.
|
||||||
|
|
||||||
|
Cobra doesn't require any special constructors. Simply create your commands.
|
||||||
|
|
||||||
|
var HugoCmd = &cobra.Command{
|
||||||
|
Use: "hugo",
|
||||||
|
Short: "Hugo is a very fast static site generator",
|
||||||
|
Long: `A Fast and Flexible Static Site Generator built with
|
||||||
|
love by spf13 and friends in Go.
|
||||||
|
Complete documentation is available at http://hugo.spf13.com`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
// Do Stuff Here
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
### Create additional commands
|
||||||
|
|
||||||
|
Additional commands can be defined.
|
||||||
|
|
||||||
|
var versionCmd = &cobra.Command{
|
||||||
|
Use: "version",
|
||||||
|
Short: "Print the version number of Hugo",
|
||||||
|
Long: `All software has versions. This is Hugo's`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("Hugo Static Site Generator v0.9 -- HEAD")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
### Attach command to its parent
|
||||||
|
In this example we are attaching it to the root, but commands can be attached at any level.
|
||||||
|
|
||||||
|
HugoCmd.AddCommand(versionCmd)
|
||||||
|
|
||||||
|
### Assign flags to a command
|
||||||
|
|
||||||
|
Since the flags are defined and used in different locations, we need to
|
||||||
|
define a variable outside with the correct scope to assign the flag to
|
||||||
|
work with.
|
||||||
|
|
||||||
|
var Verbose bool
|
||||||
|
var Source string
|
||||||
|
|
||||||
|
There are two different approaches to assign a flag.
|
||||||
|
|
||||||
|
#### Persistent Flags
|
||||||
|
|
||||||
|
A flag can be 'persistent' meaning that this flag will be available to the
|
||||||
|
command it's assigned to as well as every command under that command. For
|
||||||
|
global flags assign a flag as a persistent flag on the root.
|
||||||
|
|
||||||
|
HugoCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
|
||||||
|
|
||||||
|
#### Local Flags
|
||||||
|
|
||||||
|
A flag can also be assigned locally which will only apply to that specific command.
|
||||||
|
|
||||||
|
HugoCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")
|
||||||
|
|
||||||
|
### Remove a command from its parent
|
||||||
|
|
||||||
|
Removing a command is not a common action in simple programs but it allows 3rd parties to customize an existing command tree.
|
||||||
|
|
||||||
|
In this example, we remove the existing `VersionCmd` command of an existing root command, and we replace it by our own version.
|
||||||
|
|
||||||
|
mainlib.RootCmd.RemoveCommand(mainlib.VersionCmd)
|
||||||
|
mainlib.RootCmd.AddCommand(versionCmd)
|
||||||
|
|
||||||
|
### Once all commands and flags are defined, Execute the commands
|
||||||
|
|
||||||
|
Execute should be run on the root for clarity, though it can be called on any command.
|
||||||
|
|
||||||
|
HugoCmd.Execute()
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
In the example below we have defined three commands. Two are at the top level
|
||||||
|
and one (cmdTimes) is a child of one of the top commands. In this case the root
|
||||||
|
is not executable meaning that a subcommand is required. This is accomplished
|
||||||
|
by not providing a 'Run' for the 'rootCmd'.
|
||||||
|
|
||||||
|
We have only defined one flag for a single command.
|
||||||
|
|
||||||
|
More documentation about flags is available at https://github.com/spf13/pflag
|
||||||
|
|
||||||
|
import(
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
var echoTimes int
|
||||||
|
|
||||||
|
var cmdPrint = &cobra.Command{
|
||||||
|
Use: "print [string to print]",
|
||||||
|
Short: "Print anything to the screen",
|
||||||
|
Long: `print is for printing anything back to the screen.
|
||||||
|
For many years people have printed back to the screen.
|
||||||
|
`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("Print: " + strings.Join(args, " "))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmdEcho = &cobra.Command{
|
||||||
|
Use: "echo [string to echo]",
|
||||||
|
Short: "Echo anything to the screen",
|
||||||
|
Long: `echo is for echoing anything back.
|
||||||
|
Echo works a lot like print, except it has a child command.
|
||||||
|
`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("Print: " + strings.Join(args, " "))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmdTimes = &cobra.Command{
|
||||||
|
Use: "times [# times] [string to echo]",
|
||||||
|
Short: "Echo anything to the screen more times",
|
||||||
|
Long: `echo things multiple times back to the user by providing
|
||||||
|
a count and a string.`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
for i:=0; i < echoTimes; i++ {
|
||||||
|
fmt.Println("Echo: " + strings.Join(args, " "))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdTimes.Flags().IntVarP(&echoTimes, "times", "t", 1, "times to echo the input")
|
||||||
|
|
||||||
|
var rootCmd = &cobra.Command{Use: "app"}
|
||||||
|
rootCmd.AddCommand(cmdPrint, cmdEcho)
|
||||||
|
cmdEcho.AddCommand(cmdTimes)
|
||||||
|
rootCmd.Execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
For a more complete example of a larger application, please checkout [Hugo](http://hugo.spf13.com)
|
||||||
|
|
||||||
|
## The Help Command
|
||||||
|
|
||||||
|
Cobra automatically adds a help command to your application when you have subcommands.
|
||||||
|
This will be called when a user runs 'app help'. Additionally help will also
|
||||||
|
support all other commands as input. Say for instance you have a command called
|
||||||
|
'create' without any additional configuration cobra will work when 'app help
|
||||||
|
create' is called. Every command will automatically have the '--help' flag added.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
The following output is automatically generated by cobra. Nothing beyond the
|
||||||
|
command and flag definitions are needed.
|
||||||
|
|
||||||
|
> hugo help
|
||||||
|
|
||||||
|
A Fast and Flexible Static Site Generator built with
|
||||||
|
love by spf13 and friends in Go.
|
||||||
|
|
||||||
|
Complete documentation is available at http://hugo.spf13.com
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
hugo [flags]
|
||||||
|
hugo [command]
|
||||||
|
|
||||||
|
Available Commands:
|
||||||
|
server :: Hugo runs it's own a webserver to render the files
|
||||||
|
version :: Print the version number of Hugo
|
||||||
|
check :: Check content in the source directory
|
||||||
|
benchmark :: Benchmark hugo by building a site a number of times
|
||||||
|
help [command] :: Help about any command
|
||||||
|
|
||||||
|
Available Flags:
|
||||||
|
-b, --base-url="": hostname (and path) to the root eg. http://spf13.com/
|
||||||
|
-D, --build-drafts=false: include content marked as draft
|
||||||
|
--config="": config file (default is path/config.yaml|json|toml)
|
||||||
|
-d, --destination="": filesystem path to write files to
|
||||||
|
-s, --source="": filesystem path to read files relative from
|
||||||
|
--stepAnalysis=false: display memory and timing of different steps of the program
|
||||||
|
--uglyurls=false: if true, use /filename.html instead of /filename/
|
||||||
|
-v, --verbose=false: verbose output
|
||||||
|
-w, --watch=false: watch filesystem for changes and recreate as needed
|
||||||
|
|
||||||
|
Use "hugo help [command]" for more information about that command.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Help is just a command like any other. There is no special logic or behavior
|
||||||
|
around it. In fact you can provide your own if you want.
|
||||||
|
|
||||||
|
### Defining your own help
|
||||||
|
|
||||||
|
You can provide your own Help command or you own template for the default command to use.
|
||||||
|
|
||||||
|
The default help command is
|
||||||
|
|
||||||
|
func (c *Command) initHelp() {
|
||||||
|
if c.helpCommand == nil {
|
||||||
|
c.helpCommand = &Command{
|
||||||
|
Use: "help [command]",
|
||||||
|
Short: "Help about any command",
|
||||||
|
Long: `Help provides help for any command in the application.
|
||||||
|
Simply type ` + c.Name() + ` help [path to command] for full details.`,
|
||||||
|
Run: c.HelpFunc(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.AddCommand(c.helpCommand)
|
||||||
|
}
|
||||||
|
|
||||||
|
You can provide your own command, function or template through the following methods.
|
||||||
|
|
||||||
|
command.SetHelpCommand(cmd *Command)
|
||||||
|
|
||||||
|
command.SetHelpFunc(f func(*Command, []string))
|
||||||
|
|
||||||
|
command.SetHelpTemplate(s string)
|
||||||
|
|
||||||
|
The latter two will also apply to any children commands.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
When the user provides an invalid flag or invalid command Cobra responds by
|
||||||
|
showing the user the 'usage'
|
||||||
|
|
||||||
|
### Example
|
||||||
|
You may recognize this from the help above. That's because the default help
|
||||||
|
embeds the usage as part of it's output.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
hugo [flags]
|
||||||
|
hugo [command]
|
||||||
|
|
||||||
|
Available Commands:
|
||||||
|
server Hugo runs it's own a webserver to render the files
|
||||||
|
version Print the version number of Hugo
|
||||||
|
check Check content in the source directory
|
||||||
|
benchmark Benchmark hugo by building a site a number of times
|
||||||
|
help [command] Help about any command
|
||||||
|
|
||||||
|
Available Flags:
|
||||||
|
-b, --base-url="": hostname (and path) to the root eg. http://spf13.com/
|
||||||
|
-D, --build-drafts=false: include content marked as draft
|
||||||
|
--config="": config file (default is path/config.yaml|json|toml)
|
||||||
|
-d, --destination="": filesystem path to write files to
|
||||||
|
-s, --source="": filesystem path to read files relative from
|
||||||
|
--stepAnalysis=false: display memory and timing of different steps of the program
|
||||||
|
--uglyurls=false: if true, use /filename.html instead of /filename/
|
||||||
|
-v, --verbose=false: verbose output
|
||||||
|
-w, --watch=false: watch filesystem for changes and recreate as needed
|
||||||
|
|
||||||
|
### Defining your own usage
|
||||||
|
You can provide your own usage function or template for cobra to use.
|
||||||
|
|
||||||
|
The default usage function is
|
||||||
|
|
||||||
|
return func(c *Command) error {
|
||||||
|
err := tmpl(c.Out(), c.UsageTemplate(), c)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
Like help the function and template are over ridable through public methods.
|
||||||
|
|
||||||
|
command.SetUsageFunc(f func(*Command) error)
|
||||||
|
|
||||||
|
command.SetUsageTemplate(s string)
|
||||||
|
|
||||||
|
## PreRun or PostRun Hooks
|
||||||
|
|
||||||
|
It is possible to run functions before or after the main `Run` function of your command. The `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistendPostRun` and `PostRun` will be executed after `Run`. The `Persistent*Run` functions will be inherrited by children if they do not declare their own. These function are run in the following order:
|
||||||
|
|
||||||
|
- `PersistentPreRun`
|
||||||
|
- `PreRun`
|
||||||
|
- `Run`
|
||||||
|
- `PostRun`
|
||||||
|
- `PersistenPostRun`
|
||||||
|
|
||||||
|
And example of two commands which use all of these features is below. When the subcommand in executed it will run the root command's `PersistentPreRun` but not the root command's `PersistentPostRun`
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "root [sub]",
|
||||||
|
Short: "My root command",
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args)
|
||||||
|
},
|
||||||
|
PreRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Printf("Inside rootCmd PreRun with args: %v\n", args)
|
||||||
|
},
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Printf("Inside rootCmd Run with args: %v\n", args)
|
||||||
|
},
|
||||||
|
PostRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Printf("Inside rootCmd PostRun with args: %v\n", args)
|
||||||
|
},
|
||||||
|
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Printf("Inside rootCmd PersistentPostRun with args: %v\n", args)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var subCmd = &cobra.Command{
|
||||||
|
Use: "sub [no options!]",
|
||||||
|
Short: "My sub command",
|
||||||
|
PreRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Printf("Inside subCmd PreRun with args: %v\n", args)
|
||||||
|
},
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Printf("Inside subCmd Run with args: %v\n", args)
|
||||||
|
},
|
||||||
|
PostRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Printf("Inside subCmd PostRun with args: %v\n", args)
|
||||||
|
},
|
||||||
|
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Printf("Inside subCmd PersistentPostRun with args: %v\n", args)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rootCmd.AddCommand(subCmd)
|
||||||
|
|
||||||
|
rootCmd.SetArgs([]string{""})
|
||||||
|
_ = rootCmd.Execute()
|
||||||
|
fmt.Print("\n")
|
||||||
|
rootCmd.SetArgs([]string{"sub", "arg1", "arg2"})
|
||||||
|
_ = rootCmd.Execute()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Generating markdown formatted documentation for your command
|
||||||
|
|
||||||
|
Cobra can generate a markdown formatted document based on the subcommands, flags, etc. A simple example of how to do this for your command can be found in [Markdown Docs](md_docs.md)
|
||||||
|
|
||||||
|
## Generating bash completions for your command
|
||||||
|
|
||||||
|
Cobra can generate a bash completions file. If you add more information to your command these completions can be amazingly powerful and flexible. Read more about [Bash Completions](bash_completions.md)
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
Cobra provides a ‘DebugFlags’ method on a command which when called will print
|
||||||
|
out everything Cobra knows about the flags for each command
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
command.DebugFlags()
|
||||||
|
|
||||||
|
## Release Notes
|
||||||
|
* **0.9.0** June 17, 2014
|
||||||
|
* flags can appears anywhere in the args (provided they are unambiguous)
|
||||||
|
* --help prints usage screen for app or command
|
||||||
|
* Prefix matching for commands
|
||||||
|
* Cleaner looking help and usage output
|
||||||
|
* Extensive test suite
|
||||||
|
* **0.8.0** Nov 5, 2013
|
||||||
|
* Reworked interface to remove commander completely
|
||||||
|
* Command now primary structure
|
||||||
|
* No initialization needed
|
||||||
|
* Usage & Help templates & functions definable at any level
|
||||||
|
* Updated Readme
|
||||||
|
* **0.7.0** Sept 24, 2013
|
||||||
|
* Needs more eyes
|
||||||
|
* Test suite
|
||||||
|
* Support for automatic error messages
|
||||||
|
* Support for help command
|
||||||
|
* Support for printing to any io.Writer instead of os.Stderr
|
||||||
|
* Support for persistent flags which cascade down tree
|
||||||
|
* Ready for integration into Hugo
|
||||||
|
* **0.1.0** Sept 3, 2013
|
||||||
|
* Implement first draft
|
||||||
|
|
||||||
|
## ToDo
|
||||||
|
* Launch proper documentation site
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
1. Fork it
|
||||||
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||||
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||||
|
4. Push to the branch (`git push origin my-new-feature`)
|
||||||
|
5. Create new Pull Request
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
Names in no particular order:
|
||||||
|
|
||||||
|
* [spf13](https://github.com/spf13)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Cobra is released under the Apache 2.0 license. See [LICENSE.txt](https://github.com/spf13/cobra/blob/master/LICENSE.txt)
|
||||||
|
|
||||||
|
|
||||||
|
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/spf13/cobra/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
|
||||||
|
|
370
Godeps/_workspace/src/github.com/spf13/cobra/bash_completions.go
generated
vendored
Normal file
370
Godeps/_workspace/src/github.com/spf13/cobra/bash_completions.go
generated
vendored
Normal file
|
@ -0,0 +1,370 @@
|
||||||
|
package cobra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extentions"
|
||||||
|
BashCompOneRequiredFlag = "cobra_annotation_bash_completion_one_required_flag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func preamble(out *bytes.Buffer) {
|
||||||
|
fmt.Fprintf(out, `#!/bin/bash
|
||||||
|
|
||||||
|
|
||||||
|
__debug()
|
||||||
|
{
|
||||||
|
if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
|
||||||
|
echo "$*" >> "${BASH_COMP_DEBUG_FILE}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
__index_of_word()
|
||||||
|
{
|
||||||
|
local w word=$1
|
||||||
|
shift
|
||||||
|
index=0
|
||||||
|
for w in "$@"; do
|
||||||
|
[[ $w = "$word" ]] && return
|
||||||
|
index=$((index+1))
|
||||||
|
done
|
||||||
|
index=-1
|
||||||
|
}
|
||||||
|
|
||||||
|
__contains_word()
|
||||||
|
{
|
||||||
|
local w word=$1; shift
|
||||||
|
for w in "$@"; do
|
||||||
|
[[ $w = "$word" ]] && return
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
__handle_reply()
|
||||||
|
{
|
||||||
|
__debug "${FUNCNAME}"
|
||||||
|
case $cur in
|
||||||
|
-*)
|
||||||
|
compopt -o nospace
|
||||||
|
local allflags
|
||||||
|
if [ ${#must_have_one_flag[@]} -ne 0 ]; then
|
||||||
|
allflags=("${must_have_one_flag[@]}")
|
||||||
|
else
|
||||||
|
allflags=("${flags[*]} ${two_word_flags[*]}")
|
||||||
|
fi
|
||||||
|
COMPREPLY=( $(compgen -W "${allflags[*]}" -- "$cur") )
|
||||||
|
[[ $COMPREPLY == *= ]] || compopt +o nospace
|
||||||
|
return 0;
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# check if we are handling a flag with special work handling
|
||||||
|
local index
|
||||||
|
__index_of_word "${prev}" "${flags_with_completion[@]}"
|
||||||
|
if [[ ${index} -ge 0 ]]; then
|
||||||
|
${flags_completion[${index}]}
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# we are parsing a flag and don't have a special handler, no completion
|
||||||
|
if [[ ${cur} != "${words[cword]}" ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local completions
|
||||||
|
if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then
|
||||||
|
completions=("${must_have_one_flag[@]}")
|
||||||
|
elif [[ ${#must_have_one_noun[@]} -ne 0 ]]; then
|
||||||
|
completions=("${must_have_one_noun[@]}")
|
||||||
|
else
|
||||||
|
completions=("${commands[@]}")
|
||||||
|
fi
|
||||||
|
COMPREPLY=( $(compgen -W "${completions[*]}" -- "$cur") )
|
||||||
|
|
||||||
|
if [[ ${#COMPREPLY[@]} -eq 0 ]]; then
|
||||||
|
declare -F __custom_func >/dev/null && __custom_func
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# The arguments should be in the form "ext1|ext2|extn"
|
||||||
|
__handle_filename_extension_flag()
|
||||||
|
{
|
||||||
|
local ext="$1"
|
||||||
|
_filedir "@(${ext})"
|
||||||
|
}
|
||||||
|
|
||||||
|
__handle_flag()
|
||||||
|
{
|
||||||
|
__debug "${FUNCNAME}: c is $c words[c] is ${words[c]}"
|
||||||
|
|
||||||
|
# if a command required a flag, and we found it, unset must_have_one_flag()
|
||||||
|
local flagname=${words[c]}
|
||||||
|
# if the word contained an =
|
||||||
|
if [[ ${words[c]} == *"="* ]]; then
|
||||||
|
flagname=${flagname%%=*} # strip everything after the =
|
||||||
|
flagname="${flagname}=" # but put the = back
|
||||||
|
fi
|
||||||
|
__debug "${FUNCNAME}: looking for ${flagname}"
|
||||||
|
if __contains_word "${flagname}" "${must_have_one_flag[@]}"; then
|
||||||
|
must_have_one_flag=()
|
||||||
|
fi
|
||||||
|
|
||||||
|
# skip the argument to a two word flag
|
||||||
|
if __contains_word "${words[c]}" "${two_word_flags[@]}"; then
|
||||||
|
c=$((c+1))
|
||||||
|
# if we are looking for a flags value, don't show commands
|
||||||
|
if [[ $c -eq $cword ]]; then
|
||||||
|
commands=()
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# skip the flag itself
|
||||||
|
c=$((c+1))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
__handle_noun()
|
||||||
|
{
|
||||||
|
__debug "${FUNCNAME}: c is $c words[c] is ${words[c]}"
|
||||||
|
|
||||||
|
if __contains_word "${words[c]}" "${must_have_one_noun[@]}"; then
|
||||||
|
must_have_one_noun=()
|
||||||
|
fi
|
||||||
|
|
||||||
|
nouns+=("${words[c]}")
|
||||||
|
c=$((c+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
__handle_command()
|
||||||
|
{
|
||||||
|
__debug "${FUNCNAME}: c is $c words[c] is ${words[c]}"
|
||||||
|
|
||||||
|
local next_command
|
||||||
|
if [[ -n ${last_command} ]]; then
|
||||||
|
next_command="_${last_command}_${words[c]}"
|
||||||
|
else
|
||||||
|
next_command="_${words[c]}"
|
||||||
|
fi
|
||||||
|
c=$((c+1))
|
||||||
|
__debug "${FUNCNAME}: looking for ${next_command}"
|
||||||
|
declare -F $next_command >/dev/null && $next_command
|
||||||
|
}
|
||||||
|
|
||||||
|
__handle_word()
|
||||||
|
{
|
||||||
|
if [[ $c -ge $cword ]]; then
|
||||||
|
__handle_reply
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
__debug "${FUNCNAME}: c is $c words[c] is ${words[c]}"
|
||||||
|
if [[ "${words[c]}" == -* ]]; then
|
||||||
|
__handle_flag
|
||||||
|
elif __contains_word "${words[c]}" "${commands[@]}"; then
|
||||||
|
__handle_command
|
||||||
|
else
|
||||||
|
__handle_noun
|
||||||
|
fi
|
||||||
|
__handle_word
|
||||||
|
}
|
||||||
|
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func postscript(out *bytes.Buffer, name string) {
|
||||||
|
fmt.Fprintf(out, "__start_%s()\n", name)
|
||||||
|
fmt.Fprintf(out, `{
|
||||||
|
local cur prev words cword
|
||||||
|
_init_completion -s || return
|
||||||
|
|
||||||
|
local c=0
|
||||||
|
local flags=()
|
||||||
|
local two_word_flags=()
|
||||||
|
local flags_with_completion=()
|
||||||
|
local flags_completion=()
|
||||||
|
local commands=("%s")
|
||||||
|
local must_have_one_flag=()
|
||||||
|
local must_have_one_noun=()
|
||||||
|
local last_command
|
||||||
|
local nouns=()
|
||||||
|
|
||||||
|
__handle_word
|
||||||
|
}
|
||||||
|
|
||||||
|
`, name)
|
||||||
|
fmt.Fprintf(out, "complete -F __start_%s %s\n", name, name)
|
||||||
|
fmt.Fprintf(out, "# ex: ts=4 sw=4 et filetype=sh\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeCommands(cmd *Command, out *bytes.Buffer) {
|
||||||
|
fmt.Fprintf(out, " commands=()\n")
|
||||||
|
for _, c := range cmd.Commands() {
|
||||||
|
if len(c.Deprecated) > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Fprintf(out, " commands+=(%q)\n", c.Name())
|
||||||
|
}
|
||||||
|
fmt.Fprintf(out, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeFlagHandler(name string, annotations map[string][]string, out *bytes.Buffer) {
|
||||||
|
for key, value := range annotations {
|
||||||
|
switch key {
|
||||||
|
case BashCompFilenameExt:
|
||||||
|
fmt.Fprintf(out, " flags_with_completion+=(%q)\n", name)
|
||||||
|
|
||||||
|
if len(value) > 0 {
|
||||||
|
ext := "__handle_filename_extension_flag " + strings.Join(value, "|")
|
||||||
|
fmt.Fprintf(out, " flags_completion+=(%q)\n", ext)
|
||||||
|
} else {
|
||||||
|
ext := "_filedir"
|
||||||
|
fmt.Fprintf(out, " flags_completion+=(%q)\n", ext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeShortFlag(flag *pflag.Flag, out *bytes.Buffer) {
|
||||||
|
b := (flag.Value.Type() == "bool")
|
||||||
|
name := flag.Shorthand
|
||||||
|
format := " "
|
||||||
|
if !b {
|
||||||
|
format += "two_word_"
|
||||||
|
}
|
||||||
|
format += "flags+=(\"-%s\")\n"
|
||||||
|
fmt.Fprintf(out, format, name)
|
||||||
|
writeFlagHandler("-"+name, flag.Annotations, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeFlag(flag *pflag.Flag, out *bytes.Buffer) {
|
||||||
|
b := (flag.Value.Type() == "bool")
|
||||||
|
name := flag.Name
|
||||||
|
format := " flags+=(\"--%s"
|
||||||
|
if !b {
|
||||||
|
format += "="
|
||||||
|
}
|
||||||
|
format += "\")\n"
|
||||||
|
fmt.Fprintf(out, format, name)
|
||||||
|
writeFlagHandler("--"+name, flag.Annotations, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeFlags(cmd *Command, out *bytes.Buffer) {
|
||||||
|
fmt.Fprintf(out, ` flags=()
|
||||||
|
two_word_flags=()
|
||||||
|
flags_with_completion=()
|
||||||
|
flags_completion=()
|
||||||
|
|
||||||
|
`)
|
||||||
|
cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {
|
||||||
|
writeFlag(flag, out)
|
||||||
|
if len(flag.Shorthand) > 0 {
|
||||||
|
writeShortFlag(flag, out)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Fprintf(out, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeRequiredFlag(cmd *Command, out *bytes.Buffer) {
|
||||||
|
fmt.Fprintf(out, " must_have_one_flag=()\n")
|
||||||
|
flags := cmd.NonInheritedFlags()
|
||||||
|
flags.VisitAll(func(flag *pflag.Flag) {
|
||||||
|
for key, _ := range flag.Annotations {
|
||||||
|
switch key {
|
||||||
|
case BashCompOneRequiredFlag:
|
||||||
|
format := " must_have_one_flag+=(\"--%s"
|
||||||
|
b := (flag.Value.Type() == "bool")
|
||||||
|
if !b {
|
||||||
|
format += "="
|
||||||
|
}
|
||||||
|
format += "\")\n"
|
||||||
|
fmt.Fprintf(out, format, flag.Name)
|
||||||
|
|
||||||
|
if len(flag.Shorthand) > 0 {
|
||||||
|
fmt.Fprintf(out, " must_have_one_flag+=(\"-%s\")\n", flag.Shorthand)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeRequiredNoun(cmd *Command, out *bytes.Buffer) {
|
||||||
|
fmt.Fprintf(out, " must_have_one_noun=()\n")
|
||||||
|
sort.Sort(sort.StringSlice(cmd.ValidArgs))
|
||||||
|
for _, value := range cmd.ValidArgs {
|
||||||
|
fmt.Fprintf(out, " must_have_one_noun+=(%q)\n", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func gen(cmd *Command, out *bytes.Buffer) {
|
||||||
|
for _, c := range cmd.Commands() {
|
||||||
|
if len(c.Deprecated) > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
gen(c, out)
|
||||||
|
}
|
||||||
|
commandName := cmd.CommandPath()
|
||||||
|
commandName = strings.Replace(commandName, " ", "_", -1)
|
||||||
|
fmt.Fprintf(out, "_%s()\n{\n", commandName)
|
||||||
|
fmt.Fprintf(out, " last_command=%q\n", commandName)
|
||||||
|
writeCommands(cmd, out)
|
||||||
|
writeFlags(cmd, out)
|
||||||
|
writeRequiredFlag(cmd, out)
|
||||||
|
writeRequiredNoun(cmd, out)
|
||||||
|
fmt.Fprintf(out, "}\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *Command) GenBashCompletion(out *bytes.Buffer) {
|
||||||
|
preamble(out)
|
||||||
|
if len(cmd.BashCompletionFunction) > 0 {
|
||||||
|
fmt.Fprintf(out, "%s\n", cmd.BashCompletionFunction)
|
||||||
|
}
|
||||||
|
gen(cmd, out)
|
||||||
|
postscript(out, cmd.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *Command) GenBashCompletionFile(filename string) error {
|
||||||
|
out := new(bytes.Buffer)
|
||||||
|
|
||||||
|
cmd.GenBashCompletion(out)
|
||||||
|
|
||||||
|
outFile, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer outFile.Close()
|
||||||
|
|
||||||
|
_, err = outFile.Write(out.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag, if it exists.
|
||||||
|
func (cmd *Command) MarkFlagRequired(name string) error {
|
||||||
|
return MarkFlagRequired(cmd.Flags(), name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag in the flag set, if it exists.
|
||||||
|
func MarkFlagRequired(flags *pflag.FlagSet, name string) error {
|
||||||
|
return flags.SetAnnotation(name, BashCompOneRequiredFlag, []string{"true"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkFlagFilename adds the BashCompFilenameExt annotation to the named flag, if it exists.
|
||||||
|
// Generated bash autocompletion will select filenames for the flag, limiting to named extensions if provided.
|
||||||
|
func (cmd *Command) MarkFlagFilename(name string, extensions ...string) error {
|
||||||
|
return MarkFlagFilename(cmd.Flags(), name, extensions...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkFlagFilename adds the BashCompFilenameExt annotation to the named flag in the flag set, if it exists.
|
||||||
|
// Generated bash autocompletion will select filenames for the flag, limiting to named extensions if provided.
|
||||||
|
func MarkFlagFilename(flags *pflag.FlagSet, name string, extensions ...string) error {
|
||||||
|
return flags.SetAnnotation(name, BashCompFilenameExt, extensions)
|
||||||
|
}
|
149
Godeps/_workspace/src/github.com/spf13/cobra/bash_completions.md
generated
vendored
Normal file
149
Godeps/_workspace/src/github.com/spf13/cobra/bash_completions.md
generated
vendored
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
# Generating Bash Completions For Your Own cobra.Command
|
||||||
|
|
||||||
|
Generating bash completions from a cobra command is incredibly easy. An actual program which does so for the kubernetes kubectl binary is as follows:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
kubectl := cmd.NewFactory(nil).NewKubectlCommand(os.Stdin, ioutil.Discard, ioutil.Discard)
|
||||||
|
kubectl.GenBashCompletionFile("out.sh")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
That will get you completions of subcommands and flags. If you make additional annotations to your code, you can get even more intelligent and flexible behavior.
|
||||||
|
|
||||||
|
## Creating your own custom functions
|
||||||
|
|
||||||
|
Some more actual code that works in kubernetes:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
const (
|
||||||
|
bash_completion_func = `__kubectl_parse_get()
|
||||||
|
{
|
||||||
|
local kubectl_output out
|
||||||
|
if kubectl_output=$(kubectl get --no-headers "$1" 2>/dev/null); then
|
||||||
|
out=($(echo "${kubectl_output}" | awk '{print $1}'))
|
||||||
|
COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
__kubectl_get_resource()
|
||||||
|
{
|
||||||
|
if [[ ${#nouns[@]} -eq 0 ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
__kubectl_parse_get ${nouns[${#nouns[@]} -1]}
|
||||||
|
if [[ $? -eq 0 ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
__custom_func() {
|
||||||
|
case ${last_command} in
|
||||||
|
kubectl_get | kubectl_describe | kubectl_delete | kubectl_stop)
|
||||||
|
__kubectl_get_resource
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
```
|
||||||
|
|
||||||
|
And then I set that in my command definition:
|
||||||
|
|
||||||
|
```go
|
||||||
|
cmds := &cobra.Command{
|
||||||
|
Use: "kubectl",
|
||||||
|
Short: "kubectl controls the Kubernetes cluster manager",
|
||||||
|
Long: `kubectl controls the Kubernetes cluster manager.
|
||||||
|
|
||||||
|
Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
|
||||||
|
Run: runHelp,
|
||||||
|
BashCompletionFunction: bash_completion_func,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `BashCompletionFunction` option is really only valid/useful on the root command. Doing the above will cause `__custom_func()` to be called when the built in processor was unable to find a solution. In the case of kubernetes a valid command might look something like `kubectl get pod [mypod]`. If you type `kubectl get pod [tab][tab]` the `__customc_func()` will run because the cobra.Command only understood "kubectl" and "get." `__custom_func()` will see that the cobra.Command is "kubectl_get" and will thus call another helper `__kubectl_get_resource()`. `__kubectl_get_resource` will look at the 'nouns' collected. In our example the only noun will be `pod`. So it will call `__kubectl_parse_get pod`. `__kubectl_parse_get` will actually call out to kubernetes and get any pods. It will then set `COMPREPLY` to valid pods!
|
||||||
|
|
||||||
|
## Have the completions code complete your 'nouns'
|
||||||
|
|
||||||
|
In the above example "pod" was assumed to already be typed. But if you want `kubectl get [tab][tab]` to show a list of valid "nouns" you have to set them. Simplified code from `kubectl get` looks like:
|
||||||
|
|
||||||
|
```go
|
||||||
|
validArgs []string = { "pods", "nodes", "services", "replicationControllers" }
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "get [(-o|--output=)json|yaml|template|...] (RESOURCE [NAME] | RESOURCE/NAME ...)",
|
||||||
|
Short: "Display one or many resources",
|
||||||
|
Long: get_long,
|
||||||
|
Example: get_example,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
err := RunGet(f, out, cmd, args)
|
||||||
|
util.CheckErr(err)
|
||||||
|
},
|
||||||
|
ValidArgs: validArgs,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice we put the "ValidArgs" on the "get" subcommand. Doing so will give results like
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# kubectl get [tab][tab]
|
||||||
|
nodes pods replicationControllers services
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mark flags as required
|
||||||
|
|
||||||
|
Most of the time completions will only show subcommands. But if a flag is required to make a subcommand work, you probably want it to show up when the user types [tab][tab]. Marking a flag as 'Required' is incredibly easy.
|
||||||
|
|
||||||
|
```go
|
||||||
|
cmd.MarkFlagRequired("pod")
|
||||||
|
cmd.MarkFlagRequired("container")
|
||||||
|
```
|
||||||
|
|
||||||
|
and you'll get something like
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# kubectl exec [tab][tab][tab]
|
||||||
|
-c --container= -p --pod=
|
||||||
|
```
|
||||||
|
|
||||||
|
# Specify valid filename extensions for flags that take a filename
|
||||||
|
|
||||||
|
In this example we use --filename= and expect to get a json or yaml file as the argument. To make this easier we annotate the --filename flag with valid filename extensions.
|
||||||
|
|
||||||
|
```go
|
||||||
|
annotations := []string{"json", "yaml", "yml"}
|
||||||
|
annotation := make(map[string][]string)
|
||||||
|
annotation[cobra.BashCompFilenameExt] = annotations
|
||||||
|
|
||||||
|
flag := &pflag.Flag{
|
||||||
|
Name: "filename",
|
||||||
|
Shorthand: "f",
|
||||||
|
Usage: usage,
|
||||||
|
Value: value,
|
||||||
|
DefValue: value.String(),
|
||||||
|
Annotations: annotation,
|
||||||
|
}
|
||||||
|
cmd.Flags().AddFlag(flag)
|
||||||
|
```
|
||||||
|
|
||||||
|
Now when you run a command with this filename flag you'll get something like
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# kubectl create -f
|
||||||
|
test/ example/ rpmbuild/
|
||||||
|
hello.yml test.json
|
||||||
|
```
|
||||||
|
|
||||||
|
So while there are many other files in the CWD it only shows me subdirs and those with valid extensions.
|
80
Godeps/_workspace/src/github.com/spf13/cobra/bash_completions_test.go
generated
vendored
Normal file
80
Godeps/_workspace/src/github.com/spf13/cobra/bash_completions_test.go
generated
vendored
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
package cobra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = fmt.Println
|
||||||
|
var _ = os.Stderr
|
||||||
|
|
||||||
|
func checkOmit(t *testing.T, found, unexpected string) {
|
||||||
|
if strings.Contains(found, unexpected) {
|
||||||
|
t.Errorf("Unexpected response.\nGot: %q\nBut should not have!\n", unexpected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func check(t *testing.T, found, expected string) {
|
||||||
|
if !strings.Contains(found, expected) {
|
||||||
|
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// World worst custom function, just keep telling you to enter hello!
|
||||||
|
const (
|
||||||
|
bash_completion_func = `__custom_func() {
|
||||||
|
COMPREPLY=( "hello" )
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBashCompletions(t *testing.T) {
|
||||||
|
c := initializeWithRootCmd()
|
||||||
|
cmdEcho.AddCommand(cmdTimes)
|
||||||
|
c.AddCommand(cmdEcho, cmdPrint, cmdDeprecated)
|
||||||
|
|
||||||
|
// custom completion function
|
||||||
|
c.BashCompletionFunction = bash_completion_func
|
||||||
|
|
||||||
|
// required flag
|
||||||
|
c.MarkFlagRequired("introot")
|
||||||
|
|
||||||
|
// valid nouns
|
||||||
|
validArgs := []string{"pods", "nodes", "services", "replicationControllers"}
|
||||||
|
c.ValidArgs = validArgs
|
||||||
|
|
||||||
|
// filename
|
||||||
|
var flagval string
|
||||||
|
c.Flags().StringVar(&flagval, "filename", "", "Enter a filename")
|
||||||
|
c.MarkFlagFilename("filename", "json", "yaml", "yml")
|
||||||
|
|
||||||
|
// filename extensions
|
||||||
|
var flagvalExt string
|
||||||
|
c.Flags().StringVar(&flagvalExt, "filename-ext", "", "Enter a filename (extension limited)")
|
||||||
|
c.MarkFlagFilename("filename-ext")
|
||||||
|
|
||||||
|
out := new(bytes.Buffer)
|
||||||
|
c.GenBashCompletion(out)
|
||||||
|
str := out.String()
|
||||||
|
|
||||||
|
check(t, str, "_cobra-test")
|
||||||
|
check(t, str, "_cobra-test_echo")
|
||||||
|
check(t, str, "_cobra-test_echo_times")
|
||||||
|
check(t, str, "_cobra-test_print")
|
||||||
|
|
||||||
|
// check for required flags
|
||||||
|
check(t, str, `must_have_one_flag+=("--introot=")`)
|
||||||
|
// check for custom completion function
|
||||||
|
check(t, str, `COMPREPLY=( "hello" )`)
|
||||||
|
// check for required nouns
|
||||||
|
check(t, str, `must_have_one_noun+=("pods")`)
|
||||||
|
// check for filename extension flags
|
||||||
|
check(t, str, `flags_completion+=("_filedir")`)
|
||||||
|
// check for filename extension flags
|
||||||
|
check(t, str, `flags_completion+=("__handle_filename_extension_flag json|yaml|yml")`)
|
||||||
|
|
||||||
|
checkOmit(t, str, cmdDeprecated.Name())
|
||||||
|
}
|
112
Godeps/_workspace/src/github.com/spf13/cobra/cobra.go
generated
vendored
Normal file
112
Godeps/_workspace/src/github.com/spf13/cobra/cobra.go
generated
vendored
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
// Copyright © 2013 Steve Francia <spf@spf13.com>.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Commands similar to git, go tools and other modern CLI tools
|
||||||
|
// inspired by go, go-Commander, gh and subcommand
|
||||||
|
|
||||||
|
package cobra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
var initializers []func()
|
||||||
|
|
||||||
|
// automatic prefix matching can be a dangerous thing to automatically enable in CLI tools.
|
||||||
|
// Set this to true to enable it
|
||||||
|
var EnablePrefixMatching bool = false
|
||||||
|
|
||||||
|
// enables an information splash screen on Windows if the CLI is started from explorer.exe.
|
||||||
|
var EnableWindowsMouseTrap bool = true
|
||||||
|
|
||||||
|
var MousetrapHelpText string = `This is a command line tool
|
||||||
|
|
||||||
|
You need to open cmd.exe and run it from there.
|
||||||
|
`
|
||||||
|
|
||||||
|
//OnInitialize takes a series of func() arguments and appends them to a slice of func().
|
||||||
|
func OnInitialize(y ...func()) {
|
||||||
|
for _, x := range y {
|
||||||
|
initializers = append(initializers, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Gt takes two types and checks whether the first type is greater than the second. In case of types Arrays, Chans,
|
||||||
|
//Maps and Slices, Gt will compare their lengths. Ints are compared directly while strings are first parsed as
|
||||||
|
//ints and then compared.
|
||||||
|
func Gt(a interface{}, b interface{}) bool {
|
||||||
|
var left, right int64
|
||||||
|
av := reflect.ValueOf(a)
|
||||||
|
|
||||||
|
switch av.Kind() {
|
||||||
|
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
|
||||||
|
left = int64(av.Len())
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
left = av.Int()
|
||||||
|
case reflect.String:
|
||||||
|
left, _ = strconv.ParseInt(av.String(), 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
bv := reflect.ValueOf(b)
|
||||||
|
|
||||||
|
switch bv.Kind() {
|
||||||
|
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
|
||||||
|
right = int64(bv.Len())
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
right = bv.Int()
|
||||||
|
case reflect.String:
|
||||||
|
right, _ = strconv.ParseInt(bv.String(), 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
return left > right
|
||||||
|
}
|
||||||
|
|
||||||
|
//Eq takes two types and checks whether they are equal. Supported types are int and string. Unsupported types will panic.
|
||||||
|
func Eq(a interface{}, b interface{}) bool {
|
||||||
|
av := reflect.ValueOf(a)
|
||||||
|
bv := reflect.ValueOf(b)
|
||||||
|
|
||||||
|
switch av.Kind() {
|
||||||
|
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
|
||||||
|
panic("Eq called on unsupported type")
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return av.Int() == bv.Int()
|
||||||
|
case reflect.String:
|
||||||
|
return av.String() == bv.String()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
//rpad adds padding to the right of a string
|
||||||
|
func rpad(s string, padding int) string {
|
||||||
|
template := fmt.Sprintf("%%-%ds", padding)
|
||||||
|
return fmt.Sprintf(template, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// tmpl executes the given template text on data, writing the result to w.
|
||||||
|
func tmpl(w io.Writer, text string, data interface{}) error {
|
||||||
|
t := template.New("top")
|
||||||
|
t.Funcs(template.FuncMap{
|
||||||
|
"trim": strings.TrimSpace,
|
||||||
|
"rpad": rpad,
|
||||||
|
"gt": Gt,
|
||||||
|
"eq": Eq,
|
||||||
|
})
|
||||||
|
template.Must(t.Parse(text))
|
||||||
|
return t.Execute(w, data)
|
||||||
|
}
|
965
Godeps/_workspace/src/github.com/spf13/cobra/cobra_test.go
generated
vendored
Normal file
965
Godeps/_workspace/src/github.com/spf13/cobra/cobra_test.go
generated
vendored
Normal file
|
@ -0,0 +1,965 @@
|
||||||
|
package cobra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = fmt.Println
|
||||||
|
var _ = os.Stderr
|
||||||
|
|
||||||
|
var tp, te, tt, t1, tr []string
|
||||||
|
var rootPersPre, echoPre, echoPersPre, timesPersPre []string
|
||||||
|
var flagb1, flagb2, flagb3, flagbr, flagbp bool
|
||||||
|
var flags1, flags2a, flags2b, flags3 string
|
||||||
|
var flagi1, flagi2, flagi3, flagir int
|
||||||
|
var globalFlag1 bool
|
||||||
|
var flagEcho, rootcalled bool
|
||||||
|
var versionUsed int
|
||||||
|
|
||||||
|
const strtwoParentHelp = "help message for parent flag strtwo"
|
||||||
|
const strtwoChildHelp = "help message for child flag strtwo"
|
||||||
|
|
||||||
|
var cmdPrint = &Command{
|
||||||
|
Use: "print [string to print]",
|
||||||
|
Short: "Print anything to the screen",
|
||||||
|
Long: `an absolutely utterly useless command for testing.`,
|
||||||
|
Run: func(cmd *Command, args []string) {
|
||||||
|
tp = args
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmdEcho = &Command{
|
||||||
|
Use: "echo [string to echo]",
|
||||||
|
Aliases: []string{"say"},
|
||||||
|
Short: "Echo anything to the screen",
|
||||||
|
Long: `an utterly useless command for testing.`,
|
||||||
|
Example: "Just run cobra-test echo",
|
||||||
|
PersistentPreRun: func(cmd *Command, args []string) {
|
||||||
|
echoPersPre = args
|
||||||
|
},
|
||||||
|
PreRun: func(cmd *Command, args []string) {
|
||||||
|
echoPre = args
|
||||||
|
},
|
||||||
|
Run: func(cmd *Command, args []string) {
|
||||||
|
te = args
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmdEchoSub = &Command{
|
||||||
|
Use: "echosub [string to print]",
|
||||||
|
Short: "second sub command for echo",
|
||||||
|
Long: `an absolutely utterly useless command for testing gendocs!.`,
|
||||||
|
Run: func(cmd *Command, args []string) {
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmdDeprecated = &Command{
|
||||||
|
Use: "deprecated [can't do anything here]",
|
||||||
|
Short: "A command which is deprecated",
|
||||||
|
Long: `an absolutely utterly useless command for testing deprecation!.`,
|
||||||
|
Deprecated: "Please use echo instead",
|
||||||
|
Run: func(cmd *Command, args []string) {
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmdTimes = &Command{
|
||||||
|
Use: "times [# times] [string to echo]",
|
||||||
|
Short: "Echo anything to the screen more times",
|
||||||
|
Long: `a slightly useless command for testing.`,
|
||||||
|
PersistentPreRun: func(cmd *Command, args []string) {
|
||||||
|
timesPersPre = args
|
||||||
|
},
|
||||||
|
Run: func(cmd *Command, args []string) {
|
||||||
|
tt = args
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmdRootNoRun = &Command{
|
||||||
|
Use: "cobra-test",
|
||||||
|
Short: "The root can run it's own function",
|
||||||
|
Long: "The root description for help",
|
||||||
|
PersistentPreRun: func(cmd *Command, args []string) {
|
||||||
|
rootPersPre = args
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmdRootSameName = &Command{
|
||||||
|
Use: "print",
|
||||||
|
Short: "Root with the same name as a subcommand",
|
||||||
|
Long: "The root description for help",
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmdRootWithRun = &Command{
|
||||||
|
Use: "cobra-test",
|
||||||
|
Short: "The root can run it's own function",
|
||||||
|
Long: "The root description for help",
|
||||||
|
Run: func(cmd *Command, args []string) {
|
||||||
|
tr = args
|
||||||
|
rootcalled = true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmdSubNoRun = &Command{
|
||||||
|
Use: "subnorun",
|
||||||
|
Short: "A subcommand without a Run function",
|
||||||
|
Long: "A long output about a subcommand without a Run function",
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmdVersion1 = &Command{
|
||||||
|
Use: "version",
|
||||||
|
Short: "Print the version number",
|
||||||
|
Long: `First version of the version command`,
|
||||||
|
Run: func(cmd *Command, args []string) {
|
||||||
|
versionUsed = 1
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmdVersion2 = &Command{
|
||||||
|
Use: "version",
|
||||||
|
Short: "Print the version number",
|
||||||
|
Long: `Second version of the version command`,
|
||||||
|
Run: func(cmd *Command, args []string) {
|
||||||
|
versionUsed = 2
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func flagInit() {
|
||||||
|
cmdEcho.ResetFlags()
|
||||||
|
cmdPrint.ResetFlags()
|
||||||
|
cmdTimes.ResetFlags()
|
||||||
|
cmdRootNoRun.ResetFlags()
|
||||||
|
cmdRootSameName.ResetFlags()
|
||||||
|
cmdRootWithRun.ResetFlags()
|
||||||
|
cmdSubNoRun.ResetFlags()
|
||||||
|
cmdRootNoRun.PersistentFlags().StringVarP(&flags2a, "strtwo", "t", "two", strtwoParentHelp)
|
||||||
|
cmdEcho.Flags().IntVarP(&flagi1, "intone", "i", 123, "help message for flag intone")
|
||||||
|
cmdTimes.Flags().IntVarP(&flagi2, "inttwo", "j", 234, "help message for flag inttwo")
|
||||||
|
cmdPrint.Flags().IntVarP(&flagi3, "intthree", "i", 345, "help message for flag intthree")
|
||||||
|
cmdEcho.PersistentFlags().StringVarP(&flags1, "strone", "s", "one", "help message for flag strone")
|
||||||
|
cmdEcho.PersistentFlags().BoolVarP(&flagbp, "persistentbool", "p", false, "help message for flag persistentbool")
|
||||||
|
cmdTimes.PersistentFlags().StringVarP(&flags2b, "strtwo", "t", "2", strtwoChildHelp)
|
||||||
|
cmdPrint.PersistentFlags().StringVarP(&flags3, "strthree", "s", "three", "help message for flag strthree")
|
||||||
|
cmdEcho.Flags().BoolVarP(&flagb1, "boolone", "b", true, "help message for flag boolone")
|
||||||
|
cmdTimes.Flags().BoolVarP(&flagb2, "booltwo", "c", false, "help message for flag booltwo")
|
||||||
|
cmdPrint.Flags().BoolVarP(&flagb3, "boolthree", "b", true, "help message for flag boolthree")
|
||||||
|
cmdVersion1.ResetFlags()
|
||||||
|
cmdVersion2.ResetFlags()
|
||||||
|
}
|
||||||
|
|
||||||
|
func commandInit() {
|
||||||
|
cmdEcho.ResetCommands()
|
||||||
|
cmdPrint.ResetCommands()
|
||||||
|
cmdTimes.ResetCommands()
|
||||||
|
cmdRootNoRun.ResetCommands()
|
||||||
|
cmdRootSameName.ResetCommands()
|
||||||
|
cmdRootWithRun.ResetCommands()
|
||||||
|
cmdSubNoRun.ResetCommands()
|
||||||
|
}
|
||||||
|
|
||||||
|
func initialize() *Command {
|
||||||
|
tt, tp, te = nil, nil, nil
|
||||||
|
rootPersPre, echoPre, echoPersPre, timesPersPre = nil, nil, nil, nil
|
||||||
|
|
||||||
|
var c = cmdRootNoRun
|
||||||
|
flagInit()
|
||||||
|
commandInit()
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func initializeWithSameName() *Command {
|
||||||
|
tt, tp, te = nil, nil, nil
|
||||||
|
rootPersPre, echoPre, echoPersPre, timesPersPre = nil, nil, nil, nil
|
||||||
|
var c = cmdRootSameName
|
||||||
|
flagInit()
|
||||||
|
commandInit()
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func initializeWithRootCmd() *Command {
|
||||||
|
cmdRootWithRun.ResetCommands()
|
||||||
|
tt, tp, te, tr, rootcalled = nil, nil, nil, nil, false
|
||||||
|
flagInit()
|
||||||
|
cmdRootWithRun.Flags().BoolVarP(&flagbr, "boolroot", "b", false, "help message for flag boolroot")
|
||||||
|
cmdRootWithRun.Flags().IntVarP(&flagir, "introot", "i", 321, "help message for flag introot")
|
||||||
|
commandInit()
|
||||||
|
return cmdRootWithRun
|
||||||
|
}
|
||||||
|
|
||||||
|
type resulter struct {
|
||||||
|
Error error
|
||||||
|
Output string
|
||||||
|
Command *Command
|
||||||
|
}
|
||||||
|
|
||||||
|
func fullSetupTest(input string) resulter {
|
||||||
|
c := initializeWithRootCmd()
|
||||||
|
|
||||||
|
return fullTester(c, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
func noRRSetupTest(input string) resulter {
|
||||||
|
c := initialize()
|
||||||
|
|
||||||
|
return fullTester(c, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
func rootOnlySetupTest(input string) resulter {
|
||||||
|
c := initializeWithRootCmd()
|
||||||
|
|
||||||
|
return simpleTester(c, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
func simpleTester(c *Command, input string) resulter {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
// Testing flag with invalid input
|
||||||
|
c.SetOutput(buf)
|
||||||
|
c.SetArgs(strings.Split(input, " "))
|
||||||
|
|
||||||
|
err := c.Execute()
|
||||||
|
output := buf.String()
|
||||||
|
|
||||||
|
return resulter{err, output, c}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fullTester(c *Command, input string) resulter {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
// Testing flag with invalid input
|
||||||
|
c.SetOutput(buf)
|
||||||
|
cmdEcho.AddCommand(cmdTimes)
|
||||||
|
c.AddCommand(cmdPrint, cmdEcho, cmdSubNoRun, cmdDeprecated)
|
||||||
|
c.SetArgs(strings.Split(input, " "))
|
||||||
|
|
||||||
|
err := c.Execute()
|
||||||
|
output := buf.String()
|
||||||
|
|
||||||
|
return resulter{err, output, c}
|
||||||
|
}
|
||||||
|
|
||||||
|
func logErr(t *testing.T, found, expected string) {
|
||||||
|
out := new(bytes.Buffer)
|
||||||
|
|
||||||
|
_, _, line, ok := runtime.Caller(2)
|
||||||
|
if ok {
|
||||||
|
fmt.Fprintf(out, "Line: %d ", line)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(out, "Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
|
||||||
|
t.Errorf(out.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkResultContains(t *testing.T, x resulter, check string) {
|
||||||
|
if !strings.Contains(x.Output, check) {
|
||||||
|
logErr(t, x.Output, check)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkResultOmits(t *testing.T, x resulter, check string) {
|
||||||
|
if strings.Contains(x.Output, check) {
|
||||||
|
logErr(t, x.Output, check)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkOutputContains(t *testing.T, c *Command, check string) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
c.SetOutput(buf)
|
||||||
|
c.Execute()
|
||||||
|
|
||||||
|
if !strings.Contains(buf.String(), check) {
|
||||||
|
logErr(t, buf.String(), check)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSingleCommand(t *testing.T) {
|
||||||
|
noRRSetupTest("print one two")
|
||||||
|
|
||||||
|
if te != nil || tt != nil {
|
||||||
|
t.Error("Wrong command called")
|
||||||
|
}
|
||||||
|
if tp == nil {
|
||||||
|
t.Error("Wrong command called")
|
||||||
|
}
|
||||||
|
if strings.Join(tp, " ") != "one two" {
|
||||||
|
t.Error("Command didn't parse correctly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChildCommand(t *testing.T) {
|
||||||
|
noRRSetupTest("echo times one two")
|
||||||
|
|
||||||
|
if te != nil || tp != nil {
|
||||||
|
t.Error("Wrong command called")
|
||||||
|
}
|
||||||
|
if tt == nil {
|
||||||
|
t.Error("Wrong command called")
|
||||||
|
}
|
||||||
|
if strings.Join(tt, " ") != "one two" {
|
||||||
|
t.Error("Command didn't parse correctly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandAlias(t *testing.T) {
|
||||||
|
noRRSetupTest("say times one two")
|
||||||
|
|
||||||
|
if te != nil || tp != nil {
|
||||||
|
t.Error("Wrong command called")
|
||||||
|
}
|
||||||
|
if tt == nil {
|
||||||
|
t.Error("Wrong command called")
|
||||||
|
}
|
||||||
|
if strings.Join(tt, " ") != "one two" {
|
||||||
|
t.Error("Command didn't parse correctly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrefixMatching(t *testing.T) {
|
||||||
|
EnablePrefixMatching = true
|
||||||
|
noRRSetupTest("ech times one two")
|
||||||
|
|
||||||
|
if te != nil || tp != nil {
|
||||||
|
t.Error("Wrong command called")
|
||||||
|
}
|
||||||
|
if tt == nil {
|
||||||
|
t.Error("Wrong command called")
|
||||||
|
}
|
||||||
|
if strings.Join(tt, " ") != "one two" {
|
||||||
|
t.Error("Command didn't parse correctly")
|
||||||
|
}
|
||||||
|
|
||||||
|
EnablePrefixMatching = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoPrefixMatching(t *testing.T) {
|
||||||
|
EnablePrefixMatching = false
|
||||||
|
|
||||||
|
noRRSetupTest("ech times one two")
|
||||||
|
|
||||||
|
if !(tt == nil && te == nil && tp == nil) {
|
||||||
|
t.Error("Wrong command called")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAliasPrefixMatching(t *testing.T) {
|
||||||
|
EnablePrefixMatching = true
|
||||||
|
noRRSetupTest("sa times one two")
|
||||||
|
|
||||||
|
if te != nil || tp != nil {
|
||||||
|
t.Error("Wrong command called")
|
||||||
|
}
|
||||||
|
if tt == nil {
|
||||||
|
t.Error("Wrong command called")
|
||||||
|
}
|
||||||
|
if strings.Join(tt, " ") != "one two" {
|
||||||
|
t.Error("Command didn't parse correctly")
|
||||||
|
}
|
||||||
|
EnablePrefixMatching = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChildSameName(t *testing.T) {
|
||||||
|
c := initializeWithSameName()
|
||||||
|
c.AddCommand(cmdPrint, cmdEcho)
|
||||||
|
c.SetArgs(strings.Split("print one two", " "))
|
||||||
|
c.Execute()
|
||||||
|
|
||||||
|
if te != nil || tt != nil {
|
||||||
|
t.Error("Wrong command called")
|
||||||
|
}
|
||||||
|
if tp == nil {
|
||||||
|
t.Error("Wrong command called")
|
||||||
|
}
|
||||||
|
if strings.Join(tp, " ") != "one two" {
|
||||||
|
t.Error("Command didn't parse correctly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGrandChildSameName(t *testing.T) {
|
||||||
|
c := initializeWithSameName()
|
||||||
|
cmdTimes.AddCommand(cmdPrint)
|
||||||
|
c.AddCommand(cmdTimes)
|
||||||
|
c.SetArgs(strings.Split("times print one two", " "))
|
||||||
|
c.Execute()
|
||||||
|
|
||||||
|
if te != nil || tt != nil {
|
||||||
|
t.Error("Wrong command called")
|
||||||
|
}
|
||||||
|
if tp == nil {
|
||||||
|
t.Error("Wrong command called")
|
||||||
|
}
|
||||||
|
if strings.Join(tp, " ") != "one two" {
|
||||||
|
t.Error("Command didn't parse correctly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlagLong(t *testing.T) {
|
||||||
|
noRRSetupTest("echo --intone=13 something here")
|
||||||
|
|
||||||
|
if strings.Join(te, " ") != "something here" {
|
||||||
|
t.Errorf("flags didn't leave proper args remaining..%s given", te)
|
||||||
|
}
|
||||||
|
if flagi1 != 13 {
|
||||||
|
t.Errorf("int flag didn't get correct value, had %d", flagi1)
|
||||||
|
}
|
||||||
|
if flagi2 != 234 {
|
||||||
|
t.Errorf("default flag value changed, 234 expected, %d given", flagi2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlagShort(t *testing.T) {
|
||||||
|
noRRSetupTest("echo -i13 something here")
|
||||||
|
|
||||||
|
if strings.Join(te, " ") != "something here" {
|
||||||
|
t.Errorf("flags didn't leave proper args remaining..%s given", te)
|
||||||
|
}
|
||||||
|
if flagi1 != 13 {
|
||||||
|
t.Errorf("int flag didn't get correct value, had %d", flagi1)
|
||||||
|
}
|
||||||
|
if flagi2 != 234 {
|
||||||
|
t.Errorf("default flag value changed, 234 expected, %d given", flagi2)
|
||||||
|
}
|
||||||
|
|
||||||
|
noRRSetupTest("echo -i 13 something here")
|
||||||
|
|
||||||
|
if strings.Join(te, " ") != "something here" {
|
||||||
|
t.Errorf("flags didn't leave proper args remaining..%s given", te)
|
||||||
|
}
|
||||||
|
if flagi1 != 13 {
|
||||||
|
t.Errorf("int flag didn't get correct value, had %d", flagi1)
|
||||||
|
}
|
||||||
|
if flagi2 != 234 {
|
||||||
|
t.Errorf("default flag value changed, 234 expected, %d given", flagi2)
|
||||||
|
}
|
||||||
|
|
||||||
|
noRRSetupTest("print -i99 one two")
|
||||||
|
|
||||||
|
if strings.Join(tp, " ") != "one two" {
|
||||||
|
t.Errorf("flags didn't leave proper args remaining..%s given", tp)
|
||||||
|
}
|
||||||
|
if flagi3 != 99 {
|
||||||
|
t.Errorf("int flag didn't get correct value, had %d", flagi3)
|
||||||
|
}
|
||||||
|
if flagi1 != 123 {
|
||||||
|
t.Errorf("default flag value changed on different command with same shortname, 234 expected, %d given", flagi2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChildCommandFlags(t *testing.T) {
|
||||||
|
noRRSetupTest("echo times -j 99 one two")
|
||||||
|
|
||||||
|
if strings.Join(tt, " ") != "one two" {
|
||||||
|
t.Errorf("flags didn't leave proper args remaining..%s given", tt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Testing with flag that shouldn't be persistent
|
||||||
|
r := noRRSetupTest("echo times -j 99 -i77 one two")
|
||||||
|
|
||||||
|
if r.Error == nil {
|
||||||
|
t.Errorf("invalid flag should generate error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(r.Output, "unknown shorthand") {
|
||||||
|
t.Errorf("Wrong error message displayed, \n %s", r.Output)
|
||||||
|
}
|
||||||
|
|
||||||
|
if flagi2 != 99 {
|
||||||
|
t.Errorf("flag value should be 99, %d given", flagi2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if flagi1 != 123 {
|
||||||
|
t.Errorf("unset flag should have default value, expecting 123, given %d", flagi1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Testing with flag only existing on child
|
||||||
|
r = noRRSetupTest("echo -j 99 -i77 one two")
|
||||||
|
|
||||||
|
if r.Error == nil {
|
||||||
|
t.Errorf("invalid flag should generate error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(r.Output, "unknown shorthand flag") {
|
||||||
|
t.Errorf("Wrong error message displayed, \n %s", r.Output)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Testing with persistent flag overwritten by child
|
||||||
|
noRRSetupTest("echo times --strtwo=child one two")
|
||||||
|
|
||||||
|
if flags2b != "child" {
|
||||||
|
t.Errorf("flag value should be child, %s given", flags2b)
|
||||||
|
}
|
||||||
|
|
||||||
|
if flags2a != "two" {
|
||||||
|
t.Errorf("unset flag should have default value, expecting two, given %s", flags2a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Testing flag with invalid input
|
||||||
|
r = noRRSetupTest("echo -i10E")
|
||||||
|
|
||||||
|
if r.Error == nil {
|
||||||
|
t.Errorf("invalid input should generate error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(r.Output, "invalid argument \"10E\" for i10E") {
|
||||||
|
t.Errorf("Wrong error message displayed, \n %s", r.Output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTrailingCommandFlags(t *testing.T) {
|
||||||
|
x := fullSetupTest("echo two -x")
|
||||||
|
|
||||||
|
if x.Error == nil {
|
||||||
|
t.Errorf("invalid flag should generate error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidSubcommandFlags(t *testing.T) {
|
||||||
|
cmd := initializeWithRootCmd()
|
||||||
|
cmd.AddCommand(cmdTimes)
|
||||||
|
|
||||||
|
result := simpleTester(cmd, "times --inttwo=2 --badflag=bar")
|
||||||
|
|
||||||
|
checkResultContains(t, result, "unknown flag: --badflag")
|
||||||
|
|
||||||
|
if strings.Contains(result.Output, "unknown flag: --inttwo") {
|
||||||
|
t.Errorf("invalid --badflag flag shouldn't fail on 'unknown' --inttwo flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubcommandArgEvaluation(t *testing.T) {
|
||||||
|
cmd := initializeWithRootCmd()
|
||||||
|
|
||||||
|
first := &Command{
|
||||||
|
Use: "first",
|
||||||
|
Run: func(cmd *Command, args []string) {
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmd.AddCommand(first)
|
||||||
|
|
||||||
|
second := &Command{
|
||||||
|
Use: "second",
|
||||||
|
Run: func(cmd *Command, args []string) {
|
||||||
|
fmt.Fprintf(cmd.Out(), "%v", args)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
first.AddCommand(second)
|
||||||
|
|
||||||
|
result := simpleTester(cmd, "first second first third")
|
||||||
|
|
||||||
|
expectedOutput := fmt.Sprintf("%v", []string{"first third"})
|
||||||
|
if result.Output != expectedOutput {
|
||||||
|
t.Errorf("exptected %v, got %v", expectedOutput, result.Output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPersistentFlags(t *testing.T) {
|
||||||
|
fullSetupTest("echo -s something -p more here")
|
||||||
|
|
||||||
|
// persistentFlag should act like normal flag on it's own command
|
||||||
|
if strings.Join(te, " ") != "more here" {
|
||||||
|
t.Errorf("flags didn't leave proper args remaining..%s given", te)
|
||||||
|
}
|
||||||
|
if flags1 != "something" {
|
||||||
|
t.Errorf("string flag didn't get correct value, had %v", flags1)
|
||||||
|
}
|
||||||
|
if !flagbp {
|
||||||
|
t.Errorf("persistent bool flag not parsed correctly. Expected true, had %v", flagbp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// persistentFlag should act like normal flag on it's own command
|
||||||
|
fullSetupTest("echo times -s again -c -p test here")
|
||||||
|
|
||||||
|
if strings.Join(tt, " ") != "test here" {
|
||||||
|
t.Errorf("flags didn't leave proper args remaining..%s given", tt)
|
||||||
|
}
|
||||||
|
|
||||||
|
if flags1 != "again" {
|
||||||
|
t.Errorf("string flag didn't get correct value, had %v", flags1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !flagb2 {
|
||||||
|
t.Errorf("local flag not parsed correctly. Expected true, had %v", flagb2)
|
||||||
|
}
|
||||||
|
if !flagbp {
|
||||||
|
t.Errorf("persistent bool flag not parsed correctly. Expected true, had %v", flagbp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHelpCommand(t *testing.T) {
|
||||||
|
x := fullSetupTest("help")
|
||||||
|
checkResultContains(t, x, cmdRootWithRun.Long)
|
||||||
|
|
||||||
|
x = fullSetupTest("help echo")
|
||||||
|
checkResultContains(t, x, cmdEcho.Long)
|
||||||
|
|
||||||
|
x = fullSetupTest("help echo times")
|
||||||
|
checkResultContains(t, x, cmdTimes.Long)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChildCommandHelp(t *testing.T) {
|
||||||
|
c := noRRSetupTest("print --help")
|
||||||
|
checkResultContains(t, c, strtwoParentHelp)
|
||||||
|
r := noRRSetupTest("echo times --help")
|
||||||
|
checkResultContains(t, r, strtwoChildHelp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNonRunChildHelp(t *testing.T) {
|
||||||
|
x := noRRSetupTest("subnorun")
|
||||||
|
checkResultContains(t, x, cmdSubNoRun.Long)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunnableRootCommand(t *testing.T) {
|
||||||
|
fullSetupTest("")
|
||||||
|
|
||||||
|
if rootcalled != true {
|
||||||
|
t.Errorf("Root Function was not called")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunnableRootCommandNilInput(t *testing.T) {
|
||||||
|
empty_arg := make([]string, 0)
|
||||||
|
c := initializeWithRootCmd()
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
// Testing flag with invalid input
|
||||||
|
c.SetOutput(buf)
|
||||||
|
cmdEcho.AddCommand(cmdTimes)
|
||||||
|
c.AddCommand(cmdPrint, cmdEcho)
|
||||||
|
c.SetArgs(empty_arg)
|
||||||
|
|
||||||
|
c.Execute()
|
||||||
|
|
||||||
|
if rootcalled != true {
|
||||||
|
t.Errorf("Root Function was not called")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunnableRootCommandEmptyInput(t *testing.T) {
|
||||||
|
args := make([]string, 3)
|
||||||
|
args[0] = ""
|
||||||
|
args[1] = "--introot=12"
|
||||||
|
args[2] = ""
|
||||||
|
c := initializeWithRootCmd()
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
// Testing flag with invalid input
|
||||||
|
c.SetOutput(buf)
|
||||||
|
cmdEcho.AddCommand(cmdTimes)
|
||||||
|
c.AddCommand(cmdPrint, cmdEcho)
|
||||||
|
c.SetArgs(args)
|
||||||
|
|
||||||
|
c.Execute()
|
||||||
|
|
||||||
|
if rootcalled != true {
|
||||||
|
t.Errorf("Root Function was not called.\n\nOutput was:\n\n%s\n", buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidSubcommandWhenArgsAllowed(t *testing.T) {
|
||||||
|
fullSetupTest("echo invalid-sub")
|
||||||
|
|
||||||
|
if te[0] != "invalid-sub" {
|
||||||
|
t.Errorf("Subcommand didn't work...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRootFlags(t *testing.T) {
|
||||||
|
fullSetupTest("-i 17 -b")
|
||||||
|
|
||||||
|
if flagbr != true {
|
||||||
|
t.Errorf("flag value should be true, %v given", flagbr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if flagir != 17 {
|
||||||
|
t.Errorf("flag value should be 17, %d given", flagir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRootHelp(t *testing.T) {
|
||||||
|
x := fullSetupTest("--help")
|
||||||
|
|
||||||
|
checkResultContains(t, x, "Available Commands:")
|
||||||
|
checkResultContains(t, x, "for more information about a command")
|
||||||
|
|
||||||
|
if strings.Contains(x.Output, "unknown flag: --help") {
|
||||||
|
t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(x.Output, cmdEcho.Use) {
|
||||||
|
t.Errorf("--help shouldn't display subcommand's usage, Got: \n %s", x.Output)
|
||||||
|
}
|
||||||
|
|
||||||
|
x = fullSetupTest("echo --help")
|
||||||
|
|
||||||
|
if strings.Contains(x.Output, cmdTimes.Use) {
|
||||||
|
t.Errorf("--help shouldn't display subsubcommand's usage, Got: \n %s", x.Output)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkResultContains(t, x, "Available Commands:")
|
||||||
|
checkResultContains(t, x, "for more information about a command")
|
||||||
|
|
||||||
|
if strings.Contains(x.Output, "unknown flag: --help") {
|
||||||
|
t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlagAccess(t *testing.T) {
|
||||||
|
initialize()
|
||||||
|
|
||||||
|
local := cmdTimes.LocalFlags()
|
||||||
|
inherited := cmdTimes.InheritedFlags()
|
||||||
|
|
||||||
|
for _, f := range []string{"inttwo", "strtwo", "booltwo"} {
|
||||||
|
if local.Lookup(f) == nil {
|
||||||
|
t.Errorf("LocalFlags expected to contain %s, Got: nil", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if inherited.Lookup("strone") == nil {
|
||||||
|
t.Errorf("InheritedFlags expected to contain strone, Got: nil")
|
||||||
|
}
|
||||||
|
if inherited.Lookup("strtwo") != nil {
|
||||||
|
t.Errorf("InheritedFlags shouldn not contain overwritten flag strtwo")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoNRunnableRootCommandNilInput(t *testing.T) {
|
||||||
|
args := make([]string, 0)
|
||||||
|
c := initialize()
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
// Testing flag with invalid input
|
||||||
|
c.SetOutput(buf)
|
||||||
|
cmdEcho.AddCommand(cmdTimes)
|
||||||
|
c.AddCommand(cmdPrint, cmdEcho)
|
||||||
|
c.SetArgs(args)
|
||||||
|
|
||||||
|
c.Execute()
|
||||||
|
|
||||||
|
if !strings.Contains(buf.String(), cmdRootNoRun.Long) {
|
||||||
|
t.Errorf("Expected to get help output, Got: \n %s", buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRootNoCommandHelp(t *testing.T) {
|
||||||
|
x := rootOnlySetupTest("--help")
|
||||||
|
|
||||||
|
checkResultOmits(t, x, "Available Commands:")
|
||||||
|
checkResultOmits(t, x, "for more information about a command")
|
||||||
|
|
||||||
|
if strings.Contains(x.Output, "unknown flag: --help") {
|
||||||
|
t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output)
|
||||||
|
}
|
||||||
|
|
||||||
|
x = rootOnlySetupTest("echo --help")
|
||||||
|
|
||||||
|
checkResultOmits(t, x, "Available Commands:")
|
||||||
|
checkResultOmits(t, x, "for more information about a command")
|
||||||
|
|
||||||
|
if strings.Contains(x.Output, "unknown flag: --help") {
|
||||||
|
t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRootUnknownCommand(t *testing.T) {
|
||||||
|
r := noRRSetupTest("bogus")
|
||||||
|
s := "Error: unknown command \"bogus\" for \"cobra-test\"\nRun 'cobra-test --help' for usage.\n"
|
||||||
|
|
||||||
|
if r.Output != s {
|
||||||
|
t.Errorf("Unexpected response.\nExpecting to be:\n %q\nGot:\n %q\n", s, r.Output)
|
||||||
|
}
|
||||||
|
|
||||||
|
r = noRRSetupTest("--strtwo=a bogus")
|
||||||
|
if r.Output != s {
|
||||||
|
t.Errorf("Unexpected response.\nExpecting to be:\n %q\nGot:\n %q\n", s, r.Output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlagsBeforeCommand(t *testing.T) {
|
||||||
|
// short without space
|
||||||
|
x := fullSetupTest("-i10 echo")
|
||||||
|
if x.Error != nil {
|
||||||
|
t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// short (int) with equals
|
||||||
|
// It appears that pflags doesn't support this...
|
||||||
|
// Commenting out until support can be added
|
||||||
|
|
||||||
|
//x = noRRSetupTest("echo -i=10")
|
||||||
|
//if x.Error != nil {
|
||||||
|
//t.Errorf("Valid Input shouldn't have errors, got:\n %s", x.Error)
|
||||||
|
//}
|
||||||
|
|
||||||
|
// long with equals
|
||||||
|
x = noRRSetupTest("--intone=123 echo one two")
|
||||||
|
if x.Error != nil {
|
||||||
|
t.Errorf("Valid Input shouldn't have errors, got:\n %s", x.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// With parsing error properly reported
|
||||||
|
x = fullSetupTest("-i10E echo")
|
||||||
|
if !strings.Contains(x.Output, "invalid argument \"10E\" for i10E") {
|
||||||
|
t.Errorf("Wrong error message displayed, \n %s", x.Output)
|
||||||
|
}
|
||||||
|
|
||||||
|
//With quotes
|
||||||
|
x = fullSetupTest("-s=\"walking\" echo")
|
||||||
|
if x.Error != nil {
|
||||||
|
t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
//With quotes and space
|
||||||
|
x = fullSetupTest("-s=\"walking fast\" echo")
|
||||||
|
if x.Error != nil {
|
||||||
|
t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
//With inner quote
|
||||||
|
x = fullSetupTest("-s=\"walking \\\"Inner Quote\\\" fast\" echo")
|
||||||
|
if x.Error != nil {
|
||||||
|
t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
//With quotes and space
|
||||||
|
x = fullSetupTest("-s=\"walking \\\"Inner Quote\\\" fast\" echo")
|
||||||
|
if x.Error != nil {
|
||||||
|
t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveCommand(t *testing.T) {
|
||||||
|
versionUsed = 0
|
||||||
|
c := initializeWithRootCmd()
|
||||||
|
c.AddCommand(cmdVersion1)
|
||||||
|
c.RemoveCommand(cmdVersion1)
|
||||||
|
x := fullTester(c, "version")
|
||||||
|
if x.Error == nil {
|
||||||
|
t.Errorf("Removed command should not have been called\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandWithoutSubcommands(t *testing.T) {
|
||||||
|
c := initializeWithRootCmd()
|
||||||
|
|
||||||
|
x := simpleTester(c, "")
|
||||||
|
if x.Error != nil {
|
||||||
|
t.Errorf("Calling command without subcommands should not have error: %v", x.Error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandWithoutSubcommandsWithArg(t *testing.T) {
|
||||||
|
c := initializeWithRootCmd()
|
||||||
|
expectedArgs := []string{"arg"}
|
||||||
|
|
||||||
|
x := simpleTester(c, "arg")
|
||||||
|
if x.Error != nil {
|
||||||
|
t.Errorf("Calling command without subcommands but with arg should not have error: %v", x.Error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(expectedArgs, tr) {
|
||||||
|
t.Errorf("Calling command without subcommands but with arg has wrong args: expected: %v, actual: %v", expectedArgs, tr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReplaceCommandWithRemove(t *testing.T) {
|
||||||
|
versionUsed = 0
|
||||||
|
c := initializeWithRootCmd()
|
||||||
|
c.AddCommand(cmdVersion1)
|
||||||
|
c.RemoveCommand(cmdVersion1)
|
||||||
|
c.AddCommand(cmdVersion2)
|
||||||
|
x := fullTester(c, "version")
|
||||||
|
if x.Error != nil {
|
||||||
|
t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if versionUsed == 1 {
|
||||||
|
t.Errorf("Removed command shouldn't be called\n")
|
||||||
|
}
|
||||||
|
if versionUsed != 2 {
|
||||||
|
t.Errorf("Replacing command should have been called but didn't\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeprecatedSub(t *testing.T) {
|
||||||
|
c := fullSetupTest("deprecated")
|
||||||
|
|
||||||
|
checkResultContains(t, c, cmdDeprecated.Deprecated)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPreRun(t *testing.T) {
|
||||||
|
noRRSetupTest("echo one two")
|
||||||
|
if echoPre == nil || echoPersPre == nil {
|
||||||
|
t.Error("PreRun or PersistentPreRun not called")
|
||||||
|
}
|
||||||
|
if rootPersPre != nil || timesPersPre != nil {
|
||||||
|
t.Error("Wrong *Pre functions called!")
|
||||||
|
}
|
||||||
|
|
||||||
|
noRRSetupTest("echo times one two")
|
||||||
|
if timesPersPre == nil {
|
||||||
|
t.Error("PreRun or PersistentPreRun not called")
|
||||||
|
}
|
||||||
|
if echoPre != nil || echoPersPre != nil || rootPersPre != nil {
|
||||||
|
t.Error("Wrong *Pre functions called!")
|
||||||
|
}
|
||||||
|
|
||||||
|
noRRSetupTest("print one two")
|
||||||
|
if rootPersPre == nil {
|
||||||
|
t.Error("Parent PersistentPreRun not called but should not have been")
|
||||||
|
}
|
||||||
|
if echoPre != nil || echoPersPre != nil || timesPersPre != nil {
|
||||||
|
t.Error("Wrong *Pre functions called!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if cmdEchoSub gets PersistentPreRun from rootCmd even if is added last
|
||||||
|
func TestPeristentPreRunPropagation(t *testing.T) {
|
||||||
|
rootCmd := initialize()
|
||||||
|
|
||||||
|
// First add the cmdEchoSub to cmdPrint
|
||||||
|
cmdPrint.AddCommand(cmdEchoSub)
|
||||||
|
// Now add cmdPrint to rootCmd
|
||||||
|
rootCmd.AddCommand(cmdPrint)
|
||||||
|
|
||||||
|
rootCmd.SetArgs(strings.Split("print echosub lala", " "))
|
||||||
|
rootCmd.Execute()
|
||||||
|
|
||||||
|
if rootPersPre == nil || len(rootPersPre) == 0 || rootPersPre[0] != "lala" {
|
||||||
|
t.Error("RootCmd PersistentPreRun not called but should have been")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGlobalNormFuncPropagation(t *testing.T) {
|
||||||
|
normFunc := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||||
|
return pflag.NormalizedName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
rootCmd := initialize()
|
||||||
|
rootCmd.SetGlobalNormalizationFunc(normFunc)
|
||||||
|
if reflect.ValueOf(normFunc) != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()) {
|
||||||
|
t.Error("rootCmd seems to have a wrong normalization function")
|
||||||
|
}
|
||||||
|
|
||||||
|
// First add the cmdEchoSub to cmdPrint
|
||||||
|
cmdPrint.AddCommand(cmdEchoSub)
|
||||||
|
if cmdPrint.GlobalNormalizationFunc() != nil && cmdEchoSub.GlobalNormalizationFunc() != nil {
|
||||||
|
t.Error("cmdPrint and cmdEchoSub should had no normalization functions")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now add cmdPrint to rootCmd
|
||||||
|
rootCmd.AddCommand(cmdPrint)
|
||||||
|
if reflect.ValueOf(cmdPrint.GlobalNormalizationFunc()).Pointer() != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer() ||
|
||||||
|
reflect.ValueOf(cmdEchoSub.GlobalNormalizationFunc()).Pointer() != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer() {
|
||||||
|
t.Error("cmdPrint and cmdEchoSub should had the normalization function of rootCmd")
|
||||||
|
}
|
||||||
|
}
|
1025
Godeps/_workspace/src/github.com/spf13/cobra/command.go
generated
vendored
Normal file
1025
Godeps/_workspace/src/github.com/spf13/cobra/command.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
90
Godeps/_workspace/src/github.com/spf13/cobra/command_test.go
generated
vendored
Normal file
90
Godeps/_workspace/src/github.com/spf13/cobra/command_test.go
generated
vendored
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
package cobra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStripFlags(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input []string
|
||||||
|
output []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
[]string{"foo", "bar"},
|
||||||
|
[]string{"foo", "bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"foo", "--bar", "-b"},
|
||||||
|
[]string{"foo"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-b", "foo", "--bar", "bar"},
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-i10", "echo"},
|
||||||
|
[]string{"echo"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-i=10", "echo"},
|
||||||
|
[]string{"echo"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"--int=100", "echo"},
|
||||||
|
[]string{"echo"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-ib", "echo", "-bfoo", "baz"},
|
||||||
|
[]string{"echo", "baz"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-i=baz", "bar", "-i", "foo", "blah"},
|
||||||
|
[]string{"bar", "blah"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"--int=baz", "-bbar", "-i", "foo", "blah"},
|
||||||
|
[]string{"blah"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"--cat", "bar", "-i", "foo", "blah"},
|
||||||
|
[]string{"bar", "blah"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-c", "bar", "-i", "foo", "blah"},
|
||||||
|
[]string{"bar", "blah"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"--persist", "bar"},
|
||||||
|
[]string{"bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]string{"-p", "bar"},
|
||||||
|
[]string{"bar"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdPrint := &Command{
|
||||||
|
Use: "print [string to print]",
|
||||||
|
Short: "Print anything to the screen",
|
||||||
|
Long: `an utterly useless command for testing.`,
|
||||||
|
Run: func(cmd *Command, args []string) {
|
||||||
|
tp = args
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var flagi int
|
||||||
|
var flagstr string
|
||||||
|
var flagbool bool
|
||||||
|
cmdPrint.PersistentFlags().BoolVarP(&flagbool, "persist", "p", false, "help for persistent one")
|
||||||
|
cmdPrint.Flags().IntVarP(&flagi, "int", "i", 345, "help message for flag int")
|
||||||
|
cmdPrint.Flags().StringVarP(&flagstr, "bar", "b", "bar", "help message for flag string")
|
||||||
|
cmdPrint.Flags().BoolVarP(&flagbool, "cat", "c", false, "help message for flag bool")
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
output := stripFlags(test.input, cmdPrint)
|
||||||
|
if !reflect.DeepEqual(test.output, output) {
|
||||||
|
t.Errorf("expected: %v, got: %v", test.output, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
138
Godeps/_workspace/src/github.com/spf13/cobra/md_docs.go
generated
vendored
Normal file
138
Godeps/_workspace/src/github.com/spf13/cobra/md_docs.go
generated
vendored
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
//Copyright 2015 Red Hat Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package cobra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func printOptions(out *bytes.Buffer, cmd *Command, name string) {
|
||||||
|
flags := cmd.NonInheritedFlags()
|
||||||
|
flags.SetOutput(out)
|
||||||
|
if flags.HasFlags() {
|
||||||
|
fmt.Fprintf(out, "### Options\n\n```\n")
|
||||||
|
flags.PrintDefaults()
|
||||||
|
fmt.Fprintf(out, "```\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
parentFlags := cmd.InheritedFlags()
|
||||||
|
parentFlags.SetOutput(out)
|
||||||
|
if parentFlags.HasFlags() {
|
||||||
|
fmt.Fprintf(out, "### Options inherited from parent commands\n\n```\n")
|
||||||
|
parentFlags.PrintDefaults()
|
||||||
|
fmt.Fprintf(out, "```\n\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type byName []*Command
|
||||||
|
|
||||||
|
func (s byName) Len() int { return len(s) }
|
||||||
|
func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||||
|
func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() }
|
||||||
|
|
||||||
|
func GenMarkdown(cmd *Command, out *bytes.Buffer) {
|
||||||
|
GenMarkdownCustom(cmd, out, func(s string) string { return s })
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenMarkdownCustom(cmd *Command, out *bytes.Buffer, linkHandler func(string) string) {
|
||||||
|
name := cmd.CommandPath()
|
||||||
|
|
||||||
|
short := cmd.Short
|
||||||
|
long := cmd.Long
|
||||||
|
if len(long) == 0 {
|
||||||
|
long = short
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(out, "## %s\n\n", name)
|
||||||
|
fmt.Fprintf(out, "%s\n\n", short)
|
||||||
|
fmt.Fprintf(out, "### Synopsis\n\n")
|
||||||
|
fmt.Fprintf(out, "\n%s\n\n", long)
|
||||||
|
|
||||||
|
if cmd.Runnable() {
|
||||||
|
fmt.Fprintf(out, "```\n%s\n```\n\n", cmd.UseLine())
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmd.Example) > 0 {
|
||||||
|
fmt.Fprintf(out, "### Examples\n\n")
|
||||||
|
fmt.Fprintf(out, "```\n%s\n```\n\n", cmd.Example)
|
||||||
|
}
|
||||||
|
|
||||||
|
printOptions(out, cmd, name)
|
||||||
|
|
||||||
|
if len(cmd.Commands()) > 0 || cmd.HasParent() {
|
||||||
|
fmt.Fprintf(out, "### SEE ALSO\n")
|
||||||
|
if cmd.HasParent() {
|
||||||
|
parent := cmd.Parent()
|
||||||
|
pname := parent.CommandPath()
|
||||||
|
link := pname + ".md"
|
||||||
|
link = strings.Replace(link, " ", "_", -1)
|
||||||
|
fmt.Fprintf(out, "* [%s](%s)\t - %s\n", pname, linkHandler(link), parent.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
children := cmd.Commands()
|
||||||
|
sort.Sort(byName(children))
|
||||||
|
|
||||||
|
for _, child := range children {
|
||||||
|
if len(child.Deprecated) > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cname := name + " " + child.Name()
|
||||||
|
link := cname + ".md"
|
||||||
|
link = strings.Replace(link, " ", "_", -1)
|
||||||
|
fmt.Fprintf(out, "* [%s](%s)\t - %s\n", cname, linkHandler(link), child.Short)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(out, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(out, "###### Auto generated by spf13/cobra at %s\n", time.Now().UTC())
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenMarkdownTree(cmd *Command, dir string) {
|
||||||
|
identity := func(s string) string { return s }
|
||||||
|
emptyStr := func(s string) string { return "" }
|
||||||
|
GenMarkdownTreeCustom(cmd, dir, emptyStr, identity)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenMarkdownTreeCustom(cmd *Command, dir string, filePrepender func(string) string, linkHandler func(string) string) {
|
||||||
|
for _, c := range cmd.Commands() {
|
||||||
|
GenMarkdownTreeCustom(c, dir, filePrepender, linkHandler)
|
||||||
|
}
|
||||||
|
out := new(bytes.Buffer)
|
||||||
|
|
||||||
|
GenMarkdownCustom(cmd, out, linkHandler)
|
||||||
|
|
||||||
|
filename := cmd.CommandPath()
|
||||||
|
filename = dir + strings.Replace(filename, " ", "_", -1) + ".md"
|
||||||
|
outFile, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer outFile.Close()
|
||||||
|
_, err = outFile.WriteString(filePrepender(filename))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
_, err = outFile.Write(out.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue