Adds new s3 driver using aws-sdk-go instead of goamz
Keeps old s3 driver, renames to s3goamz, registers new s3 driver as both "s3" and "s3aws" Changes cloudfront middleware to use aws-sdk-go Signed-off-by: Brian Bland <brian.bland@docker.com>
This commit is contained in:
parent
87ae10b867
commit
1319be2cae
123 changed files with 28539 additions and 170 deletions
49
Godeps/Godeps.json
generated
49
Godeps/Godeps.json
generated
|
@ -50,6 +50,41 @@
|
||||||
"Comment": "v0.7.3",
|
"Comment": "v0.7.3",
|
||||||
"Rev": "55eb11d21d2a31a3cc93838241d04800f52e823d"
|
"Rev": "55eb11d21d2a31a3cc93838241d04800f52e823d"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/aws/aws-sdk-go/aws",
|
||||||
|
"Comment": "v1.1.0-14-g49c3892",
|
||||||
|
"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/aws/aws-sdk-go/private/endpoints",
|
||||||
|
"Comment": "v1.1.0-14-g49c3892",
|
||||||
|
"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol",
|
||||||
|
"Comment": "v1.1.0-14-g49c3892",
|
||||||
|
"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/aws/aws-sdk-go/private/signer/v4",
|
||||||
|
"Comment": "v1.1.0-14-g49c3892",
|
||||||
|
"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/aws/aws-sdk-go/private/waiter",
|
||||||
|
"Comment": "v1.1.0-14-g49c3892",
|
||||||
|
"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/aws/aws-sdk-go/service/cloudfront/sign",
|
||||||
|
"Comment": "v1.1.0-14-g49c3892",
|
||||||
|
"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/aws/aws-sdk-go/service/s3",
|
||||||
|
"Comment": "v1.1.0-14-g49c3892",
|
||||||
|
"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/bugsnag/bugsnag-go",
|
"ImportPath": "github.com/bugsnag/bugsnag-go",
|
||||||
"Comment": "v1.0.2-5-gb1d1530",
|
"Comment": "v1.0.2-5-gb1d1530",
|
||||||
|
@ -79,10 +114,6 @@
|
||||||
"ImportPath": "github.com/docker/goamz/aws",
|
"ImportPath": "github.com/docker/goamz/aws",
|
||||||
"Rev": "f0a21f5b2e12f83a505ecf79b633bb2035cf6f85"
|
"Rev": "f0a21f5b2e12f83a505ecf79b633bb2035cf6f85"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"ImportPath": "github.com/docker/goamz/cloudfront",
|
|
||||||
"Rev": "f0a21f5b2e12f83a505ecf79b633bb2035cf6f85"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/goamz/s3",
|
"ImportPath": "github.com/docker/goamz/s3",
|
||||||
"Rev": "f0a21f5b2e12f83a505ecf79b633bb2035cf6f85"
|
"Rev": "f0a21f5b2e12f83a505ecf79b633bb2035cf6f85"
|
||||||
|
@ -99,6 +130,11 @@
|
||||||
"ImportPath": "github.com/garyburd/redigo/redis",
|
"ImportPath": "github.com/garyburd/redigo/redis",
|
||||||
"Rev": "535138d7bcd717d6531c701ef5933d98b1866257"
|
"Rev": "535138d7bcd717d6531c701ef5933d98b1866257"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/go-ini/ini",
|
||||||
|
"Comment": "v1.8.6",
|
||||||
|
"Rev": "afbd495e5aaea13597b5e14fe514ddeaa4d76fc3"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/gorilla/context",
|
"ImportPath": "github.com/gorilla/context",
|
||||||
"Rev": "14f550f51af52180c2eefed15e5fd18d63c0a64a"
|
"Rev": "14f550f51af52180c2eefed15e5fd18d63c0a64a"
|
||||||
|
@ -119,6 +155,11 @@
|
||||||
"ImportPath": "github.com/mitchellh/mapstructure",
|
"ImportPath": "github.com/mitchellh/mapstructure",
|
||||||
"Rev": "482a9fd5fa83e8c4e7817413b80f3eb8feec03ef"
|
"Rev": "482a9fd5fa83e8c4e7817413b80f3eb8feec03ef"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/jmespath/go-jmespath",
|
||||||
|
"Comment": "0.2.2-12-g0b12d6b",
|
||||||
|
"Rev": "0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/ncw/swift",
|
"ImportPath": "github.com/ncw/swift",
|
||||||
"Rev": "c54732e87b0b283d1baf0a18db689d0aea460ba3"
|
"Rev": "c54732e87b0b283d1baf0a18db689d0aea460ba3"
|
||||||
|
|
127
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awserr/error.go
generated
vendored
Normal file
127
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awserr/error.go
generated
vendored
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
// 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:", awsErr.Code(), awsErr.Message())
|
||||||
|
//
|
||||||
|
// // Prints out full error message, including original error if there was one.
|
||||||
|
// log.Println("Error:", awsErr.Error())
|
||||||
|
//
|
||||||
|
// // Get original error
|
||||||
|
// if origErr := awsErr.OrigErr(); 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchError is a batch of errors which also wraps lower level errors with code, message,
|
||||||
|
// and original errors. Calling Error() will only return the error that is at the end
|
||||||
|
// of the list.
|
||||||
|
type BatchError 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.
|
||||||
|
OrigErrs() []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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBatchError returns an baseError with an expectation of an array of errors
|
||||||
|
func NewBatchError(code, message string, errs []error) BatchError {
|
||||||
|
return newBaseErrors(code, message, errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
197
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awserr/types.go
generated
vendored
Normal file
197
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awserr/types.go
generated
vendored
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
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.
|
||||||
|
errs []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 {
|
||||||
|
b := &baseError{
|
||||||
|
code: code,
|
||||||
|
message: message,
|
||||||
|
}
|
||||||
|
|
||||||
|
if origErr != nil {
|
||||||
|
b.errs = append(b.errs, origErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// newBaseErrors returns an error object for the code, message, and errors.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// origErrs is the error objects which will be nested under the new errors to be returned.
|
||||||
|
func newBaseErrors(code, message string, origErrs []error) *baseError {
|
||||||
|
b := &baseError{
|
||||||
|
code: code,
|
||||||
|
message: message,
|
||||||
|
errs: origErrs,
|
||||||
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the string representation of the error.
|
||||||
|
//
|
||||||
|
// See ErrorWithExtra for formatting.
|
||||||
|
//
|
||||||
|
// Satisfies the error interface.
|
||||||
|
func (b baseError) Error() string {
|
||||||
|
size := len(b.errs)
|
||||||
|
if size > 0 {
|
||||||
|
return SprintError(b.code, b.message, "", errorList(b.errs))
|
||||||
|
}
|
||||||
|
|
||||||
|
return SprintError(b.code, b.message, "", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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. This only returns the first element in the list. If the full list is
|
||||||
|
// needed, use BatchError
|
||||||
|
func (b baseError) OrigErr() error {
|
||||||
|
if size := len(b.errs); size > 0 {
|
||||||
|
return b.errs[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrigErrs returns the original errors if one was set. An empty slice is returned if
|
||||||
|
// no error was set:w
|
||||||
|
func (b baseError) OrigErrs() []error {
|
||||||
|
return b.errs
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// An error list that satisfies the golang interface
|
||||||
|
type errorList []error
|
||||||
|
|
||||||
|
// Error returns the string representation of the error.
|
||||||
|
//
|
||||||
|
// Satisfies the error interface.
|
||||||
|
func (e errorList) Error() string {
|
||||||
|
msg := ""
|
||||||
|
// How do we want to handle the array size being zero
|
||||||
|
if size := len(e); size > 0 {
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
msg += fmt.Sprintf("%s", e[i].Error())
|
||||||
|
// We check the next index to see if it is within the slice.
|
||||||
|
// If it is, then we append a newline. We do this, because unit tests
|
||||||
|
// could be broken with the additional '\n'
|
||||||
|
if i+1 < size {
|
||||||
|
msg += "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
100
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awsutil/copy.go
generated
vendored
Normal file
100
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awsutil/copy.go
generated
vendored
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
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:
|
||||||
|
t := dst.Type()
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
name := t.Field(i).Name
|
||||||
|
srcVal := src.FieldByName(name)
|
||||||
|
dstVal := dst.FieldByName(name)
|
||||||
|
if srcVal.IsValid() && dstVal.CanSet() {
|
||||||
|
rcopy(dstVal, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awsutil/equal.go
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awsutil/equal.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package awsutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeepEqual returns if the two values are deeply equal like reflect.DeepEqual.
|
||||||
|
// In addition to this, this method will also dereference the input values if
|
||||||
|
// possible so the DeepEqual performed will not fail if one parameter is a
|
||||||
|
// pointer and the other is not.
|
||||||
|
//
|
||||||
|
// DeepEqual will not perform indirection of nested values of the input parameters.
|
||||||
|
func DeepEqual(a, b interface{}) bool {
|
||||||
|
ra := reflect.Indirect(reflect.ValueOf(a))
|
||||||
|
rb := reflect.Indirect(reflect.ValueOf(b))
|
||||||
|
|
||||||
|
if raValid, rbValid := ra.IsValid(), rb.IsValid(); !raValid && !rbValid {
|
||||||
|
// If the elements are both nil, and of the same type the are equal
|
||||||
|
// If they are of different types they are not equal
|
||||||
|
return reflect.TypeOf(a) == reflect.TypeOf(b)
|
||||||
|
} else if raValid != rbValid {
|
||||||
|
// Both values must be valid to be equal
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return reflect.DeepEqual(ra.Interface(), rb.Interface())
|
||||||
|
}
|
222
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awsutil/path_value.go
generated
vendored
Normal file
222
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awsutil/path_value.go
generated
vendored
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
package awsutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jmespath/go-jmespath"
|
||||||
|
)
|
||||||
|
|
||||||
|
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, createPath, caseSensitive, nilTerm bool) []reflect.Value {
|
||||||
|
pathparts := strings.Split(path, "||")
|
||||||
|
if len(pathparts) > 1 {
|
||||||
|
for _, pathpart := range pathparts {
|
||||||
|
vals := rValuesAtPath(v, pathpart, createPath, caseSensitive, nilTerm)
|
||||||
|
if 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 nilTerm && value.Kind() == reflect.Ptr && len(components[1:]) == 0 {
|
||||||
|
if !value.IsNil() {
|
||||||
|
value.Set(reflect.Zero(value.Type()))
|
||||||
|
}
|
||||||
|
return []reflect.Value{value}
|
||||||
|
}
|
||||||
|
|
||||||
|
if createPath && value.Kind() == reflect.Ptr && value.IsNil() {
|
||||||
|
// TODO if the value is the terminus it should not be created
|
||||||
|
// if the value to be set to its position is nil.
|
||||||
|
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 !createPath && 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 createPath {
|
||||||
|
// 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 !createPath && 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 values at the case insensitive lexical
|
||||||
|
// path inside of a structure.
|
||||||
|
func ValuesAtPath(i interface{}, path string) ([]interface{}, error) {
|
||||||
|
result, err := jmespath.Search(path, i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
v := reflect.ValueOf(result)
|
||||||
|
if !v.IsValid() || (v.Kind() == reflect.Ptr && v.IsNil()) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if s, ok := result.([]interface{}); ok {
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
if v.Kind() == reflect.Map && v.Len() == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if v.Kind() == reflect.Slice {
|
||||||
|
out := make([]interface{}, v.Len())
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
out[i] = v.Index(i).Interface()
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return []interface{}{result}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetValueAtPath sets a value at the case insensitive lexical path inside
|
||||||
|
// of a structure.
|
||||||
|
func SetValueAtPath(i interface{}, path string, v interface{}) {
|
||||||
|
if rvals := rValuesAtPath(i, path, true, false, v == nil); rvals != nil {
|
||||||
|
for _, rval := range rvals {
|
||||||
|
if rval.Kind() == reflect.Ptr && rval.IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
setValue(rval, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setValue(dstVal reflect.Value, src interface{}) {
|
||||||
|
if dstVal.Kind() == reflect.Ptr {
|
||||||
|
dstVal = reflect.Indirect(dstVal)
|
||||||
|
}
|
||||||
|
srcVal := reflect.ValueOf(src)
|
||||||
|
|
||||||
|
if !srcVal.IsValid() { // src is literal nil
|
||||||
|
if dstVal.CanAddr() {
|
||||||
|
// Convert to pointer so that pointer's value can be nil'ed
|
||||||
|
// dstVal = dstVal.Addr()
|
||||||
|
}
|
||||||
|
dstVal.Set(reflect.Zero(dstVal.Type()))
|
||||||
|
|
||||||
|
} else if srcVal.Kind() == reflect.Ptr {
|
||||||
|
if srcVal.IsNil() {
|
||||||
|
srcVal = reflect.Zero(dstVal.Type())
|
||||||
|
} else {
|
||||||
|
srcVal = reflect.ValueOf(src).Elem()
|
||||||
|
}
|
||||||
|
dstVal.Set(srcVal)
|
||||||
|
} else {
|
||||||
|
dstVal.Set(srcVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
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())
|
||||||
|
}
|
||||||
|
}
|
89
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awsutil/string_value.go
generated
vendored
Normal file
89
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awsutil/string_value.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package awsutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StringValue returns the string representation of a value.
|
||||||
|
func StringValue(i interface{}) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
stringValue(reflect.ValueOf(i), 0, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringValue(v reflect.Value, indent int, buf *bytes.Buffer) {
|
||||||
|
for v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
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.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 + ": ")
|
||||||
|
stringValue(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)
|
||||||
|
stringValue(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() + ": ")
|
||||||
|
stringValue(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"
|
||||||
|
}
|
||||||
|
fmt.Fprintf(buf, format, v.Interface())
|
||||||
|
}
|
||||||
|
}
|
120
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/client/client.go
generated
vendored
Normal file
120
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/client/client.go
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http/httputil"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client/metadata"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Config provides configuration to a service client instance.
|
||||||
|
type Config struct {
|
||||||
|
Config *aws.Config
|
||||||
|
Handlers request.Handlers
|
||||||
|
Endpoint, SigningRegion string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigProvider provides a generic way for a service client to receive
|
||||||
|
// the ClientConfig without circular dependencies.
|
||||||
|
type ConfigProvider interface {
|
||||||
|
ClientConfig(serviceName string, cfgs ...*aws.Config) Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Client implements the base client request and response handling
|
||||||
|
// used by all service clients.
|
||||||
|
type Client struct {
|
||||||
|
request.Retryer
|
||||||
|
metadata.ClientInfo
|
||||||
|
|
||||||
|
Config aws.Config
|
||||||
|
Handlers request.Handlers
|
||||||
|
}
|
||||||
|
|
||||||
|
// New will return a pointer to a new initialized service client.
|
||||||
|
func New(cfg aws.Config, info metadata.ClientInfo, handlers request.Handlers, options ...func(*Client)) *Client {
|
||||||
|
svc := &Client{
|
||||||
|
Config: cfg,
|
||||||
|
ClientInfo: info,
|
||||||
|
Handlers: handlers,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch retryer, ok := cfg.Retryer.(request.Retryer); {
|
||||||
|
case ok:
|
||||||
|
svc.Retryer = retryer
|
||||||
|
case cfg.Retryer != nil && cfg.Logger != nil:
|
||||||
|
s := fmt.Sprintf("WARNING: %T does not implement request.Retryer; using DefaultRetryer instead", cfg.Retryer)
|
||||||
|
cfg.Logger.Log(s)
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
maxRetries := aws.IntValue(cfg.MaxRetries)
|
||||||
|
if cfg.MaxRetries == nil || maxRetries == aws.UseServiceDefaultRetries {
|
||||||
|
maxRetries = 3
|
||||||
|
}
|
||||||
|
svc.Retryer = DefaultRetryer{NumMaxRetries: maxRetries}
|
||||||
|
}
|
||||||
|
|
||||||
|
svc.AddDebugHandlers()
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
option(svc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return svc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRequest returns a new Request pointer for the service API
|
||||||
|
// operation and parameters.
|
||||||
|
func (c *Client) NewRequest(operation *request.Operation, params interface{}, data interface{}) *request.Request {
|
||||||
|
return request.New(c.Config, c.ClientInfo, c.Handlers, c.Retryer, operation, params, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddDebugHandlers injects debug logging handlers into the service to log request
|
||||||
|
// debug information.
|
||||||
|
func (c *Client) AddDebugHandlers() {
|
||||||
|
if !c.Config.LogLevel.AtLeast(aws.LogDebug) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Handlers.Send.PushFront(logRequest)
|
||||||
|
c.Handlers.Send.PushBack(logResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
const logReqMsg = `DEBUG: Request %s/%s Details:
|
||||||
|
---[ REQUEST POST-SIGN ]-----------------------------
|
||||||
|
%s
|
||||||
|
-----------------------------------------------------`
|
||||||
|
|
||||||
|
func logRequest(r *request.Request) {
|
||||||
|
logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
|
||||||
|
dumpedBody, _ := httputil.DumpRequestOut(r.HTTPRequest, logBody)
|
||||||
|
|
||||||
|
if logBody {
|
||||||
|
// Reset the request body because dumpRequest will re-wrap the r.HTTPRequest's
|
||||||
|
// Body as a NoOpCloser and will not be reset after read by the HTTP
|
||||||
|
// client reader.
|
||||||
|
r.Body.Seek(r.BodyStart, 0)
|
||||||
|
r.HTTPRequest.Body = ioutil.NopCloser(r.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Config.Logger.Log(fmt.Sprintf(logReqMsg, r.ClientInfo.ServiceName, r.Operation.Name, string(dumpedBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
const logRespMsg = `DEBUG: Response %s/%s Details:
|
||||||
|
---[ RESPONSE ]--------------------------------------
|
||||||
|
%s
|
||||||
|
-----------------------------------------------------`
|
||||||
|
|
||||||
|
func logResponse(r *request.Request) {
|
||||||
|
var msg = "no response data"
|
||||||
|
if r.HTTPResponse != nil {
|
||||||
|
logBody := r.Config.LogLevel.Matches(aws.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.ClientInfo.ServiceName, r.Operation.Name, msg))
|
||||||
|
}
|
45
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/client/default_retryer.go
generated
vendored
Normal file
45
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/client/default_retryer.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultRetryer implements basic retry logic using exponential backoff for
|
||||||
|
// most services. If you want to implement custom retry logic, implement the
|
||||||
|
// request.Retryer interface or create a structure type that composes this
|
||||||
|
// struct and override the specific methods. For example, to override only
|
||||||
|
// the MaxRetries method:
|
||||||
|
//
|
||||||
|
// type retryer struct {
|
||||||
|
// service.DefaultRetryer
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // This implementation always has 100 max retries
|
||||||
|
// func (d retryer) MaxRetries() uint { return 100 }
|
||||||
|
type DefaultRetryer struct {
|
||||||
|
NumMaxRetries int
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxRetries returns the number of maximum returns the service will use to make
|
||||||
|
// an individual API request.
|
||||||
|
func (d DefaultRetryer) MaxRetries() int {
|
||||||
|
return d.NumMaxRetries
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetryRules returns the delay duration before retrying this request again
|
||||||
|
func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration {
|
||||||
|
delay := int(math.Pow(2, float64(r.RetryCount))) * (rand.Intn(30) + 30)
|
||||||
|
return time.Duration(delay) * time.Millisecond
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShouldRetry returns if the request should be retried.
|
||||||
|
func (d DefaultRetryer) ShouldRetry(r *request.Request) bool {
|
||||||
|
if r.HTTPResponse.StatusCode >= 500 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return r.IsErrorRetryable()
|
||||||
|
}
|
12
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/client/metadata/client_info.go
generated
vendored
Normal file
12
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/client/metadata/client_info.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
// ClientInfo wraps immutable data from the client.Client structure.
|
||||||
|
type ClientInfo struct {
|
||||||
|
ServiceName string
|
||||||
|
APIVersion string
|
||||||
|
Endpoint string
|
||||||
|
SigningName string
|
||||||
|
SigningRegion string
|
||||||
|
JSONVersion string
|
||||||
|
TargetPrefix string
|
||||||
|
}
|
311
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/config.go
generated
vendored
Normal file
311
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/config.go
generated
vendored
Normal file
|
@ -0,0 +1,311 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UseServiceDefaultRetries instructs the config to use the service's own default
|
||||||
|
// number of retries. This will be the default action if Config.MaxRetries
|
||||||
|
// is nil also.
|
||||||
|
const UseServiceDefaultRetries = -1
|
||||||
|
|
||||||
|
// RequestRetryer is an alias for a type that implements the request.Retryer interface.
|
||||||
|
type RequestRetryer interface{}
|
||||||
|
|
||||||
|
// A Config provides service configuration for service clients. By default,
|
||||||
|
// all clients will use the {defaults.DefaultConfig} structure.
|
||||||
|
type Config struct {
|
||||||
|
// Enables verbose error printing of all credential chain errors.
|
||||||
|
// Should be used when wanting to see all errors while attempting to retreive
|
||||||
|
// credentials.
|
||||||
|
CredentialsChainVerboseErrors *bool
|
||||||
|
|
||||||
|
// The credentials object to use when signing requests. Defaults to
|
||||||
|
// a chain of credential providers to search for credentials in environment
|
||||||
|
// variables, shared credential file, and EC2 Instance Roles.
|
||||||
|
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
|
||||||
|
|
||||||
|
// Retryer guides how HTTP requests should be retried in case of recoverable failures.
|
||||||
|
//
|
||||||
|
// When nil or the value does not implement the request.Retryer interface,
|
||||||
|
// the request.DefaultRetryer will be used.
|
||||||
|
//
|
||||||
|
// When both Retryer and MaxRetries are non-nil, the former is used and
|
||||||
|
// the latter ignored.
|
||||||
|
//
|
||||||
|
// To set the Retryer field in a type-safe manner and with chaining, use
|
||||||
|
// the request.WithRetryer helper function:
|
||||||
|
//
|
||||||
|
// cfg := request.WithRetryer(aws.NewConfig(), myRetryer)
|
||||||
|
//
|
||||||
|
Retryer RequestRetryer
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// Set this to `true` to disable the EC2Metadata client from overriding the
|
||||||
|
// default http.Client's Timeout. This is helpful if you do not want the EC2Metadata
|
||||||
|
// client to create a new http.Client. This options is only meaningful if you're not
|
||||||
|
// already using a custom HTTP client with the SDK. Enabled by default.
|
||||||
|
//
|
||||||
|
// Must be set and provided to the session.New() in order to disable the EC2Metadata
|
||||||
|
// overriding the timeout for default credentials chain.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// sess := session.New(aws.NewConfig().WithEC2MetadataDiableTimeoutOverride(true))
|
||||||
|
// svc := s3.New(sess)
|
||||||
|
//
|
||||||
|
EC2MetadataDisableTimeoutOverride *bool
|
||||||
|
|
||||||
|
SleepDelay func(time.Duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCredentialsChainVerboseErrors sets a config verbose errors boolean and returning
|
||||||
|
// a Config pointer.
|
||||||
|
func (c *Config) WithCredentialsChainVerboseErrors(verboseErrs bool) *Config {
|
||||||
|
c.CredentialsChainVerboseErrors = &verboseErrs
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithEC2MetadataDisableTimeoutOverride sets a config EC2MetadataDisableTimeoutOverride value
|
||||||
|
// returning a Config pointer for chaining.
|
||||||
|
func (c *Config) WithEC2MetadataDisableTimeoutOverride(enable bool) *Config {
|
||||||
|
c.EC2MetadataDisableTimeoutOverride = &enable
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSleepDelay overrides the function used to sleep while waiting for the
|
||||||
|
// next retry. Defaults to time.Sleep.
|
||||||
|
func (c *Config) WithSleepDelay(fn func(time.Duration)) *Config {
|
||||||
|
c.SleepDelay = fn
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeIn merges the passed in configs into the existing config object.
|
||||||
|
func (c *Config) MergeIn(cfgs ...*Config) {
|
||||||
|
for _, other := range cfgs {
|
||||||
|
mergeInConfig(c, other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeInConfig(dst *Config, other *Config) {
|
||||||
|
if other == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.CredentialsChainVerboseErrors != nil {
|
||||||
|
dst.CredentialsChainVerboseErrors = other.CredentialsChainVerboseErrors
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Retryer != nil {
|
||||||
|
dst.Retryer = other.Retryer
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.DisableParamValidation != nil {
|
||||||
|
dst.DisableParamValidation = other.DisableParamValidation
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.DisableComputeChecksums != nil {
|
||||||
|
dst.DisableComputeChecksums = other.DisableComputeChecksums
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.S3ForcePathStyle != nil {
|
||||||
|
dst.S3ForcePathStyle = other.S3ForcePathStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.EC2MetadataDisableTimeoutOverride != nil {
|
||||||
|
dst.EC2MetadataDisableTimeoutOverride = other.EC2MetadataDisableTimeoutOverride
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.SleepDelay != nil {
|
||||||
|
dst.SleepDelay = other.SleepDelay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy will return a shallow copy of the Config object. If any additional
|
||||||
|
// configurations are provided they will be merged into the new config returned.
|
||||||
|
func (c *Config) Copy(cfgs ...*Config) *Config {
|
||||||
|
dst := &Config{}
|
||||||
|
dst.MergeIn(c)
|
||||||
|
|
||||||
|
for _, cfg := range cfgs {
|
||||||
|
dst.MergeIn(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
357
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/convert_types.go
generated
vendored
Normal file
357
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/convert_types.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
|
||||||
|
}
|
139
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/corehandlers/handlers.go
generated
vendored
Normal file
139
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/corehandlers/handlers.go
generated
vendored
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
package corehandlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interface for matching types which also have a Len method.
|
||||||
|
type lener interface {
|
||||||
|
Len() int
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildContentLengthHandler 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.
|
||||||
|
var BuildContentLengthHandler = request.NamedHandler{Name: "core.BuildContentLengthHandler", Fn: func(r *request.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))
|
||||||
|
}}
|
||||||
|
|
||||||
|
// SDKVersionUserAgentHandler is a request handler for adding the SDK Version to the user agent.
|
||||||
|
var SDKVersionUserAgentHandler = request.NamedHandler{
|
||||||
|
Name: "core.SDKVersionUserAgentHandler",
|
||||||
|
Fn: request.MakeAddToUserAgentHandler(aws.SDKName, aws.SDKVersion,
|
||||||
|
runtime.Version(), runtime.GOOS, runtime.GOARCH),
|
||||||
|
}
|
||||||
|
|
||||||
|
var reStatusCode = regexp.MustCompile(`^(\d{3})`)
|
||||||
|
|
||||||
|
// SendHandler is a request handler to send service request using HTTP client.
|
||||||
|
var SendHandler = request.NamedHandler{Name: "core.SendHandler", Fn: func(r *request.Request) {
|
||||||
|
var err error
|
||||||
|
r.HTTPResponse, err = r.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 && e.Err != nil {
|
||||||
|
if s := reStatusCode.FindStringSubmatch(e.Err.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.HTTPResponse == 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 = aws.Bool(true) // network errors are retryable
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
// ValidateResponseHandler is a request handler to validate service response.
|
||||||
|
var ValidateResponseHandler = request.NamedHandler{Name: "core.ValidateResponseHandler", Fn: func(r *request.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.
|
||||||
|
var AfterRetryHandler = request.NamedHandler{Name: "core.AfterRetryHandler", Fn: func(r *request.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 = aws.Bool(r.ShouldRetry(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.WillRetry() {
|
||||||
|
r.RetryDelay = r.RetryRules(r)
|
||||||
|
r.Config.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.IsErrorExpired() {
|
||||||
|
r.Config.Credentials.Expire()
|
||||||
|
}
|
||||||
|
|
||||||
|
r.RetryCount++
|
||||||
|
r.Error = 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.
|
||||||
|
var ValidateEndpointHandler = request.NamedHandler{Name: "core.ValidateEndpointHandler", Fn: func(r *request.Request) {
|
||||||
|
if r.ClientInfo.SigningRegion == "" && aws.StringValue(r.Config.Region) == "" {
|
||||||
|
r.Error = aws.ErrMissingRegion
|
||||||
|
} else if r.ClientInfo.Endpoint == "" {
|
||||||
|
r.Error = aws.ErrMissingEndpoint
|
||||||
|
}
|
||||||
|
}}
|
144
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/corehandlers/param_validator.go
generated
vendored
Normal file
144
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/corehandlers/param_validator.go
generated
vendored
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
package corehandlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValidateParametersHandler is a request handler to validate the input parameters.
|
||||||
|
// Validating parameters only has meaning if done prior to the request being sent.
|
||||||
|
var ValidateParametersHandler = request.NamedHandler{Name: "core.ValidateParametersHandler", Fn: func(r *request.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)
|
||||||
|
|
||||||
|
err := validateField(f, fvalue, validateFieldRequired, validateFieldMin)
|
||||||
|
if err != nil {
|
||||||
|
v.errors = append(v.errors, fmt.Sprintf("%s: %s", err.Error(), path+prefix+f.Name))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
v.validateAny(fvalue, path+prefix+f.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type validatorFunc func(f reflect.StructField, fvalue reflect.Value) error
|
||||||
|
|
||||||
|
func validateField(f reflect.StructField, fvalue reflect.Value, funcs ...validatorFunc) error {
|
||||||
|
for _, fn := range funcs {
|
||||||
|
if err := fn(f, fvalue); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validates that a field has a valid value provided for required fields.
|
||||||
|
func validateFieldRequired(f reflect.StructField, fvalue reflect.Value) error {
|
||||||
|
if f.Tag.Get("required") == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch fvalue.Kind() {
|
||||||
|
case reflect.Ptr, reflect.Slice, reflect.Map:
|
||||||
|
if fvalue.IsNil() {
|
||||||
|
return fmt.Errorf("missing required parameter")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if !fvalue.IsValid() {
|
||||||
|
return fmt.Errorf("missing required parameter")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validates that if a value is provided for a field, that value must be at
|
||||||
|
// least a minimum length.
|
||||||
|
func validateFieldMin(f reflect.StructField, fvalue reflect.Value) error {
|
||||||
|
minStr := f.Tag.Get("min")
|
||||||
|
if minStr == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
min, _ := strconv.ParseInt(minStr, 10, 64)
|
||||||
|
|
||||||
|
kind := fvalue.Kind()
|
||||||
|
if kind == reflect.Ptr {
|
||||||
|
if fvalue.IsNil() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fvalue = fvalue.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch fvalue.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
if int64(fvalue.Len()) < min {
|
||||||
|
return fmt.Errorf("field too short, minimum length %d", min)
|
||||||
|
}
|
||||||
|
case reflect.Slice, reflect.Map:
|
||||||
|
if fvalue.IsNil() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if int64(fvalue.Len()) < min {
|
||||||
|
return fmt.Errorf("field too short, minimum length %d", min)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO min can also apply to number minimum value.
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
100
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/chain_provider.go
generated
vendored
Normal file
100
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/chain_provider.go
generated
vendored
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrNoValidProvidersFoundInChain Is returned when there are no valid
|
||||||
|
// providers in the ChainProvider.
|
||||||
|
//
|
||||||
|
// This has been deprecated. For verbose error messaging set
|
||||||
|
// aws.Config.CredentialsChainVerboseErrors to true
|
||||||
|
//
|
||||||
|
// @readonly
|
||||||
|
ErrNoValidProvidersFoundInChain = awserr.New("NoCredentialProviders",
|
||||||
|
`no valid providers in chain. Deprecated.
|
||||||
|
For verbose messaging see aws.Config.CredentialsChainVerboseErrors`,
|
||||||
|
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{
|
||||||
|
// Client: ec2metadata.New(sess),
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// // Usage of ChainCredentials with aws.Config
|
||||||
|
// svc := ec2.New(&aws.Config{Credentials: creds})
|
||||||
|
//
|
||||||
|
type ChainProvider struct {
|
||||||
|
Providers []Provider
|
||||||
|
curr Provider
|
||||||
|
VerboseErrors bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
var errs []error
|
||||||
|
for _, p := range c.Providers {
|
||||||
|
creds, err := p.Retrieve()
|
||||||
|
if err == nil {
|
||||||
|
c.curr = p
|
||||||
|
return creds, nil
|
||||||
|
}
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
c.curr = nil
|
||||||
|
|
||||||
|
var err error
|
||||||
|
err = ErrNoValidProvidersFoundInChain
|
||||||
|
if c.VerboseErrors {
|
||||||
|
err = awserr.NewBatchError("NoCredentialProviders", "no valid providers in chain", errs)
|
||||||
|
}
|
||||||
|
return Value{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
223
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/credentials.go
generated
vendored
Normal file
223
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/credentials.go
generated
vendored
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AnonymousCredentials is 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
|
||||||
|
|
||||||
|
// Provider used to get credentials
|
||||||
|
ProviderName 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()
|
||||||
|
}
|
178
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go
generated
vendored
Normal file
178
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go
generated
vendored
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
package ec2rolecreds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProviderName provides a name of EC2Role provider
|
||||||
|
const ProviderName = "EC2RoleProvider"
|
||||||
|
|
||||||
|
// 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 := &ec2rolecreds.EC2RoleProvider{
|
||||||
|
// // Pass in a custom timeout to be used when requesting
|
||||||
|
// // IAM EC2 Role credentials.
|
||||||
|
// Client: ec2metadata.New(sess, aws.Config{
|
||||||
|
// HTTPClient: &http.Client{Timeout: 10 * time.Second},
|
||||||
|
// }),
|
||||||
|
//
|
||||||
|
// // 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 {
|
||||||
|
credentials.Expiry
|
||||||
|
|
||||||
|
// Required EC2Metadata client to use when connecting to EC2 metadata service.
|
||||||
|
Client *ec2metadata.EC2Metadata
|
||||||
|
|
||||||
|
// 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 EC2RoleProvider. Takes a ConfigProvider to create a EC2Metadata client.
|
||||||
|
// The ConfigProvider is satisfied by the session.Session type.
|
||||||
|
func NewCredentials(c client.ConfigProvider, options ...func(*EC2RoleProvider)) *credentials.Credentials {
|
||||||
|
p := &EC2RoleProvider{
|
||||||
|
Client: ec2metadata.New(c),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
option(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return credentials.NewCredentials(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCredentialsWithClient returns a pointer to a new Credentials object wrapping
|
||||||
|
// the EC2RoleProvider. Takes a EC2Metadata client to use when connecting to EC2
|
||||||
|
// metadata service.
|
||||||
|
func NewCredentialsWithClient(client *ec2metadata.EC2Metadata, options ...func(*EC2RoleProvider)) *credentials.Credentials {
|
||||||
|
p := &EC2RoleProvider{
|
||||||
|
Client: client,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
option(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return credentials.NewCredentials(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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() (credentials.Value, error) {
|
||||||
|
credsList, err := requestCredList(m.Client)
|
||||||
|
if err != nil {
|
||||||
|
return credentials.Value{ProviderName: ProviderName}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(credsList) == 0 {
|
||||||
|
return credentials.Value{ProviderName: ProviderName}, awserr.New("EmptyEC2RoleList", "empty EC2 Role list", nil)
|
||||||
|
}
|
||||||
|
credsName := credsList[0]
|
||||||
|
|
||||||
|
roleCreds, err := requestCred(m.Client, credsName)
|
||||||
|
if err != nil {
|
||||||
|
return credentials.Value{ProviderName: ProviderName}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m.SetExpiration(roleCreds.Expiration, m.ExpiryWindow)
|
||||||
|
|
||||||
|
return credentials.Value{
|
||||||
|
AccessKeyID: roleCreds.AccessKeyID,
|
||||||
|
SecretAccessKey: roleCreds.SecretAccessKey,
|
||||||
|
SessionToken: roleCreds.Token,
|
||||||
|
ProviderName: ProviderName,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// A ec2RoleCredRespBody provides the shape for unmarshalling credential
|
||||||
|
// request responses.
|
||||||
|
type ec2RoleCredRespBody struct {
|
||||||
|
// Success State
|
||||||
|
Expiration time.Time
|
||||||
|
AccessKeyID string
|
||||||
|
SecretAccessKey string
|
||||||
|
Token string
|
||||||
|
|
||||||
|
// Error state
|
||||||
|
Code string
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
const iamSecurityCredsPath = "/iam/security-credentials"
|
||||||
|
|
||||||
|
// 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 *ec2metadata.EC2Metadata) ([]string, error) {
|
||||||
|
resp, err := client.GetMetadata(iamSecurityCredsPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, awserr.New("EC2RoleRequestError", "failed to list EC2 Roles", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
credsList := []string{}
|
||||||
|
s := bufio.NewScanner(strings.NewReader(resp))
|
||||||
|
for s.Scan() {
|
||||||
|
credsList = append(credsList, s.Text())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
return nil, awserr.New("SerializationError", "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 *ec2metadata.EC2Metadata, credsName string) (ec2RoleCredRespBody, error) {
|
||||||
|
resp, err := client.GetMetadata(path.Join(iamSecurityCredsPath, credsName))
|
||||||
|
if err != nil {
|
||||||
|
return ec2RoleCredRespBody{},
|
||||||
|
awserr.New("EC2RoleRequestError",
|
||||||
|
fmt.Sprintf("failed to get %s EC2 Role credentials", credsName),
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
|
respCreds := ec2RoleCredRespBody{}
|
||||||
|
if err := json.NewDecoder(strings.NewReader(resp)).Decode(&respCreds); err != nil {
|
||||||
|
return ec2RoleCredRespBody{},
|
||||||
|
awserr.New("SerializationError",
|
||||||
|
fmt.Sprintf("failed to decode %s EC2 Role credentials", credsName),
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if respCreds.Code != "Success" {
|
||||||
|
// If an error code was returned something failed requesting the role.
|
||||||
|
return ec2RoleCredRespBody{}, awserr.New(respCreds.Code, respCreds.Message, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return respCreds, nil
|
||||||
|
}
|
77
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/env_provider.go
generated
vendored
Normal file
77
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/env_provider.go
generated
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EnvProviderName provides a name of Env provider
|
||||||
|
const EnvProviderName = "EnvProvider"
|
||||||
|
|
||||||
|
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{ProviderName: EnvProviderName}, ErrAccessKeyIDNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if secret == "" {
|
||||||
|
return Value{ProviderName: EnvProviderName}, ErrSecretAccessKeyNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
e.retrieved = true
|
||||||
|
return Value{
|
||||||
|
AccessKeyID: id,
|
||||||
|
SecretAccessKey: secret,
|
||||||
|
SessionToken: os.Getenv("AWS_SESSION_TOKEN"),
|
||||||
|
ProviderName: EnvProviderName,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExpired returns if the credentials have been retrieved.
|
||||||
|
func (e *EnvProvider) IsExpired() bool {
|
||||||
|
return !e.retrieved
|
||||||
|
}
|
12
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/example.ini
generated
vendored
Normal file
12
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/example.ini
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[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
|
||||||
|
|
||||||
|
[with_colon]
|
||||||
|
aws_access_key_id: accessKey
|
||||||
|
aws_secret_access_key: secret
|
151
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/shared_credentials_provider.go
generated
vendored
Normal file
151
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/shared_credentials_provider.go
generated
vendored
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/go-ini/ini"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SharedCredsProviderName provides a name of SharedCreds provider
|
||||||
|
const SharedCredsProviderName = "SharedCredentialsProvider"
|
||||||
|
|
||||||
|
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 look for "AWS_SHARED_CREDENTIALS_FILE" env variable. If the
|
||||||
|
// env value is empty will default to current user's home directory.
|
||||||
|
// Linux/OSX: "$HOME/.aws/credentials"
|
||||||
|
// Windows: "%USERPROFILE%\.aws\credentials"
|
||||||
|
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{ProviderName: SharedCredsProviderName}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
creds, err := loadProfile(filename, p.profile())
|
||||||
|
if err != nil {
|
||||||
|
return Value{ProviderName: SharedCredsProviderName}, 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.Load(filename)
|
||||||
|
if err != nil {
|
||||||
|
return Value{ProviderName: SharedCredsProviderName}, awserr.New("SharedCredsLoad", "failed to load shared credentials file", err)
|
||||||
|
}
|
||||||
|
iniProfile, err := config.GetSection(profile)
|
||||||
|
if err != nil {
|
||||||
|
return Value{ProviderName: SharedCredsProviderName}, awserr.New("SharedCredsLoad", "failed to get profile", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := iniProfile.GetKey("aws_access_key_id")
|
||||||
|
if err != nil {
|
||||||
|
return Value{ProviderName: SharedCredsProviderName}, awserr.New("SharedCredsAccessKey",
|
||||||
|
fmt.Sprintf("shared credentials %s in %s did not contain aws_access_key_id", profile, filename),
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secret, err := iniProfile.GetKey("aws_secret_access_key")
|
||||||
|
if err != nil {
|
||||||
|
return Value{ProviderName: SharedCredsProviderName}, awserr.New("SharedCredsSecret",
|
||||||
|
fmt.Sprintf("shared credentials %s in %s did not contain aws_secret_access_key", profile, filename),
|
||||||
|
nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to empty string if not found
|
||||||
|
token := iniProfile.Key("aws_session_token")
|
||||||
|
|
||||||
|
return Value{
|
||||||
|
AccessKeyID: id.String(),
|
||||||
|
SecretAccessKey: secret.String(),
|
||||||
|
SessionToken: token.String(),
|
||||||
|
ProviderName: SharedCredsProviderName,
|
||||||
|
}, 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 == "" {
|
||||||
|
if p.Filename = os.Getenv("AWS_SHARED_CREDENTIALS_FILE"); p.Filename != "" {
|
||||||
|
return p.Filename, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
48
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/static_provider.go
generated
vendored
Normal file
48
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/static_provider.go
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StaticProviderName provides a name of Static provider
|
||||||
|
const StaticProviderName = "StaticProvider"
|
||||||
|
|
||||||
|
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{ProviderName: StaticProviderName}, ErrStaticCredentialsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Value.ProviderName = StaticProviderName
|
||||||
|
return s.Value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExpired returns if the credentials are expired.
|
||||||
|
//
|
||||||
|
// For StaticProvider, the credentials never expired.
|
||||||
|
func (s *StaticProvider) IsExpired() bool {
|
||||||
|
return false
|
||||||
|
}
|
134
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/stscreds/assume_role_provider.go
generated
vendored
Normal file
134
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/stscreds/assume_role_provider.go
generated
vendored
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
// 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"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go/service/sts"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProviderName provides a name of AssumeRole provider
|
||||||
|
const ProviderName = "AssumeRoleProvider"
|
||||||
|
|
||||||
|
// AssumeRoler represents the minimal subset of the STS client API used by this provider.
|
||||||
|
type AssumeRoler interface {
|
||||||
|
AssumeRole(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultDuration is the default amount of time in minutes that the credentials
|
||||||
|
// will be valid for.
|
||||||
|
var DefaultDuration = time.Duration(15) * time.Minute
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
type AssumeRoleProvider struct {
|
||||||
|
credentials.Expiry
|
||||||
|
|
||||||
|
// STS client to make assume role request with.
|
||||||
|
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
|
||||||
|
|
||||||
|
// Optional ExternalID to pass along, defaults to nil if not set.
|
||||||
|
ExternalID *string
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// Takes a Config provider to create the STS client. The ConfigProvider is
|
||||||
|
// satisfied by the session.Session type.
|
||||||
|
func NewCredentials(c client.ConfigProvider, roleARN string, options ...func(*AssumeRoleProvider)) *credentials.Credentials {
|
||||||
|
p := &AssumeRoleProvider{
|
||||||
|
Client: sts.New(c),
|
||||||
|
RoleARN: roleARN,
|
||||||
|
Duration: DefaultDuration,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
option(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return credentials.NewCredentials(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCredentialsWithClient 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.
|
||||||
|
//
|
||||||
|
// Takes an AssumeRoler which can be satisfiede by the STS client.
|
||||||
|
func NewCredentialsWithClient(svc AssumeRoler, roleARN string, options ...func(*AssumeRoleProvider)) *credentials.Credentials {
|
||||||
|
p := &AssumeRoleProvider{
|
||||||
|
Client: svc,
|
||||||
|
RoleARN: roleARN,
|
||||||
|
Duration: DefaultDuration,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
option(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return credentials.NewCredentials(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.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 = DefaultDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
ExternalId: p.ExternalID,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return credentials.Value{ProviderName: ProviderName}, 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,
|
||||||
|
ProviderName: ProviderName,
|
||||||
|
}, nil
|
||||||
|
}
|
96
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/defaults/defaults.go
generated
vendored
Normal file
96
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/defaults/defaults.go
generated
vendored
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
// Package defaults is a collection of helpers to retrieve the SDK's default
|
||||||
|
// configuration and handlers.
|
||||||
|
//
|
||||||
|
// Generally this package shouldn't be used directly, but session.Session
|
||||||
|
// instead. This package is useful when you need to reset the defaults
|
||||||
|
// of a session or service client to the SDK defaults before setting
|
||||||
|
// additional parameters.
|
||||||
|
package defaults
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/corehandlers"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/private/endpoints"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Defaults provides a collection of default values for SDK clients.
|
||||||
|
type Defaults struct {
|
||||||
|
Config *aws.Config
|
||||||
|
Handlers request.Handlers
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the SDK's default values with Config and handlers pre-configured.
|
||||||
|
func Get() Defaults {
|
||||||
|
cfg := Config()
|
||||||
|
handlers := Handlers()
|
||||||
|
cfg.Credentials = CredChain(cfg, handlers)
|
||||||
|
|
||||||
|
return Defaults{
|
||||||
|
Config: cfg,
|
||||||
|
Handlers: handlers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config returns the default configuration without credentials.
|
||||||
|
// To retrieve a config with credentials also included use
|
||||||
|
// `defaults.Get().Config` instead.
|
||||||
|
//
|
||||||
|
// Generally you shouldn't need to use this method directly, but
|
||||||
|
// is available if you need to reset the configuration of an
|
||||||
|
// existing service client or session.
|
||||||
|
func Config() *aws.Config {
|
||||||
|
return aws.NewConfig().
|
||||||
|
WithCredentials(credentials.AnonymousCredentials).
|
||||||
|
WithRegion(os.Getenv("AWS_REGION")).
|
||||||
|
WithHTTPClient(http.DefaultClient).
|
||||||
|
WithMaxRetries(aws.UseServiceDefaultRetries).
|
||||||
|
WithLogger(aws.NewDefaultLogger()).
|
||||||
|
WithLogLevel(aws.LogOff).
|
||||||
|
WithSleepDelay(time.Sleep)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handlers returns the default request handlers.
|
||||||
|
//
|
||||||
|
// Generally you shouldn't need to use this method directly, but
|
||||||
|
// is available if you need to reset the request handlers of an
|
||||||
|
// existing service client or session.
|
||||||
|
func Handlers() request.Handlers {
|
||||||
|
var handlers request.Handlers
|
||||||
|
|
||||||
|
handlers.Validate.PushBackNamed(corehandlers.ValidateEndpointHandler)
|
||||||
|
handlers.Build.PushBackNamed(corehandlers.SDKVersionUserAgentHandler)
|
||||||
|
handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
|
||||||
|
handlers.Send.PushBackNamed(corehandlers.SendHandler)
|
||||||
|
handlers.AfterRetry.PushBackNamed(corehandlers.AfterRetryHandler)
|
||||||
|
handlers.ValidateResponse.PushBackNamed(corehandlers.ValidateResponseHandler)
|
||||||
|
|
||||||
|
return handlers
|
||||||
|
}
|
||||||
|
|
||||||
|
// CredChain returns the default credential chain.
|
||||||
|
//
|
||||||
|
// Generally you shouldn't need to use this method directly, but
|
||||||
|
// is available if you need to reset the credentials of an
|
||||||
|
// existing service client or session's Config.
|
||||||
|
func CredChain(cfg *aws.Config, handlers request.Handlers) *credentials.Credentials {
|
||||||
|
endpoint, signingRegion := endpoints.EndpointForRegion(ec2metadata.ServiceName, *cfg.Region, true)
|
||||||
|
|
||||||
|
return credentials.NewCredentials(&credentials.ChainProvider{
|
||||||
|
VerboseErrors: aws.BoolValue(cfg.CredentialsChainVerboseErrors),
|
||||||
|
Providers: []credentials.Provider{
|
||||||
|
&credentials.EnvProvider{},
|
||||||
|
&credentials.SharedCredentialsProvider{Filename: "", Profile: ""},
|
||||||
|
&ec2rolecreds.EC2RoleProvider{
|
||||||
|
Client: ec2metadata.NewClient(*cfg, handlers, endpoint, signingRegion),
|
||||||
|
ExpiryWindow: 5 * time.Minute,
|
||||||
|
},
|
||||||
|
}})
|
||||||
|
}
|
43
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/ec2metadata/api.go
generated
vendored
Normal file
43
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/ec2metadata/api.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package ec2metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetMetadata uses the path provided to request
|
||||||
|
func (c *EC2Metadata) GetMetadata(p string) (string, error) {
|
||||||
|
op := &request.Operation{
|
||||||
|
Name: "GetMetadata",
|
||||||
|
HTTPMethod: "GET",
|
||||||
|
HTTPPath: path.Join("/", "meta-data", p),
|
||||||
|
}
|
||||||
|
|
||||||
|
output := &metadataOutput{}
|
||||||
|
req := c.NewRequest(op, nil, output)
|
||||||
|
|
||||||
|
return output.Content, req.Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Region returns the region the instance is running in.
|
||||||
|
func (c *EC2Metadata) Region() (string, error) {
|
||||||
|
resp, err := c.GetMetadata("placement/availability-zone")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns region without the suffix. Eg: us-west-2a becomes us-west-2
|
||||||
|
return resp[:len(resp)-1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Available returns if the application has access to the EC2 Metadata service.
|
||||||
|
// Can be used to determine if application is running within an EC2 Instance and
|
||||||
|
// the metadata service is available.
|
||||||
|
func (c *EC2Metadata) Available() bool {
|
||||||
|
if _, err := c.GetMetadata("instance-id"); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
117
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/ec2metadata/service.go
generated
vendored
Normal file
117
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/ec2metadata/service.go
generated
vendored
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
// Package ec2metadata provides the client for making API calls to the
|
||||||
|
// EC2 Metadata service.
|
||||||
|
package ec2metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client/metadata"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServiceName is the name of the service.
|
||||||
|
const ServiceName = "ec2metadata"
|
||||||
|
|
||||||
|
// A EC2Metadata is an EC2 Metadata service Client.
|
||||||
|
type EC2Metadata struct {
|
||||||
|
*client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new instance of the EC2Metadata client with a session.
|
||||||
|
// This client is safe to use across multiple goroutines.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// // Create a EC2Metadata client from just a session.
|
||||||
|
// svc := ec2metadata.New(mySession)
|
||||||
|
//
|
||||||
|
// // Create a EC2Metadata client with additional configuration
|
||||||
|
// svc := ec2metadata.New(mySession, aws.NewConfig().WithLogLevel(aws.LogDebugHTTPBody))
|
||||||
|
func New(p client.ConfigProvider, cfgs ...*aws.Config) *EC2Metadata {
|
||||||
|
c := p.ClientConfig(ServiceName, cfgs...)
|
||||||
|
return NewClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns a new EC2Metadata client. Should be used to create
|
||||||
|
// a client when not using a session. Generally using just New with a session
|
||||||
|
// is preferred.
|
||||||
|
//
|
||||||
|
// If an unmodified HTTP client is provided from the stdlib default, or no client
|
||||||
|
// the EC2RoleProvider's EC2Metadata HTTP client's timeout will be shortened.
|
||||||
|
// To disable this set Config.EC2MetadataDisableTimeoutOverride to false. Enabled by default.
|
||||||
|
func NewClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string, opts ...func(*client.Client)) *EC2Metadata {
|
||||||
|
if !aws.BoolValue(cfg.EC2MetadataDisableTimeoutOverride) && httpClientZero(cfg.HTTPClient) {
|
||||||
|
// If the http client is unmodified and this feature is not disabled
|
||||||
|
// set custom timeouts for EC2Metadata requests.
|
||||||
|
cfg.HTTPClient = &http.Client{
|
||||||
|
// use a shorter timeout than default because the metadata
|
||||||
|
// service is local if it is running, and to fail faster
|
||||||
|
// if not running on an ec2 instance.
|
||||||
|
Timeout: 5 * time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svc := &EC2Metadata{
|
||||||
|
Client: client.New(
|
||||||
|
cfg,
|
||||||
|
metadata.ClientInfo{
|
||||||
|
ServiceName: ServiceName,
|
||||||
|
Endpoint: endpoint,
|
||||||
|
APIVersion: "latest",
|
||||||
|
},
|
||||||
|
handlers,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
svc.Handlers.Unmarshal.PushBack(unmarshalHandler)
|
||||||
|
svc.Handlers.UnmarshalError.PushBack(unmarshalError)
|
||||||
|
svc.Handlers.Validate.Clear()
|
||||||
|
svc.Handlers.Validate.PushBack(validateEndpointHandler)
|
||||||
|
|
||||||
|
// Add additional options to the service config
|
||||||
|
for _, option := range opts {
|
||||||
|
option(svc.Client)
|
||||||
|
}
|
||||||
|
|
||||||
|
return svc
|
||||||
|
}
|
||||||
|
|
||||||
|
func httpClientZero(c *http.Client) bool {
|
||||||
|
return c == nil || (c.Transport == nil && c.CheckRedirect == nil && c.Jar == nil && c.Timeout == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
type metadataOutput struct {
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalHandler(r *request.Request) {
|
||||||
|
defer r.HTTPResponse.Body.Close()
|
||||||
|
b, err := ioutil.ReadAll(r.HTTPResponse.Body)
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "unable to unmarshal EC2 metadata respose", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := r.Data.(*metadataOutput)
|
||||||
|
data.Content = string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalError(r *request.Request) {
|
||||||
|
defer r.HTTPResponse.Body.Close()
|
||||||
|
_, err := ioutil.ReadAll(r.HTTPResponse.Body)
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "unable to unmarshal EC2 metadata error respose", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO extract the error...
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateEndpointHandler(r *request.Request) {
|
||||||
|
if r.ClientInfo.Endpoint == "" {
|
||||||
|
r.Error = aws.ErrMissingEndpoint
|
||||||
|
}
|
||||||
|
}
|
17
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/errors.go
generated
vendored
Normal file
17
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import "github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrMissingRegion is an error that is returned if region configuration is
|
||||||
|
// not found.
|
||||||
|
//
|
||||||
|
// @readonly
|
||||||
|
ErrMissingRegion = 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 = awserr.New("MissingEndpoint", "'Endpoint' configuration is required for this service", nil)
|
||||||
|
)
|
98
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/logger.go
generated
vendored
Normal file
98
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/logger.go
generated
vendored
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
// LogDebugWithRequestRetries states the SDK should log when service requests will
|
||||||
|
// be retried. This should be used to log when you want to log when service
|
||||||
|
// requests are being retried. Will also enable LogDebug.
|
||||||
|
LogDebugWithRequestRetries
|
||||||
|
|
||||||
|
// LogDebugWithRequestErrors states the SDK should log when service requests fail
|
||||||
|
// to build, send, validate, or unmarshal.
|
||||||
|
LogDebugWithRequestErrors
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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...)
|
||||||
|
}
|
140
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/handlers.go
generated
vendored
Normal file
140
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/handlers.go
generated
vendored
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
package request
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 []NamedHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
// A NamedHandler is a struct that contains a name and function callback.
|
||||||
|
type NamedHandler struct {
|
||||||
|
Name string
|
||||||
|
Fn func(*Request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy creates a copy of the handler list.
|
||||||
|
func (l *HandlerList) copy() HandlerList {
|
||||||
|
var n HandlerList
|
||||||
|
n.list = append([]NamedHandler{}, l.list...)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear clears the handler list.
|
||||||
|
func (l *HandlerList) Clear() {
|
||||||
|
l.list = []NamedHandler{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of handlers in the list.
|
||||||
|
func (l *HandlerList) Len() int {
|
||||||
|
return len(l.list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushBack pushes handler f to the back of the handler list.
|
||||||
|
func (l *HandlerList) PushBack(f func(*Request)) {
|
||||||
|
l.list = append(l.list, NamedHandler{"__anonymous", f})
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushFront pushes handler f to the front of the handler list.
|
||||||
|
func (l *HandlerList) PushFront(f func(*Request)) {
|
||||||
|
l.list = append([]NamedHandler{{"__anonymous", f}}, l.list...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushBackNamed pushes named handler f to the back of the handler list.
|
||||||
|
func (l *HandlerList) PushBackNamed(n NamedHandler) {
|
||||||
|
l.list = append(l.list, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushFrontNamed pushes named handler f to the front of the handler list.
|
||||||
|
func (l *HandlerList) PushFrontNamed(n NamedHandler) {
|
||||||
|
l.list = append([]NamedHandler{n}, l.list...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes a NamedHandler n
|
||||||
|
func (l *HandlerList) Remove(n NamedHandler) {
|
||||||
|
newlist := []NamedHandler{}
|
||||||
|
for _, m := range l.list {
|
||||||
|
if m.Name != n.Name {
|
||||||
|
newlist = append(newlist, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.list = newlist
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run executes all handlers in the list with a given request object.
|
||||||
|
func (l *HandlerList) Run(r *Request) {
|
||||||
|
for _, f := range l.list {
|
||||||
|
f.Fn(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeAddToUserAgentHandler will add the name/version pair to the User-Agent request
|
||||||
|
// header. If the extra parameters are provided they will be added as metadata to the
|
||||||
|
// name/version pair resulting in the following format.
|
||||||
|
// "name/version (extra0; extra1; ...)"
|
||||||
|
// The user agent part will be concatenated with this current request's user agent string.
|
||||||
|
func MakeAddToUserAgentHandler(name, version string, extra ...string) func(*Request) {
|
||||||
|
ua := fmt.Sprintf("%s/%s", name, version)
|
||||||
|
if len(extra) > 0 {
|
||||||
|
ua += fmt.Sprintf(" (%s)", strings.Join(extra, "; "))
|
||||||
|
}
|
||||||
|
return func(r *Request) {
|
||||||
|
AddToUserAgent(r, ua)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeAddToUserAgentFreeFormHandler adds the input to the User-Agent request header.
|
||||||
|
// The input string will be concatenated with the current request's user agent string.
|
||||||
|
func MakeAddToUserAgentFreeFormHandler(s string) func(*Request) {
|
||||||
|
return func(r *Request) {
|
||||||
|
AddToUserAgent(r, s)
|
||||||
|
}
|
||||||
|
}
|
294
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/request.go
generated
vendored
Normal file
294
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/request.go
generated
vendored
Normal file
|
@ -0,0 +1,294 @@
|
||||||
|
package request
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Request is the service request to be made.
|
||||||
|
type Request struct {
|
||||||
|
Config aws.Config
|
||||||
|
ClientInfo metadata.ClientInfo
|
||||||
|
Handlers Handlers
|
||||||
|
|
||||||
|
Retryer
|
||||||
|
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 int
|
||||||
|
Retryable *bool
|
||||||
|
RetryDelay time.Duration
|
||||||
|
NotHoist bool
|
||||||
|
SignedHeaderVals http.Header
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// New 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 New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
|
||||||
|
retryer Retryer, 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(clientInfo.Endpoint + p)
|
||||||
|
|
||||||
|
r := &Request{
|
||||||
|
Config: cfg,
|
||||||
|
ClientInfo: clientInfo,
|
||||||
|
Handlers: handlers.Copy(),
|
||||||
|
|
||||||
|
Retryer: retryer,
|
||||||
|
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 && aws.BoolValue(r.Retryable) && r.RetryCount < r.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.NotHoist = false
|
||||||
|
r.Sign()
|
||||||
|
if r.Error != nil {
|
||||||
|
return "", r.Error
|
||||||
|
}
|
||||||
|
return r.HTTPRequest.URL.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PresignRequest behaves just like presign, but hoists all headers and signs them.
|
||||||
|
// Also returns the signed hash back to the user
|
||||||
|
func (r *Request) PresignRequest(expireTime time.Duration) (string, http.Header, error) {
|
||||||
|
r.ExpireTime = expireTime
|
||||||
|
r.NotHoist = true
|
||||||
|
r.Sign()
|
||||||
|
if r.Error != nil {
|
||||||
|
return "", nil, r.Error
|
||||||
|
}
|
||||||
|
return r.HTTPRequest.URL.String(), r.SignedHeaderVals, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func debugLogReqError(r *Request, stage string, retrying bool, err error) {
|
||||||
|
if !r.Config.LogLevel.Matches(aws.LogDebugWithRequestErrors) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
retryStr := "not retrying"
|
||||||
|
if retrying {
|
||||||
|
retryStr = "will retry"
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Config.Logger.Log(fmt.Sprintf("DEBUG: %s %s/%s failed, %s, error %v",
|
||||||
|
stage, r.ClientInfo.ServiceName, r.Operation.Name, retryStr, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
debugLogReqError(r, "Validate Request", false, r.Error)
|
||||||
|
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 {
|
||||||
|
debugLogReqError(r, "Build Request", false, r.Error)
|
||||||
|
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 aws.BoolValue(r.Retryable) {
|
||||||
|
if r.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) {
|
||||||
|
r.Config.Logger.Log(fmt.Sprintf("DEBUG: Retrying Request %s/%s, attempt %d",
|
||||||
|
r.ClientInfo.ServiceName, r.Operation.Name, r.RetryCount))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.HTTPRequest.Body = ioutil.NopCloser(r.Body)
|
||||||
|
}
|
||||||
|
r.Retryable = nil
|
||||||
|
|
||||||
|
r.Handlers.Send.Run(r)
|
||||||
|
if r.Error != nil {
|
||||||
|
err := r.Error
|
||||||
|
r.Handlers.Retry.Run(r)
|
||||||
|
r.Handlers.AfterRetry.Run(r)
|
||||||
|
if r.Error != nil {
|
||||||
|
debugLogReqError(r, "Send Request", false, r.Error)
|
||||||
|
return r.Error
|
||||||
|
}
|
||||||
|
debugLogReqError(r, "Send Request", true, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Handlers.UnmarshalMeta.Run(r)
|
||||||
|
r.Handlers.ValidateResponse.Run(r)
|
||||||
|
if r.Error != nil {
|
||||||
|
err := r.Error
|
||||||
|
r.Handlers.UnmarshalError.Run(r)
|
||||||
|
r.Handlers.Retry.Run(r)
|
||||||
|
r.Handlers.AfterRetry.Run(r)
|
||||||
|
if r.Error != nil {
|
||||||
|
debugLogReqError(r, "Validate Response", false, r.Error)
|
||||||
|
return r.Error
|
||||||
|
}
|
||||||
|
debugLogReqError(r, "Validate Response", true, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Handlers.Unmarshal.Run(r)
|
||||||
|
if r.Error != nil {
|
||||||
|
err := r.Error
|
||||||
|
r.Handlers.Retry.Run(r)
|
||||||
|
r.Handlers.AfterRetry.Run(r)
|
||||||
|
if r.Error != nil {
|
||||||
|
debugLogReqError(r, "Unmarshal Response", false, r.Error)
|
||||||
|
return r.Error
|
||||||
|
}
|
||||||
|
debugLogReqError(r, "Unmarshal Response", true, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddToUserAgent adds the string to the end of the request's current user agent.
|
||||||
|
func AddToUserAgent(r *Request, s string) {
|
||||||
|
curUA := r.HTTPRequest.Header.Get("User-Agent")
|
||||||
|
if len(curUA) > 0 {
|
||||||
|
s = curUA + " " + s
|
||||||
|
}
|
||||||
|
r.HTTPRequest.Header.Set("User-Agent", s)
|
||||||
|
}
|
104
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/request_pagination.go
generated
vendored
Normal file
104
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/request_pagination.go
generated
vendored
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
package request
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
//type Paginater interface {
|
||||||
|
// HasNextPage() bool
|
||||||
|
// NextPage() *Request
|
||||||
|
// EachPage(fn func(data interface{}, isLastPage bool) (shouldContinue bool)) error
|
||||||
|
//}
|
||||||
|
|
||||||
|
// HasNextPage returns true if this request has more pages of data available.
|
||||||
|
func (r *Request) HasNextPage() bool {
|
||||||
|
return len(r.nextPageTokens()) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.ValuesAtPath(r.Data, r.Operation.TruncationToken)
|
||||||
|
if len(tr) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := tr[0].(type) {
|
||||||
|
case *bool:
|
||||||
|
if !aws.BoolValue(v) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case bool:
|
||||||
|
if v == false {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens := []interface{}{}
|
||||||
|
tokenAdded := false
|
||||||
|
for _, outToken := range r.Operation.OutputTokens {
|
||||||
|
v, _ := awsutil.ValuesAtPath(r.Data, outToken)
|
||||||
|
if len(v) > 0 {
|
||||||
|
tokens = append(tokens, v[0])
|
||||||
|
tokenAdded = true
|
||||||
|
} else {
|
||||||
|
tokens = append(tokens, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !tokenAdded {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 len(tokens) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
data := reflect.New(reflect.TypeOf(r.Data).Elem()).Interface()
|
||||||
|
nr := New(r.Config, r.ClientInfo, r.Handlers, r.Retryer, r.Operation, awsutil.CopyOf(r.Params), data)
|
||||||
|
for i, intok := range nr.Operation.InputTokens {
|
||||||
|
awsutil.SetValueAtPath(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() {
|
||||||
|
if err := page.Send(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if getNextPage := fn(page.Data, !page.HasNextPage()); !getNextPage {
|
||||||
|
return page.Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
82
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/retryer.go
generated
vendored
Normal file
82
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/retryer.go
generated
vendored
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
package request
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Retryer is an interface to control retry logic for a given service.
|
||||||
|
// The default implementation used by most services is the service.DefaultRetryer
|
||||||
|
// structure, which contains basic retry logic using exponential backoff.
|
||||||
|
type Retryer interface {
|
||||||
|
RetryRules(*Request) time.Duration
|
||||||
|
ShouldRetry(*Request) bool
|
||||||
|
MaxRetries() int
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithRetryer sets a config Retryer value to the given Config returning it
|
||||||
|
// for chaining.
|
||||||
|
func WithRetryer(cfg *aws.Config, retryer Retryer) *aws.Config {
|
||||||
|
cfg.Retryer = retryer
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// retryableCodes is a collection of service response codes which are retry-able
|
||||||
|
// without any further action.
|
||||||
|
var retryableCodes = map[string]struct{}{
|
||||||
|
"RequestError": {},
|
||||||
|
"RequestTimeout": {},
|
||||||
|
"ProvisionedThroughputExceededException": {},
|
||||||
|
"Throttling": {},
|
||||||
|
"ThrottlingException": {},
|
||||||
|
"RequestLimitExceeded": {},
|
||||||
|
"RequestThrottled": {},
|
||||||
|
"LimitExceededException": {}, // Deleting 10+ DynamoDb tables at once
|
||||||
|
"TooManyRequestsException": {}, // Lambda functions
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrorRetryable returns whether the error is retryable, based on its Code.
|
||||||
|
// Returns false if the request has no Error set.
|
||||||
|
func (r *Request) IsErrorRetryable() bool {
|
||||||
|
if r.Error != nil {
|
||||||
|
if err, ok := r.Error.(awserr.Error); ok {
|
||||||
|
return isCodeRetryable(err.Code())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrorExpired returns whether the error code is a credential expiry error.
|
||||||
|
// Returns false if the request has no Error set.
|
||||||
|
func (r *Request) IsErrorExpired() bool {
|
||||||
|
if r.Error != nil {
|
||||||
|
if err, ok := r.Error.(awserr.Error); ok {
|
||||||
|
return isCodeExpiredCreds(err.Code())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
120
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/session/session.go
generated
vendored
Normal file
120
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/session/session.go
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
// Package session provides a way to create service clients with shared configuration
|
||||||
|
// and handlers.
|
||||||
|
//
|
||||||
|
// Generally this package should be used instead of the `defaults` package.
|
||||||
|
//
|
||||||
|
// A session should be used to share configurations and request handlers between multiple
|
||||||
|
// service clients. When service clients need specific configuration aws.Config can be
|
||||||
|
// used to provide additional configuration directly to the service client.
|
||||||
|
package session
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/corehandlers"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/defaults"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/private/endpoints"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Session provides a central location to create service clients from and
|
||||||
|
// store configurations and request handlers for those services.
|
||||||
|
//
|
||||||
|
// Sessions are safe to create service clients concurrently, but it is not safe
|
||||||
|
// to mutate the session concurrently.
|
||||||
|
type Session struct {
|
||||||
|
Config *aws.Config
|
||||||
|
Handlers request.Handlers
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new instance of the handlers merging in the provided Configs
|
||||||
|
// on top of the SDK's default configurations. Once the session is created it
|
||||||
|
// can be mutated to modify Configs or Handlers. The session is safe to be read
|
||||||
|
// concurrently, but it should not be written to concurrently.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// // Create a session with the default config and request handlers.
|
||||||
|
// sess := session.New()
|
||||||
|
//
|
||||||
|
// // Create a session with a custom region
|
||||||
|
// sess := session.New(&aws.Config{Region: aws.String("us-east-1")})
|
||||||
|
//
|
||||||
|
// // Create a session, and add additional handlers for all service
|
||||||
|
// // clients created with the session to inherit. Adds logging handler.
|
||||||
|
// sess := session.New()
|
||||||
|
// sess.Handlers.Send.PushFront(func(r *request.Request) {
|
||||||
|
// // Log every request made and its payload
|
||||||
|
// logger.Println("Request: %s/%s, Payload: %s", r.ClientInfo.ServiceName, r.Operation, r.Params)
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// // Create a S3 client instance from a session
|
||||||
|
// sess := session.New()
|
||||||
|
// svc := s3.New(sess)
|
||||||
|
func New(cfgs ...*aws.Config) *Session {
|
||||||
|
cfg := defaults.Config()
|
||||||
|
handlers := defaults.Handlers()
|
||||||
|
|
||||||
|
// Apply the passed in configs so the configuration can be applied to the
|
||||||
|
// default credential chain
|
||||||
|
cfg.MergeIn(cfgs...)
|
||||||
|
cfg.Credentials = defaults.CredChain(cfg, handlers)
|
||||||
|
|
||||||
|
// Reapply any passed in configs to override credentials if set
|
||||||
|
cfg.MergeIn(cfgs...)
|
||||||
|
|
||||||
|
s := &Session{
|
||||||
|
Config: cfg,
|
||||||
|
Handlers: handlers,
|
||||||
|
}
|
||||||
|
|
||||||
|
initHandlers(s)
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func initHandlers(s *Session) {
|
||||||
|
// Add the Validate parameter handler if it is not disabled.
|
||||||
|
s.Handlers.Validate.Remove(corehandlers.ValidateParametersHandler)
|
||||||
|
if !aws.BoolValue(s.Config.DisableParamValidation) {
|
||||||
|
s.Handlers.Validate.PushBackNamed(corehandlers.ValidateParametersHandler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy creates and returns a copy of the current session, coping the config
|
||||||
|
// and handlers. If any additional configs are provided they will be merged
|
||||||
|
// on top of the session's copied config.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// // Create a copy of the current session, configured for the us-west-2 region.
|
||||||
|
// sess.Copy(&aws.Config{Region: aws.String("us-west-2"})
|
||||||
|
func (s *Session) Copy(cfgs ...*aws.Config) *Session {
|
||||||
|
newSession := &Session{
|
||||||
|
Config: s.Config.Copy(cfgs...),
|
||||||
|
Handlers: s.Handlers.Copy(),
|
||||||
|
}
|
||||||
|
|
||||||
|
initHandlers(newSession)
|
||||||
|
|
||||||
|
return newSession
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientConfig satisfies the client.ConfigProvider interface and is used to
|
||||||
|
// configure the service client instances. Passing the Session to the service
|
||||||
|
// client's constructor (New) will use this method to configure the client.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// sess := session.New()
|
||||||
|
// s3.New(sess)
|
||||||
|
func (s *Session) ClientConfig(serviceName string, cfgs ...*aws.Config) client.Config {
|
||||||
|
s = s.Copy(cfgs...)
|
||||||
|
endpoint, signingRegion := endpoints.NormalizeEndpoint(
|
||||||
|
aws.StringValue(s.Config.Endpoint), serviceName,
|
||||||
|
aws.StringValue(s.Config.Region), aws.BoolValue(s.Config.DisableSSL))
|
||||||
|
|
||||||
|
return client.Config{
|
||||||
|
Config: s.Config,
|
||||||
|
Handlers: s.Handlers,
|
||||||
|
Endpoint: endpoint,
|
||||||
|
SigningRegion: signingRegion,
|
||||||
|
}
|
||||||
|
}
|
88
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/types.go
generated
vendored
Normal file
88
Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/types.go
generated
vendored
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReadSeekCloser wraps a io.Reader returning a ReaderSeekerCloser
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// A WriteAtBuffer provides a in memory buffer supporting the io.WriterAt interface
|
||||||
|
// Can be used with the s3manager.Downloader to download content to a buffer
|
||||||
|
// in memory. Safe to use concurrently.
|
||||||
|
type WriteAtBuffer struct {
|
||||||
|
buf []byte
|
||||||
|
m sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteAt writes a slice of bytes to a buffer starting at the position provided
|
||||||
|
// The number of bytes written will be returned, or error. Can overwrite previous
|
||||||
|
// written slices if the write ats overlap.
|
||||||
|
func (b *WriteAtBuffer) WriteAt(p []byte, pos int64) (n int, err error) {
|
||||||
|
b.m.Lock()
|
||||||
|
defer b.m.Unlock()
|
||||||
|
|
||||||
|
expLen := pos + int64(len(p))
|
||||||
|
if int64(len(b.buf)) < expLen {
|
||||||
|
newBuf := make([]byte, expLen)
|
||||||
|
copy(newBuf, b.buf)
|
||||||
|
b.buf = newBuf
|
||||||
|
}
|
||||||
|
copy(b.buf[pos:], p)
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns a slice of bytes written to the buffer.
|
||||||
|
func (b *WriteAtBuffer) Bytes() []byte {
|
||||||
|
b.m.Lock()
|
||||||
|
defer b.m.Unlock()
|
||||||
|
return b.buf[:len(b.buf):len(b.buf)]
|
||||||
|
}
|
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 = "1.1.0"
|
65
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/endpoints/endpoints.go
generated
vendored
Normal file
65
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/endpoints/endpoints.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
// 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 (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NormalizeEndpoint takes and endpoint and service API information to return a
|
||||||
|
// normalized endpoint and signing region. If the endpoint is not an empty string
|
||||||
|
// the service name and region will be used to look up the service's API endpoint.
|
||||||
|
// If the endpoint is provided the scheme will be added if it is not present.
|
||||||
|
func NormalizeEndpoint(endpoint, serviceName, region string, disableSSL bool) (normEndpoint, signingRegion string) {
|
||||||
|
if endpoint == "" {
|
||||||
|
return EndpointForRegion(serviceName, region, disableSSL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return AddScheme(endpoint, disableSSL), ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, disableSSL bool) (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
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AddScheme(endpoint, disableSSL), signingRegion
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regular expression to determine if the endpoint string is prefixed with a scheme.
|
||||||
|
var schemeRE = regexp.MustCompile("^([^:]+)://")
|
||||||
|
|
||||||
|
// AddScheme adds the HTTP or HTTPS schemes to a endpoint URL if there is no
|
||||||
|
// scheme. If disableSSL is true HTTP will be added instead of the default HTTPS.
|
||||||
|
func AddScheme(endpoint string, disableSSL bool) string {
|
||||||
|
if endpoint != "" && !schemeRE.MatchString(endpoint) {
|
||||||
|
scheme := "https"
|
||||||
|
if disableSSL {
|
||||||
|
scheme = "http"
|
||||||
|
}
|
||||||
|
endpoint = fmt.Sprintf("%s://%s", scheme, endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
return endpoint
|
||||||
|
}
|
92
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/endpoints/endpoints.json
generated
vendored
Normal file
92
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/endpoints/endpoints.json
generated
vendored
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"*/data.iot": {
|
||||||
|
"endpoint": "",
|
||||||
|
"signingRegion": "us-east-1"
|
||||||
|
},
|
||||||
|
"*/ec2metadata": {
|
||||||
|
"endpoint": "http://169.254.169.254/latest",
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"*/waf": {
|
||||||
|
"endpoint": "waf.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"
|
||||||
|
},
|
||||||
|
"ap-northeast-2/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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
104
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/endpoints/endpoints_map.go
generated
vendored
Normal file
104
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/endpoints/endpoints_map.go
generated
vendored
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
"*/data.iot": {
|
||||||
|
Endpoint: "",
|
||||||
|
SigningRegion: "us-east-1",
|
||||||
|
},
|
||||||
|
"*/ec2metadata": {
|
||||||
|
Endpoint: "http://169.254.169.254/latest",
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
"*/waf": {
|
||||||
|
Endpoint: "waf.amazonaws.com",
|
||||||
|
SigningRegion: "us-east-1",
|
||||||
|
},
|
||||||
|
"ap-northeast-1/s3": {
|
||||||
|
Endpoint: "s3-{region}.amazonaws.com",
|
||||||
|
},
|
||||||
|
"ap-northeast-2/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",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
35
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/ec2query/build.go
generated
vendored
Normal file
35
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/ec2query/build.go
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
// Package ec2query provides serialisation of AWS EC2 requests and responses.
|
||||||
|
package ec2query
|
||||||
|
|
||||||
|
//go:generate go run ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/input/ec2.json build_test.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol/query/queryutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuildHandler is a named request handler for building ec2query protocol requests
|
||||||
|
var BuildHandler = request.NamedHandler{Name: "awssdk.ec2query.Build", Fn: Build}
|
||||||
|
|
||||||
|
// Build builds a request for the EC2 protocol.
|
||||||
|
func Build(r *request.Request) {
|
||||||
|
body := url.Values{
|
||||||
|
"Action": {r.Operation.Name},
|
||||||
|
"Version": {r.ClientInfo.APIVersion},
|
||||||
|
}
|
||||||
|
if err := queryutil.Parse(body, r.Params, true); err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed encoding EC2 Query request", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
63
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/ec2query/unmarshal.go
generated
vendored
Normal file
63
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/ec2query/unmarshal.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package ec2query
|
||||||
|
|
||||||
|
//go:generate go run ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/output/ec2.json unmarshal_test.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnmarshalHandler is a named request handler for unmarshaling ec2query protocol requests
|
||||||
|
var UnmarshalHandler = request.NamedHandler{Name: "awssdk.ec2query.Unmarshal", Fn: Unmarshal}
|
||||||
|
|
||||||
|
// UnmarshalMetaHandler is a named request handler for unmarshaling ec2query protocol request metadata
|
||||||
|
var UnmarshalMetaHandler = request.NamedHandler{Name: "awssdk.ec2query.UnmarshalMeta", Fn: UnmarshalMeta}
|
||||||
|
|
||||||
|
// UnmarshalErrorHandler is a named request handler for unmarshaling ec2query protocol request errors
|
||||||
|
var UnmarshalErrorHandler = request.NamedHandler{Name: "awssdk.ec2query.UnmarshalError", Fn: UnmarshalError}
|
||||||
|
|
||||||
|
// Unmarshal unmarshals a response body for the EC2 protocol.
|
||||||
|
func Unmarshal(r *request.Request) {
|
||||||
|
defer r.HTTPResponse.Body.Close()
|
||||||
|
if r.DataFilled() {
|
||||||
|
decoder := xml.NewDecoder(r.HTTPResponse.Body)
|
||||||
|
err := xmlutil.UnmarshalXML(r.Data, decoder, "")
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed decoding EC2 Query response", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalMeta unmarshals response headers for the EC2 protocol.
|
||||||
|
func UnmarshalMeta(r *request.Request) {
|
||||||
|
// TODO implement unmarshaling of request IDs
|
||||||
|
}
|
||||||
|
|
||||||
|
type xmlErrorResponse struct {
|
||||||
|
XMLName xml.Name `xml:"Response"`
|
||||||
|
Code string `xml:"Errors>Error>Code"`
|
||||||
|
Message string `xml:"Errors>Error>Message"`
|
||||||
|
RequestID string `xml:"RequestId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalError unmarshals a response error for the EC2 protocol.
|
||||||
|
func UnmarshalError(r *request.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 decoding EC2 Query error response", err)
|
||||||
|
} else {
|
||||||
|
r.Error = awserr.NewRequestFailure(
|
||||||
|
awserr.New(resp.Code, resp.Message, nil),
|
||||||
|
r.HTTPResponse.StatusCode,
|
||||||
|
resp.RequestID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
75
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/idempotency.go
generated
vendored
Normal file
75
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/idempotency.go
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RandReader is the random reader the protocol package will use to read
|
||||||
|
// random bytes from. This is exported for testing, and should not be used.
|
||||||
|
var RandReader = rand.Reader
|
||||||
|
|
||||||
|
const idempotencyTokenFillTag = `idempotencyToken`
|
||||||
|
|
||||||
|
// CanSetIdempotencyToken returns true if the struct field should be
|
||||||
|
// automatically populated with a Idempotency token.
|
||||||
|
//
|
||||||
|
// Only *string and string type fields that are tagged with idempotencyToken
|
||||||
|
// which are not already set can be auto filled.
|
||||||
|
func CanSetIdempotencyToken(v reflect.Value, f reflect.StructField) bool {
|
||||||
|
switch u := v.Interface().(type) {
|
||||||
|
// To auto fill an Idempotency token the field must be a string,
|
||||||
|
// tagged for auto fill, and have a zero value.
|
||||||
|
case *string:
|
||||||
|
return u == nil && len(f.Tag.Get(idempotencyTokenFillTag)) != 0
|
||||||
|
case string:
|
||||||
|
return len(u) == 0 && len(f.Tag.Get(idempotencyTokenFillTag)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIdempotencyToken returns a randomly generated idempotency token.
|
||||||
|
func GetIdempotencyToken() string {
|
||||||
|
b := make([]byte, 16)
|
||||||
|
RandReader.Read(b)
|
||||||
|
|
||||||
|
return UUIDVersion4(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIdempotencyToken will set the value provided with a Idempotency Token.
|
||||||
|
// Given that the value can be set. Will panic if value is not setable.
|
||||||
|
func SetIdempotencyToken(v reflect.Value) {
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
if v.IsNil() && v.CanSet() {
|
||||||
|
v.Set(reflect.New(v.Type().Elem()))
|
||||||
|
}
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
v = reflect.Indirect(v)
|
||||||
|
|
||||||
|
if !v.CanSet() {
|
||||||
|
panic(fmt.Sprintf("unable to set idempotnecy token %v", v))
|
||||||
|
}
|
||||||
|
|
||||||
|
b := make([]byte, 16)
|
||||||
|
_, err := rand.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
// TODO handle error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Set(reflect.ValueOf(UUIDVersion4(b)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDVersion4 returns a Version 4 random UUID from the byte slice provided
|
||||||
|
func UUIDVersion4(u []byte) string {
|
||||||
|
// https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29
|
||||||
|
// 13th character is "4"
|
||||||
|
u[6] = (u[6] | 0x40) & 0x4F
|
||||||
|
// 17th character is "8", "9", "a", or "b"
|
||||||
|
u[8] = (u[8] | 0x80) & 0xBF
|
||||||
|
|
||||||
|
return fmt.Sprintf(`%X-%X-%X-%X-%X`, u[0:4], u[4:6], u[6:8], u[8:10], u[10:])
|
||||||
|
}
|
251
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/build.go
generated
vendored
Normal file
251
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/build.go
generated
vendored
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
// Package jsonutil provides JSON serialisation of AWS requests and responses.
|
||||||
|
package jsonutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
var timeType = reflect.ValueOf(time.Time{}).Type()
|
||||||
|
var byteSliceType = reflect.ValueOf([]byte{}).Type()
|
||||||
|
|
||||||
|
// BuildJSON builds a JSON string for a given object v.
|
||||||
|
func BuildJSON(v interface{}) ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
err := buildAny(reflect.ValueOf(v), &buf, "")
|
||||||
|
return buf.Bytes(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildAny(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
|
||||||
|
value = reflect.Indirect(value)
|
||||||
|
if !value.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
vtype := value.Type()
|
||||||
|
|
||||||
|
t := tag.Get("type")
|
||||||
|
if t == "" {
|
||||||
|
switch vtype.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
// also it can't be a time object
|
||||||
|
if value.Type() != timeType {
|
||||||
|
t = "structure"
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
// also it can't be a byte slice
|
||||||
|
if _, ok := value.Interface().([]byte); !ok {
|
||||||
|
t = "list"
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
t = "map"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t {
|
||||||
|
case "structure":
|
||||||
|
if field, ok := vtype.FieldByName("_"); ok {
|
||||||
|
tag = field.Tag
|
||||||
|
}
|
||||||
|
return buildStruct(value, buf, tag)
|
||||||
|
case "list":
|
||||||
|
return buildList(value, buf, tag)
|
||||||
|
case "map":
|
||||||
|
return buildMap(value, buf, tag)
|
||||||
|
default:
|
||||||
|
return buildScalar(value, buf, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildStruct(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
|
||||||
|
if !value.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteByte('{')
|
||||||
|
|
||||||
|
t := value.Type()
|
||||||
|
first := true
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
member := value.Field(i)
|
||||||
|
field := t.Field(i)
|
||||||
|
|
||||||
|
if field.PkgPath != "" {
|
||||||
|
continue // ignore unexported fields
|
||||||
|
}
|
||||||
|
if field.Tag.Get("json") == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if field.Tag.Get("location") != "" {
|
||||||
|
continue // ignore non-body elements
|
||||||
|
}
|
||||||
|
|
||||||
|
if protocol.CanSetIdempotencyToken(member, field) {
|
||||||
|
token := protocol.GetIdempotencyToken()
|
||||||
|
member = reflect.ValueOf(&token)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (member.Kind() == reflect.Ptr || member.Kind() == reflect.Slice || member.Kind() == reflect.Map) && member.IsNil() {
|
||||||
|
continue // ignore unset fields
|
||||||
|
}
|
||||||
|
|
||||||
|
if first {
|
||||||
|
first = false
|
||||||
|
} else {
|
||||||
|
buf.WriteByte(',')
|
||||||
|
}
|
||||||
|
|
||||||
|
// figure out what this field is called
|
||||||
|
name := field.Name
|
||||||
|
if locName := field.Tag.Get("locationName"); locName != "" {
|
||||||
|
name = locName
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(buf, "%q:", name)
|
||||||
|
|
||||||
|
err := buildAny(member, buf, field.Tag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString("}")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildList(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
|
||||||
|
buf.WriteString("[")
|
||||||
|
|
||||||
|
for i := 0; i < value.Len(); i++ {
|
||||||
|
buildAny(value.Index(i), buf, "")
|
||||||
|
|
||||||
|
if i < value.Len()-1 {
|
||||||
|
buf.WriteString(",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString("]")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type sortedValues []reflect.Value
|
||||||
|
|
||||||
|
func (sv sortedValues) Len() int { return len(sv) }
|
||||||
|
func (sv sortedValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] }
|
||||||
|
func (sv sortedValues) Less(i, j int) bool { return sv[i].String() < sv[j].String() }
|
||||||
|
|
||||||
|
func buildMap(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
|
||||||
|
buf.WriteString("{")
|
||||||
|
|
||||||
|
var sv sortedValues = value.MapKeys()
|
||||||
|
sort.Sort(sv)
|
||||||
|
|
||||||
|
for i, k := range sv {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteByte(',')
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(buf, "%q:", k)
|
||||||
|
buildAny(value.MapIndex(k), buf, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString("}")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildScalar(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
|
||||||
|
switch value.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
writeString(value.String(), buf)
|
||||||
|
case reflect.Bool:
|
||||||
|
buf.WriteString(strconv.FormatBool(value.Bool()))
|
||||||
|
case reflect.Int64:
|
||||||
|
buf.WriteString(strconv.FormatInt(value.Int(), 10))
|
||||||
|
case reflect.Float64:
|
||||||
|
buf.WriteString(strconv.FormatFloat(value.Float(), 'f', -1, 64))
|
||||||
|
default:
|
||||||
|
switch value.Type() {
|
||||||
|
case timeType:
|
||||||
|
converted := value.Interface().(time.Time)
|
||||||
|
buf.WriteString(strconv.FormatInt(converted.UTC().Unix(), 10))
|
||||||
|
case byteSliceType:
|
||||||
|
if !value.IsNil() {
|
||||||
|
converted := value.Interface().([]byte)
|
||||||
|
buf.WriteByte('"')
|
||||||
|
if len(converted) < 1024 {
|
||||||
|
// for small buffers, using Encode directly is much faster.
|
||||||
|
dst := make([]byte, base64.StdEncoding.EncodedLen(len(converted)))
|
||||||
|
base64.StdEncoding.Encode(dst, converted)
|
||||||
|
buf.Write(dst)
|
||||||
|
} else {
|
||||||
|
// for large buffers, avoid unnecessary extra temporary
|
||||||
|
// buffer space.
|
||||||
|
enc := base64.NewEncoder(base64.StdEncoding, buf)
|
||||||
|
enc.Write(converted)
|
||||||
|
enc.Close()
|
||||||
|
}
|
||||||
|
buf.WriteByte('"')
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported JSON value %v (%s)", value.Interface(), value.Type())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeString(s string, buf *bytes.Buffer) {
|
||||||
|
buf.WriteByte('"')
|
||||||
|
for _, r := range s {
|
||||||
|
if r == '"' {
|
||||||
|
buf.WriteString(`\"`)
|
||||||
|
} else if r == '\\' {
|
||||||
|
buf.WriteString(`\\`)
|
||||||
|
} else if r == '\b' {
|
||||||
|
buf.WriteString(`\b`)
|
||||||
|
} else if r == '\f' {
|
||||||
|
buf.WriteString(`\f`)
|
||||||
|
} else if r == '\r' {
|
||||||
|
buf.WriteString(`\r`)
|
||||||
|
} else if r == '\t' {
|
||||||
|
buf.WriteString(`\t`)
|
||||||
|
} else if r == '\n' {
|
||||||
|
buf.WriteString(`\n`)
|
||||||
|
} else if r < 32 {
|
||||||
|
fmt.Fprintf(buf, "\\u%0.4x", r)
|
||||||
|
} else {
|
||||||
|
buf.WriteRune(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteByte('"')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
213
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/unmarshal.go
generated
vendored
Normal file
213
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/unmarshal.go
generated
vendored
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
package jsonutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnmarshalJSON reads a stream and unmarshals the results in object v.
|
||||||
|
func UnmarshalJSON(v interface{}, stream io.Reader) error {
|
||||||
|
var out interface{}
|
||||||
|
|
||||||
|
b, err := ioutil.ReadAll(stream)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(b) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(b, &out); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return unmarshalAny(reflect.ValueOf(v), out, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalAny(value reflect.Value, data interface{}, tag reflect.StructTag) error {
|
||||||
|
vtype := value.Type()
|
||||||
|
if vtype.Kind() == reflect.Ptr {
|
||||||
|
vtype = vtype.Elem() // check kind of actual element type
|
||||||
|
}
|
||||||
|
|
||||||
|
t := tag.Get("type")
|
||||||
|
if t == "" {
|
||||||
|
switch vtype.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
// also it can't be a time object
|
||||||
|
if _, ok := value.Interface().(*time.Time); !ok {
|
||||||
|
t = "structure"
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
// also it can't be a byte slice
|
||||||
|
if _, ok := value.Interface().([]byte); !ok {
|
||||||
|
t = "list"
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
t = "map"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t {
|
||||||
|
case "structure":
|
||||||
|
if field, ok := vtype.FieldByName("_"); ok {
|
||||||
|
tag = field.Tag
|
||||||
|
}
|
||||||
|
return unmarshalStruct(value, data, tag)
|
||||||
|
case "list":
|
||||||
|
return unmarshalList(value, data, tag)
|
||||||
|
case "map":
|
||||||
|
return unmarshalMap(value, data, tag)
|
||||||
|
default:
|
||||||
|
return unmarshalScalar(value, data, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalStruct(value reflect.Value, data interface{}, tag reflect.StructTag) error {
|
||||||
|
if data == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
mapData, ok := data.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("JSON value is not a structure (%#v)", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
t := value.Type()
|
||||||
|
if value.Kind() == reflect.Ptr {
|
||||||
|
if value.IsNil() { // create the structure if it's nil
|
||||||
|
s := reflect.New(value.Type().Elem())
|
||||||
|
value.Set(s)
|
||||||
|
value = s
|
||||||
|
}
|
||||||
|
|
||||||
|
value = value.Elem()
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
// unwrap any payloads
|
||||||
|
if payload := tag.Get("payload"); payload != "" {
|
||||||
|
field, _ := t.FieldByName(payload)
|
||||||
|
return unmarshalAny(value.FieldByName(payload), data, field.Tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
if field.PkgPath != "" {
|
||||||
|
continue // ignore unexported fields
|
||||||
|
}
|
||||||
|
|
||||||
|
// figure out what this field is called
|
||||||
|
name := field.Name
|
||||||
|
if locName := field.Tag.Get("locationName"); locName != "" {
|
||||||
|
name = locName
|
||||||
|
}
|
||||||
|
|
||||||
|
member := value.FieldByIndex(field.Index)
|
||||||
|
err := unmarshalAny(member, mapData[name], field.Tag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalList(value reflect.Value, data interface{}, tag reflect.StructTag) error {
|
||||||
|
if data == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
listData, ok := data.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("JSON value is not a list (%#v)", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.IsNil() {
|
||||||
|
l := len(listData)
|
||||||
|
value.Set(reflect.MakeSlice(value.Type(), l, l))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range listData {
|
||||||
|
err := unmarshalAny(value.Index(i), c, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalMap(value reflect.Value, data interface{}, tag reflect.StructTag) error {
|
||||||
|
if data == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
mapData, ok := data.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("JSON value is not a map (%#v)", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.IsNil() {
|
||||||
|
value.Set(reflect.MakeMap(value.Type()))
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range mapData {
|
||||||
|
kvalue := reflect.ValueOf(k)
|
||||||
|
vvalue := reflect.New(value.Type().Elem()).Elem()
|
||||||
|
|
||||||
|
unmarshalAny(vvalue, v, "")
|
||||||
|
value.SetMapIndex(kvalue, vvalue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTag) error {
|
||||||
|
errf := func() error {
|
||||||
|
return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch d := data.(type) {
|
||||||
|
case nil:
|
||||||
|
return nil // nothing to do here
|
||||||
|
case string:
|
||||||
|
switch value.Interface().(type) {
|
||||||
|
case *string:
|
||||||
|
value.Set(reflect.ValueOf(&d))
|
||||||
|
case []byte:
|
||||||
|
b, err := base64.StdEncoding.DecodeString(d)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
value.Set(reflect.ValueOf(b))
|
||||||
|
default:
|
||||||
|
return errf()
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
switch value.Interface().(type) {
|
||||||
|
case *int64:
|
||||||
|
di := int64(d)
|
||||||
|
value.Set(reflect.ValueOf(&di))
|
||||||
|
case *float64:
|
||||||
|
value.Set(reflect.ValueOf(&d))
|
||||||
|
case *time.Time:
|
||||||
|
t := time.Unix(int64(d), 0).UTC()
|
||||||
|
value.Set(reflect.ValueOf(&t))
|
||||||
|
default:
|
||||||
|
return errf()
|
||||||
|
}
|
||||||
|
case bool:
|
||||||
|
switch value.Interface().(type) {
|
||||||
|
case *bool:
|
||||||
|
value.Set(reflect.ValueOf(&d))
|
||||||
|
default:
|
||||||
|
return errf()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported JSON value (%v)", data)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
111
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/jsonrpc/jsonrpc.go
generated
vendored
Normal file
111
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/jsonrpc/jsonrpc.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
// Package jsonrpc provides JSON RPC utilities for serialisation of AWS
|
||||||
|
// requests and responses.
|
||||||
|
package jsonrpc
|
||||||
|
|
||||||
|
//go:generate go run ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/input/json.json build_test.go
|
||||||
|
//go:generate go run ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/output/json.json unmarshal_test.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol/json/jsonutil"
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
var emptyJSON = []byte("{}")
|
||||||
|
|
||||||
|
// BuildHandler is a named request handler for building jsonrpc protocol requests
|
||||||
|
var BuildHandler = request.NamedHandler{Name: "awssdk.jsonrpc.Build", Fn: Build}
|
||||||
|
|
||||||
|
// UnmarshalHandler is a named request handler for unmarshaling jsonrpc protocol requests
|
||||||
|
var UnmarshalHandler = request.NamedHandler{Name: "awssdk.jsonrpc.Unmarshal", Fn: Unmarshal}
|
||||||
|
|
||||||
|
// UnmarshalMetaHandler is a named request handler for unmarshaling jsonrpc protocol request metadata
|
||||||
|
var UnmarshalMetaHandler = request.NamedHandler{Name: "awssdk.jsonrpc.UnmarshalMeta", Fn: UnmarshalMeta}
|
||||||
|
|
||||||
|
// UnmarshalErrorHandler is a named request handler for unmarshaling jsonrpc protocol request errors
|
||||||
|
var UnmarshalErrorHandler = request.NamedHandler{Name: "awssdk.jsonrpc.UnmarshalError", Fn: UnmarshalError}
|
||||||
|
|
||||||
|
// Build builds a JSON payload for a JSON RPC request.
|
||||||
|
func Build(req *request.Request) {
|
||||||
|
var buf []byte
|
||||||
|
var err error
|
||||||
|
if req.ParamsFilled() {
|
||||||
|
buf, err = jsonutil.BuildJSON(req.Params)
|
||||||
|
if err != nil {
|
||||||
|
req.Error = awserr.New("SerializationError", "failed encoding JSON RPC request", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buf = emptyJSON
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.ClientInfo.TargetPrefix != "" || string(buf) != "{}" {
|
||||||
|
req.SetBufferBody(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.ClientInfo.TargetPrefix != "" {
|
||||||
|
target := req.ClientInfo.TargetPrefix + "." + req.Operation.Name
|
||||||
|
req.HTTPRequest.Header.Add("X-Amz-Target", target)
|
||||||
|
}
|
||||||
|
if req.ClientInfo.JSONVersion != "" {
|
||||||
|
jsonVersion := req.ClientInfo.JSONVersion
|
||||||
|
req.HTTPRequest.Header.Add("Content-Type", "application/x-amz-json-"+jsonVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal unmarshals a response for a JSON RPC service.
|
||||||
|
func Unmarshal(req *request.Request) {
|
||||||
|
defer req.HTTPResponse.Body.Close()
|
||||||
|
if req.DataFilled() {
|
||||||
|
err := jsonutil.UnmarshalJSON(req.Data, req.HTTPResponse.Body)
|
||||||
|
if err != nil {
|
||||||
|
req.Error = awserr.New("SerializationError", "failed decoding JSON RPC response", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalMeta unmarshals headers from a response for a JSON RPC service.
|
||||||
|
func UnmarshalMeta(req *request.Request) {
|
||||||
|
rest.UnmarshalMeta(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalError unmarshals an error response for a JSON RPC service.
|
||||||
|
func UnmarshalError(req *request.Request) {
|
||||||
|
defer req.HTTPResponse.Body.Close()
|
||||||
|
bodyBytes, err := ioutil.ReadAll(req.HTTPResponse.Body)
|
||||||
|
if err != nil {
|
||||||
|
req.Error = awserr.New("SerializationError", "failed reading JSON RPC error response", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(bodyBytes) == 0 {
|
||||||
|
req.Error = awserr.NewRequestFailure(
|
||||||
|
awserr.New("SerializationError", req.HTTPResponse.Status, nil),
|
||||||
|
req.HTTPResponse.StatusCode,
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var jsonErr jsonErrorResponse
|
||||||
|
if err := json.Unmarshal(bodyBytes, &jsonErr); err != nil {
|
||||||
|
req.Error = awserr.New("SerializationError", "failed decoding JSON RPC error response", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
codes := strings.SplitN(jsonErr.Code, "#", 2)
|
||||||
|
req.Error = awserr.NewRequestFailure(
|
||||||
|
awserr.New(codes[len(codes)-1], jsonErr.Message, nil),
|
||||||
|
req.HTTPResponse.StatusCode,
|
||||||
|
req.RequestID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type jsonErrorResponse struct {
|
||||||
|
Code string `json:"__type"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
36
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/query/build.go
generated
vendored
Normal file
36
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/query/build.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// Package query provides serialisation of AWS query requests, and responses.
|
||||||
|
package query
|
||||||
|
|
||||||
|
//go:generate go run ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/input/query.json build_test.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol/query/queryutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuildHandler is a named request handler for building query protocol requests
|
||||||
|
var BuildHandler = request.NamedHandler{Name: "awssdk.query.Build", Fn: Build}
|
||||||
|
|
||||||
|
// Build builds a request for an AWS Query service.
|
||||||
|
func Build(r *request.Request) {
|
||||||
|
body := url.Values{
|
||||||
|
"Action": {r.Operation.Name},
|
||||||
|
"Version": {r.ClientInfo.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()
|
||||||
|
}
|
||||||
|
}
|
230
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/query/queryutil/queryutil.go
generated
vendored
Normal file
230
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/query/queryutil/queryutil.go
generated
vendored
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
package queryutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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++ {
|
||||||
|
elemValue := elemOf(value.Field(i))
|
||||||
|
field := t.Field(i)
|
||||||
|
|
||||||
|
if field.PkgPath != "" {
|
||||||
|
continue // ignore unexported fields
|
||||||
|
}
|
||||||
|
|
||||||
|
if protocol.CanSetIdempotencyToken(value.Field(i), field) {
|
||||||
|
token := protocol.GetIdempotencyToken()
|
||||||
|
elemValue = reflect.ValueOf(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
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, elemValue, 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
|
||||||
|
}
|
35
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/query/unmarshal.go
generated
vendored
Normal file
35
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/query/unmarshal.go
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package query
|
||||||
|
|
||||||
|
//go:generate go run ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/output/query.json unmarshal_test.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnmarshalHandler is a named request handler for unmarshaling query protocol requests
|
||||||
|
var UnmarshalHandler = request.NamedHandler{Name: "awssdk.query.Unmarshal", Fn: Unmarshal}
|
||||||
|
|
||||||
|
// UnmarshalMetaHandler is a named request handler for unmarshaling query protocol request metadata
|
||||||
|
var UnmarshalMetaHandler = request.NamedHandler{Name: "awssdk.query.UnmarshalMeta", Fn: UnmarshalMeta}
|
||||||
|
|
||||||
|
// Unmarshal unmarshals a response for an AWS Query service.
|
||||||
|
func Unmarshal(r *request.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 *request.Request) {
|
||||||
|
r.RequestID = r.HTTPResponse.Header.Get("X-Amzn-Requestid")
|
||||||
|
}
|
40
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/query/unmarshal_error.go
generated
vendored
Normal file
40
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/query/unmarshal_error.go
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package query
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
type xmlErrorResponse struct {
|
||||||
|
XMLName xml.Name `xml:"ErrorResponse"`
|
||||||
|
Code string `xml:"Error>Code"`
|
||||||
|
Message string `xml:"Error>Message"`
|
||||||
|
RequestID string `xml:"RequestId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalErrorHandler is a name request handler to unmarshal request errors
|
||||||
|
var UnmarshalErrorHandler = request.NamedHandler{Name: "awssdk.query.UnmarshalError", Fn: UnmarshalError}
|
||||||
|
|
||||||
|
// UnmarshalError unmarshals an error response for an AWS Query service.
|
||||||
|
func UnmarshalError(r *request.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 {
|
||||||
|
reqID := resp.RequestID
|
||||||
|
if reqID == "" {
|
||||||
|
reqID = r.RequestID
|
||||||
|
}
|
||||||
|
r.Error = awserr.NewRequestFailure(
|
||||||
|
awserr.New(resp.Code, resp.Message, nil),
|
||||||
|
r.HTTPResponse.StatusCode,
|
||||||
|
reqID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
257
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/rest/build.go
generated
vendored
Normal file
257
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/rest/build.go
generated
vendored
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
// Package rest provides RESTful serialization of AWS requests and responses.
|
||||||
|
package rest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
var errValueNotSet = fmt.Errorf("value not set")
|
||||||
|
|
||||||
|
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 == '~'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildHandler is a named request handler for building rest protocol requests
|
||||||
|
var BuildHandler = request.NamedHandler{Name: "awssdk.rest.Build", Fn: Build}
|
||||||
|
|
||||||
|
// Build builds the REST component of a service request.
|
||||||
|
func Build(r *request.Request) {
|
||||||
|
if r.ParamsFilled() {
|
||||||
|
v := reflect.ValueOf(r.Params).Elem()
|
||||||
|
buildLocationElements(r, v)
|
||||||
|
buildBody(r, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildLocationElements(r *request.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
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
switch field.Tag.Get("location") {
|
||||||
|
case "headers": // header maps
|
||||||
|
err = buildHeaderMap(&r.HTTPRequest.Header, m, field.Tag.Get("locationName"))
|
||||||
|
case "header":
|
||||||
|
err = buildHeader(&r.HTTPRequest.Header, m, name)
|
||||||
|
case "uri":
|
||||||
|
err = buildURI(r.HTTPRequest.URL, m, name)
|
||||||
|
case "querystring":
|
||||||
|
err = buildQueryString(query, m, name)
|
||||||
|
}
|
||||||
|
r.Error = err
|
||||||
|
}
|
||||||
|
if r.Error != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r.HTTPRequest.URL.RawQuery = query.Encode()
|
||||||
|
updatePath(r.HTTPRequest.URL, r.HTTPRequest.URL.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildBody(r *request.Request, v reflect.Value) {
|
||||||
|
if field, ok := v.Type().FieldByName("_"); 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(header *http.Header, v reflect.Value, name string) error {
|
||||||
|
str, err := convertType(v)
|
||||||
|
if err == errValueNotSet {
|
||||||
|
return nil
|
||||||
|
} else if err != nil {
|
||||||
|
return awserr.New("SerializationError", "failed to encode REST request", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
header.Add(name, str)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildHeaderMap(header *http.Header, v reflect.Value, prefix string) error {
|
||||||
|
for _, key := range v.MapKeys() {
|
||||||
|
str, err := convertType(v.MapIndex(key))
|
||||||
|
if err == errValueNotSet {
|
||||||
|
continue
|
||||||
|
} else if err != nil {
|
||||||
|
return awserr.New("SerializationError", "failed to encode REST request", err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
header.Add(prefix+key.String(), str)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildURI(u *url.URL, v reflect.Value, name string) error {
|
||||||
|
value, err := convertType(v)
|
||||||
|
if err == errValueNotSet {
|
||||||
|
return nil
|
||||||
|
} else if err != nil {
|
||||||
|
return awserr.New("SerializationError", "failed to encode REST request", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uri := u.Path
|
||||||
|
uri = strings.Replace(uri, "{"+name+"}", EscapePath(value, true), -1)
|
||||||
|
uri = strings.Replace(uri, "{"+name+"+}", EscapePath(value, false), -1)
|
||||||
|
u.Path = uri
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildQueryString(query url.Values, v reflect.Value, name string) error {
|
||||||
|
switch value := v.Interface().(type) {
|
||||||
|
case []*string:
|
||||||
|
for _, item := range value {
|
||||||
|
query.Add(name, *item)
|
||||||
|
}
|
||||||
|
case map[string]*string:
|
||||||
|
for key, item := range value {
|
||||||
|
query.Add(key, *item)
|
||||||
|
}
|
||||||
|
case map[string][]*string:
|
||||||
|
for key, items := range value {
|
||||||
|
for _, item := range items {
|
||||||
|
query.Add(key, *item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
str, err := convertType(v)
|
||||||
|
if err == errValueNotSet {
|
||||||
|
return nil
|
||||||
|
} else if err != nil {
|
||||||
|
return awserr.New("SerializationError", "failed to encode REST request", err)
|
||||||
|
}
|
||||||
|
query.Set(name, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updatePath(url *url.URL, urlPath string) {
|
||||||
|
scheme, query := url.Scheme, url.RawQuery
|
||||||
|
|
||||||
|
hasSlash := strings.HasSuffix(urlPath, "/")
|
||||||
|
|
||||||
|
// clean up path
|
||||||
|
urlPath = path.Clean(urlPath)
|
||||||
|
if hasSlash && !strings.HasSuffix(urlPath, "/") {
|
||||||
|
urlPath += "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 "", errValueNotSet
|
||||||
|
}
|
||||||
|
|
||||||
|
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 "", err
|
||||||
|
}
|
||||||
|
return str, nil
|
||||||
|
}
|
45
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/rest/payload.go
generated
vendored
Normal file
45
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/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("_"); 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("_"); ok {
|
||||||
|
if payloadName := field.Tag.Get("payload"); payloadName != "" {
|
||||||
|
if member, ok := v.Type().FieldByName(payloadName); ok {
|
||||||
|
return member.Tag.Get("type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
193
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/rest/unmarshal.go
generated
vendored
Normal file
193
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/rest/unmarshal.go
generated
vendored
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
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"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnmarshalHandler is a named request handler for unmarshaling rest protocol requests
|
||||||
|
var UnmarshalHandler = request.NamedHandler{Name: "awssdk.rest.Unmarshal", Fn: Unmarshal}
|
||||||
|
|
||||||
|
// UnmarshalMetaHandler is a named request handler for unmarshaling rest protocol request metadata
|
||||||
|
var UnmarshalMetaHandler = request.NamedHandler{Name: "awssdk.rest.UnmarshalMeta", Fn: UnmarshalMeta}
|
||||||
|
|
||||||
|
// Unmarshal unmarshals the REST component of a response in a REST service.
|
||||||
|
func Unmarshal(r *request.Request) {
|
||||||
|
if r.DataFilled() {
|
||||||
|
v := reflect.Indirect(reflect.ValueOf(r.Data))
|
||||||
|
unmarshalBody(r, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalMeta unmarshals the REST metadata of a response in a REST service
|
||||||
|
func UnmarshalMeta(r *request.Request) {
|
||||||
|
r.RequestID = r.HTTPResponse.Header.Get("X-Amzn-Requestid")
|
||||||
|
if r.RequestID == "" {
|
||||||
|
// Alternative version of request id in the header
|
||||||
|
r.RequestID = r.HTTPResponse.Header.Get("X-Amz-Request-Id")
|
||||||
|
}
|
||||||
|
if r.DataFilled() {
|
||||||
|
v := reflect.Indirect(reflect.ValueOf(r.Data))
|
||||||
|
unmarshalLocationElements(r, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalBody(r *request.Request, v reflect.Value) {
|
||||||
|
if field, ok := v.Type().FieldByName("_"); 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 *request.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
|
||||||
|
}
|
91
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/restjson/restjson.go
generated
vendored
Normal file
91
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/restjson/restjson.go
generated
vendored
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
// Package restjson provides RESTful JSON serialisation of AWS
|
||||||
|
// requests and responses.
|
||||||
|
package restjson
|
||||||
|
|
||||||
|
//go:generate go run ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/input/rest-json.json build_test.go
|
||||||
|
//go:generate go run ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/output/rest-json.json unmarshal_test.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol/jsonrpc"
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuildHandler is a named request handler for building restjson protocol requests
|
||||||
|
var BuildHandler = request.NamedHandler{Name: "awssdk.restjson.Build", Fn: Build}
|
||||||
|
|
||||||
|
// UnmarshalHandler is a named request handler for unmarshaling restjson protocol requests
|
||||||
|
var UnmarshalHandler = request.NamedHandler{Name: "awssdk.restjson.Unmarshal", Fn: Unmarshal}
|
||||||
|
|
||||||
|
// UnmarshalMetaHandler is a named request handler for unmarshaling restjson protocol request metadata
|
||||||
|
var UnmarshalMetaHandler = request.NamedHandler{Name: "awssdk.restjson.UnmarshalMeta", Fn: UnmarshalMeta}
|
||||||
|
|
||||||
|
// UnmarshalErrorHandler is a named request handler for unmarshaling restjson protocol request errors
|
||||||
|
var UnmarshalErrorHandler = request.NamedHandler{Name: "awssdk.restjson.UnmarshalError", Fn: UnmarshalError}
|
||||||
|
|
||||||
|
// Build builds a request for the REST JSON protocol.
|
||||||
|
func Build(r *request.Request) {
|
||||||
|
rest.Build(r)
|
||||||
|
|
||||||
|
if t := rest.PayloadType(r.Params); t == "structure" || t == "" {
|
||||||
|
jsonrpc.Build(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal unmarshals a response body for the REST JSON protocol.
|
||||||
|
func Unmarshal(r *request.Request) {
|
||||||
|
if t := rest.PayloadType(r.Data); t == "structure" || t == "" {
|
||||||
|
jsonrpc.Unmarshal(r)
|
||||||
|
} else {
|
||||||
|
rest.Unmarshal(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalMeta unmarshals response headers for the REST JSON protocol.
|
||||||
|
func UnmarshalMeta(r *request.Request) {
|
||||||
|
rest.UnmarshalMeta(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalError unmarshals a response error for the REST JSON protocol.
|
||||||
|
func UnmarshalError(r *request.Request) {
|
||||||
|
code := r.HTTPResponse.Header.Get("X-Amzn-Errortype")
|
||||||
|
bodyBytes, err := ioutil.ReadAll(r.HTTPResponse.Body)
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed reading REST JSON error response", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(bodyBytes) == 0 {
|
||||||
|
r.Error = awserr.NewRequestFailure(
|
||||||
|
awserr.New("SerializationError", r.HTTPResponse.Status, nil),
|
||||||
|
r.HTTPResponse.StatusCode,
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var jsonErr jsonErrorResponse
|
||||||
|
if err := json.Unmarshal(bodyBytes, &jsonErr); err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "failed decoding REST JSON error response", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if code == "" {
|
||||||
|
code = jsonErr.Code
|
||||||
|
}
|
||||||
|
|
||||||
|
code = strings.SplitN(code, ":", 2)[0]
|
||||||
|
r.Error = awserr.NewRequestFailure(
|
||||||
|
awserr.New(code, jsonErr.Message, nil),
|
||||||
|
r.HTTPResponse.StatusCode,
|
||||||
|
r.RequestID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type jsonErrorResponse struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
69
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/restxml/restxml.go
generated
vendored
Normal file
69
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/restxml/restxml.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
// Package restxml provides RESTful XML serialisation of AWS
|
||||||
|
// requests and responses.
|
||||||
|
package restxml
|
||||||
|
|
||||||
|
//go:generate go run ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/input/rest-xml.json build_test.go
|
||||||
|
//go:generate go run ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/output/rest-xml.json unmarshal_test.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol/query"
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol/rest"
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuildHandler is a named request handler for building restxml protocol requests
|
||||||
|
var BuildHandler = request.NamedHandler{Name: "awssdk.restxml.Build", Fn: Build}
|
||||||
|
|
||||||
|
// UnmarshalHandler is a named request handler for unmarshaling restxml protocol requests
|
||||||
|
var UnmarshalHandler = request.NamedHandler{Name: "awssdk.restxml.Unmarshal", Fn: Unmarshal}
|
||||||
|
|
||||||
|
// UnmarshalMetaHandler is a named request handler for unmarshaling restxml protocol request metadata
|
||||||
|
var UnmarshalMetaHandler = request.NamedHandler{Name: "awssdk.restxml.UnmarshalMeta", Fn: UnmarshalMeta}
|
||||||
|
|
||||||
|
// UnmarshalErrorHandler is a named request handler for unmarshaling restxml protocol request errors
|
||||||
|
var UnmarshalErrorHandler = request.NamedHandler{Name: "awssdk.restxml.UnmarshalError", Fn: UnmarshalError}
|
||||||
|
|
||||||
|
// Build builds a request payload for the REST XML protocol.
|
||||||
|
func Build(r *request.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 encode rest XML request", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.SetBufferBody(buf.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal unmarshals a payload response for the REST XML protocol.
|
||||||
|
func Unmarshal(r *request.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
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rest.Unmarshal(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalMeta unmarshals response headers for the REST XML protocol.
|
||||||
|
func UnmarshalMeta(r *request.Request) {
|
||||||
|
rest.UnmarshalMeta(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalError unmarshals a response error for the REST XML protocol.
|
||||||
|
func UnmarshalError(r *request.Request) {
|
||||||
|
query.UnmarshalError(r)
|
||||||
|
}
|
21
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/unmarshal.go
generated
vendored
Normal file
21
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/unmarshal.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnmarshalDiscardBodyHandler is a named request handler to empty and close a response's body
|
||||||
|
var UnmarshalDiscardBodyHandler = request.NamedHandler{Name: "awssdk.shared.UnmarshalDiscardBody", Fn: UnmarshalDiscardBody}
|
||||||
|
|
||||||
|
// UnmarshalDiscardBody is a request handler to empty a response's body and closing it.
|
||||||
|
func UnmarshalDiscardBody(r *request.Request) {
|
||||||
|
if r.HTTPResponse == nil || r.HTTPResponse.Body == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
io.Copy(ioutil.Discard, r.HTTPResponse.Body)
|
||||||
|
r.HTTPResponse.Body.Close()
|
||||||
|
}
|
293
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/build.go
generated
vendored
Normal file
293
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/build.go
generated
vendored
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
// Package xmlutil provides XML serialisation of AWS requests and responses.
|
||||||
|
package xmlutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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("_"); 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++ {
|
||||||
|
member := elemOf(value.Field(i))
|
||||||
|
field := t.Field(i)
|
||||||
|
|
||||||
|
if field.PkgPath != "" {
|
||||||
|
continue // ignore unexported fields
|
||||||
|
}
|
||||||
|
|
||||||
|
mTag := field.Tag
|
||||||
|
if mTag.Get("location") != "" { // skip non-body members
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if protocol.CanSetIdempotencyToken(value.Field(i), field) {
|
||||||
|
token := protocol.GetIdempotencyToken()
|
||||||
|
member = reflect.ValueOf(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
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/private/protocol/xml/xmlutil/unmarshal.go
generated
vendored
Normal file
260
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/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("_"); 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/private/protocol/xml/xmlutil/xml_to_struct.go
generated
vendored
Normal file
105
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/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()
|
||||||
|
}
|
82
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/signer/v4/header_rules.go
generated
vendored
Normal file
82
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/signer/v4/header_rules.go
generated
vendored
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
package v4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// validator houses a set of rule needed for validation of a
|
||||||
|
// string value
|
||||||
|
type rules []rule
|
||||||
|
|
||||||
|
// rule interface allows for more flexible rules and just simply
|
||||||
|
// checks whether or not a value adheres to that rule
|
||||||
|
type rule interface {
|
||||||
|
IsValid(value string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid will iterate through all rules and see if any rules
|
||||||
|
// apply to the value and supports nested rules
|
||||||
|
func (r rules) IsValid(value string) bool {
|
||||||
|
for _, rule := range r {
|
||||||
|
if rule.IsValid(value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapRule generic rule for maps
|
||||||
|
type mapRule map[string]struct{}
|
||||||
|
|
||||||
|
// IsValid for the map rule satisfies whether it exists in the map
|
||||||
|
func (m mapRule) IsValid(value string) bool {
|
||||||
|
_, ok := m[value]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// whitelist is a generic rule for whitelisting
|
||||||
|
type whitelist struct {
|
||||||
|
rule
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid for whitelist checks if the value is within the whitelist
|
||||||
|
func (w whitelist) IsValid(value string) bool {
|
||||||
|
return w.rule.IsValid(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// blacklist is a generic rule for blacklisting
|
||||||
|
type blacklist struct {
|
||||||
|
rule
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid for whitelist checks if the value is within the whitelist
|
||||||
|
func (b blacklist) IsValid(value string) bool {
|
||||||
|
return !b.rule.IsValid(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
type patterns []string
|
||||||
|
|
||||||
|
// IsValid for patterns checks each pattern and returns if a match has
|
||||||
|
// been found
|
||||||
|
func (p patterns) IsValid(value string) bool {
|
||||||
|
for _, pattern := range p {
|
||||||
|
if strings.HasPrefix(http.CanonicalHeaderKey(value), pattern) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// inclusiveRules rules allow for rules to depend on one another
|
||||||
|
type inclusiveRules []rule
|
||||||
|
|
||||||
|
// IsValid will return true if all rules are true
|
||||||
|
func (r inclusiveRules) IsValid(value string) bool {
|
||||||
|
for _, rule := range r {
|
||||||
|
if !rule.IsValid(value) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
438
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/signer/v4/v4.go
generated
vendored
Normal file
438
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/signer/v4/v4.go
generated
vendored
Normal file
|
@ -0,0 +1,438 @@
|
||||||
|
// 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/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
authHeaderPrefix = "AWS4-HMAC-SHA256"
|
||||||
|
timeFormat = "20060102T150405Z"
|
||||||
|
shortTimeFormat = "20060102"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ignoredHeaders = rules{
|
||||||
|
blacklist{
|
||||||
|
mapRule{
|
||||||
|
"Content-Length": struct{}{},
|
||||||
|
"User-Agent": struct{}{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// requiredSignedHeaders is a whitelist for build canonical headers.
|
||||||
|
var requiredSignedHeaders = rules{
|
||||||
|
whitelist{
|
||||||
|
mapRule{
|
||||||
|
"Cache-Control": struct{}{},
|
||||||
|
"Content-Disposition": struct{}{},
|
||||||
|
"Content-Encoding": struct{}{},
|
||||||
|
"Content-Language": struct{}{},
|
||||||
|
"Content-Md5": struct{}{},
|
||||||
|
"Content-Type": struct{}{},
|
||||||
|
"Expires": struct{}{},
|
||||||
|
"If-Match": struct{}{},
|
||||||
|
"If-Modified-Since": struct{}{},
|
||||||
|
"If-None-Match": struct{}{},
|
||||||
|
"If-Unmodified-Since": struct{}{},
|
||||||
|
"Range": struct{}{},
|
||||||
|
"X-Amz-Acl": struct{}{},
|
||||||
|
"X-Amz-Copy-Source": struct{}{},
|
||||||
|
"X-Amz-Copy-Source-If-Match": struct{}{},
|
||||||
|
"X-Amz-Copy-Source-If-Modified-Since": struct{}{},
|
||||||
|
"X-Amz-Copy-Source-If-None-Match": struct{}{},
|
||||||
|
"X-Amz-Copy-Source-If-Unmodified-Since": struct{}{},
|
||||||
|
"X-Amz-Copy-Source-Range": struct{}{},
|
||||||
|
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": struct{}{},
|
||||||
|
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key": struct{}{},
|
||||||
|
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5": struct{}{},
|
||||||
|
"X-Amz-Grant-Full-control": struct{}{},
|
||||||
|
"X-Amz-Grant-Read": struct{}{},
|
||||||
|
"X-Amz-Grant-Read-Acp": struct{}{},
|
||||||
|
"X-Amz-Grant-Write": struct{}{},
|
||||||
|
"X-Amz-Grant-Write-Acp": struct{}{},
|
||||||
|
"X-Amz-Metadata-Directive": struct{}{},
|
||||||
|
"X-Amz-Mfa": struct{}{},
|
||||||
|
"X-Amz-Request-Payer": struct{}{},
|
||||||
|
"X-Amz-Server-Side-Encryption": struct{}{},
|
||||||
|
"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": struct{}{},
|
||||||
|
"X-Amz-Server-Side-Encryption-Customer-Algorithm": struct{}{},
|
||||||
|
"X-Amz-Server-Side-Encryption-Customer-Key": struct{}{},
|
||||||
|
"X-Amz-Server-Side-Encryption-Customer-Key-Md5": struct{}{},
|
||||||
|
"X-Amz-Storage-Class": struct{}{},
|
||||||
|
"X-Amz-Website-Redirect-Location": struct{}{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
patterns{"X-Amz-Meta-"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// allowedHoisting is a whitelist for build query headers. The boolean value
|
||||||
|
// represents whether or not it is a pattern.
|
||||||
|
var allowedQueryHoisting = inclusiveRules{
|
||||||
|
blacklist{requiredSignedHeaders},
|
||||||
|
patterns{"X-Amz-"},
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
notHoist bool
|
||||||
|
signedHeaderVals http.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 *request.Request) {
|
||||||
|
// If the request does not need to be signed ignore the signing of the
|
||||||
|
// request if the AnonymousCredentials object is used.
|
||||||
|
if req.Config.Credentials == credentials.AnonymousCredentials {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
region := req.ClientInfo.SigningRegion
|
||||||
|
if region == "" {
|
||||||
|
region = aws.StringValue(req.Config.Region)
|
||||||
|
}
|
||||||
|
|
||||||
|
name := req.ClientInfo.SigningName
|
||||||
|
if name == "" {
|
||||||
|
name = req.ClientInfo.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.Config.Credentials,
|
||||||
|
Debug: req.Config.LogLevel.Value(),
|
||||||
|
Logger: req.Config.Logger,
|
||||||
|
notHoist: req.NotHoist,
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Error = s.sign()
|
||||||
|
req.SignedHeaderVals = s.signedHeaderVals
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
unsignedHeaders := v4.Request.Header
|
||||||
|
if v4.isPresign {
|
||||||
|
if !v4.notHoist {
|
||||||
|
urlValues := url.Values{}
|
||||||
|
urlValues, unsignedHeaders = buildQuery(allowedQueryHoisting, unsignedHeaders) // no depends
|
||||||
|
for k := range urlValues {
|
||||||
|
v4.Query[k] = urlValues[k]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v4.buildCanonicalHeaders(ignoredHeaders, unsignedHeaders)
|
||||||
|
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 buildQuery(r rule, header http.Header) (url.Values, http.Header) {
|
||||||
|
query := url.Values{}
|
||||||
|
unsignedHeaders := http.Header{}
|
||||||
|
for k, h := range header {
|
||||||
|
if r.IsValid(k) {
|
||||||
|
query[k] = h
|
||||||
|
} else {
|
||||||
|
unsignedHeaders[k] = h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return query, unsignedHeaders
|
||||||
|
}
|
||||||
|
func (v4 *signer) buildCanonicalHeaders(r rule, header http.Header) {
|
||||||
|
var headers []string
|
||||||
|
headers = append(headers, "host")
|
||||||
|
for k, v := range header {
|
||||||
|
canonicalKey := http.CanonicalHeaderKey(k)
|
||||||
|
if !r.IsValid(canonicalKey) {
|
||||||
|
continue // ignored header
|
||||||
|
}
|
||||||
|
|
||||||
|
lowerCaseKey := strings.ToLower(k)
|
||||||
|
headers = append(headers, lowerCaseKey)
|
||||||
|
|
||||||
|
if v4.signedHeaderVals == nil {
|
||||||
|
v4.signedHeaderVals = make(http.Header)
|
||||||
|
}
|
||||||
|
v4.signedHeaderVals[lowerCaseKey] = v
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
134
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/waiter/waiter.go
generated
vendored
Normal file
134
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/waiter/waiter.go
generated
vendored
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
package waiter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"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/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Config provides a collection of configuration values to setup a generated
|
||||||
|
// waiter code with.
|
||||||
|
type Config struct {
|
||||||
|
Name string
|
||||||
|
Delay int
|
||||||
|
MaxAttempts int
|
||||||
|
Operation string
|
||||||
|
Acceptors []WaitAcceptor
|
||||||
|
}
|
||||||
|
|
||||||
|
// A WaitAcceptor provides the information needed to wait for an API operation
|
||||||
|
// to complete.
|
||||||
|
type WaitAcceptor struct {
|
||||||
|
Expected interface{}
|
||||||
|
Matcher string
|
||||||
|
State string
|
||||||
|
Argument string
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Waiter provides waiting for an operation to complete.
|
||||||
|
type Waiter struct {
|
||||||
|
Config
|
||||||
|
Client interface{}
|
||||||
|
Input interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait waits for an operation to complete, expire max attempts, or fail. Error
|
||||||
|
// is returned if the operation fails.
|
||||||
|
func (w *Waiter) Wait() error {
|
||||||
|
client := reflect.ValueOf(w.Client)
|
||||||
|
in := reflect.ValueOf(w.Input)
|
||||||
|
method := client.MethodByName(w.Config.Operation + "Request")
|
||||||
|
|
||||||
|
for i := 0; i < w.MaxAttempts; i++ {
|
||||||
|
res := method.Call([]reflect.Value{in})
|
||||||
|
req := res[0].Interface().(*request.Request)
|
||||||
|
req.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Waiter"))
|
||||||
|
|
||||||
|
err := req.Send()
|
||||||
|
for _, a := range w.Acceptors {
|
||||||
|
result := false
|
||||||
|
var vals []interface{}
|
||||||
|
switch a.Matcher {
|
||||||
|
case "pathAll", "path":
|
||||||
|
// Require all matches to be equal for result to match
|
||||||
|
vals, _ = awsutil.ValuesAtPath(req.Data, a.Argument)
|
||||||
|
if len(vals) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
result = true
|
||||||
|
for _, val := range vals {
|
||||||
|
if !awsutil.DeepEqual(val, a.Expected) {
|
||||||
|
result = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "pathAny":
|
||||||
|
// Only a single match needs to equal for the result to match
|
||||||
|
vals, _ = awsutil.ValuesAtPath(req.Data, a.Argument)
|
||||||
|
for _, val := range vals {
|
||||||
|
if awsutil.DeepEqual(val, a.Expected) {
|
||||||
|
result = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "status":
|
||||||
|
s := a.Expected.(int)
|
||||||
|
result = s == req.HTTPResponse.StatusCode
|
||||||
|
case "error":
|
||||||
|
if aerr, ok := err.(awserr.Error); ok {
|
||||||
|
result = aerr.Code() == a.Expected.(string)
|
||||||
|
}
|
||||||
|
case "pathList":
|
||||||
|
// ignored matcher
|
||||||
|
default:
|
||||||
|
logf(client, "WARNING: Waiter for %s encountered unexpected matcher: %s",
|
||||||
|
w.Config.Operation, a.Matcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !result {
|
||||||
|
// If there was no matching result found there is nothing more to do
|
||||||
|
// for this response, retry the request.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch a.State {
|
||||||
|
case "success":
|
||||||
|
// waiter completed
|
||||||
|
return nil
|
||||||
|
case "failure":
|
||||||
|
// Waiter failure state triggered
|
||||||
|
return awserr.New("ResourceNotReady",
|
||||||
|
fmt.Sprintf("failed waiting for successful resource state"), err)
|
||||||
|
case "retry":
|
||||||
|
// clear the error and retry the operation
|
||||||
|
err = nil
|
||||||
|
default:
|
||||||
|
logf(client, "WARNING: Waiter for %s encountered unexpected state: %s",
|
||||||
|
w.Config.Operation, a.State)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Second * time.Duration(w.Delay))
|
||||||
|
}
|
||||||
|
|
||||||
|
return awserr.New("ResourceNotReady",
|
||||||
|
fmt.Sprintf("exceeded %d wait attempts", w.MaxAttempts), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func logf(client reflect.Value, msg string, args ...interface{}) {
|
||||||
|
cfgVal := client.FieldByName("Config")
|
||||||
|
if !cfgVal.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cfg, ok := cfgVal.Interface().(*aws.Config); ok && cfg.Logger != nil {
|
||||||
|
cfg.Logger.Log(fmt.Sprintf(msg, args...))
|
||||||
|
}
|
||||||
|
}
|
210
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/cloudfront/sign/policy.go
generated
vendored
Normal file
210
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/cloudfront/sign/policy.go
generated
vendored
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// An AWSEpochTime wraps a time value providing JSON serialization needed for
|
||||||
|
// AWS Policy epoch time fields.
|
||||||
|
type AWSEpochTime struct {
|
||||||
|
time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAWSEpochTime returns a new AWSEpochTime pointer wrapping the Go time provided.
|
||||||
|
func NewAWSEpochTime(t time.Time) *AWSEpochTime {
|
||||||
|
return &AWSEpochTime{t}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON serializes the epoch time as AWS Profile epoch time.
|
||||||
|
func (t AWSEpochTime) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(fmt.Sprintf(`{"AWS:EpochTime":%d}`, t.UTC().Unix())), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// An IPAddress wraps an IPAddress source IP providing JSON serialization information
|
||||||
|
type IPAddress struct {
|
||||||
|
SourceIP string `json:"AWS:SourceIp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Condition defines the restrictions for how a signed URL can be used.
|
||||||
|
type Condition struct {
|
||||||
|
// Optional IP address mask the signed URL must be requested from.
|
||||||
|
IPAddress *IPAddress `json:"IpAddress,omitempty"`
|
||||||
|
|
||||||
|
// Optional date that the signed URL cannot be used until. It is invalid
|
||||||
|
// to make requests with the signed URL prior to this date.
|
||||||
|
DateGreaterThan *AWSEpochTime `json:",omitempty"`
|
||||||
|
|
||||||
|
// Required date that the signed URL will expire. A DateLessThan is required
|
||||||
|
// sign cloud front URLs
|
||||||
|
DateLessThan *AWSEpochTime `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Statement is a collection of conditions for resources
|
||||||
|
type Statement struct {
|
||||||
|
// The Web or RTMP resource the URL will be signed for
|
||||||
|
Resource string
|
||||||
|
|
||||||
|
// The set of conditions for this resource
|
||||||
|
Condition Condition
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Policy defines the resources that a signed will be signed for.
|
||||||
|
//
|
||||||
|
// See the following page for more information on how policies are constructed.
|
||||||
|
// http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-custom-policy.html#private-content-custom-policy-statement
|
||||||
|
type Policy struct {
|
||||||
|
// List of resource and condition statements.
|
||||||
|
// Signed URLs should only provide a single statement.
|
||||||
|
Statements []Statement `json:"Statement"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override for testing to mock out usage of crypto/rand.Reader
|
||||||
|
var randReader = rand.Reader
|
||||||
|
|
||||||
|
// Sign will sign a policy using an RSA private key. It will return a base 64
|
||||||
|
// encoded signature and policy if no error is encountered.
|
||||||
|
//
|
||||||
|
// The signature and policy should be added to the signed URL following the
|
||||||
|
// guidelines in:
|
||||||
|
// http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-urls.html
|
||||||
|
func (p *Policy) Sign(privKey *rsa.PrivateKey) (b64Signature, b64Policy []byte, err error) {
|
||||||
|
if err = p.Validate(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build and escape the policy
|
||||||
|
b64Policy, jsonPolicy, err := encodePolicy(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
awsEscapeEncoded(b64Policy)
|
||||||
|
|
||||||
|
// Build and escape the signature
|
||||||
|
b64Signature, err = signEncodedPolicy(randReader, jsonPolicy, privKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
awsEscapeEncoded(b64Signature)
|
||||||
|
|
||||||
|
return b64Signature, b64Policy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate verifies that the policy is valid and usable, and returns an
|
||||||
|
// error if there is a problem.
|
||||||
|
func (p *Policy) Validate() error {
|
||||||
|
if len(p.Statements) == 0 {
|
||||||
|
return fmt.Errorf("at least one policy statement is required")
|
||||||
|
}
|
||||||
|
for i, s := range p.Statements {
|
||||||
|
if s.Resource == "" {
|
||||||
|
return fmt.Errorf("statement at index %d does not have a resource", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResource constructs, validates, and returns a resource URL string. An
|
||||||
|
// error will be returned if unable to create the resource string.
|
||||||
|
func CreateResource(scheme, u string) (string, error) {
|
||||||
|
scheme = strings.ToLower(scheme)
|
||||||
|
|
||||||
|
if scheme == "http" || scheme == "https" {
|
||||||
|
return u, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if scheme == "rtmp" {
|
||||||
|
parsed, err := url.Parse(u)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to parse rtmp URL, err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rtmpURL := strings.TrimLeft(parsed.Path, "/")
|
||||||
|
if parsed.RawQuery != "" {
|
||||||
|
rtmpURL = fmt.Sprintf("%s?%s", rtmpURL, parsed.RawQuery)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtmpURL, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("invalid URL scheme must be http, https, or rtmp. Provided: %s", scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCannedPolicy returns a new Canned Policy constructed using the resource
|
||||||
|
// and expires time. This can be used to generate the basic model for a Policy
|
||||||
|
// that can be then augmented with additional conditions.
|
||||||
|
//
|
||||||
|
// See the following page for more information on how policies are constructed.
|
||||||
|
// http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-custom-policy.html#private-content-custom-policy-statement
|
||||||
|
func NewCannedPolicy(resource string, expires time.Time) *Policy {
|
||||||
|
return &Policy{
|
||||||
|
Statements: []Statement{
|
||||||
|
{
|
||||||
|
Resource: resource,
|
||||||
|
Condition: Condition{
|
||||||
|
DateLessThan: NewAWSEpochTime(expires),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodePolicy encodes the Policy as JSON and also base 64 encodes it.
|
||||||
|
func encodePolicy(p *Policy) (b64Policy, jsonPolicy []byte, err error) {
|
||||||
|
jsonPolicy, err = json.Marshal(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to encode policy, %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove leading and trailing white space, JSON encoding will note include
|
||||||
|
// whitespace within the encoding.
|
||||||
|
jsonPolicy = bytes.TrimSpace(jsonPolicy)
|
||||||
|
|
||||||
|
b64Policy = make([]byte, base64.StdEncoding.EncodedLen(len(jsonPolicy)))
|
||||||
|
base64.StdEncoding.Encode(b64Policy, jsonPolicy)
|
||||||
|
return b64Policy, jsonPolicy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// signEncodedPolicy will sign and base 64 encode the JSON encoded policy.
|
||||||
|
func signEncodedPolicy(randReader io.Reader, jsonPolicy []byte, privKey *rsa.PrivateKey) ([]byte, error) {
|
||||||
|
hash := sha1.New()
|
||||||
|
if _, err := bytes.NewReader(jsonPolicy).WriteTo(hash); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to calculate signing hash, %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := rsa.SignPKCS1v15(randReader, privKey, crypto.SHA1, hash.Sum(nil))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to sign policy, %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
b64Sig := make([]byte, base64.StdEncoding.EncodedLen(len(sig)))
|
||||||
|
base64.StdEncoding.Encode(b64Sig, sig)
|
||||||
|
return b64Sig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// special characters to be replaced with awsEscapeEncoded
|
||||||
|
var invalidEncodedChar = map[byte]byte{
|
||||||
|
'+': '-',
|
||||||
|
'=': '_',
|
||||||
|
'/': '~',
|
||||||
|
}
|
||||||
|
|
||||||
|
// awsEscapeEncoded will replace base64 encoding's special characters to be URL safe.
|
||||||
|
func awsEscapeEncoded(b []byte) {
|
||||||
|
for i, v := range b {
|
||||||
|
if r, ok := invalidEncodedChar[v]; ok {
|
||||||
|
b[i] = r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/cloudfront/sign/privkey.go
generated
vendored
Normal file
68
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/cloudfront/sign/privkey.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoadPEMPrivKeyFile reads a PEM encoded RSA private key from the file name.
|
||||||
|
// A new RSA private key will be returned if no error.
|
||||||
|
func LoadPEMPrivKeyFile(name string) (*rsa.PrivateKey, error) {
|
||||||
|
file, err := os.Open(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
return LoadPEMPrivKey(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadPEMPrivKey reads a PEM encoded RSA private key from the io.Reader.
|
||||||
|
// A new RSA private key will be returned if no error.
|
||||||
|
func LoadPEMPrivKey(reader io.Reader) (*rsa.PrivateKey, error) {
|
||||||
|
block, err := loadPem(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadEncryptedPEMPrivKey decrypts the PEM encoded private key using the
|
||||||
|
// password provided returning a RSA private key. If the PEM data is invalid,
|
||||||
|
// or unable to decrypt an error will be returned.
|
||||||
|
func LoadEncryptedPEMPrivKey(reader io.Reader, password []byte) (*rsa.PrivateKey, error) {
|
||||||
|
block, err := loadPem(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
decryptedBlock, err := x509.DecryptPEMBlock(block, password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return x509.ParsePKCS1PrivateKey(decryptedBlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadPem(reader io.Reader) (*pem.Block, error) {
|
||||||
|
b, err := ioutil.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
block, _ := pem.Decode(b)
|
||||||
|
if block == nil {
|
||||||
|
// pem.Decode will set block to nil if there is no PEM data in the input
|
||||||
|
// the second parameter will contain the provided bytes that failed
|
||||||
|
// to be decoded.
|
||||||
|
return nil, fmt.Errorf("no valid PEM data provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
return block, nil
|
||||||
|
}
|
30
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/cloudfront/sign/randomreader.go
generated
vendored
Normal file
30
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/cloudfront/sign/randomreader.go
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A randomReader wraps a math/rand.Rand within an reader so that it can used
|
||||||
|
// as a predictable testing replacement for crypto/rand.Reader
|
||||||
|
type randomReader struct {
|
||||||
|
b *bytes.Buffer
|
||||||
|
r *rand.Rand
|
||||||
|
}
|
||||||
|
|
||||||
|
// newRandomReader returns a new instance of the random reader
|
||||||
|
func newRandomReader(r *rand.Rand) *randomReader {
|
||||||
|
return &randomReader{b: &bytes.Buffer{}, r: r}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read will read random bytes from up to the length of b.
|
||||||
|
func (m *randomReader) Read(b []byte) (int, error) {
|
||||||
|
for i := 0; i < len(b); {
|
||||||
|
binary.Write(m.b, binary.LittleEndian, m.r.Int63())
|
||||||
|
n, _ := m.b.Read(b[i:])
|
||||||
|
i += n
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(b), nil
|
||||||
|
}
|
205
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/cloudfront/sign/sign_url.go
generated
vendored
Normal file
205
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/cloudfront/sign/sign_url.go
generated
vendored
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
// Package sign provides utilities to generate signed URLs for Amazon CloudFront.
|
||||||
|
//
|
||||||
|
// More information about signed URLs and their structure can be found at:
|
||||||
|
// http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-canned-policy.html
|
||||||
|
//
|
||||||
|
// To sign a URL create a URLSigner with your private key and credential pair key ID.
|
||||||
|
// Once you have a URLSigner instance you can call Sign or SignWithPolicy to
|
||||||
|
// sign the URLs.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// // Sign URL to be valid for 1 hour from now.
|
||||||
|
// signer := sign.NewURLSigner(keyID, privKey)
|
||||||
|
// signedURL, err := signer.Sign(rawURL, time.Now().Add(1*time.Hour))
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("Failed to sign url, err: %s\n", err.Error())
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// An URLSigner provides URL signing utilities to sign URLs for Amazon CloudFront
|
||||||
|
// resources. Using a private key and Credential Key Pair key ID the URLSigner
|
||||||
|
// only needs to be created once per Credential Key Pair key ID and private key.
|
||||||
|
//
|
||||||
|
// The signer is safe to use concurrently.
|
||||||
|
type URLSigner struct {
|
||||||
|
keyID string
|
||||||
|
privKey *rsa.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewURLSigner constructs and returns a new URLSigner to be used to for signing
|
||||||
|
// Amazon CloudFront URL resources with.
|
||||||
|
func NewURLSigner(keyID string, privKey *rsa.PrivateKey) *URLSigner {
|
||||||
|
return &URLSigner{
|
||||||
|
keyID: keyID,
|
||||||
|
privKey: privKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign will sign a single URL to expire at the time of expires sign using the
|
||||||
|
// Amazon CloudFront default Canned Policy. The URL will be signed with the
|
||||||
|
// private key and Credential Key Pair Key ID previously provided to URLSigner.
|
||||||
|
//
|
||||||
|
// This is the default method of signing Amazon CloudFront URLs. If extra policy
|
||||||
|
// conditions are need other than URL expiry use SignWithPolicy instead.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// // Sign URL to be valid for 1 hour from now.
|
||||||
|
// signer := sign.NewURLSigner(keyID, privKey)
|
||||||
|
// signedURL, err := signer.Sign(rawURL, time.Now().Add(1*time.Hour))
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("Failed to sign url, err: %s\n", err.Error())
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
func (s URLSigner) Sign(url string, expires time.Time) (string, error) {
|
||||||
|
scheme, cleanedURL, err := cleanURLScheme(url)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
resource, err := CreateResource(scheme, url)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return signURL(scheme, cleanedURL, s.keyID, NewCannedPolicy(resource, expires), false, s.privKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignWithPolicy will sign a URL with the Policy provided. The URL will be
|
||||||
|
// signed with the private key and Credential Key Pair Key ID previously provided to URLSigner.
|
||||||
|
//
|
||||||
|
// Use this signing method if you are looking to sign a URL with more than just
|
||||||
|
// the URL's expiry time, or reusing Policies between multiple URL signings.
|
||||||
|
// If only the expiry time is needed you can use Sign and provide just the
|
||||||
|
// URL's expiry time. A minimum of at least one policy statement is required for a signed URL.
|
||||||
|
//
|
||||||
|
// Note: It is not safe to use Polices between multiple signers concurrently
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// // Sign URL to be valid for 30 minutes from now, expires one hour from now, and
|
||||||
|
// // restricted to the 192.0.2.0/24 IP address range.
|
||||||
|
// policy := &sign.Policy{
|
||||||
|
// Statements: []Statement{
|
||||||
|
// {
|
||||||
|
// Resource: rawURL,
|
||||||
|
// Condition: Condition{
|
||||||
|
// // Optional IP source address range
|
||||||
|
// IPAddress: &IPAddress{SourceIP: "192.0.2.0/24"},
|
||||||
|
// // Optional date URL is not valid until
|
||||||
|
// DateGreaterThan: &AWSEpochTime{time.Now().Add(30 * time.Minute)},
|
||||||
|
// // Required date the URL will expire after
|
||||||
|
// DateLessThan: &AWSEpochTime{time.Now().Add(1 * time.Hour)},
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// signer := sign.NewURLSigner(keyID, privKey)
|
||||||
|
// signedURL, err := signer.SignWithPolicy(rawURL, policy)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("Failed to sign url, err: %s\n", err.Error())
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
func (s URLSigner) SignWithPolicy(url string, p *Policy) (string, error) {
|
||||||
|
scheme, cleanedURL, err := cleanURLScheme(url)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return signURL(scheme, cleanedURL, s.keyID, p, true, s.privKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func signURL(scheme, url, keyID string, p *Policy, customPolicy bool, privKey *rsa.PrivateKey) (string, error) {
|
||||||
|
// Validation URL elements
|
||||||
|
if err := validateURL(url); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
b64Signature, b64Policy, err := p.Sign(privKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// build and return signed URL
|
||||||
|
builtURL := buildSignedURL(url, keyID, p, customPolicy, b64Policy, b64Signature)
|
||||||
|
if scheme == "rtmp" {
|
||||||
|
return buildRTMPURL(builtURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return builtURL, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildSignedURL(baseURL, keyID string, p *Policy, customPolicy bool, b64Policy, b64Signature []byte) string {
|
||||||
|
pred := "?"
|
||||||
|
if strings.Contains(baseURL, "?") {
|
||||||
|
pred = "&"
|
||||||
|
}
|
||||||
|
signedURL := baseURL + pred
|
||||||
|
|
||||||
|
if customPolicy {
|
||||||
|
signedURL += "Policy=" + string(b64Policy)
|
||||||
|
} else {
|
||||||
|
signedURL += fmt.Sprintf("Expires=%d", p.Statements[0].Condition.DateLessThan.UTC().Unix())
|
||||||
|
}
|
||||||
|
signedURL += fmt.Sprintf("&Signature=%s&Key-Pair-Id=%s", string(b64Signature), keyID)
|
||||||
|
|
||||||
|
return signedURL
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildRTMPURL(u string) (string, error) {
|
||||||
|
parsed, err := url.Parse(u)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to parse rtmp signed URL, err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rtmpURL := strings.TrimLeft(parsed.Path, "/")
|
||||||
|
if parsed.RawQuery != "" {
|
||||||
|
rtmpURL = fmt.Sprintf("%s?%s", rtmpURL, parsed.RawQuery)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtmpURL, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanURLScheme(u string) (scheme, cleanedURL string, err error) {
|
||||||
|
parts := strings.SplitN(u, "://", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return "", "", fmt.Errorf("invalid URL, missing scheme and domain/path")
|
||||||
|
}
|
||||||
|
scheme = strings.Replace(parts[0], "*", "", 1)
|
||||||
|
cleanedURL = fmt.Sprintf("%s://%s", scheme, parts[1])
|
||||||
|
|
||||||
|
return strings.ToLower(scheme), cleanedURL, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var illegalQueryParms = []string{"Expires", "Policy", "Signature", "Key-Pair-Id"}
|
||||||
|
|
||||||
|
func validateURL(u string) error {
|
||||||
|
parsed, err := url.Parse(u)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to parse URL, err: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if parsed.Scheme == "" {
|
||||||
|
return fmt.Errorf("URL missing valid scheme, %s", u)
|
||||||
|
}
|
||||||
|
|
||||||
|
q := parsed.Query()
|
||||||
|
for _, p := range illegalQueryParms {
|
||||||
|
if _, ok := q[p]; ok {
|
||||||
|
return fmt.Errorf("%s cannot be a query parameter for a signed URL", p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
6311
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/api.go
generated
vendored
Normal file
6311
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
43
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/bucket_location.go
generated
vendored
Normal file
43
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/bucket_location.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
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"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
var reBucketLocation = regexp.MustCompile(`>([^<>]+)<\/Location`)
|
||||||
|
|
||||||
|
func buildGetBucketLocation(r *request.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 *request.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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
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/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// contentMD5 computes and sets the HTTP Content-MD5 header for requests that
|
||||||
|
// require it.
|
||||||
|
func contentMD5(r *request.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))
|
||||||
|
}
|
37
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/customizations.go
generated
vendored
Normal file
37
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/customizations.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package s3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
initClient = func(c *client.Client) {
|
||||||
|
// Support building custom host-style bucket endpoints
|
||||||
|
c.Handlers.Build.PushFront(updateHostWithBucket)
|
||||||
|
|
||||||
|
// Require SSL when using SSE keys
|
||||||
|
c.Handlers.Validate.PushBack(validateSSERequiresSSL)
|
||||||
|
c.Handlers.Build.PushBack(computeSSEKeys)
|
||||||
|
|
||||||
|
// S3 uses custom error unmarshaling logic
|
||||||
|
c.Handlers.UnmarshalError.Clear()
|
||||||
|
c.Handlers.UnmarshalError.PushBack(unmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
initRequest = func(r *request.Request) {
|
||||||
|
switch r.Operation.Name {
|
||||||
|
case opPutBucketCors, opPutBucketLifecycle, opPutBucketPolicy, opPutBucketTagging, opDeleteObjects, opPutBucketLifecycleConfiguration:
|
||||||
|
// 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)
|
||||||
|
case opCopyObject, opUploadPartCopy, opCompleteMultipartUpload:
|
||||||
|
r.Handlers.Unmarshal.PushFront(copyMultipartStatusOKUnmarhsalError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/host_style_bucket.go
generated
vendored
Normal file
60
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/host_style_bucket.go
generated
vendored
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package s3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 *request.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
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBucketLocation should be able to be called from any region within
|
||||||
|
// a partition, and return the associated region of the bucket.
|
||||||
|
if r.Operation.Name == opGetBucketLocation {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use host-style if the bucket is DNS compatible
|
||||||
|
return dnsCompatibleBucketName(bucket)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateHostWithBucket(r *request.Request) {
|
||||||
|
b, _ := awsutil.ValuesAtPath(r.Params, "Bucket")
|
||||||
|
if len(b) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if bucket := b[0].(*string); aws.StringValue(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 = "/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
246
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3iface/interface.go
generated
vendored
Normal file
246
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3iface/interface.go
generated
vendored
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
// 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/request"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// S3API is the interface type for s3.S3.
|
||||||
|
type S3API interface {
|
||||||
|
AbortMultipartUploadRequest(*s3.AbortMultipartUploadInput) (*request.Request, *s3.AbortMultipartUploadOutput)
|
||||||
|
|
||||||
|
AbortMultipartUpload(*s3.AbortMultipartUploadInput) (*s3.AbortMultipartUploadOutput, error)
|
||||||
|
|
||||||
|
CompleteMultipartUploadRequest(*s3.CompleteMultipartUploadInput) (*request.Request, *s3.CompleteMultipartUploadOutput)
|
||||||
|
|
||||||
|
CompleteMultipartUpload(*s3.CompleteMultipartUploadInput) (*s3.CompleteMultipartUploadOutput, error)
|
||||||
|
|
||||||
|
CopyObjectRequest(*s3.CopyObjectInput) (*request.Request, *s3.CopyObjectOutput)
|
||||||
|
|
||||||
|
CopyObject(*s3.CopyObjectInput) (*s3.CopyObjectOutput, error)
|
||||||
|
|
||||||
|
CreateBucketRequest(*s3.CreateBucketInput) (*request.Request, *s3.CreateBucketOutput)
|
||||||
|
|
||||||
|
CreateBucket(*s3.CreateBucketInput) (*s3.CreateBucketOutput, error)
|
||||||
|
|
||||||
|
CreateMultipartUploadRequest(*s3.CreateMultipartUploadInput) (*request.Request, *s3.CreateMultipartUploadOutput)
|
||||||
|
|
||||||
|
CreateMultipartUpload(*s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error)
|
||||||
|
|
||||||
|
DeleteBucketRequest(*s3.DeleteBucketInput) (*request.Request, *s3.DeleteBucketOutput)
|
||||||
|
|
||||||
|
DeleteBucket(*s3.DeleteBucketInput) (*s3.DeleteBucketOutput, error)
|
||||||
|
|
||||||
|
DeleteBucketCorsRequest(*s3.DeleteBucketCorsInput) (*request.Request, *s3.DeleteBucketCorsOutput)
|
||||||
|
|
||||||
|
DeleteBucketCors(*s3.DeleteBucketCorsInput) (*s3.DeleteBucketCorsOutput, error)
|
||||||
|
|
||||||
|
DeleteBucketLifecycleRequest(*s3.DeleteBucketLifecycleInput) (*request.Request, *s3.DeleteBucketLifecycleOutput)
|
||||||
|
|
||||||
|
DeleteBucketLifecycle(*s3.DeleteBucketLifecycleInput) (*s3.DeleteBucketLifecycleOutput, error)
|
||||||
|
|
||||||
|
DeleteBucketPolicyRequest(*s3.DeleteBucketPolicyInput) (*request.Request, *s3.DeleteBucketPolicyOutput)
|
||||||
|
|
||||||
|
DeleteBucketPolicy(*s3.DeleteBucketPolicyInput) (*s3.DeleteBucketPolicyOutput, error)
|
||||||
|
|
||||||
|
DeleteBucketReplicationRequest(*s3.DeleteBucketReplicationInput) (*request.Request, *s3.DeleteBucketReplicationOutput)
|
||||||
|
|
||||||
|
DeleteBucketReplication(*s3.DeleteBucketReplicationInput) (*s3.DeleteBucketReplicationOutput, error)
|
||||||
|
|
||||||
|
DeleteBucketTaggingRequest(*s3.DeleteBucketTaggingInput) (*request.Request, *s3.DeleteBucketTaggingOutput)
|
||||||
|
|
||||||
|
DeleteBucketTagging(*s3.DeleteBucketTaggingInput) (*s3.DeleteBucketTaggingOutput, error)
|
||||||
|
|
||||||
|
DeleteBucketWebsiteRequest(*s3.DeleteBucketWebsiteInput) (*request.Request, *s3.DeleteBucketWebsiteOutput)
|
||||||
|
|
||||||
|
DeleteBucketWebsite(*s3.DeleteBucketWebsiteInput) (*s3.DeleteBucketWebsiteOutput, error)
|
||||||
|
|
||||||
|
DeleteObjectRequest(*s3.DeleteObjectInput) (*request.Request, *s3.DeleteObjectOutput)
|
||||||
|
|
||||||
|
DeleteObject(*s3.DeleteObjectInput) (*s3.DeleteObjectOutput, error)
|
||||||
|
|
||||||
|
DeleteObjectsRequest(*s3.DeleteObjectsInput) (*request.Request, *s3.DeleteObjectsOutput)
|
||||||
|
|
||||||
|
DeleteObjects(*s3.DeleteObjectsInput) (*s3.DeleteObjectsOutput, error)
|
||||||
|
|
||||||
|
GetBucketAclRequest(*s3.GetBucketAclInput) (*request.Request, *s3.GetBucketAclOutput)
|
||||||
|
|
||||||
|
GetBucketAcl(*s3.GetBucketAclInput) (*s3.GetBucketAclOutput, error)
|
||||||
|
|
||||||
|
GetBucketCorsRequest(*s3.GetBucketCorsInput) (*request.Request, *s3.GetBucketCorsOutput)
|
||||||
|
|
||||||
|
GetBucketCors(*s3.GetBucketCorsInput) (*s3.GetBucketCorsOutput, error)
|
||||||
|
|
||||||
|
GetBucketLifecycleRequest(*s3.GetBucketLifecycleInput) (*request.Request, *s3.GetBucketLifecycleOutput)
|
||||||
|
|
||||||
|
GetBucketLifecycle(*s3.GetBucketLifecycleInput) (*s3.GetBucketLifecycleOutput, error)
|
||||||
|
|
||||||
|
GetBucketLifecycleConfigurationRequest(*s3.GetBucketLifecycleConfigurationInput) (*request.Request, *s3.GetBucketLifecycleConfigurationOutput)
|
||||||
|
|
||||||
|
GetBucketLifecycleConfiguration(*s3.GetBucketLifecycleConfigurationInput) (*s3.GetBucketLifecycleConfigurationOutput, error)
|
||||||
|
|
||||||
|
GetBucketLocationRequest(*s3.GetBucketLocationInput) (*request.Request, *s3.GetBucketLocationOutput)
|
||||||
|
|
||||||
|
GetBucketLocation(*s3.GetBucketLocationInput) (*s3.GetBucketLocationOutput, error)
|
||||||
|
|
||||||
|
GetBucketLoggingRequest(*s3.GetBucketLoggingInput) (*request.Request, *s3.GetBucketLoggingOutput)
|
||||||
|
|
||||||
|
GetBucketLogging(*s3.GetBucketLoggingInput) (*s3.GetBucketLoggingOutput, error)
|
||||||
|
|
||||||
|
GetBucketNotificationRequest(*s3.GetBucketNotificationConfigurationRequest) (*request.Request, *s3.NotificationConfigurationDeprecated)
|
||||||
|
|
||||||
|
GetBucketNotification(*s3.GetBucketNotificationConfigurationRequest) (*s3.NotificationConfigurationDeprecated, error)
|
||||||
|
|
||||||
|
GetBucketNotificationConfigurationRequest(*s3.GetBucketNotificationConfigurationRequest) (*request.Request, *s3.NotificationConfiguration)
|
||||||
|
|
||||||
|
GetBucketNotificationConfiguration(*s3.GetBucketNotificationConfigurationRequest) (*s3.NotificationConfiguration, error)
|
||||||
|
|
||||||
|
GetBucketPolicyRequest(*s3.GetBucketPolicyInput) (*request.Request, *s3.GetBucketPolicyOutput)
|
||||||
|
|
||||||
|
GetBucketPolicy(*s3.GetBucketPolicyInput) (*s3.GetBucketPolicyOutput, error)
|
||||||
|
|
||||||
|
GetBucketReplicationRequest(*s3.GetBucketReplicationInput) (*request.Request, *s3.GetBucketReplicationOutput)
|
||||||
|
|
||||||
|
GetBucketReplication(*s3.GetBucketReplicationInput) (*s3.GetBucketReplicationOutput, error)
|
||||||
|
|
||||||
|
GetBucketRequestPaymentRequest(*s3.GetBucketRequestPaymentInput) (*request.Request, *s3.GetBucketRequestPaymentOutput)
|
||||||
|
|
||||||
|
GetBucketRequestPayment(*s3.GetBucketRequestPaymentInput) (*s3.GetBucketRequestPaymentOutput, error)
|
||||||
|
|
||||||
|
GetBucketTaggingRequest(*s3.GetBucketTaggingInput) (*request.Request, *s3.GetBucketTaggingOutput)
|
||||||
|
|
||||||
|
GetBucketTagging(*s3.GetBucketTaggingInput) (*s3.GetBucketTaggingOutput, error)
|
||||||
|
|
||||||
|
GetBucketVersioningRequest(*s3.GetBucketVersioningInput) (*request.Request, *s3.GetBucketVersioningOutput)
|
||||||
|
|
||||||
|
GetBucketVersioning(*s3.GetBucketVersioningInput) (*s3.GetBucketVersioningOutput, error)
|
||||||
|
|
||||||
|
GetBucketWebsiteRequest(*s3.GetBucketWebsiteInput) (*request.Request, *s3.GetBucketWebsiteOutput)
|
||||||
|
|
||||||
|
GetBucketWebsite(*s3.GetBucketWebsiteInput) (*s3.GetBucketWebsiteOutput, error)
|
||||||
|
|
||||||
|
GetObjectRequest(*s3.GetObjectInput) (*request.Request, *s3.GetObjectOutput)
|
||||||
|
|
||||||
|
GetObject(*s3.GetObjectInput) (*s3.GetObjectOutput, error)
|
||||||
|
|
||||||
|
GetObjectAclRequest(*s3.GetObjectAclInput) (*request.Request, *s3.GetObjectAclOutput)
|
||||||
|
|
||||||
|
GetObjectAcl(*s3.GetObjectAclInput) (*s3.GetObjectAclOutput, error)
|
||||||
|
|
||||||
|
GetObjectTorrentRequest(*s3.GetObjectTorrentInput) (*request.Request, *s3.GetObjectTorrentOutput)
|
||||||
|
|
||||||
|
GetObjectTorrent(*s3.GetObjectTorrentInput) (*s3.GetObjectTorrentOutput, error)
|
||||||
|
|
||||||
|
HeadBucketRequest(*s3.HeadBucketInput) (*request.Request, *s3.HeadBucketOutput)
|
||||||
|
|
||||||
|
HeadBucket(*s3.HeadBucketInput) (*s3.HeadBucketOutput, error)
|
||||||
|
|
||||||
|
HeadObjectRequest(*s3.HeadObjectInput) (*request.Request, *s3.HeadObjectOutput)
|
||||||
|
|
||||||
|
HeadObject(*s3.HeadObjectInput) (*s3.HeadObjectOutput, error)
|
||||||
|
|
||||||
|
ListBucketsRequest(*s3.ListBucketsInput) (*request.Request, *s3.ListBucketsOutput)
|
||||||
|
|
||||||
|
ListBuckets(*s3.ListBucketsInput) (*s3.ListBucketsOutput, error)
|
||||||
|
|
||||||
|
ListMultipartUploadsRequest(*s3.ListMultipartUploadsInput) (*request.Request, *s3.ListMultipartUploadsOutput)
|
||||||
|
|
||||||
|
ListMultipartUploads(*s3.ListMultipartUploadsInput) (*s3.ListMultipartUploadsOutput, error)
|
||||||
|
|
||||||
|
ListMultipartUploadsPages(*s3.ListMultipartUploadsInput, func(*s3.ListMultipartUploadsOutput, bool) bool) error
|
||||||
|
|
||||||
|
ListObjectVersionsRequest(*s3.ListObjectVersionsInput) (*request.Request, *s3.ListObjectVersionsOutput)
|
||||||
|
|
||||||
|
ListObjectVersions(*s3.ListObjectVersionsInput) (*s3.ListObjectVersionsOutput, error)
|
||||||
|
|
||||||
|
ListObjectVersionsPages(*s3.ListObjectVersionsInput, func(*s3.ListObjectVersionsOutput, bool) bool) error
|
||||||
|
|
||||||
|
ListObjectsRequest(*s3.ListObjectsInput) (*request.Request, *s3.ListObjectsOutput)
|
||||||
|
|
||||||
|
ListObjects(*s3.ListObjectsInput) (*s3.ListObjectsOutput, error)
|
||||||
|
|
||||||
|
ListObjectsPages(*s3.ListObjectsInput, func(*s3.ListObjectsOutput, bool) bool) error
|
||||||
|
|
||||||
|
ListPartsRequest(*s3.ListPartsInput) (*request.Request, *s3.ListPartsOutput)
|
||||||
|
|
||||||
|
ListParts(*s3.ListPartsInput) (*s3.ListPartsOutput, error)
|
||||||
|
|
||||||
|
ListPartsPages(*s3.ListPartsInput, func(*s3.ListPartsOutput, bool) bool) error
|
||||||
|
|
||||||
|
PutBucketAclRequest(*s3.PutBucketAclInput) (*request.Request, *s3.PutBucketAclOutput)
|
||||||
|
|
||||||
|
PutBucketAcl(*s3.PutBucketAclInput) (*s3.PutBucketAclOutput, error)
|
||||||
|
|
||||||
|
PutBucketCorsRequest(*s3.PutBucketCorsInput) (*request.Request, *s3.PutBucketCorsOutput)
|
||||||
|
|
||||||
|
PutBucketCors(*s3.PutBucketCorsInput) (*s3.PutBucketCorsOutput, error)
|
||||||
|
|
||||||
|
PutBucketLifecycleRequest(*s3.PutBucketLifecycleInput) (*request.Request, *s3.PutBucketLifecycleOutput)
|
||||||
|
|
||||||
|
PutBucketLifecycle(*s3.PutBucketLifecycleInput) (*s3.PutBucketLifecycleOutput, error)
|
||||||
|
|
||||||
|
PutBucketLifecycleConfigurationRequest(*s3.PutBucketLifecycleConfigurationInput) (*request.Request, *s3.PutBucketLifecycleConfigurationOutput)
|
||||||
|
|
||||||
|
PutBucketLifecycleConfiguration(*s3.PutBucketLifecycleConfigurationInput) (*s3.PutBucketLifecycleConfigurationOutput, error)
|
||||||
|
|
||||||
|
PutBucketLoggingRequest(*s3.PutBucketLoggingInput) (*request.Request, *s3.PutBucketLoggingOutput)
|
||||||
|
|
||||||
|
PutBucketLogging(*s3.PutBucketLoggingInput) (*s3.PutBucketLoggingOutput, error)
|
||||||
|
|
||||||
|
PutBucketNotificationRequest(*s3.PutBucketNotificationInput) (*request.Request, *s3.PutBucketNotificationOutput)
|
||||||
|
|
||||||
|
PutBucketNotification(*s3.PutBucketNotificationInput) (*s3.PutBucketNotificationOutput, error)
|
||||||
|
|
||||||
|
PutBucketNotificationConfigurationRequest(*s3.PutBucketNotificationConfigurationInput) (*request.Request, *s3.PutBucketNotificationConfigurationOutput)
|
||||||
|
|
||||||
|
PutBucketNotificationConfiguration(*s3.PutBucketNotificationConfigurationInput) (*s3.PutBucketNotificationConfigurationOutput, error)
|
||||||
|
|
||||||
|
PutBucketPolicyRequest(*s3.PutBucketPolicyInput) (*request.Request, *s3.PutBucketPolicyOutput)
|
||||||
|
|
||||||
|
PutBucketPolicy(*s3.PutBucketPolicyInput) (*s3.PutBucketPolicyOutput, error)
|
||||||
|
|
||||||
|
PutBucketReplicationRequest(*s3.PutBucketReplicationInput) (*request.Request, *s3.PutBucketReplicationOutput)
|
||||||
|
|
||||||
|
PutBucketReplication(*s3.PutBucketReplicationInput) (*s3.PutBucketReplicationOutput, error)
|
||||||
|
|
||||||
|
PutBucketRequestPaymentRequest(*s3.PutBucketRequestPaymentInput) (*request.Request, *s3.PutBucketRequestPaymentOutput)
|
||||||
|
|
||||||
|
PutBucketRequestPayment(*s3.PutBucketRequestPaymentInput) (*s3.PutBucketRequestPaymentOutput, error)
|
||||||
|
|
||||||
|
PutBucketTaggingRequest(*s3.PutBucketTaggingInput) (*request.Request, *s3.PutBucketTaggingOutput)
|
||||||
|
|
||||||
|
PutBucketTagging(*s3.PutBucketTaggingInput) (*s3.PutBucketTaggingOutput, error)
|
||||||
|
|
||||||
|
PutBucketVersioningRequest(*s3.PutBucketVersioningInput) (*request.Request, *s3.PutBucketVersioningOutput)
|
||||||
|
|
||||||
|
PutBucketVersioning(*s3.PutBucketVersioningInput) (*s3.PutBucketVersioningOutput, error)
|
||||||
|
|
||||||
|
PutBucketWebsiteRequest(*s3.PutBucketWebsiteInput) (*request.Request, *s3.PutBucketWebsiteOutput)
|
||||||
|
|
||||||
|
PutBucketWebsite(*s3.PutBucketWebsiteInput) (*s3.PutBucketWebsiteOutput, error)
|
||||||
|
|
||||||
|
PutObjectRequest(*s3.PutObjectInput) (*request.Request, *s3.PutObjectOutput)
|
||||||
|
|
||||||
|
PutObject(*s3.PutObjectInput) (*s3.PutObjectOutput, error)
|
||||||
|
|
||||||
|
PutObjectAclRequest(*s3.PutObjectAclInput) (*request.Request, *s3.PutObjectAclOutput)
|
||||||
|
|
||||||
|
PutObjectAcl(*s3.PutObjectAclInput) (*s3.PutObjectAclOutput, error)
|
||||||
|
|
||||||
|
RestoreObjectRequest(*s3.RestoreObjectInput) (*request.Request, *s3.RestoreObjectOutput)
|
||||||
|
|
||||||
|
RestoreObject(*s3.RestoreObjectInput) (*s3.RestoreObjectOutput, error)
|
||||||
|
|
||||||
|
UploadPartRequest(*s3.UploadPartInput) (*request.Request, *s3.UploadPartOutput)
|
||||||
|
|
||||||
|
UploadPart(*s3.UploadPartInput) (*s3.UploadPartOutput, error)
|
||||||
|
|
||||||
|
UploadPartCopyRequest(*s3.UploadPartCopyInput) (*request.Request, *s3.UploadPartCopyOutput)
|
||||||
|
|
||||||
|
UploadPartCopy(*s3.UploadPartCopyInput) (*s3.UploadPartCopyOutput, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ S3API = (*s3.S3)(nil)
|
3
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3manager/doc.go
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3manager/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Package s3manager provides utilities to upload and download objects from
|
||||||
|
// S3 concurrently. Helpful for when working with large objects.
|
||||||
|
package s3manager
|
354
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3manager/download.go
generated
vendored
Normal file
354
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3manager/download.go
generated
vendored
Normal file
|
@ -0,0 +1,354 @@
|
||||||
|
package s3manager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3/s3iface"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultDownloadPartSize is the default range of bytes to get at a time when
|
||||||
|
// using Download().
|
||||||
|
const DefaultDownloadPartSize = 1024 * 1024 * 5
|
||||||
|
|
||||||
|
// DefaultDownloadConcurrency is the default number of goroutines to spin up
|
||||||
|
// when using Download().
|
||||||
|
const DefaultDownloadConcurrency = 5
|
||||||
|
|
||||||
|
// The Downloader structure that calls Download(). It is safe to call Download()
|
||||||
|
// on this structure for multiple objects and across concurrent goroutines.
|
||||||
|
// Mutating the Downloader's properties is not safe to be done concurrently.
|
||||||
|
type Downloader 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.
|
||||||
|
S3 s3iface.S3API
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDownloader creates a new Downloader instance to downloads objects from
|
||||||
|
// S3 in concurrent chunks. Pass in additional functional options to customize
|
||||||
|
// the downloader behavior. Requires a client.ConfigProvider in order to create
|
||||||
|
// a S3 service client. The session.Session satisfies the client.ConfigProvider
|
||||||
|
// interface.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// // The session the S3 Downloader will use
|
||||||
|
// sess := session.New()
|
||||||
|
//
|
||||||
|
// // Create a downloader with the session and default options
|
||||||
|
// downloader := s3manager.NewDownloader(sess)
|
||||||
|
//
|
||||||
|
// // Create a downloader with the session and custom options
|
||||||
|
// downloader := s3manager.NewDownloader(sess, func(d *s3manager.Uploader) {
|
||||||
|
// d.PartSize = 64 * 1024 * 1024 // 64MB per part
|
||||||
|
// })
|
||||||
|
func NewDownloader(c client.ConfigProvider, options ...func(*Downloader)) *Downloader {
|
||||||
|
d := &Downloader{
|
||||||
|
S3: s3.New(c),
|
||||||
|
PartSize: DefaultDownloadPartSize,
|
||||||
|
Concurrency: DefaultDownloadConcurrency,
|
||||||
|
}
|
||||||
|
for _, option := range options {
|
||||||
|
option(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDownloaderWithClient creates a new Downloader instance to downloads
|
||||||
|
// objects from S3 in concurrent chunks. Pass in additional functional
|
||||||
|
// options to customize the downloader behavior. Requires a S3 service client
|
||||||
|
// to make S3 API calls.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// // The S3 client the S3 Downloader will use
|
||||||
|
// s3Svc := s3.new(session.New())
|
||||||
|
//
|
||||||
|
// // Create a downloader with the s3 client and default options
|
||||||
|
// downloader := s3manager.NewDownloaderWithClient(s3Svc)
|
||||||
|
//
|
||||||
|
// // Create a downloader with the s3 client and custom options
|
||||||
|
// downloader := s3manager.NewDownloaderWithClient(s3Svc, func(d *s3manager.Uploader) {
|
||||||
|
// d.PartSize = 64 * 1024 * 1024 // 64MB per part
|
||||||
|
// })
|
||||||
|
func NewDownloaderWithClient(svc s3iface.S3API, options ...func(*Downloader)) *Downloader {
|
||||||
|
d := &Downloader{
|
||||||
|
S3: svc,
|
||||||
|
PartSize: DefaultDownloadPartSize,
|
||||||
|
Concurrency: DefaultDownloadConcurrency,
|
||||||
|
}
|
||||||
|
for _, option := range options {
|
||||||
|
option(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download downloads an object in S3 and writes the payload into w using
|
||||||
|
// concurrent GET requests.
|
||||||
|
//
|
||||||
|
// Additional functional options can be provided to configure the individual
|
||||||
|
// upload. These options are copies of the Uploader instance Upload is called from.
|
||||||
|
// Modifying the options will not impact the original Uploader instance.
|
||||||
|
//
|
||||||
|
// It is safe to call this method concurrently across goroutines.
|
||||||
|
//
|
||||||
|
// The w io.WriterAt can be satisfied by an os.File to do multipart concurrent
|
||||||
|
// downloads, or in memory []byte wrapper using aws.WriteAtBuffer.
|
||||||
|
func (d Downloader) Download(w io.WriterAt, input *s3.GetObjectInput, options ...func(*Downloader)) (n int64, err error) {
|
||||||
|
impl := downloader{w: w, in: input, ctx: d}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
option(&impl.ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return impl.download()
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloader is the implementation structure used internally by Downloader.
|
||||||
|
type downloader struct {
|
||||||
|
ctx Downloader
|
||||||
|
|
||||||
|
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.ctx.Concurrency == 0 {
|
||||||
|
d.ctx.Concurrency = DefaultDownloadConcurrency
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.ctx.PartSize == 0 {
|
||||||
|
d.ctx.PartSize = DefaultDownloadPartSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// download performs the implementation of the object download across ranged
|
||||||
|
// GETs.
|
||||||
|
func (d *downloader) download() (n int64, err error) {
|
||||||
|
d.init()
|
||||||
|
|
||||||
|
// Spin off first worker to check additional header information
|
||||||
|
d.getChunk()
|
||||||
|
|
||||||
|
if total := d.getTotalBytes(); total >= 0 {
|
||||||
|
// Spin up workers
|
||||||
|
ch := make(chan dlchunk, d.ctx.Concurrency)
|
||||||
|
|
||||||
|
for i := 0; i < d.ctx.Concurrency; i++ {
|
||||||
|
d.wg.Add(1)
|
||||||
|
go d.downloadPart(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign work
|
||||||
|
for d.getErr() == nil {
|
||||||
|
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.ctx.PartSize}
|
||||||
|
d.pos += d.ctx.PartSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for completion
|
||||||
|
close(ch)
|
||||||
|
d.wg.Wait()
|
||||||
|
} else {
|
||||||
|
// Checking if we read anything new
|
||||||
|
for d.err == nil {
|
||||||
|
d.getChunk()
|
||||||
|
}
|
||||||
|
|
||||||
|
// We expect a 416 error letting us know we are done downloading the
|
||||||
|
// total bytes. Since we do not know the content's length, this will
|
||||||
|
// keep grabbing chunks of data until the range of bytes specified in
|
||||||
|
// the request is out of range of the content. Once, this happens, a
|
||||||
|
// 416 should occur.
|
||||||
|
e, ok := d.err.(awserr.RequestFailure)
|
||||||
|
if ok && e.StatusCode() == http.StatusRequestedRangeNotSatisfiable {
|
||||||
|
d.err = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
d.downloadChunk(chunk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getChunk grabs a chunk of data from the body.
|
||||||
|
// Not thread safe. Should only used when grabbing data on a single thread.
|
||||||
|
func (d *downloader) getChunk() {
|
||||||
|
chunk := dlchunk{w: d.w, start: d.pos, size: d.ctx.PartSize}
|
||||||
|
d.pos += d.ctx.PartSize
|
||||||
|
d.downloadChunk(chunk)
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloadChunk downloads the chunk froom s3
|
||||||
|
func (d *downloader) downloadChunk(chunk dlchunk) {
|
||||||
|
if d.getErr() != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 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
|
||||||
|
|
||||||
|
req, resp := d.ctx.S3.GetObjectRequest(in)
|
||||||
|
req.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("S3Manager"))
|
||||||
|
err := req.Send()
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// setTotalBytes is a thread-safe setter for setting the total byte status.
|
||||||
|
// Will extract the object's total bytes from the Content-Range if the file
|
||||||
|
// will be chunked, or Content-Length. Content-Length is used when the response
|
||||||
|
// does not include a Content-Range. Meaning the object was not chunked. This
|
||||||
|
// occurs when the full file fits within the PartSize directive.
|
||||||
|
func (d *downloader) setTotalBytes(resp *s3.GetObjectOutput) {
|
||||||
|
d.m.Lock()
|
||||||
|
defer d.m.Unlock()
|
||||||
|
|
||||||
|
if d.totalBytes >= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.ContentRange == nil {
|
||||||
|
// ContentRange is nil when the full file contents is provied, and
|
||||||
|
// is not chunked. Use ContentLength instead.
|
||||||
|
if resp.ContentLength != nil {
|
||||||
|
d.totalBytes = *resp.ContentLength
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parts := strings.Split(*resp.ContentRange, "/")
|
||||||
|
|
||||||
|
total := int64(-1)
|
||||||
|
var err error
|
||||||
|
// Checking for whether or not a numbered total exists
|
||||||
|
// If one does not exist, we will assume the total to be -1, undefined,
|
||||||
|
// and sequentially download each chunk until hitting a 416 error
|
||||||
|
totalStr := parts[len(parts)-1]
|
||||||
|
if totalStr != "*" {
|
||||||
|
total, err = strconv.ParseInt(totalStr, 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
|
||||||
|
}
|
23
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3manager/s3manageriface/interface.go
generated
vendored
Normal file
23
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3manager/s3manageriface/interface.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
// Package s3manageriface provides an interface for the s3manager package
|
||||||
|
package s3manageriface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DownloaderAPI is the interface type for s3manager.Downloader.
|
||||||
|
type DownloaderAPI interface {
|
||||||
|
Download(io.WriterAt, *s3.GetObjectInput, ...func(*s3manager.Downloader)) (int64, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ DownloaderAPI = (*s3manager.Downloader)(nil)
|
||||||
|
|
||||||
|
// UploaderAPI is the interface type for s3manager.Uploader.
|
||||||
|
type UploaderAPI interface {
|
||||||
|
Upload(*s3manager.UploadInput, ...func(*s3manager.Uploader)) (*s3manager.UploadOutput, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ UploaderAPI = (*s3manager.Uploader)(nil)
|
661
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3manager/upload.go
generated
vendored
Normal file
661
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/s3manager/upload.go
generated
vendored
Normal file
|
@ -0,0 +1,661 @@
|
||||||
|
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/aws/client"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
"github.com/aws/aws-sdk-go/service/s3/s3iface"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MaxUploadParts is the maximum allowed number of parts in a multi-part upload
|
||||||
|
// on Amazon S3.
|
||||||
|
const MaxUploadParts = 10000
|
||||||
|
|
||||||
|
// MinUploadPartSize is the minimum allowed part size when uploading a part to
|
||||||
|
// Amazon S3.
|
||||||
|
const MinUploadPartSize int64 = 1024 * 1024 * 5
|
||||||
|
|
||||||
|
// DefaultUploadPartSize is the default part size to buffer chunks of a
|
||||||
|
// payload into.
|
||||||
|
const DefaultUploadPartSize = MinUploadPartSize
|
||||||
|
|
||||||
|
// DefaultUploadConcurrency is the default number of goroutines to spin up when
|
||||||
|
// using Upload().
|
||||||
|
const DefaultUploadConcurrency = 5
|
||||||
|
|
||||||
|
// 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 version of the object that was uploaded. Will only be populated if
|
||||||
|
// the S3 Bucket is versioned. If the bucket is not versioned this field
|
||||||
|
// will not be set.
|
||||||
|
VersionID *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
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Uploader structure that calls Upload(). It is safe to call Upload()
|
||||||
|
// on this structure for multiple objects and across concurrent goroutines.
|
||||||
|
// Mutating the Uploader's properties is not safe to be done concurrently.
|
||||||
|
type Uploader 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
|
||||||
|
|
||||||
|
// MaxUploadParts is the max number of parts which will be uploaded to S3.
|
||||||
|
// Will be used to calculate the partsize of the object to be uploaded.
|
||||||
|
// E.g: 5GB file, with MaxUploadParts set to 100, will upload the file
|
||||||
|
// as 100, 50MB parts.
|
||||||
|
// With a limited of s3.MaxUploadParts (10,000 parts).
|
||||||
|
MaxUploadParts int
|
||||||
|
|
||||||
|
// The client to use when uploading to S3.
|
||||||
|
S3 s3iface.S3API
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUploader creates a new Uploader instance to upload objects to S3. Pass In
|
||||||
|
// additional functional options to customize the uploader's behavior. Requires a
|
||||||
|
// client.ConfigProvider in order to create a S3 service client. The session.Session
|
||||||
|
// satisfies the client.ConfigProvider interface.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// // The session the S3 Uploader will use
|
||||||
|
// sess := session.New()
|
||||||
|
//
|
||||||
|
// // Create an uploader with the session and default options
|
||||||
|
// uploader := s3manager.NewUploader(sess)
|
||||||
|
//
|
||||||
|
// // Create an uploader with the session and custom options
|
||||||
|
// uploader := s3manager.NewUploader(session, func(u *s3manager.Uploader) {
|
||||||
|
// u.PartSize = 64 * 1024 * 1024 // 64MB per part
|
||||||
|
// })
|
||||||
|
func NewUploader(c client.ConfigProvider, options ...func(*Uploader)) *Uploader {
|
||||||
|
u := &Uploader{
|
||||||
|
S3: s3.New(c),
|
||||||
|
PartSize: DefaultUploadPartSize,
|
||||||
|
Concurrency: DefaultUploadConcurrency,
|
||||||
|
LeavePartsOnError: false,
|
||||||
|
MaxUploadParts: MaxUploadParts,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
option(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUploaderWithClient creates a new Uploader instance to upload objects to S3. Pass in
|
||||||
|
// additional functional options to customize the uploader's behavior. Requires
|
||||||
|
// a S3 service client to make S3 API calls.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// // S3 service client the Upload manager will use.
|
||||||
|
// s3Svc := s3.New(session.New())
|
||||||
|
//
|
||||||
|
// // Create an uploader with S3 client and default options
|
||||||
|
// uploader := s3manager.NewUploaderWithClient(s3Svc)
|
||||||
|
//
|
||||||
|
// // Create an uploader with S3 client and custom options
|
||||||
|
// uploader := s3manager.NewUploaderWithClient(s3Svc, func(u *s3manager.Uploader) {
|
||||||
|
// u.PartSize = 64 * 1024 * 1024 // 64MB per part
|
||||||
|
// })
|
||||||
|
func NewUploaderWithClient(svc s3iface.S3API, options ...func(*Uploader)) *Uploader {
|
||||||
|
u := &Uploader{
|
||||||
|
S3: svc,
|
||||||
|
PartSize: DefaultUploadPartSize,
|
||||||
|
Concurrency: DefaultUploadConcurrency,
|
||||||
|
LeavePartsOnError: false,
|
||||||
|
MaxUploadParts: MaxUploadParts,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
option(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 Uploader's parameters.
|
||||||
|
//
|
||||||
|
// Additional functional options can be provided to configure the individual
|
||||||
|
// upload. These options are copies of the Uploader instance Upload is called from.
|
||||||
|
// Modifying the options will not impact the original Uploader instance.
|
||||||
|
//
|
||||||
|
// It is safe to call this method concurrently across goroutines.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// // Upload input parameters
|
||||||
|
// upParams := &s3manager.UploadInput{
|
||||||
|
// Bucket: &bucketName,
|
||||||
|
// Key: &keyName,
|
||||||
|
// Body: file,
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Perform an upload.
|
||||||
|
// result, err := uploader.Upload(upParams)
|
||||||
|
//
|
||||||
|
// // Perform upload with options different than the those in the Uploader.
|
||||||
|
// result, err := uploader.Upload(upParams, func(u *s3manager.Uploader) {
|
||||||
|
// u.PartSize = 10 * 1024 * 1024 // 10MB part size
|
||||||
|
// u.LeavePartsOnError = true // Dont delete the parts if the upload fails.
|
||||||
|
// })
|
||||||
|
func (u Uploader) Upload(input *UploadInput, options ...func(*Uploader)) (*UploadOutput, error) {
|
||||||
|
i := uploader{in: input, ctx: u}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
option(&i.ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return i.upload()
|
||||||
|
}
|
||||||
|
|
||||||
|
// internal structure to manage an upload to S3.
|
||||||
|
type uploader struct {
|
||||||
|
ctx Uploader
|
||||||
|
|
||||||
|
in *UploadInput
|
||||||
|
|
||||||
|
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.ctx.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.ctx.Concurrency == 0 {
|
||||||
|
u.ctx.Concurrency = DefaultUploadConcurrency
|
||||||
|
}
|
||||||
|
if u.ctx.PartSize == 0 {
|
||||||
|
u.ctx.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 and account for
|
||||||
|
// integer division truncation.
|
||||||
|
if u.totalSize/u.ctx.PartSize >= int64(u.ctx.MaxUploadParts) {
|
||||||
|
// Add one to the part size to account for remainders
|
||||||
|
// during the size calculation. e.g odd number of bytes.
|
||||||
|
u.ctx.PartSize = (u.totalSize / int64(u.ctx.MaxUploadParts)) + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.ctx.PartSize
|
||||||
|
if u.totalSize >= 0 {
|
||||||
|
bytesLeft := u.totalSize - u.readerPos
|
||||||
|
|
||||||
|
if bytesLeft == 0 {
|
||||||
|
err = io.EOF
|
||||||
|
n = bytesLeft
|
||||||
|
} else if bytesLeft <= u.ctx.PartSize {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
n = bytesLeft
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := io.NewSectionReader(r, u.readerPos, n)
|
||||||
|
u.readerPos += n
|
||||||
|
|
||||||
|
return buf, err
|
||||||
|
|
||||||
|
default:
|
||||||
|
packet := make([]byte, u.ctx.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, out := u.ctx.S3.PutObjectRequest(params)
|
||||||
|
req.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("S3Manager"))
|
||||||
|
if err := req.Send(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
url := req.HTTPRequest.URL.String()
|
||||||
|
return &UploadOutput{
|
||||||
|
Location: url,
|
||||||
|
VersionID: out.VersionId,
|
||||||
|
}, 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
|
||||||
|
req, resp := u.ctx.S3.CreateMultipartUploadRequest(params)
|
||||||
|
req.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("S3Manager"))
|
||||||
|
if err := req.Send(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u.uploadID = *resp.UploadId
|
||||||
|
|
||||||
|
// Create the workers
|
||||||
|
ch := make(chan chunk, u.ctx.Concurrency)
|
||||||
|
for i := 0; i < u.ctx.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(u.ctx.MaxUploadParts) || num > int64(MaxUploadParts) {
|
||||||
|
var msg string
|
||||||
|
if num > int64(u.ctx.MaxUploadParts) {
|
||||||
|
msg = fmt.Sprintf("exceeded total allowed configured MaxUploadParts (%d). Adjust PartSize to fit in this limit",
|
||||||
|
u.ctx.MaxUploadParts)
|
||||||
|
} else {
|
||||||
|
msg = fmt.Sprintf("exceeded total allowed S3 limit MaxUploadParts (%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,
|
||||||
|
VersionID: complete.VersionId,
|
||||||
|
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 {
|
||||||
|
req, resp := u.ctx.S3.UploadPartRequest(&s3.UploadPartInput{
|
||||||
|
Bucket: u.in.Bucket,
|
||||||
|
Key: u.in.Key,
|
||||||
|
Body: c.buf,
|
||||||
|
UploadId: &u.uploadID,
|
||||||
|
PartNumber: &c.num,
|
||||||
|
})
|
||||||
|
req.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("S3Manager"))
|
||||||
|
if err := req.Send(); 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.ctx.LeavePartsOnError {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req, _ := u.ctx.S3.AbortMultipartUploadRequest(&s3.AbortMultipartUploadInput{
|
||||||
|
Bucket: u.in.Bucket,
|
||||||
|
Key: u.in.Key,
|
||||||
|
UploadId: &u.uploadID,
|
||||||
|
})
|
||||||
|
req.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("S3Manager"))
|
||||||
|
req.Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
req, resp := u.ctx.S3.CompleteMultipartUploadRequest(&s3.CompleteMultipartUploadInput{
|
||||||
|
Bucket: u.in.Bucket,
|
||||||
|
Key: u.in.Key,
|
||||||
|
UploadId: &u.uploadID,
|
||||||
|
MultipartUpload: &s3.CompletedMultipartUpload{Parts: u.parts},
|
||||||
|
})
|
||||||
|
req.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("S3Manager"))
|
||||||
|
if err := req.Send(); err != nil {
|
||||||
|
u.seterr(err)
|
||||||
|
u.fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp
|
||||||
|
}
|
86
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/service.go
generated
vendored
Normal file
86
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/service.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
|
||||||
|
|
||||||
|
package s3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client/metadata"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol/restxml"
|
||||||
|
"github.com/aws/aws-sdk-go/private/signer/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
// S3 is a client for Amazon S3.
|
||||||
|
//The service client's operations are safe to be used concurrently.
|
||||||
|
// It is not safe to mutate any of the client's properties though.
|
||||||
|
type S3 struct {
|
||||||
|
*client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used for custom client initialization logic
|
||||||
|
var initClient func(*client.Client)
|
||||||
|
|
||||||
|
// Used for custom request initialization logic
|
||||||
|
var initRequest func(*request.Request)
|
||||||
|
|
||||||
|
// A ServiceName is the name of the service the client will make API calls to.
|
||||||
|
const ServiceName = "s3"
|
||||||
|
|
||||||
|
// New creates a new instance of the S3 client with a session.
|
||||||
|
// If additional configuration is needed for the client instance use the optional
|
||||||
|
// aws.Config parameter to add your extra config.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// // Create a S3 client from just a session.
|
||||||
|
// svc := s3.New(mySession)
|
||||||
|
//
|
||||||
|
// // Create a S3 client with additional configuration
|
||||||
|
// svc := s3.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
|
||||||
|
func New(p client.ConfigProvider, cfgs ...*aws.Config) *S3 {
|
||||||
|
c := p.ClientConfig(ServiceName, cfgs...)
|
||||||
|
return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newClient creates, initializes and returns a new service client instance.
|
||||||
|
func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *S3 {
|
||||||
|
svc := &S3{
|
||||||
|
Client: client.New(
|
||||||
|
cfg,
|
||||||
|
metadata.ClientInfo{
|
||||||
|
ServiceName: ServiceName,
|
||||||
|
SigningRegion: signingRegion,
|
||||||
|
Endpoint: endpoint,
|
||||||
|
APIVersion: "2006-03-01",
|
||||||
|
},
|
||||||
|
handlers,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handlers
|
||||||
|
svc.Handlers.Sign.PushBack(v4.Sign)
|
||||||
|
svc.Handlers.Build.PushBackNamed(restxml.BuildHandler)
|
||||||
|
svc.Handlers.Unmarshal.PushBackNamed(restxml.UnmarshalHandler)
|
||||||
|
svc.Handlers.UnmarshalMeta.PushBackNamed(restxml.UnmarshalMetaHandler)
|
||||||
|
svc.Handlers.UnmarshalError.PushBackNamed(restxml.UnmarshalErrorHandler)
|
||||||
|
|
||||||
|
// Run custom client initialization if present
|
||||||
|
if initClient != nil {
|
||||||
|
initClient(svc.Client)
|
||||||
|
}
|
||||||
|
|
||||||
|
return svc
|
||||||
|
}
|
||||||
|
|
||||||
|
// newRequest creates a new request for a S3 operation and runs any
|
||||||
|
// custom request initialization.
|
||||||
|
func (c *S3) newRequest(op *request.Operation, params, data interface{}) *request.Request {
|
||||||
|
req := c.NewRequest(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/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errSSERequiresSSL = awserr.New("ConfigError", "cannot send SSE keys over HTTP.", nil)
|
||||||
|
|
||||||
|
func validateSSERequiresSSL(r *request.Request) {
|
||||||
|
if r.HTTPRequest.URL.Scheme != "https" {
|
||||||
|
p, _ := awsutil.ValuesAtPath(r.Params, "SSECustomerKey||CopySourceSSECustomerKey")
|
||||||
|
if len(p) > 0 {
|
||||||
|
r.Error = errSSERequiresSSL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeSSEKeys(r *request.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/statusok_error.go
generated
vendored
Normal file
36
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/statusok_error.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package s3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
func copyMultipartStatusOKUnmarhsalError(r *request.Request) {
|
||||||
|
b, err := ioutil.ReadAll(r.HTTPResponse.Body)
|
||||||
|
if err != nil {
|
||||||
|
r.Error = awserr.New("SerializationError", "unable to read response body", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
body := bytes.NewReader(b)
|
||||||
|
r.HTTPResponse.Body = aws.ReadSeekCloser(body)
|
||||||
|
defer r.HTTPResponse.Body.(aws.ReaderSeekerCloser).Seek(0, 0)
|
||||||
|
|
||||||
|
if body.Len() == 0 {
|
||||||
|
// If there is no body don't attempt to parse the body.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
unmarshalError(r)
|
||||||
|
if err, ok := r.Error.(awserr.Error); ok && err != nil {
|
||||||
|
if err.Code() == "SerializationError" {
|
||||||
|
r.Error = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.HTTPResponse.StatusCode = http.StatusServiceUnavailable
|
||||||
|
}
|
||||||
|
}
|
57
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/unmarshal_error.go
generated
vendored
Normal file
57
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/unmarshal_error.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package s3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
type xmlErrorResponse struct {
|
||||||
|
XMLName xml.Name `xml:"Error"`
|
||||||
|
Code string `xml:"Code"`
|
||||||
|
Message string `xml:"Message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalError(r *request.Request) {
|
||||||
|
defer r.HTTPResponse.Body.Close()
|
||||||
|
|
||||||
|
if r.HTTPResponse.StatusCode == http.StatusMovedPermanently {
|
||||||
|
r.Error = awserr.NewRequestFailure(
|
||||||
|
awserr.New("BucketRegionError",
|
||||||
|
fmt.Sprintf("incorrect region, the bucket is not in '%s' region",
|
||||||
|
aws.StringValue(r.Config.Region)),
|
||||||
|
nil),
|
||||||
|
r.HTTPResponse.StatusCode,
|
||||||
|
r.RequestID,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.HTTPResponse.ContentLength == 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,
|
||||||
|
r.RequestID,
|
||||||
|
)
|
||||||
|
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,
|
||||||
|
r.RequestID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
117
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/waiters.go
generated
vendored
Normal file
117
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/s3/waiters.go
generated
vendored
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
|
||||||
|
|
||||||
|
package s3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go/private/waiter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *S3) WaitUntilBucketExists(input *HeadBucketInput) error {
|
||||||
|
waiterCfg := waiter.Config{
|
||||||
|
Operation: "HeadBucket",
|
||||||
|
Delay: 5,
|
||||||
|
MaxAttempts: 20,
|
||||||
|
Acceptors: []waiter.WaitAcceptor{
|
||||||
|
{
|
||||||
|
State: "success",
|
||||||
|
Matcher: "status",
|
||||||
|
Argument: "",
|
||||||
|
Expected: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
State: "success",
|
||||||
|
Matcher: "status",
|
||||||
|
Argument: "",
|
||||||
|
Expected: 403,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
State: "retry",
|
||||||
|
Matcher: "status",
|
||||||
|
Argument: "",
|
||||||
|
Expected: 404,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
w := waiter.Waiter{
|
||||||
|
Client: c,
|
||||||
|
Input: input,
|
||||||
|
Config: waiterCfg,
|
||||||
|
}
|
||||||
|
return w.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *S3) WaitUntilBucketNotExists(input *HeadBucketInput) error {
|
||||||
|
waiterCfg := waiter.Config{
|
||||||
|
Operation: "HeadBucket",
|
||||||
|
Delay: 5,
|
||||||
|
MaxAttempts: 20,
|
||||||
|
Acceptors: []waiter.WaitAcceptor{
|
||||||
|
{
|
||||||
|
State: "success",
|
||||||
|
Matcher: "status",
|
||||||
|
Argument: "",
|
||||||
|
Expected: 404,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
w := waiter.Waiter{
|
||||||
|
Client: c,
|
||||||
|
Input: input,
|
||||||
|
Config: waiterCfg,
|
||||||
|
}
|
||||||
|
return w.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *S3) WaitUntilObjectExists(input *HeadObjectInput) error {
|
||||||
|
waiterCfg := waiter.Config{
|
||||||
|
Operation: "HeadObject",
|
||||||
|
Delay: 5,
|
||||||
|
MaxAttempts: 20,
|
||||||
|
Acceptors: []waiter.WaitAcceptor{
|
||||||
|
{
|
||||||
|
State: "success",
|
||||||
|
Matcher: "status",
|
||||||
|
Argument: "",
|
||||||
|
Expected: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
State: "retry",
|
||||||
|
Matcher: "status",
|
||||||
|
Argument: "",
|
||||||
|
Expected: 404,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
w := waiter.Waiter{
|
||||||
|
Client: c,
|
||||||
|
Input: input,
|
||||||
|
Config: waiterCfg,
|
||||||
|
}
|
||||||
|
return w.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *S3) WaitUntilObjectNotExists(input *HeadObjectInput) error {
|
||||||
|
waiterCfg := waiter.Config{
|
||||||
|
Operation: "HeadObject",
|
||||||
|
Delay: 5,
|
||||||
|
MaxAttempts: 20,
|
||||||
|
Acceptors: []waiter.WaitAcceptor{
|
||||||
|
{
|
||||||
|
State: "success",
|
||||||
|
Matcher: "status",
|
||||||
|
Argument: "",
|
||||||
|
Expected: 404,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
w := waiter.Waiter{
|
||||||
|
Client: c,
|
||||||
|
Input: input,
|
||||||
|
Config: waiterCfg,
|
||||||
|
}
|
||||||
|
return w.Wait()
|
||||||
|
}
|
143
Godeps/_workspace/src/github.com/docker/goamz/cloudfront/cloudfront.go
generated
vendored
143
Godeps/_workspace/src/github.com/docker/goamz/cloudfront/cloudfront.go
generated
vendored
|
@ -1,143 +0,0 @@
|
||||||
package cloudfront
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/sha1"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/docker/goamz/aws"
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CloudFront struct {
|
|
||||||
BaseURL string
|
|
||||||
keyPairId string
|
|
||||||
key *rsa.PrivateKey
|
|
||||||
}
|
|
||||||
|
|
||||||
var base64Replacer = strings.NewReplacer("=", "_", "+", "-", "/", "~")
|
|
||||||
|
|
||||||
func NewKeyLess(auth aws.Auth, baseurl string) *CloudFront {
|
|
||||||
return &CloudFront{keyPairId: auth.AccessKey, BaseURL: baseurl}
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(baseurl string, key *rsa.PrivateKey, keyPairId string) *CloudFront {
|
|
||||||
return &CloudFront{
|
|
||||||
BaseURL: baseurl,
|
|
||||||
keyPairId: keyPairId,
|
|
||||||
key: key,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type epochTime struct {
|
|
||||||
EpochTime int64 `json:"AWS:EpochTime"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type condition struct {
|
|
||||||
DateLessThan epochTime
|
|
||||||
}
|
|
||||||
|
|
||||||
type statement struct {
|
|
||||||
Resource string
|
|
||||||
Condition condition
|
|
||||||
}
|
|
||||||
|
|
||||||
type policy struct {
|
|
||||||
Statement []statement
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildPolicy(resource string, expireTime time.Time) ([]byte, error) {
|
|
||||||
p := &policy{
|
|
||||||
Statement: []statement{
|
|
||||||
statement{
|
|
||||||
Resource: resource,
|
|
||||||
Condition: condition{
|
|
||||||
DateLessThan: epochTime{
|
|
||||||
EpochTime: expireTime.Truncate(time.Millisecond).Unix(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Marshal(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cf *CloudFront) generateSignature(policy []byte) (string, error) {
|
|
||||||
hash := sha1.New()
|
|
||||||
_, err := hash.Write(policy)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
hashed := hash.Sum(nil)
|
|
||||||
var signed []byte
|
|
||||||
if cf.key.Validate() == nil {
|
|
||||||
signed, err = rsa.SignPKCS1v15(nil, cf.key, crypto.SHA1, hashed)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
signed = hashed
|
|
||||||
}
|
|
||||||
encoded := base64Replacer.Replace(base64.StdEncoding.EncodeToString(signed))
|
|
||||||
|
|
||||||
return encoded, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a signed url using RSAwithSHA1 as specified by
|
|
||||||
// http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-canned-policy.html#private-content-canned-policy-creating-signature
|
|
||||||
func (cf *CloudFront) CannedSignedURL(path, queryString string, expires time.Time) (string, error) {
|
|
||||||
resource := cf.BaseURL + path
|
|
||||||
if queryString != "" {
|
|
||||||
resource = path + "?" + queryString
|
|
||||||
}
|
|
||||||
|
|
||||||
policy, err := buildPolicy(resource, expires)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
signature, err := cf.generateSignature(policy)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TOOD: Do this once
|
|
||||||
uri, err := url.Parse(cf.BaseURL)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
uri.RawQuery = queryString
|
|
||||||
if queryString != "" {
|
|
||||||
uri.RawQuery += "&"
|
|
||||||
}
|
|
||||||
|
|
||||||
expireTime := expires.Truncate(time.Millisecond).Unix()
|
|
||||||
|
|
||||||
uri.Path = path
|
|
||||||
uri.RawQuery += fmt.Sprintf("Expires=%d&Signature=%s&Key-Pair-Id=%s", expireTime, signature, cf.keyPairId)
|
|
||||||
|
|
||||||
return uri.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cloudfront *CloudFront) SignedURL(path, querystrings string, expires time.Time) string {
|
|
||||||
policy := `{"Statement":[{"Resource":"` + path + "?" + querystrings + `,"Condition":{"DateLessThan":{"AWS:EpochTime":` + strconv.FormatInt(expires.Truncate(time.Millisecond).Unix(), 10) + `}}}]}`
|
|
||||||
|
|
||||||
hash := sha1.New()
|
|
||||||
hash.Write([]byte(policy))
|
|
||||||
b := hash.Sum(nil)
|
|
||||||
he := base64.StdEncoding.EncodeToString(b)
|
|
||||||
|
|
||||||
policySha1 := he
|
|
||||||
|
|
||||||
url := cloudfront.BaseURL + path + "?" + querystrings + "&Expires=" + strconv.FormatInt(expires.Unix(), 10) + "&Signature=" + policySha1 + "&Key-Pair-Id=" + cloudfront.keyPairId
|
|
||||||
|
|
||||||
return url
|
|
||||||
}
|
|
4
Godeps/_workspace/src/github.com/go-ini/ini/.gitignore
generated
vendored
Normal file
4
Godeps/_workspace/src/github.com/go-ini/ini/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
testdata/conf_out.ini
|
||||||
|
ini.sublime-project
|
||||||
|
ini.sublime-workspace
|
||||||
|
testdata/conf_reflect.ini
|
191
Godeps/_workspace/src/github.com/go-ini/ini/LICENSE
generated
vendored
Normal file
191
Godeps/_workspace/src/github.com/go-ini/ini/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
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:
|
||||||
|
|
||||||
|
You must give any other recipients of the Work or Derivative Works a copy of
|
||||||
|
this License; and
|
||||||
|
You must cause any modified files to carry prominent notices stating that You
|
||||||
|
changed the files; and
|
||||||
|
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
|
||||||
|
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.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following boilerplate
|
||||||
|
notice, with the fields enclosed by brackets "[]" replaced with your own
|
||||||
|
identifying information. (Don't include the brackets!) The text should be
|
||||||
|
enclosed in the appropriate comment syntax for the file format. We also
|
||||||
|
recommend that a file or class name and description of purpose be included on
|
||||||
|
the same "printed page" as the copyright notice for easier identification within
|
||||||
|
third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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.
|
590
Godeps/_workspace/src/github.com/go-ini/ini/README.md
generated
vendored
Normal file
590
Godeps/_workspace/src/github.com/go-ini/ini/README.md
generated
vendored
Normal file
|
@ -0,0 +1,590 @@
|
||||||
|
ini [![Build Status](https://drone.io/github.com/go-ini/ini/status.png)](https://drone.io/github.com/go-ini/ini/latest) [![](http://gocover.io/_badge/github.com/go-ini/ini)](http://gocover.io/github.com/go-ini/ini)
|
||||||
|
===
|
||||||
|
|
||||||
|
![](https://avatars0.githubusercontent.com/u/10216035?v=3&s=200)
|
||||||
|
|
||||||
|
Package ini provides INI file read and write functionality in Go.
|
||||||
|
|
||||||
|
[简体中文](README_ZH.md)
|
||||||
|
|
||||||
|
## Feature
|
||||||
|
|
||||||
|
- Load multiple data sources(`[]byte` or file) with overwrites.
|
||||||
|
- Read with recursion values.
|
||||||
|
- Read with parent-child sections.
|
||||||
|
- Read with auto-increment key names.
|
||||||
|
- Read with multiple-line values.
|
||||||
|
- Read with tons of helper methods.
|
||||||
|
- Read and convert values to Go types.
|
||||||
|
- Read and **WRITE** comments of sections and keys.
|
||||||
|
- Manipulate sections, keys and comments with ease.
|
||||||
|
- Keep sections and keys in order as you parse and save.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To use a tagged revision:
|
||||||
|
|
||||||
|
go get gopkg.in/ini.v1
|
||||||
|
|
||||||
|
To use with latest changes:
|
||||||
|
|
||||||
|
go get github.com/go-ini/ini
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
If you want to test on your machine, please apply `-t` flag:
|
||||||
|
|
||||||
|
go get -t gopkg.in/ini.v1
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### Loading from data sources
|
||||||
|
|
||||||
|
A **Data Source** is either raw data in type `[]byte` or a file name with type `string` and you can load **as many as** data sources you want. Passing other types will simply return an error.
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg, err := ini.Load([]byte("raw data"), "filename")
|
||||||
|
```
|
||||||
|
|
||||||
|
Or start with an empty object:
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg := ini.Empty()
|
||||||
|
```
|
||||||
|
|
||||||
|
When you cannot decide how many data sources to load at the beginning, you still able to **Append()** them later.
|
||||||
|
|
||||||
|
```go
|
||||||
|
err := cfg.Append("other file", []byte("other raw data"))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Working with sections
|
||||||
|
|
||||||
|
To get a section, you would need to:
|
||||||
|
|
||||||
|
```go
|
||||||
|
section, err := cfg.GetSection("section name")
|
||||||
|
```
|
||||||
|
|
||||||
|
For a shortcut for default section, just give an empty string as name:
|
||||||
|
|
||||||
|
```go
|
||||||
|
section, err := cfg.GetSection("")
|
||||||
|
```
|
||||||
|
|
||||||
|
When you're pretty sure the section exists, following code could make your life easier:
|
||||||
|
|
||||||
|
```go
|
||||||
|
section := cfg.Section("")
|
||||||
|
```
|
||||||
|
|
||||||
|
What happens when the section somehow does not exist? Don't panic, it automatically creates and returns a new section to you.
|
||||||
|
|
||||||
|
To create a new section:
|
||||||
|
|
||||||
|
```go
|
||||||
|
err := cfg.NewSection("new section")
|
||||||
|
```
|
||||||
|
|
||||||
|
To get a list of sections or section names:
|
||||||
|
|
||||||
|
```go
|
||||||
|
sections := cfg.Sections()
|
||||||
|
names := cfg.SectionStrings()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Working with keys
|
||||||
|
|
||||||
|
To get a key under a section:
|
||||||
|
|
||||||
|
```go
|
||||||
|
key, err := cfg.Section("").GetKey("key name")
|
||||||
|
```
|
||||||
|
|
||||||
|
Same rule applies to key operations:
|
||||||
|
|
||||||
|
```go
|
||||||
|
key := cfg.Section("").Key("key name")
|
||||||
|
```
|
||||||
|
|
||||||
|
To check if a key exists:
|
||||||
|
|
||||||
|
```go
|
||||||
|
yes := cfg.Section("").HasKey("key name")
|
||||||
|
```
|
||||||
|
|
||||||
|
To create a new key:
|
||||||
|
|
||||||
|
```go
|
||||||
|
err := cfg.Section("").NewKey("name", "value")
|
||||||
|
```
|
||||||
|
|
||||||
|
To get a list of keys or key names:
|
||||||
|
|
||||||
|
```go
|
||||||
|
keys := cfg.Section("").Keys()
|
||||||
|
names := cfg.Section("").KeyStrings()
|
||||||
|
```
|
||||||
|
|
||||||
|
To get a clone hash of keys and corresponding values:
|
||||||
|
|
||||||
|
```go
|
||||||
|
hash := cfg.GetSection("").KeysHash()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Working with values
|
||||||
|
|
||||||
|
To get a string value:
|
||||||
|
|
||||||
|
```go
|
||||||
|
val := cfg.Section("").Key("key name").String()
|
||||||
|
```
|
||||||
|
|
||||||
|
To validate key value on the fly:
|
||||||
|
|
||||||
|
```go
|
||||||
|
val := cfg.Section("").Key("key name").Validate(func(in string) string {
|
||||||
|
if len(in) == 0 {
|
||||||
|
return "default"
|
||||||
|
}
|
||||||
|
return in
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
If you do not want any auto-transformation (such as recursive read) for the values, you can get raw value directly (this way you get much better performance):
|
||||||
|
|
||||||
|
```go
|
||||||
|
val := cfg.Section("").Key("key name").Value()
|
||||||
|
```
|
||||||
|
|
||||||
|
To check if raw value exists:
|
||||||
|
|
||||||
|
```go
|
||||||
|
yes := cfg.Section("").HasValue("test value")
|
||||||
|
```
|
||||||
|
|
||||||
|
To get value with types:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// For boolean values:
|
||||||
|
// true when value is: 1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On
|
||||||
|
// false when value is: 0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off
|
||||||
|
v, err = cfg.Section("").Key("BOOL").Bool()
|
||||||
|
v, err = cfg.Section("").Key("FLOAT64").Float64()
|
||||||
|
v, err = cfg.Section("").Key("INT").Int()
|
||||||
|
v, err = cfg.Section("").Key("INT64").Int64()
|
||||||
|
v, err = cfg.Section("").Key("UINT").Uint()
|
||||||
|
v, err = cfg.Section("").Key("UINT64").Uint64()
|
||||||
|
v, err = cfg.Section("").Key("TIME").TimeFormat(time.RFC3339)
|
||||||
|
v, err = cfg.Section("").Key("TIME").Time() // RFC3339
|
||||||
|
|
||||||
|
v = cfg.Section("").Key("BOOL").MustBool()
|
||||||
|
v = cfg.Section("").Key("FLOAT64").MustFloat64()
|
||||||
|
v = cfg.Section("").Key("INT").MustInt()
|
||||||
|
v = cfg.Section("").Key("INT64").MustInt64()
|
||||||
|
v = cfg.Section("").Key("UINT").MustUint()
|
||||||
|
v = cfg.Section("").Key("UINT64").MustUint64()
|
||||||
|
v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339)
|
||||||
|
v = cfg.Section("").Key("TIME").MustTime() // RFC3339
|
||||||
|
|
||||||
|
// Methods start with Must also accept one argument for default value
|
||||||
|
// when key not found or fail to parse value to given type.
|
||||||
|
// Except method MustString, which you have to pass a default value.
|
||||||
|
|
||||||
|
v = cfg.Section("").Key("String").MustString("default")
|
||||||
|
v = cfg.Section("").Key("BOOL").MustBool(true)
|
||||||
|
v = cfg.Section("").Key("FLOAT64").MustFloat64(1.25)
|
||||||
|
v = cfg.Section("").Key("INT").MustInt(10)
|
||||||
|
v = cfg.Section("").Key("INT64").MustInt64(99)
|
||||||
|
v = cfg.Section("").Key("UINT").MustUint(3)
|
||||||
|
v = cfg.Section("").Key("UINT64").MustUint64(6)
|
||||||
|
v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339, time.Now())
|
||||||
|
v = cfg.Section("").Key("TIME").MustTime(time.Now()) // RFC3339
|
||||||
|
```
|
||||||
|
|
||||||
|
What if my value is three-line long?
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[advance]
|
||||||
|
ADDRESS = """404 road,
|
||||||
|
NotFound, State, 5000
|
||||||
|
Earth"""
|
||||||
|
```
|
||||||
|
|
||||||
|
Not a problem!
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg.Section("advance").Key("ADDRESS").String()
|
||||||
|
|
||||||
|
/* --- start ---
|
||||||
|
404 road,
|
||||||
|
NotFound, State, 5000
|
||||||
|
Earth
|
||||||
|
------ end --- */
|
||||||
|
```
|
||||||
|
|
||||||
|
That's cool, how about continuation lines?
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[advance]
|
||||||
|
two_lines = how about \
|
||||||
|
continuation lines?
|
||||||
|
lots_of_lines = 1 \
|
||||||
|
2 \
|
||||||
|
3 \
|
||||||
|
4
|
||||||
|
```
|
||||||
|
|
||||||
|
Piece of cake!
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg.Section("advance").Key("two_lines").String() // how about continuation lines?
|
||||||
|
cfg.Section("advance").Key("lots_of_lines").String() // 1 2 3 4
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that single quotes around values will be stripped:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
foo = "some value" // foo: some value
|
||||||
|
bar = 'some value' // bar: some value
|
||||||
|
```
|
||||||
|
|
||||||
|
That's all? Hmm, no.
|
||||||
|
|
||||||
|
#### Helper methods of working with values
|
||||||
|
|
||||||
|
To get value with given candidates:
|
||||||
|
|
||||||
|
```go
|
||||||
|
v = cfg.Section("").Key("STRING").In("default", []string{"str", "arr", "types"})
|
||||||
|
v = cfg.Section("").Key("FLOAT64").InFloat64(1.1, []float64{1.25, 2.5, 3.75})
|
||||||
|
v = cfg.Section("").Key("INT").InInt(5, []int{10, 20, 30})
|
||||||
|
v = cfg.Section("").Key("INT64").InInt64(10, []int64{10, 20, 30})
|
||||||
|
v = cfg.Section("").Key("UINT").InUint(4, []int{3, 6, 9})
|
||||||
|
v = cfg.Section("").Key("UINT64").InUint64(8, []int64{3, 6, 9})
|
||||||
|
v = cfg.Section("").Key("TIME").InTimeFormat(time.RFC3339, time.Now(), []time.Time{time1, time2, time3})
|
||||||
|
v = cfg.Section("").Key("TIME").InTime(time.Now(), []time.Time{time1, time2, time3}) // RFC3339
|
||||||
|
```
|
||||||
|
|
||||||
|
Default value will be presented if value of key is not in candidates you given, and default value does not need be one of candidates.
|
||||||
|
|
||||||
|
To validate value in a given range:
|
||||||
|
|
||||||
|
```go
|
||||||
|
vals = cfg.Section("").Key("FLOAT64").RangeFloat64(0.0, 1.1, 2.2)
|
||||||
|
vals = cfg.Section("").Key("INT").RangeInt(0, 10, 20)
|
||||||
|
vals = cfg.Section("").Key("INT64").RangeInt64(0, 10, 20)
|
||||||
|
vals = cfg.Section("").Key("UINT").RangeUint(0, 3, 9)
|
||||||
|
vals = cfg.Section("").Key("UINT64").RangeUint64(0, 3, 9)
|
||||||
|
vals = cfg.Section("").Key("TIME").RangeTimeFormat(time.RFC3339, time.Now(), minTime, maxTime)
|
||||||
|
vals = cfg.Section("").Key("TIME").RangeTime(time.Now(), minTime, maxTime) // RFC3339
|
||||||
|
```
|
||||||
|
|
||||||
|
To auto-split value into slice:
|
||||||
|
|
||||||
|
```go
|
||||||
|
vals = cfg.Section("").Key("STRINGS").Strings(",")
|
||||||
|
vals = cfg.Section("").Key("FLOAT64S").Float64s(",")
|
||||||
|
vals = cfg.Section("").Key("INTS").Ints(",")
|
||||||
|
vals = cfg.Section("").Key("INT64S").Int64s(",")
|
||||||
|
vals = cfg.Section("").Key("UINTS").Uints(",")
|
||||||
|
vals = cfg.Section("").Key("UINT64S").Uint64s(",")
|
||||||
|
vals = cfg.Section("").Key("TIMES").Times(",")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Save your configuration
|
||||||
|
|
||||||
|
Finally, it's time to save your configuration to somewhere.
|
||||||
|
|
||||||
|
A typical way to save configuration is writing it to a file:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ...
|
||||||
|
err = cfg.SaveTo("my.ini")
|
||||||
|
err = cfg.SaveToIndent("my.ini", "\t")
|
||||||
|
```
|
||||||
|
|
||||||
|
Another way to save is writing to a `io.Writer` interface:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ...
|
||||||
|
cfg.WriteTo(writer)
|
||||||
|
cfg.WriteToIndent(writer, "\t")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
### Recursive Values
|
||||||
|
|
||||||
|
For all value of keys, there is a special syntax `%(<name>)s`, where `<name>` is the key name in same section or default section, and `%(<name>)s` will be replaced by corresponding value(empty string if key not found). You can use this syntax at most 99 level of recursions.
|
||||||
|
|
||||||
|
```ini
|
||||||
|
NAME = ini
|
||||||
|
|
||||||
|
[author]
|
||||||
|
NAME = Unknwon
|
||||||
|
GITHUB = https://github.com/%(NAME)s
|
||||||
|
|
||||||
|
[package]
|
||||||
|
FULL_NAME = github.com/go-ini/%(NAME)s
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg.Section("author").Key("GITHUB").String() // https://github.com/Unknwon
|
||||||
|
cfg.Section("package").Key("FULL_NAME").String() // github.com/go-ini/ini
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parent-child Sections
|
||||||
|
|
||||||
|
You can use `.` in section name to indicate parent-child relationship between two or more sections. If the key not found in the child section, library will try again on its parent section until there is no parent section.
|
||||||
|
|
||||||
|
```ini
|
||||||
|
NAME = ini
|
||||||
|
VERSION = v1
|
||||||
|
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
|
||||||
|
|
||||||
|
[package]
|
||||||
|
CLONE_URL = https://%(IMPORT_PATH)s
|
||||||
|
|
||||||
|
[package.sub]
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg.Section("package.sub").Key("CLONE_URL").String() // https://gopkg.in/ini.v1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Auto-increment Key Names
|
||||||
|
|
||||||
|
If key name is `-` in data source, then it would be seen as special syntax for auto-increment key name start from 1, and every section is independent on counter.
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[features]
|
||||||
|
-: Support read/write comments of keys and sections
|
||||||
|
-: Support auto-increment of key names
|
||||||
|
-: Support load multiple files to overwrite key values
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg.Section("features").KeyStrings() // []{"#1", "#2", "#3"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Map To Struct
|
||||||
|
|
||||||
|
Want more objective way to play with INI? Cool.
|
||||||
|
|
||||||
|
```ini
|
||||||
|
Name = Unknwon
|
||||||
|
age = 21
|
||||||
|
Male = true
|
||||||
|
Born = 1993-01-01T20:17:05Z
|
||||||
|
|
||||||
|
[Note]
|
||||||
|
Content = Hi is a good man!
|
||||||
|
Cities = HangZhou, Boston
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Note struct {
|
||||||
|
Content string
|
||||||
|
Cities []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int `ini:"age"`
|
||||||
|
Male bool
|
||||||
|
Born time.Time
|
||||||
|
Note
|
||||||
|
Created time.Time `ini:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg, err := ini.Load("path/to/ini")
|
||||||
|
// ...
|
||||||
|
p := new(Person)
|
||||||
|
err = cfg.MapTo(p)
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Things can be simpler.
|
||||||
|
err = ini.MapTo(p, "path/to/ini")
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Just map a section? Fine.
|
||||||
|
n := new(Note)
|
||||||
|
err = cfg.Section("Note").MapTo(n)
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Can I have default value for field? Absolutely.
|
||||||
|
|
||||||
|
Assign it before you map to struct. It will keep the value as it is if the key is not presented or got wrong type.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ...
|
||||||
|
p := &Person{
|
||||||
|
Name: "Joe",
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
It's really cool, but what's the point if you can't give me my file back from struct?
|
||||||
|
|
||||||
|
### Reflect From Struct
|
||||||
|
|
||||||
|
Why not?
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Embeded struct {
|
||||||
|
Dates []time.Time `delim:"|"`
|
||||||
|
Places []string
|
||||||
|
None []int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Author struct {
|
||||||
|
Name string `ini:"NAME"`
|
||||||
|
Male bool
|
||||||
|
Age int
|
||||||
|
GPA float64
|
||||||
|
NeverMind string `ini:"-"`
|
||||||
|
*Embeded
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
a := &Author{"Unknwon", true, 21, 2.8, "",
|
||||||
|
&Embeded{
|
||||||
|
[]time.Time{time.Now(), time.Now()},
|
||||||
|
[]string{"HangZhou", "Boston"},
|
||||||
|
[]int{},
|
||||||
|
}}
|
||||||
|
cfg := ini.Empty()
|
||||||
|
err = ini.ReflectFrom(cfg, a)
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
So, what do I get?
|
||||||
|
|
||||||
|
```ini
|
||||||
|
NAME = Unknwon
|
||||||
|
Male = true
|
||||||
|
Age = 21
|
||||||
|
GPA = 2.8
|
||||||
|
|
||||||
|
[Embeded]
|
||||||
|
Dates = 2015-08-07T22:14:22+08:00|2015-08-07T22:14:22+08:00
|
||||||
|
Places = HangZhou,Boston
|
||||||
|
None =
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Name Mapper
|
||||||
|
|
||||||
|
To save your time and make your code cleaner, this library supports [`NameMapper`](https://gowalker.org/gopkg.in/ini.v1#NameMapper) between struct field and actual section and key name.
|
||||||
|
|
||||||
|
There are 2 built-in name mappers:
|
||||||
|
|
||||||
|
- `AllCapsUnderscore`: it converts to format `ALL_CAPS_UNDERSCORE` then match section or key.
|
||||||
|
- `TitleUnderscore`: it converts to format `title_underscore` then match section or key.
|
||||||
|
|
||||||
|
To use them:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Info struct {
|
||||||
|
PackageName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("package_name=ini"))
|
||||||
|
// ...
|
||||||
|
|
||||||
|
cfg, err := ini.Load([]byte("PACKAGE_NAME=ini"))
|
||||||
|
// ...
|
||||||
|
info := new(Info)
|
||||||
|
cfg.NameMapper = ini.AllCapsUnderscore
|
||||||
|
err = cfg.MapTo(info)
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Same rules of name mapper apply to `ini.ReflectFromWithMapper` function.
|
||||||
|
|
||||||
|
#### Other Notes On Map/Reflect
|
||||||
|
|
||||||
|
Any embedded struct is treated as a section by default, and there is no automatic parent-child relations in map/reflect feature:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Child struct {
|
||||||
|
Age string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Parent struct {
|
||||||
|
Name string
|
||||||
|
Child
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
City string
|
||||||
|
Parent
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example configuration:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
City = Boston
|
||||||
|
|
||||||
|
[Parent]
|
||||||
|
Name = Unknwon
|
||||||
|
|
||||||
|
[Child]
|
||||||
|
Age = 21
|
||||||
|
```
|
||||||
|
|
||||||
|
What if, yes, I'm paranoid, I want embedded struct to be in the same section. Well, all roads lead to Rome.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Child struct {
|
||||||
|
Age string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Parent struct {
|
||||||
|
Name string
|
||||||
|
Child `ini:"Parent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
City string
|
||||||
|
Parent
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example configuration:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
City = Boston
|
||||||
|
|
||||||
|
[Parent]
|
||||||
|
Name = Unknwon
|
||||||
|
Age = 21
|
||||||
|
```
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
- [API Documentation](https://gowalker.org/gopkg.in/ini.v1)
|
||||||
|
- [File An Issue](https://github.com/go-ini/ini/issues/new)
|
||||||
|
|
||||||
|
## FAQs
|
||||||
|
|
||||||
|
### What does `BlockMode` field do?
|
||||||
|
|
||||||
|
By default, library lets you read and write values so we need a locker to make sure your data is safe. But in cases that you are very sure about only reading data through the library, you can set `cfg.BlockMode = false` to speed up read operations about **50-70%** faster.
|
||||||
|
|
||||||
|
### Why another INI library?
|
||||||
|
|
||||||
|
Many people are using my another INI library [goconfig](https://github.com/Unknwon/goconfig), so the reason for this one is I would like to make more Go style code. Also when you set `cfg.BlockMode = false`, this one is about **10-30%** faster.
|
||||||
|
|
||||||
|
To make those changes I have to confirm API broken, so it's safer to keep it in another place and start using `gopkg.in` to version my package at this time.(PS: shorter import path)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text.
|
577
Godeps/_workspace/src/github.com/go-ini/ini/README_ZH.md
generated
vendored
Normal file
577
Godeps/_workspace/src/github.com/go-ini/ini/README_ZH.md
generated
vendored
Normal file
|
@ -0,0 +1,577 @@
|
||||||
|
本包提供了 Go 语言中读写 INI 文件的功能。
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
- 支持覆盖加载多个数据源(`[]byte` 或文件)
|
||||||
|
- 支持递归读取键值
|
||||||
|
- 支持读取父子分区
|
||||||
|
- 支持读取自增键名
|
||||||
|
- 支持读取多行的键值
|
||||||
|
- 支持大量辅助方法
|
||||||
|
- 支持在读取时直接转换为 Go 语言类型
|
||||||
|
- 支持读取和 **写入** 分区和键的注释
|
||||||
|
- 轻松操作分区、键值和注释
|
||||||
|
- 在保存文件时分区和键值会保持原有的顺序
|
||||||
|
|
||||||
|
## 下载安装
|
||||||
|
|
||||||
|
使用一个特定版本:
|
||||||
|
|
||||||
|
go get gopkg.in/ini.v1
|
||||||
|
|
||||||
|
使用最新版:
|
||||||
|
|
||||||
|
go get github.com/go-ini/ini
|
||||||
|
|
||||||
|
### 测试安装
|
||||||
|
|
||||||
|
如果您想要在自己的机器上运行测试,请使用 `-t` 标记:
|
||||||
|
|
||||||
|
go get -t gopkg.in/ini.v1
|
||||||
|
|
||||||
|
## 开始使用
|
||||||
|
|
||||||
|
### 从数据源加载
|
||||||
|
|
||||||
|
一个 **数据源** 可以是 `[]byte` 类型的原始数据,或 `string` 类型的文件路径。您可以加载 **任意多个** 数据源。如果您传递其它类型的数据源,则会直接返回错误。
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg, err := ini.Load([]byte("raw data"), "filename")
|
||||||
|
```
|
||||||
|
|
||||||
|
或者从一个空白的文件开始:
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg := ini.Empty()
|
||||||
|
```
|
||||||
|
|
||||||
|
当您在一开始无法决定需要加载哪些数据源时,仍可以使用 **Append()** 在需要的时候加载它们。
|
||||||
|
|
||||||
|
```go
|
||||||
|
err := cfg.Append("other file", []byte("other raw data"))
|
||||||
|
```
|
||||||
|
|
||||||
|
### 操作分区(Section)
|
||||||
|
|
||||||
|
获取指定分区:
|
||||||
|
|
||||||
|
```go
|
||||||
|
section, err := cfg.GetSection("section name")
|
||||||
|
```
|
||||||
|
|
||||||
|
如果您想要获取默认分区,则可以用空字符串代替分区名:
|
||||||
|
|
||||||
|
```go
|
||||||
|
section, err := cfg.GetSection("")
|
||||||
|
```
|
||||||
|
|
||||||
|
当您非常确定某个分区是存在的,可以使用以下简便方法:
|
||||||
|
|
||||||
|
```go
|
||||||
|
section := cfg.Section("")
|
||||||
|
```
|
||||||
|
|
||||||
|
如果不小心判断错了,要获取的分区其实是不存在的,那会发生什么呢?没事的,它会自动创建并返回一个对应的分区对象给您。
|
||||||
|
|
||||||
|
创建一个分区:
|
||||||
|
|
||||||
|
```go
|
||||||
|
err := cfg.NewSection("new section")
|
||||||
|
```
|
||||||
|
|
||||||
|
获取所有分区对象或名称:
|
||||||
|
|
||||||
|
```go
|
||||||
|
sections := cfg.Sections()
|
||||||
|
names := cfg.SectionStrings()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 操作键(Key)
|
||||||
|
|
||||||
|
获取某个分区下的键:
|
||||||
|
|
||||||
|
```go
|
||||||
|
key, err := cfg.Section("").GetKey("key name")
|
||||||
|
```
|
||||||
|
|
||||||
|
和分区一样,您也可以直接获取键而忽略错误处理:
|
||||||
|
|
||||||
|
```go
|
||||||
|
key := cfg.Section("").Key("key name")
|
||||||
|
```
|
||||||
|
|
||||||
|
判断某个键是否存在:
|
||||||
|
|
||||||
|
```go
|
||||||
|
yes := cfg.Section("").HasKey("key name")
|
||||||
|
```
|
||||||
|
|
||||||
|
创建一个新的键:
|
||||||
|
|
||||||
|
```go
|
||||||
|
err := cfg.Section("").NewKey("name", "value")
|
||||||
|
```
|
||||||
|
|
||||||
|
获取分区下的所有键或键名:
|
||||||
|
|
||||||
|
```go
|
||||||
|
keys := cfg.Section("").Keys()
|
||||||
|
names := cfg.Section("").KeyStrings()
|
||||||
|
```
|
||||||
|
|
||||||
|
获取分区下的所有键值对的克隆:
|
||||||
|
|
||||||
|
```go
|
||||||
|
hash := cfg.GetSection("").KeysHash()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 操作键值(Value)
|
||||||
|
|
||||||
|
获取一个类型为字符串(string)的值:
|
||||||
|
|
||||||
|
```go
|
||||||
|
val := cfg.Section("").Key("key name").String()
|
||||||
|
```
|
||||||
|
|
||||||
|
获取值的同时通过自定义函数进行处理验证:
|
||||||
|
|
||||||
|
```go
|
||||||
|
val := cfg.Section("").Key("key name").Validate(func(in string) string {
|
||||||
|
if len(in) == 0 {
|
||||||
|
return "default"
|
||||||
|
}
|
||||||
|
return in
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
如果您不需要任何对值的自动转变功能(例如递归读取),可以直接获取原值(这种方式性能最佳):
|
||||||
|
|
||||||
|
```go
|
||||||
|
val := cfg.Section("").Key("key name").Value()
|
||||||
|
```
|
||||||
|
|
||||||
|
判断某个原值是否存在:
|
||||||
|
|
||||||
|
```go
|
||||||
|
yes := cfg.Section("").HasValue("test value")
|
||||||
|
```
|
||||||
|
|
||||||
|
获取其它类型的值:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 布尔值的规则:
|
||||||
|
// true 当值为:1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On
|
||||||
|
// false 当值为:0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off
|
||||||
|
v, err = cfg.Section("").Key("BOOL").Bool()
|
||||||
|
v, err = cfg.Section("").Key("FLOAT64").Float64()
|
||||||
|
v, err = cfg.Section("").Key("INT").Int()
|
||||||
|
v, err = cfg.Section("").Key("INT64").Int64()
|
||||||
|
v, err = cfg.Section("").Key("UINT").Uint()
|
||||||
|
v, err = cfg.Section("").Key("UINT64").Uint64()
|
||||||
|
v, err = cfg.Section("").Key("TIME").TimeFormat(time.RFC3339)
|
||||||
|
v, err = cfg.Section("").Key("TIME").Time() // RFC3339
|
||||||
|
|
||||||
|
v = cfg.Section("").Key("BOOL").MustBool()
|
||||||
|
v = cfg.Section("").Key("FLOAT64").MustFloat64()
|
||||||
|
v = cfg.Section("").Key("INT").MustInt()
|
||||||
|
v = cfg.Section("").Key("INT64").MustInt64()
|
||||||
|
v = cfg.Section("").Key("UINT").MustUint()
|
||||||
|
v = cfg.Section("").Key("UINT64").MustUint64()
|
||||||
|
v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339)
|
||||||
|
v = cfg.Section("").Key("TIME").MustTime() // RFC3339
|
||||||
|
|
||||||
|
// 由 Must 开头的方法名允许接收一个相同类型的参数来作为默认值,
|
||||||
|
// 当键不存在或者转换失败时,则会直接返回该默认值。
|
||||||
|
// 但是,MustString 方法必须传递一个默认值。
|
||||||
|
|
||||||
|
v = cfg.Seciont("").Key("String").MustString("default")
|
||||||
|
v = cfg.Section("").Key("BOOL").MustBool(true)
|
||||||
|
v = cfg.Section("").Key("FLOAT64").MustFloat64(1.25)
|
||||||
|
v = cfg.Section("").Key("INT").MustInt(10)
|
||||||
|
v = cfg.Section("").Key("INT64").MustInt64(99)
|
||||||
|
v = cfg.Section("").Key("UINT").MustUint(3)
|
||||||
|
v = cfg.Section("").Key("UINT64").MustUint64(6)
|
||||||
|
v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339, time.Now())
|
||||||
|
v = cfg.Section("").Key("TIME").MustTime(time.Now()) // RFC3339
|
||||||
|
```
|
||||||
|
|
||||||
|
如果我的值有好多行怎么办?
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[advance]
|
||||||
|
ADDRESS = """404 road,
|
||||||
|
NotFound, State, 5000
|
||||||
|
Earth"""
|
||||||
|
```
|
||||||
|
|
||||||
|
嗯哼?小 case!
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg.Section("advance").Key("ADDRESS").String()
|
||||||
|
|
||||||
|
/* --- start ---
|
||||||
|
404 road,
|
||||||
|
NotFound, State, 5000
|
||||||
|
Earth
|
||||||
|
------ end --- */
|
||||||
|
```
|
||||||
|
|
||||||
|
赞爆了!那要是我属于一行的内容写不下想要写到第二行怎么办?
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[advance]
|
||||||
|
two_lines = how about \
|
||||||
|
continuation lines?
|
||||||
|
lots_of_lines = 1 \
|
||||||
|
2 \
|
||||||
|
3 \
|
||||||
|
4
|
||||||
|
```
|
||||||
|
|
||||||
|
简直是小菜一碟!
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg.Section("advance").Key("two_lines").String() // how about continuation lines?
|
||||||
|
cfg.Section("advance").Key("lots_of_lines").String() // 1 2 3 4
|
||||||
|
```
|
||||||
|
|
||||||
|
需要注意的是,值两侧的单引号会被自动剔除:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
foo = "some value" // foo: some value
|
||||||
|
bar = 'some value' // bar: some value
|
||||||
|
```
|
||||||
|
|
||||||
|
这就是全部了?哈哈,当然不是。
|
||||||
|
|
||||||
|
#### 操作键值的辅助方法
|
||||||
|
|
||||||
|
获取键值时设定候选值:
|
||||||
|
|
||||||
|
```go
|
||||||
|
v = cfg.Section("").Key("STRING").In("default", []string{"str", "arr", "types"})
|
||||||
|
v = cfg.Section("").Key("FLOAT64").InFloat64(1.1, []float64{1.25, 2.5, 3.75})
|
||||||
|
v = cfg.Section("").Key("INT").InInt(5, []int{10, 20, 30})
|
||||||
|
v = cfg.Section("").Key("INT64").InInt64(10, []int64{10, 20, 30})
|
||||||
|
v = cfg.Section("").Key("UINT").InUint(4, []int{3, 6, 9})
|
||||||
|
v = cfg.Section("").Key("UINT64").InUint64(8, []int64{3, 6, 9})
|
||||||
|
v = cfg.Section("").Key("TIME").InTimeFormat(time.RFC3339, time.Now(), []time.Time{time1, time2, time3})
|
||||||
|
v = cfg.Section("").Key("TIME").InTime(time.Now(), []time.Time{time1, time2, time3}) // RFC3339
|
||||||
|
```
|
||||||
|
|
||||||
|
如果获取到的值不是候选值的任意一个,则会返回默认值,而默认值不需要是候选值中的一员。
|
||||||
|
|
||||||
|
验证获取的值是否在指定范围内:
|
||||||
|
|
||||||
|
```go
|
||||||
|
vals = cfg.Section("").Key("FLOAT64").RangeFloat64(0.0, 1.1, 2.2)
|
||||||
|
vals = cfg.Section("").Key("INT").RangeInt(0, 10, 20)
|
||||||
|
vals = cfg.Section("").Key("INT64").RangeInt64(0, 10, 20)
|
||||||
|
vals = cfg.Section("").Key("UINT").RangeUint(0, 3, 9)
|
||||||
|
vals = cfg.Section("").Key("UINT64").RangeUint64(0, 3, 9)
|
||||||
|
vals = cfg.Section("").Key("TIME").RangeTimeFormat(time.RFC3339, time.Now(), minTime, maxTime)
|
||||||
|
vals = cfg.Section("").Key("TIME").RangeTime(time.Now(), minTime, maxTime) // RFC3339
|
||||||
|
```
|
||||||
|
|
||||||
|
自动分割键值为切片(slice):
|
||||||
|
|
||||||
|
```go
|
||||||
|
vals = cfg.Section("").Key("STRINGS").Strings(",")
|
||||||
|
vals = cfg.Section("").Key("FLOAT64S").Float64s(",")
|
||||||
|
vals = cfg.Section("").Key("INTS").Ints(",")
|
||||||
|
vals = cfg.Section("").Key("INT64S").Int64s(",")
|
||||||
|
vals = cfg.Section("").Key("UINTS").Uints(",")
|
||||||
|
vals = cfg.Section("").Key("UINT64S").Uint64s(",")
|
||||||
|
vals = cfg.Section("").Key("TIMES").Times(",")
|
||||||
|
```
|
||||||
|
|
||||||
|
### 保存配置
|
||||||
|
|
||||||
|
终于到了这个时刻,是时候保存一下配置了。
|
||||||
|
|
||||||
|
比较原始的做法是输出配置到某个文件:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ...
|
||||||
|
err = cfg.SaveTo("my.ini")
|
||||||
|
err = cfg.SaveToIndent("my.ini", "\t")
|
||||||
|
```
|
||||||
|
|
||||||
|
另一个比较高级的做法是写入到任何实现 `io.Writer` 接口的对象中:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ...
|
||||||
|
cfg.WriteTo(writer)
|
||||||
|
cfg.WriteToIndent(writer, "\t")
|
||||||
|
```
|
||||||
|
|
||||||
|
### 高级用法
|
||||||
|
|
||||||
|
#### 递归读取键值
|
||||||
|
|
||||||
|
在获取所有键值的过程中,特殊语法 `%(<name>)s` 会被应用,其中 `<name>` 可以是相同分区或者默认分区下的键名。字符串 `%(<name>)s` 会被相应的键值所替代,如果指定的键不存在,则会用空字符串替代。您可以最多使用 99 层的递归嵌套。
|
||||||
|
|
||||||
|
```ini
|
||||||
|
NAME = ini
|
||||||
|
|
||||||
|
[author]
|
||||||
|
NAME = Unknwon
|
||||||
|
GITHUB = https://github.com/%(NAME)s
|
||||||
|
|
||||||
|
[package]
|
||||||
|
FULL_NAME = github.com/go-ini/%(NAME)s
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg.Section("author").Key("GITHUB").String() // https://github.com/Unknwon
|
||||||
|
cfg.Section("package").Key("FULL_NAME").String() // github.com/go-ini/ini
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 读取父子分区
|
||||||
|
|
||||||
|
您可以在分区名称中使用 `.` 来表示两个或多个分区之间的父子关系。如果某个键在子分区中不存在,则会去它的父分区中再次寻找,直到没有父分区为止。
|
||||||
|
|
||||||
|
```ini
|
||||||
|
NAME = ini
|
||||||
|
VERSION = v1
|
||||||
|
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
|
||||||
|
|
||||||
|
[package]
|
||||||
|
CLONE_URL = https://%(IMPORT_PATH)s
|
||||||
|
|
||||||
|
[package.sub]
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg.Section("package.sub").Key("CLONE_URL").String() // https://gopkg.in/ini.v1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 读取自增键名
|
||||||
|
|
||||||
|
如果数据源中的键名为 `-`,则认为该键使用了自增键名的特殊语法。计数器从 1 开始,并且分区之间是相互独立的。
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[features]
|
||||||
|
-: Support read/write comments of keys and sections
|
||||||
|
-: Support auto-increment of key names
|
||||||
|
-: Support load multiple files to overwrite key values
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg.Section("features").KeyStrings() // []{"#1", "#2", "#3"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 映射到结构
|
||||||
|
|
||||||
|
想要使用更加面向对象的方式玩转 INI 吗?好主意。
|
||||||
|
|
||||||
|
```ini
|
||||||
|
Name = Unknwon
|
||||||
|
age = 21
|
||||||
|
Male = true
|
||||||
|
Born = 1993-01-01T20:17:05Z
|
||||||
|
|
||||||
|
[Note]
|
||||||
|
Content = Hi is a good man!
|
||||||
|
Cities = HangZhou, Boston
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Note struct {
|
||||||
|
Content string
|
||||||
|
Cities []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int `ini:"age"`
|
||||||
|
Male bool
|
||||||
|
Born time.Time
|
||||||
|
Note
|
||||||
|
Created time.Time `ini:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg, err := ini.Load("path/to/ini")
|
||||||
|
// ...
|
||||||
|
p := new(Person)
|
||||||
|
err = cfg.MapTo(p)
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// 一切竟可以如此的简单。
|
||||||
|
err = ini.MapTo(p, "path/to/ini")
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// 嗯哼?只需要映射一个分区吗?
|
||||||
|
n := new(Note)
|
||||||
|
err = cfg.Section("Note").MapTo(n)
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
结构的字段怎么设置默认值呢?很简单,只要在映射之前对指定字段进行赋值就可以了。如果键未找到或者类型错误,该值不会发生改变。
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ...
|
||||||
|
p := &Person{
|
||||||
|
Name: "Joe",
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
这样玩 INI 真的好酷啊!然而,如果不能还给我原来的配置文件,有什么卵用?
|
||||||
|
|
||||||
|
### 从结构反射
|
||||||
|
|
||||||
|
可是,我有说不能吗?
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Embeded struct {
|
||||||
|
Dates []time.Time `delim:"|"`
|
||||||
|
Places []string
|
||||||
|
None []int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Author struct {
|
||||||
|
Name string `ini:"NAME"`
|
||||||
|
Male bool
|
||||||
|
Age int
|
||||||
|
GPA float64
|
||||||
|
NeverMind string `ini:"-"`
|
||||||
|
*Embeded
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
a := &Author{"Unknwon", true, 21, 2.8, "",
|
||||||
|
&Embeded{
|
||||||
|
[]time.Time{time.Now(), time.Now()},
|
||||||
|
[]string{"HangZhou", "Boston"},
|
||||||
|
[]int{},
|
||||||
|
}}
|
||||||
|
cfg := ini.Empty()
|
||||||
|
err = ini.ReflectFrom(cfg, a)
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
瞧瞧,奇迹发生了。
|
||||||
|
|
||||||
|
```ini
|
||||||
|
NAME = Unknwon
|
||||||
|
Male = true
|
||||||
|
Age = 21
|
||||||
|
GPA = 2.8
|
||||||
|
|
||||||
|
[Embeded]
|
||||||
|
Dates = 2015-08-07T22:14:22+08:00|2015-08-07T22:14:22+08:00
|
||||||
|
Places = HangZhou,Boston
|
||||||
|
None =
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 名称映射器(Name Mapper)
|
||||||
|
|
||||||
|
为了节省您的时间并简化代码,本库支持类型为 [`NameMapper`](https://gowalker.org/gopkg.in/ini.v1#NameMapper) 的名称映射器,该映射器负责结构字段名与分区名和键名之间的映射。
|
||||||
|
|
||||||
|
目前有 2 款内置的映射器:
|
||||||
|
|
||||||
|
- `AllCapsUnderscore`:该映射器将字段名转换至格式 `ALL_CAPS_UNDERSCORE` 后再去匹配分区名和键名。
|
||||||
|
- `TitleUnderscore`:该映射器将字段名转换至格式 `title_underscore` 后再去匹配分区名和键名。
|
||||||
|
|
||||||
|
使用方法:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Info struct{
|
||||||
|
PackageName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("package_name=ini"))
|
||||||
|
// ...
|
||||||
|
|
||||||
|
cfg, err := ini.Load([]byte("PACKAGE_NAME=ini"))
|
||||||
|
// ...
|
||||||
|
info := new(Info)
|
||||||
|
cfg.NameMapper = ini.AllCapsUnderscore
|
||||||
|
err = cfg.MapTo(info)
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
使用函数 `ini.ReflectFromWithMapper` 时也可应用相同的规则。
|
||||||
|
|
||||||
|
#### 映射/反射的其它说明
|
||||||
|
|
||||||
|
任何嵌入的结构都会被默认认作一个不同的分区,并且不会自动产生所谓的父子分区关联:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Child struct {
|
||||||
|
Age string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Parent struct {
|
||||||
|
Name string
|
||||||
|
Child
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
City string
|
||||||
|
Parent
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
示例配置文件:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
City = Boston
|
||||||
|
|
||||||
|
[Parent]
|
||||||
|
Name = Unknwon
|
||||||
|
|
||||||
|
[Child]
|
||||||
|
Age = 21
|
||||||
|
```
|
||||||
|
|
||||||
|
很好,但是,我就是要嵌入结构也在同一个分区。好吧,你爹是李刚!
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Child struct {
|
||||||
|
Age string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Parent struct {
|
||||||
|
Name string
|
||||||
|
Child `ini:"Parent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
City string
|
||||||
|
Parent
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
示例配置文件:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
City = Boston
|
||||||
|
|
||||||
|
[Parent]
|
||||||
|
Name = Unknwon
|
||||||
|
Age = 21
|
||||||
|
```
|
||||||
|
|
||||||
|
## 获取帮助
|
||||||
|
|
||||||
|
- [API 文档](https://gowalker.org/gopkg.in/ini.v1)
|
||||||
|
- [创建工单](https://github.com/go-ini/ini/issues/new)
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### 字段 `BlockMode` 是什么?
|
||||||
|
|
||||||
|
默认情况下,本库会在您进行读写操作时采用锁机制来确保数据时间。但在某些情况下,您非常确定只进行读操作。此时,您可以通过设置 `cfg.BlockMode = false` 来将读操作提升大约 **50-70%** 的性能。
|
||||||
|
|
||||||
|
### 为什么要写另一个 INI 解析库?
|
||||||
|
|
||||||
|
许多人都在使用我的 [goconfig](https://github.com/Unknwon/goconfig) 来完成对 INI 文件的操作,但我希望使用更加 Go 风格的代码。并且当您设置 `cfg.BlockMode = false` 时,会有大约 **10-30%** 的性能提升。
|
||||||
|
|
||||||
|
为了做出这些改变,我必须对 API 进行破坏,所以新开一个仓库是最安全的做法。除此之外,本库直接使用 `gopkg.in` 来进行版本化发布。(其实真相是导入路径更短了)
|
1027
Godeps/_workspace/src/github.com/go-ini/ini/ini.go
generated
vendored
Normal file
1027
Godeps/_workspace/src/github.com/go-ini/ini/ini.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
312
Godeps/_workspace/src/github.com/go-ini/ini/parser.go
generated
vendored
Normal file
312
Godeps/_workspace/src/github.com/go-ini/ini/parser.go
generated
vendored
Normal file
|
@ -0,0 +1,312 @@
|
||||||
|
// Copyright 2015 Unknwon
|
||||||
|
//
|
||||||
|
// 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 ini
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tokenType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
_TOKEN_INVALID tokenType = iota
|
||||||
|
_TOKEN_COMMENT
|
||||||
|
_TOKEN_SECTION
|
||||||
|
_TOKEN_KEY
|
||||||
|
)
|
||||||
|
|
||||||
|
type parser struct {
|
||||||
|
buf *bufio.Reader
|
||||||
|
isEOF bool
|
||||||
|
count int
|
||||||
|
comment *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func newParser(r io.Reader) *parser {
|
||||||
|
return &parser{
|
||||||
|
buf: bufio.NewReader(r),
|
||||||
|
count: 1,
|
||||||
|
comment: &bytes.Buffer{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOM handles header of BOM-UTF8 format.
|
||||||
|
// http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding
|
||||||
|
func (p *parser) BOM() error {
|
||||||
|
mask, err := p.buf.Peek(3)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return err
|
||||||
|
} else if len(mask) < 3 {
|
||||||
|
return nil
|
||||||
|
} else if mask[0] == 239 && mask[1] == 187 && mask[2] == 191 {
|
||||||
|
p.buf.Read(mask)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) readUntil(delim byte) ([]byte, error) {
|
||||||
|
data, err := p.buf.ReadBytes(delim)
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
p.isEOF = true
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanComment(in []byte) ([]byte, bool) {
|
||||||
|
i := bytes.IndexAny(in, "#;")
|
||||||
|
if i == -1 {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return in[i:], true
|
||||||
|
}
|
||||||
|
|
||||||
|
func readKeyName(in []byte) (string, int, error) {
|
||||||
|
line := string(in)
|
||||||
|
|
||||||
|
// Check if key name surrounded by quotes.
|
||||||
|
var keyQuote string
|
||||||
|
if line[0] == '"' {
|
||||||
|
if len(line) > 6 && string(line[0:3]) == `"""` {
|
||||||
|
keyQuote = `"""`
|
||||||
|
} else {
|
||||||
|
keyQuote = `"`
|
||||||
|
}
|
||||||
|
} else if line[0] == '`' {
|
||||||
|
keyQuote = "`"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get out key name
|
||||||
|
endIdx := -1
|
||||||
|
if len(keyQuote) > 0 {
|
||||||
|
startIdx := len(keyQuote)
|
||||||
|
// FIXME: fail case -> """"""name"""=value
|
||||||
|
pos := strings.Index(line[startIdx:], keyQuote)
|
||||||
|
if pos == -1 {
|
||||||
|
return "", -1, fmt.Errorf("missing closing key quote: %s", line)
|
||||||
|
}
|
||||||
|
pos += startIdx
|
||||||
|
|
||||||
|
// Find key-value delimiter
|
||||||
|
i := strings.IndexAny(line[pos+startIdx:], "=:")
|
||||||
|
if i < 0 {
|
||||||
|
return "", -1, fmt.Errorf("key-value delimiter not found: %s", line)
|
||||||
|
}
|
||||||
|
endIdx = pos + i
|
||||||
|
return strings.TrimSpace(line[startIdx:pos]), endIdx + startIdx + 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
endIdx = strings.IndexAny(line, "=:")
|
||||||
|
if endIdx < 0 {
|
||||||
|
return "", -1, fmt.Errorf("key-value delimiter not found: %s", line)
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(line[0:endIdx]), endIdx + 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) readMultilines(line, val, valQuote string) (string, error) {
|
||||||
|
for {
|
||||||
|
data, err := p.readUntil('\n')
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
next := string(data)
|
||||||
|
|
||||||
|
pos := strings.LastIndex(next, valQuote)
|
||||||
|
if pos > -1 {
|
||||||
|
val += next[:pos]
|
||||||
|
|
||||||
|
comment, has := cleanComment([]byte(next[pos:]))
|
||||||
|
if has {
|
||||||
|
p.comment.Write(bytes.TrimSpace(comment))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
val += next
|
||||||
|
if p.isEOF {
|
||||||
|
return "", fmt.Errorf("missing closing key quote from '%s' to '%s'", line, next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) readContinuationLines(val string) (string, error) {
|
||||||
|
for {
|
||||||
|
data, err := p.readUntil('\n')
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
next := strings.TrimSpace(string(data))
|
||||||
|
|
||||||
|
if len(next) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
val += next
|
||||||
|
if val[len(val)-1] != '\\' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
val = val[:len(val)-1]
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasSurroundedQuote check if and only if the first and last characters
|
||||||
|
// are quotes \" or \'.
|
||||||
|
// It returns false if any other parts also contain same kind of quotes.
|
||||||
|
func hasSurroundedQuote(in string, quote byte) bool {
|
||||||
|
return len(in) > 2 && in[0] == quote && in[len(in)-1] == quote &&
|
||||||
|
strings.IndexByte(in[1:], quote) == len(in)-2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) readValue(in []byte) (string, error) {
|
||||||
|
line := strings.TrimLeftFunc(string(in), unicode.IsSpace)
|
||||||
|
if len(line) == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var valQuote string
|
||||||
|
if len(line) > 3 && string(line[0:3]) == `"""` {
|
||||||
|
valQuote = `"""`
|
||||||
|
} else if line[0] == '`' {
|
||||||
|
valQuote = "`"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(valQuote) > 0 {
|
||||||
|
startIdx := len(valQuote)
|
||||||
|
pos := strings.LastIndex(line[startIdx:], valQuote)
|
||||||
|
// Check for multi-line value
|
||||||
|
if pos == -1 {
|
||||||
|
return p.readMultilines(line, line[startIdx:], valQuote)
|
||||||
|
}
|
||||||
|
|
||||||
|
return line[startIdx : pos+startIdx], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Won't be able to reach here if value only contains whitespace.
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
|
||||||
|
// Check continuation lines
|
||||||
|
if line[len(line)-1] == '\\' {
|
||||||
|
return p.readContinuationLines(line[:len(line)-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
i := strings.IndexAny(line, "#;")
|
||||||
|
if i > -1 {
|
||||||
|
p.comment.WriteString(line[i:])
|
||||||
|
line = strings.TrimSpace(line[:i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim single quotes
|
||||||
|
if hasSurroundedQuote(line, '\'') ||
|
||||||
|
hasSurroundedQuote(line, '"') {
|
||||||
|
line = line[1 : len(line)-1]
|
||||||
|
}
|
||||||
|
return line, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse parses data through an io.Reader.
|
||||||
|
func (f *File) parse(reader io.Reader) (err error) {
|
||||||
|
p := newParser(reader)
|
||||||
|
if err = p.BOM(); err != nil {
|
||||||
|
return fmt.Errorf("BOM: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore error because default section name is never empty string.
|
||||||
|
section, _ := f.NewSection(DEFAULT_SECTION)
|
||||||
|
|
||||||
|
var line []byte
|
||||||
|
for !p.isEOF {
|
||||||
|
line, err = p.readUntil('\n')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
line = bytes.TrimLeftFunc(line, unicode.IsSpace)
|
||||||
|
if len(line) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comments
|
||||||
|
if line[0] == '#' || line[0] == ';' {
|
||||||
|
// Note: we do not care ending line break,
|
||||||
|
// it is needed for adding second line,
|
||||||
|
// so just clean it once at the end when set to value.
|
||||||
|
p.comment.Write(line)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section
|
||||||
|
if line[0] == '[' {
|
||||||
|
// Read to the next ']' (TODO: support quoted strings)
|
||||||
|
closeIdx := bytes.IndexByte(line, ']')
|
||||||
|
if closeIdx == -1 {
|
||||||
|
return fmt.Errorf("unclosed section: %s", line)
|
||||||
|
}
|
||||||
|
|
||||||
|
section, err = f.NewSection(string(line[1:closeIdx]))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
comment, has := cleanComment(line[closeIdx+1:])
|
||||||
|
if has {
|
||||||
|
p.comment.Write(comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
section.Comment = strings.TrimSpace(p.comment.String())
|
||||||
|
|
||||||
|
// Reset aotu-counter and comments
|
||||||
|
p.comment.Reset()
|
||||||
|
p.count = 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
kname, offset, err := readKeyName(line)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto increment.
|
||||||
|
isAutoIncr := false
|
||||||
|
if kname == "-" {
|
||||||
|
isAutoIncr = true
|
||||||
|
kname = "#" + strconv.Itoa(p.count)
|
||||||
|
p.count++
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := section.NewKey(kname, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
key.isAutoIncr = isAutoIncr
|
||||||
|
|
||||||
|
value, err := p.readValue(line[offset:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
key.SetValue(value)
|
||||||
|
key.Comment = strings.TrimSpace(p.comment.String())
|
||||||
|
p.comment.Reset()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
351
Godeps/_workspace/src/github.com/go-ini/ini/struct.go
generated
vendored
Normal file
351
Godeps/_workspace/src/github.com/go-ini/ini/struct.go
generated
vendored
Normal file
|
@ -0,0 +1,351 @@
|
||||||
|
// Copyright 2014 Unknwon
|
||||||
|
//
|
||||||
|
// 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 ini
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NameMapper represents a ini tag name mapper.
|
||||||
|
type NameMapper func(string) string
|
||||||
|
|
||||||
|
// Built-in name getters.
|
||||||
|
var (
|
||||||
|
// AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE.
|
||||||
|
AllCapsUnderscore NameMapper = func(raw string) string {
|
||||||
|
newstr := make([]rune, 0, len(raw))
|
||||||
|
for i, chr := range raw {
|
||||||
|
if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
|
||||||
|
if i > 0 {
|
||||||
|
newstr = append(newstr, '_')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newstr = append(newstr, unicode.ToUpper(chr))
|
||||||
|
}
|
||||||
|
return string(newstr)
|
||||||
|
}
|
||||||
|
// TitleUnderscore converts to format title_underscore.
|
||||||
|
TitleUnderscore NameMapper = func(raw string) string {
|
||||||
|
newstr := make([]rune, 0, len(raw))
|
||||||
|
for i, chr := range raw {
|
||||||
|
if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
|
||||||
|
if i > 0 {
|
||||||
|
newstr = append(newstr, '_')
|
||||||
|
}
|
||||||
|
chr -= ('A' - 'a')
|
||||||
|
}
|
||||||
|
newstr = append(newstr, chr)
|
||||||
|
}
|
||||||
|
return string(newstr)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Section) parseFieldName(raw, actual string) string {
|
||||||
|
if len(actual) > 0 {
|
||||||
|
return actual
|
||||||
|
}
|
||||||
|
if s.f.NameMapper != nil {
|
||||||
|
return s.f.NameMapper(raw)
|
||||||
|
}
|
||||||
|
return raw
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDelim(actual string) string {
|
||||||
|
if len(actual) > 0 {
|
||||||
|
return actual
|
||||||
|
}
|
||||||
|
return ","
|
||||||
|
}
|
||||||
|
|
||||||
|
var reflectTime = reflect.TypeOf(time.Now()).Kind()
|
||||||
|
|
||||||
|
// setWithProperType sets proper value to field based on its type,
|
||||||
|
// but it does not return error for failing parsing,
|
||||||
|
// because we want to use default value that is already assigned to strcut.
|
||||||
|
func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string) error {
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
if len(key.String()) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
field.SetString(key.String())
|
||||||
|
case reflect.Bool:
|
||||||
|
boolVal, err := key.Bool()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
field.SetBool(boolVal)
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
durationVal, err := key.Duration()
|
||||||
|
// Skip zero value
|
||||||
|
if err == nil && int(durationVal) > 0 {
|
||||||
|
field.Set(reflect.ValueOf(durationVal))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
intVal, err := key.Int64()
|
||||||
|
if err != nil || intVal == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
field.SetInt(intVal)
|
||||||
|
// byte is an alias for uint8, so supporting uint8 breaks support for byte
|
||||||
|
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
durationVal, err := key.Duration()
|
||||||
|
if err == nil {
|
||||||
|
field.Set(reflect.ValueOf(durationVal))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
uintVal, err := key.Uint64()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
field.SetUint(uintVal)
|
||||||
|
|
||||||
|
case reflect.Float64:
|
||||||
|
floatVal, err := key.Float64()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
field.SetFloat(floatVal)
|
||||||
|
case reflectTime:
|
||||||
|
timeVal, err := key.Time()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
field.Set(reflect.ValueOf(timeVal))
|
||||||
|
case reflect.Slice:
|
||||||
|
vals := key.Strings(delim)
|
||||||
|
numVals := len(vals)
|
||||||
|
if numVals == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sliceOf := field.Type().Elem().Kind()
|
||||||
|
|
||||||
|
var times []time.Time
|
||||||
|
if sliceOf == reflectTime {
|
||||||
|
times = key.Times(delim)
|
||||||
|
}
|
||||||
|
|
||||||
|
slice := reflect.MakeSlice(field.Type(), numVals, numVals)
|
||||||
|
for i := 0; i < numVals; i++ {
|
||||||
|
switch sliceOf {
|
||||||
|
case reflectTime:
|
||||||
|
slice.Index(i).Set(reflect.ValueOf(times[i]))
|
||||||
|
default:
|
||||||
|
slice.Index(i).Set(reflect.ValueOf(vals[i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field.Set(slice)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported type '%s'", t)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Section) mapTo(val reflect.Value) error {
|
||||||
|
if val.Kind() == reflect.Ptr {
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
typ := val.Type()
|
||||||
|
|
||||||
|
for i := 0; i < typ.NumField(); i++ {
|
||||||
|
field := val.Field(i)
|
||||||
|
tpField := typ.Field(i)
|
||||||
|
|
||||||
|
tag := tpField.Tag.Get("ini")
|
||||||
|
if tag == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldName := s.parseFieldName(tpField.Name, tag)
|
||||||
|
if len(fieldName) == 0 || !field.CanSet() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
isAnonymous := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous
|
||||||
|
isStruct := tpField.Type.Kind() == reflect.Struct
|
||||||
|
if isAnonymous {
|
||||||
|
field.Set(reflect.New(tpField.Type.Elem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if isAnonymous || isStruct {
|
||||||
|
if sec, err := s.f.GetSection(fieldName); err == nil {
|
||||||
|
if err = sec.mapTo(field); err != nil {
|
||||||
|
return fmt.Errorf("error mapping field(%s): %v", fieldName, err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if key, err := s.GetKey(fieldName); err == nil {
|
||||||
|
if err = setWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim"))); err != nil {
|
||||||
|
return fmt.Errorf("error mapping field(%s): %v", fieldName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapTo maps section to given struct.
|
||||||
|
func (s *Section) MapTo(v interface{}) error {
|
||||||
|
typ := reflect.TypeOf(v)
|
||||||
|
val := reflect.ValueOf(v)
|
||||||
|
if typ.Kind() == reflect.Ptr {
|
||||||
|
typ = typ.Elem()
|
||||||
|
val = val.Elem()
|
||||||
|
} else {
|
||||||
|
return errors.New("cannot map to non-pointer struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.mapTo(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapTo maps file to given struct.
|
||||||
|
func (f *File) MapTo(v interface{}) error {
|
||||||
|
return f.Section("").MapTo(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapTo maps data sources to given struct with name mapper.
|
||||||
|
func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error {
|
||||||
|
cfg, err := Load(source, others...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cfg.NameMapper = mapper
|
||||||
|
return cfg.MapTo(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapTo maps data sources to given struct.
|
||||||
|
func MapTo(v, source interface{}, others ...interface{}) error {
|
||||||
|
return MapToWithMapper(v, nil, source, others...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reflectWithProperType does the opposite thing with setWithProperType.
|
||||||
|
func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string) error {
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
key.SetValue(field.String())
|
||||||
|
case reflect.Bool,
|
||||||
|
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||||
|
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||||
|
reflect.Float64,
|
||||||
|
reflectTime:
|
||||||
|
key.SetValue(fmt.Sprint(field))
|
||||||
|
case reflect.Slice:
|
||||||
|
vals := field.Slice(0, field.Len())
|
||||||
|
if field.Len() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
isTime := fmt.Sprint(field.Type()) == "[]time.Time"
|
||||||
|
for i := 0; i < field.Len(); i++ {
|
||||||
|
if isTime {
|
||||||
|
buf.WriteString(vals.Index(i).Interface().(time.Time).Format(time.RFC3339))
|
||||||
|
} else {
|
||||||
|
buf.WriteString(fmt.Sprint(vals.Index(i)))
|
||||||
|
}
|
||||||
|
buf.WriteString(delim)
|
||||||
|
}
|
||||||
|
key.SetValue(buf.String()[:buf.Len()-1])
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported type '%s'", t)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Section) reflectFrom(val reflect.Value) error {
|
||||||
|
if val.Kind() == reflect.Ptr {
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
typ := val.Type()
|
||||||
|
|
||||||
|
for i := 0; i < typ.NumField(); i++ {
|
||||||
|
field := val.Field(i)
|
||||||
|
tpField := typ.Field(i)
|
||||||
|
|
||||||
|
tag := tpField.Tag.Get("ini")
|
||||||
|
if tag == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldName := s.parseFieldName(tpField.Name, tag)
|
||||||
|
if len(fieldName) == 0 || !field.CanSet() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous) ||
|
||||||
|
(tpField.Type.Kind() == reflect.Struct) {
|
||||||
|
// Note: The only error here is section doesn't exist.
|
||||||
|
sec, err := s.f.GetSection(fieldName)
|
||||||
|
if err != nil {
|
||||||
|
// Note: fieldName can never be empty here, ignore error.
|
||||||
|
sec, _ = s.f.NewSection(fieldName)
|
||||||
|
}
|
||||||
|
if err = sec.reflectFrom(field); err != nil {
|
||||||
|
return fmt.Errorf("error reflecting field(%s): %v", fieldName, err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Same reason as secion.
|
||||||
|
key, err := s.GetKey(fieldName)
|
||||||
|
if err != nil {
|
||||||
|
key, _ = s.NewKey(fieldName, "")
|
||||||
|
}
|
||||||
|
if err = reflectWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim"))); err != nil {
|
||||||
|
return fmt.Errorf("error reflecting field(%s): %v", fieldName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReflectFrom reflects secion from given struct.
|
||||||
|
func (s *Section) ReflectFrom(v interface{}) error {
|
||||||
|
typ := reflect.TypeOf(v)
|
||||||
|
val := reflect.ValueOf(v)
|
||||||
|
if typ.Kind() == reflect.Ptr {
|
||||||
|
typ = typ.Elem()
|
||||||
|
val = val.Elem()
|
||||||
|
} else {
|
||||||
|
return errors.New("cannot reflect from non-pointer struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.reflectFrom(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReflectFrom reflects file from given struct.
|
||||||
|
func (f *File) ReflectFrom(v interface{}) error {
|
||||||
|
return f.Section("").ReflectFrom(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReflectFrom reflects data sources from given struct with name mapper.
|
||||||
|
func ReflectFromWithMapper(cfg *File, v interface{}, mapper NameMapper) error {
|
||||||
|
cfg.NameMapper = mapper
|
||||||
|
return cfg.ReflectFrom(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReflectFrom reflects data sources from given struct.
|
||||||
|
func ReflectFrom(cfg *File, v interface{}) error {
|
||||||
|
return ReflectFromWithMapper(cfg, v, nil)
|
||||||
|
}
|
4
Godeps/_workspace/src/github.com/jmespath/go-jmespath/.gitignore
generated
vendored
Normal file
4
Godeps/_workspace/src/github.com/jmespath/go-jmespath/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
jpgo
|
||||||
|
jmespath-fuzz.zip
|
||||||
|
cpu.out
|
||||||
|
go-jmespath.test
|
9
Godeps/_workspace/src/github.com/jmespath/go-jmespath/.travis.yml
generated
vendored
Normal file
9
Godeps/_workspace/src/github.com/jmespath/go-jmespath/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
language: go
|
||||||
|
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.4
|
||||||
|
|
||||||
|
install: go get -v -t ./...
|
||||||
|
script: make test
|
13
Godeps/_workspace/src/github.com/jmespath/go-jmespath/LICENSE
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/jmespath/go-jmespath/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
Copyright 2015 James Saryerwinnie
|
||||||
|
|
||||||
|
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.
|
44
Godeps/_workspace/src/github.com/jmespath/go-jmespath/Makefile
generated
vendored
Normal file
44
Godeps/_workspace/src/github.com/jmespath/go-jmespath/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
|
||||||
|
CMD = jpgo
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "Please use \`make <target>' where <target> is one of"
|
||||||
|
@echo " test to run all the tests"
|
||||||
|
@echo " build to build the library and jp executable"
|
||||||
|
@echo " generate to run codegen"
|
||||||
|
|
||||||
|
|
||||||
|
generate:
|
||||||
|
go generate ./...
|
||||||
|
|
||||||
|
build:
|
||||||
|
rm -f $(CMD)
|
||||||
|
go build ./...
|
||||||
|
rm -f cmd/$(CMD)/$(CMD) && cd cmd/$(CMD)/ && go build ./...
|
||||||
|
mv cmd/$(CMD)/$(CMD) .
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test -v ./...
|
||||||
|
|
||||||
|
check:
|
||||||
|
go vet ./...
|
||||||
|
@echo "golint ./..."
|
||||||
|
@lint=`golint ./...`; \
|
||||||
|
lint=`echo "$$lint" | grep -v "astnodetype_string.go" | grep -v "toktype_string.go"`; \
|
||||||
|
echo "$$lint"; \
|
||||||
|
if [ "$$lint" != "" ]; then exit 1; fi
|
||||||
|
|
||||||
|
htmlc:
|
||||||
|
go test -coverprofile="/tmp/jpcov" && go tool cover -html="/tmp/jpcov" && unlink /tmp/jpcov
|
||||||
|
|
||||||
|
buildfuzz:
|
||||||
|
go-fuzz-build github.com/jmespath/go-jmespath/fuzz
|
||||||
|
|
||||||
|
fuzz: buildfuzz
|
||||||
|
go-fuzz -bin=./jmespath-fuzz.zip -workdir=fuzz/testdata
|
||||||
|
|
||||||
|
bench:
|
||||||
|
go test -bench . -cpuprofile cpu.out
|
||||||
|
|
||||||
|
pprof-cpu:
|
||||||
|
go tool pprof ./go-jmespath.test ./cpu.out
|
7
Godeps/_workspace/src/github.com/jmespath/go-jmespath/README.md
generated
vendored
Normal file
7
Godeps/_workspace/src/github.com/jmespath/go-jmespath/README.md
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# go-jmespath - A JMESPath implementation in Go
|
||||||
|
|
||||||
|
[![Build Status](https://img.shields.io/travis/jmespath/go-jmespath.svg)](https://travis-ci.org/jmespath/go-jmespath)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
See http://jmespath.org for more info.
|
49
Godeps/_workspace/src/github.com/jmespath/go-jmespath/api.go
generated
vendored
Normal file
49
Godeps/_workspace/src/github.com/jmespath/go-jmespath/api.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package jmespath
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// JmesPath is the epresentation of a compiled JMES path query. A JmesPath is
|
||||||
|
// safe for concurrent use by multiple goroutines.
|
||||||
|
type JMESPath struct {
|
||||||
|
ast ASTNode
|
||||||
|
intr *treeInterpreter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile parses a JMESPath expression and returns, if successful, a JMESPath
|
||||||
|
// object that can be used to match against data.
|
||||||
|
func Compile(expression string) (*JMESPath, error) {
|
||||||
|
parser := NewParser()
|
||||||
|
ast, err := parser.Parse(expression)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
jmespath := &JMESPath{ast: ast, intr: newInterpreter()}
|
||||||
|
return jmespath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustCompile is like Compile but panics if the expression cannot be parsed.
|
||||||
|
// It simplifies safe initialization of global variables holding compiled
|
||||||
|
// JMESPaths.
|
||||||
|
func MustCompile(expression string) *JMESPath {
|
||||||
|
jmespath, err := Compile(expression)
|
||||||
|
if err != nil {
|
||||||
|
panic(`jmespath: Compile(` + strconv.Quote(expression) + `): ` + err.Error())
|
||||||
|
}
|
||||||
|
return jmespath
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search evaluates a JMESPath expression against input data and returns the result.
|
||||||
|
func (jp *JMESPath) Search(data interface{}) (interface{}, error) {
|
||||||
|
return jp.intr.Execute(jp.ast, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search evaluates a JMESPath expression against input data and returns the result.
|
||||||
|
func Search(expression string, data interface{}) (interface{}, error) {
|
||||||
|
intr := newInterpreter()
|
||||||
|
parser := NewParser()
|
||||||
|
ast, err := parser.Parse(expression)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return intr.Execute(ast, data)
|
||||||
|
}
|
16
Godeps/_workspace/src/github.com/jmespath/go-jmespath/astnodetype_string.go
generated
vendored
Normal file
16
Godeps/_workspace/src/github.com/jmespath/go-jmespath/astnodetype_string.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// generated by stringer -type astNodeType; DO NOT EDIT
|
||||||
|
|
||||||
|
package jmespath
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
const _astNodeType_name = "ASTEmptyASTComparatorASTCurrentNodeASTExpRefASTFunctionExpressionASTFieldASTFilterProjectionASTFlattenASTIdentityASTIndexASTIndexExpressionASTKeyValPairASTLiteralASTMultiSelectHashASTMultiSelectListASTOrExpressionASTAndExpressionASTNotExpressionASTPipeASTProjectionASTSubexpressionASTSliceASTValueProjection"
|
||||||
|
|
||||||
|
var _astNodeType_index = [...]uint16{0, 8, 21, 35, 44, 65, 73, 92, 102, 113, 121, 139, 152, 162, 180, 198, 213, 229, 245, 252, 265, 281, 289, 307}
|
||||||
|
|
||||||
|
func (i astNodeType) String() string {
|
||||||
|
if i < 0 || i >= astNodeType(len(_astNodeType_index)-1) {
|
||||||
|
return fmt.Sprintf("astNodeType(%d)", i)
|
||||||
|
}
|
||||||
|
return _astNodeType_name[_astNodeType_index[i]:_astNodeType_index[i+1]]
|
||||||
|
}
|
96
Godeps/_workspace/src/github.com/jmespath/go-jmespath/compliance/basic.json
generated
vendored
Normal file
96
Godeps/_workspace/src/github.com/jmespath/go-jmespath/compliance/basic.json
generated
vendored
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
[{
|
||||||
|
"given":
|
||||||
|
{"foo": {"bar": {"baz": "correct"}}},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"expression": "foo",
|
||||||
|
"result": {"bar": {"baz": "correct"}}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo.bar",
|
||||||
|
"result": {"baz": "correct"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo.bar.baz",
|
||||||
|
"result": "correct"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo\n.\nbar\n.baz",
|
||||||
|
"result": "correct"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo.bar.baz.bad",
|
||||||
|
"result": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo.bar.bad",
|
||||||
|
"result": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo.bad",
|
||||||
|
"result": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "bad",
|
||||||
|
"result": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "bad.morebad.morebad",
|
||||||
|
"result": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given":
|
||||||
|
{"foo": {"bar": ["one", "two", "three"]}},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"expression": "foo",
|
||||||
|
"result": {"bar": ["one", "two", "three"]}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo.bar",
|
||||||
|
"result": ["one", "two", "three"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": ["one", "two", "three"],
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"expression": "one",
|
||||||
|
"result": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "two",
|
||||||
|
"result": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "three",
|
||||||
|
"result": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "one.two",
|
||||||
|
"result": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given":
|
||||||
|
{"foo": {"1": ["one", "two", "three"], "-1": "bar"}},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"expression": "foo.\"1\"",
|
||||||
|
"result": ["one", "two", "three"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo.\"1\"[0]",
|
||||||
|
"result": "one"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo.\"-1\"",
|
||||||
|
"result": "bar"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
257
Godeps/_workspace/src/github.com/jmespath/go-jmespath/compliance/boolean.json
generated
vendored
Normal file
257
Godeps/_workspace/src/github.com/jmespath/go-jmespath/compliance/boolean.json
generated
vendored
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"given": {
|
||||||
|
"outer": {
|
||||||
|
"foo": "foo",
|
||||||
|
"bar": "bar",
|
||||||
|
"baz": "baz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"expression": "outer.foo || outer.bar",
|
||||||
|
"result": "foo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "outer.foo||outer.bar",
|
||||||
|
"result": "foo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "outer.bar || outer.baz",
|
||||||
|
"result": "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "outer.bar||outer.baz",
|
||||||
|
"result": "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "outer.bad || outer.foo",
|
||||||
|
"result": "foo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "outer.bad||outer.foo",
|
||||||
|
"result": "foo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "outer.foo || outer.bad",
|
||||||
|
"result": "foo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "outer.foo||outer.bad",
|
||||||
|
"result": "foo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "outer.bad || outer.alsobad",
|
||||||
|
"result": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "outer.bad||outer.alsobad",
|
||||||
|
"result": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": {
|
||||||
|
"outer": {
|
||||||
|
"foo": "foo",
|
||||||
|
"bool": false,
|
||||||
|
"empty_list": [],
|
||||||
|
"empty_string": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"expression": "outer.empty_string || outer.foo",
|
||||||
|
"result": "foo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "outer.nokey || outer.bool || outer.empty_list || outer.empty_string || outer.foo",
|
||||||
|
"result": "foo"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": {
|
||||||
|
"True": true,
|
||||||
|
"False": false,
|
||||||
|
"Number": 5,
|
||||||
|
"EmptyList": [],
|
||||||
|
"Zero": 0
|
||||||
|
},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"expression": "True && False",
|
||||||
|
"result": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "False && True",
|
||||||
|
"result": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "True && True",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "False && False",
|
||||||
|
"result": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "True && Number",
|
||||||
|
"result": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "Number && True",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "Number && False",
|
||||||
|
"result": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "Number && EmptyList",
|
||||||
|
"result": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "Number && True",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "EmptyList && True",
|
||||||
|
"result": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "EmptyList && False",
|
||||||
|
"result": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "True || False",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "True || True",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "False || True",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "False || False",
|
||||||
|
"result": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "Number || EmptyList",
|
||||||
|
"result": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "Number || True",
|
||||||
|
"result": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "Number || True && False",
|
||||||
|
"result": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "(Number || True) && False",
|
||||||
|
"result": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "Number || (True && False)",
|
||||||
|
"result": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "!True",
|
||||||
|
"result": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "!False",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "!Number",
|
||||||
|
"result": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "!EmptyList",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "True && !False",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "True && !EmptyList",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "!False && !EmptyList",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "!(True && False)",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "!Zero",
|
||||||
|
"result": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "!!Zero",
|
||||||
|
"result": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": {
|
||||||
|
"one": 1,
|
||||||
|
"two": 2,
|
||||||
|
"three": 3
|
||||||
|
},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"expression": "one < two",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "one <= two",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "one == one",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "one == two",
|
||||||
|
"result": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "one > two",
|
||||||
|
"result": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "one >= two",
|
||||||
|
"result": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "one != two",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "one < two && three > one",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "one < two || three > one",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "one < two || three < one",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "two < one || three < one",
|
||||||
|
"result": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
25
Godeps/_workspace/src/github.com/jmespath/go-jmespath/compliance/current.json
generated
vendored
Normal file
25
Godeps/_workspace/src/github.com/jmespath/go-jmespath/compliance/current.json
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"given": {
|
||||||
|
"foo": [{"name": "a"}, {"name": "b"}],
|
||||||
|
"bar": {"baz": "qux"}
|
||||||
|
},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"expression": "@",
|
||||||
|
"result": {
|
||||||
|
"foo": [{"name": "a"}, {"name": "b"}],
|
||||||
|
"bar": {"baz": "qux"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "@.bar",
|
||||||
|
"result": {"baz": "qux"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "@.foo[0]",
|
||||||
|
"result": {"name": "a"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
46
Godeps/_workspace/src/github.com/jmespath/go-jmespath/compliance/escape.json
generated
vendored
Normal file
46
Godeps/_workspace/src/github.com/jmespath/go-jmespath/compliance/escape.json
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
[{
|
||||||
|
"given": {
|
||||||
|
"foo.bar": "dot",
|
||||||
|
"foo bar": "space",
|
||||||
|
"foo\nbar": "newline",
|
||||||
|
"foo\"bar": "doublequote",
|
||||||
|
"c:\\\\windows\\path": "windows",
|
||||||
|
"/unix/path": "unix",
|
||||||
|
"\"\"\"": "threequotes",
|
||||||
|
"bar": {"baz": "qux"}
|
||||||
|
},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"expression": "\"foo.bar\"",
|
||||||
|
"result": "dot"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "\"foo bar\"",
|
||||||
|
"result": "space"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "\"foo\\nbar\"",
|
||||||
|
"result": "newline"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "\"foo\\\"bar\"",
|
||||||
|
"result": "doublequote"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "\"c:\\\\\\\\windows\\\\path\"",
|
||||||
|
"result": "windows"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "\"/unix/path\"",
|
||||||
|
"result": "unix"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "\"\\\"\\\"\\\"\"",
|
||||||
|
"result": "threequotes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "\"bar\".\"baz\"",
|
||||||
|
"result": "qux"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
468
Godeps/_workspace/src/github.com/jmespath/go-jmespath/compliance/filters.json
generated
vendored
Normal file
468
Godeps/_workspace/src/github.com/jmespath/go-jmespath/compliance/filters.json
generated
vendored
Normal file
|
@ -0,0 +1,468 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"given": {"foo": [{"name": "a"}, {"name": "b"}]},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"comment": "Matching a literal",
|
||||||
|
"expression": "foo[?name == 'a']",
|
||||||
|
"result": [{"name": "a"}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": {"foo": [0, 1], "bar": [2, 3]},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"comment": "Matching a literal",
|
||||||
|
"expression": "*[?[0] == `0`]",
|
||||||
|
"result": [[], []]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": {"foo": [{"first": "foo", "last": "bar"},
|
||||||
|
{"first": "foo", "last": "foo"},
|
||||||
|
{"first": "foo", "last": "baz"}]},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"comment": "Matching an expression",
|
||||||
|
"expression": "foo[?first == last]",
|
||||||
|
"result": [{"first": "foo", "last": "foo"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Verify projection created from filter",
|
||||||
|
"expression": "foo[?first == last].first",
|
||||||
|
"result": ["foo"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": {"foo": [{"age": 20},
|
||||||
|
{"age": 25},
|
||||||
|
{"age": 30}]},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"comment": "Greater than with a number",
|
||||||
|
"expression": "foo[?age > `25`]",
|
||||||
|
"result": [{"age": 30}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?age >= `25`]",
|
||||||
|
"result": [{"age": 25}, {"age": 30}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Greater than with a number",
|
||||||
|
"expression": "foo[?age > `30`]",
|
||||||
|
"result": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Greater than with a number",
|
||||||
|
"expression": "foo[?age < `25`]",
|
||||||
|
"result": [{"age": 20}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Greater than with a number",
|
||||||
|
"expression": "foo[?age <= `25`]",
|
||||||
|
"result": [{"age": 20}, {"age": 25}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Greater than with a number",
|
||||||
|
"expression": "foo[?age < `20`]",
|
||||||
|
"result": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?age == `20`]",
|
||||||
|
"result": [{"age": 20}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?age != `20`]",
|
||||||
|
"result": [{"age": 25}, {"age": 30}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": {"foo": [{"top": {"name": "a"}},
|
||||||
|
{"top": {"name": "b"}}]},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"comment": "Filter with subexpression",
|
||||||
|
"expression": "foo[?top.name == 'a']",
|
||||||
|
"result": [{"top": {"name": "a"}}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": {"foo": [{"top": {"first": "foo", "last": "bar"}},
|
||||||
|
{"top": {"first": "foo", "last": "foo"}},
|
||||||
|
{"top": {"first": "foo", "last": "baz"}}]},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"comment": "Matching an expression",
|
||||||
|
"expression": "foo[?top.first == top.last]",
|
||||||
|
"result": [{"top": {"first": "foo", "last": "foo"}}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Matching a JSON array",
|
||||||
|
"expression": "foo[?top == `{\"first\": \"foo\", \"last\": \"bar\"}`]",
|
||||||
|
"result": [{"top": {"first": "foo", "last": "bar"}}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": {"foo": [
|
||||||
|
{"key": true},
|
||||||
|
{"key": false},
|
||||||
|
{"key": 0},
|
||||||
|
{"key": 1},
|
||||||
|
{"key": [0]},
|
||||||
|
{"key": {"bar": [0]}},
|
||||||
|
{"key": null},
|
||||||
|
{"key": [1]},
|
||||||
|
{"key": {"a":2}}
|
||||||
|
]},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"expression": "foo[?key == `true`]",
|
||||||
|
"result": [{"key": true}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?key == `false`]",
|
||||||
|
"result": [{"key": false}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?key == `0`]",
|
||||||
|
"result": [{"key": 0}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?key == `1`]",
|
||||||
|
"result": [{"key": 1}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?key == `[0]`]",
|
||||||
|
"result": [{"key": [0]}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?key == `{\"bar\": [0]}`]",
|
||||||
|
"result": [{"key": {"bar": [0]}}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?key == `null`]",
|
||||||
|
"result": [{"key": null}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?key == `[1]`]",
|
||||||
|
"result": [{"key": [1]}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?key == `{\"a\":2}`]",
|
||||||
|
"result": [{"key": {"a":2}}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?`true` == key]",
|
||||||
|
"result": [{"key": true}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?`false` == key]",
|
||||||
|
"result": [{"key": false}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?`0` == key]",
|
||||||
|
"result": [{"key": 0}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?`1` == key]",
|
||||||
|
"result": [{"key": 1}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?`[0]` == key]",
|
||||||
|
"result": [{"key": [0]}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?`{\"bar\": [0]}` == key]",
|
||||||
|
"result": [{"key": {"bar": [0]}}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?`null` == key]",
|
||||||
|
"result": [{"key": null}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?`[1]` == key]",
|
||||||
|
"result": [{"key": [1]}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?`{\"a\":2}` == key]",
|
||||||
|
"result": [{"key": {"a":2}}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?key != `true`]",
|
||||||
|
"result": [{"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
|
||||||
|
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?key != `false`]",
|
||||||
|
"result": [{"key": true}, {"key": 0}, {"key": 1}, {"key": [0]},
|
||||||
|
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?key != `0`]",
|
||||||
|
"result": [{"key": true}, {"key": false}, {"key": 1}, {"key": [0]},
|
||||||
|
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?key != `1`]",
|
||||||
|
"result": [{"key": true}, {"key": false}, {"key": 0}, {"key": [0]},
|
||||||
|
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?key != `null`]",
|
||||||
|
"result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
|
||||||
|
{"key": {"bar": [0]}}, {"key": [1]}, {"key": {"a":2}}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?key != `[1]`]",
|
||||||
|
"result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
|
||||||
|
{"key": {"bar": [0]}}, {"key": null}, {"key": {"a":2}}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?key != `{\"a\":2}`]",
|
||||||
|
"result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
|
||||||
|
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?`true` != key]",
|
||||||
|
"result": [{"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
|
||||||
|
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?`false` != key]",
|
||||||
|
"result": [{"key": true}, {"key": 0}, {"key": 1}, {"key": [0]},
|
||||||
|
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?`0` != key]",
|
||||||
|
"result": [{"key": true}, {"key": false}, {"key": 1}, {"key": [0]},
|
||||||
|
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?`1` != key]",
|
||||||
|
"result": [{"key": true}, {"key": false}, {"key": 0}, {"key": [0]},
|
||||||
|
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?`null` != key]",
|
||||||
|
"result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
|
||||||
|
{"key": {"bar": [0]}}, {"key": [1]}, {"key": {"a":2}}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?`[1]` != key]",
|
||||||
|
"result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
|
||||||
|
{"key": {"bar": [0]}}, {"key": null}, {"key": {"a":2}}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?`{\"a\":2}` != key]",
|
||||||
|
"result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
|
||||||
|
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": {"reservations": [
|
||||||
|
{"instances": [
|
||||||
|
{"foo": 1, "bar": 2}, {"foo": 1, "bar": 3},
|
||||||
|
{"foo": 1, "bar": 2}, {"foo": 2, "bar": 1}]}]},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"expression": "reservations[].instances[?bar==`1`]",
|
||||||
|
"result": [[{"foo": 2, "bar": 1}]]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "reservations[*].instances[?bar==`1`]",
|
||||||
|
"result": [[{"foo": 2, "bar": 1}]]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "reservations[].instances[?bar==`1`][]",
|
||||||
|
"result": [{"foo": 2, "bar": 1}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": {
|
||||||
|
"baz": "other",
|
||||||
|
"foo": [
|
||||||
|
{"bar": 1}, {"bar": 2}, {"bar": 3}, {"bar": 4}, {"bar": 1, "baz": 2}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"expression": "foo[?bar==`1`].bar[0]",
|
||||||
|
"result": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": {
|
||||||
|
"foo": [
|
||||||
|
{"a": 1, "b": {"c": "x"}},
|
||||||
|
{"a": 1, "b": {"c": "y"}},
|
||||||
|
{"a": 1, "b": {"c": "z"}},
|
||||||
|
{"a": 2, "b": {"c": "z"}},
|
||||||
|
{"a": 1, "baz": 2}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"expression": "foo[?a==`1`].b.c",
|
||||||
|
"result": ["x", "y", "z"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": {"foo": [{"name": "a"}, {"name": "b"}, {"name": "c"}]},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"comment": "Filter with or expression",
|
||||||
|
"expression": "foo[?name == 'a' || name == 'b']",
|
||||||
|
"result": [{"name": "a"}, {"name": "b"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?name == 'a' || name == 'e']",
|
||||||
|
"result": [{"name": "a"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?name == 'a' || name == 'b' || name == 'c']",
|
||||||
|
"result": [{"name": "a"}, {"name": "b"}, {"name": "c"}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": {"foo": [{"a": 1, "b": 2}, {"a": 1, "b": 3}]},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"comment": "Filter with and expression",
|
||||||
|
"expression": "foo[?a == `1` && b == `2`]",
|
||||||
|
"result": [{"a": 1, "b": 2}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?a == `1` && b == `4`]",
|
||||||
|
"result": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": {"foo": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"comment": "Filter with Or and And expressions",
|
||||||
|
"expression": "foo[?c == `3` || a == `1` && b == `4`]",
|
||||||
|
"result": [{"a": 1, "b": 2, "c": 3}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?b == `2` || a == `3` && b == `4`]",
|
||||||
|
"result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?a == `3` && b == `4` || b == `2`]",
|
||||||
|
"result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?(a == `3` && b == `4`) || b == `2`]",
|
||||||
|
"result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?((a == `3` && b == `4`)) || b == `2`]",
|
||||||
|
"result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?a == `3` && (b == `4` || b == `2`)]",
|
||||||
|
"result": [{"a": 3, "b": 4}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "foo[?a == `3` && ((b == `4` || b == `2`))]",
|
||||||
|
"result": [{"a": 3, "b": 4}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": {"foo": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"comment": "Verify precedence of or/and expressions",
|
||||||
|
"expression": "foo[?a == `1` || b ==`2` && c == `5`]",
|
||||||
|
"result": [{"a": 1, "b": 2, "c": 3}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Parentheses can alter precedence",
|
||||||
|
"expression": "foo[?(a == `1` || b ==`2`) && c == `5`]",
|
||||||
|
"result": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Not expressions combined with and/or",
|
||||||
|
"expression": "foo[?!(a == `1` || b ==`2`)]",
|
||||||
|
"result": [{"a": 3, "b": 4}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": {
|
||||||
|
"foo": [
|
||||||
|
{"key": true},
|
||||||
|
{"key": false},
|
||||||
|
{"key": []},
|
||||||
|
{"key": {}},
|
||||||
|
{"key": [0]},
|
||||||
|
{"key": {"a": "b"}},
|
||||||
|
{"key": 0},
|
||||||
|
{"key": 1},
|
||||||
|
{"key": null},
|
||||||
|
{"notkey": true}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"comment": "Unary filter expression",
|
||||||
|
"expression": "foo[?key]",
|
||||||
|
"result": [
|
||||||
|
{"key": true}, {"key": [0]}, {"key": {"a": "b"}},
|
||||||
|
{"key": 0}, {"key": 1}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Unary not filter expression",
|
||||||
|
"expression": "foo[?!key]",
|
||||||
|
"result": [
|
||||||
|
{"key": false}, {"key": []}, {"key": {}},
|
||||||
|
{"key": null}, {"notkey": true}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Equality with null RHS",
|
||||||
|
"expression": "foo[?key == `null`]",
|
||||||
|
"result": [
|
||||||
|
{"key": null}, {"notkey": true}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"given": {
|
||||||
|
"foo": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||||
|
},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"comment": "Using @ in a filter expression",
|
||||||
|
"expression": "foo[?@ < `5`]",
|
||||||
|
"result": [0, 1, 2, 3, 4]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Using @ in a filter expression",
|
||||||
|
"expression": "foo[?`5` > @]",
|
||||||
|
"result": [0, 1, 2, 3, 4]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Using @ in a filter expression",
|
||||||
|
"expression": "foo[?@ == @]",
|
||||||
|
"result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
825
Godeps/_workspace/src/github.com/jmespath/go-jmespath/compliance/functions.json
generated
vendored
Normal file
825
Godeps/_workspace/src/github.com/jmespath/go-jmespath/compliance/functions.json
generated
vendored
Normal file
|
@ -0,0 +1,825 @@
|
||||||
|
[{
|
||||||
|
"given":
|
||||||
|
{
|
||||||
|
"foo": -1,
|
||||||
|
"zero": 0,
|
||||||
|
"numbers": [-1, 3, 4, 5],
|
||||||
|
"array": [-1, 3, 4, 5, "a", "100"],
|
||||||
|
"strings": ["a", "b", "c"],
|
||||||
|
"decimals": [1.01, 1.2, -1.5],
|
||||||
|
"str": "Str",
|
||||||
|
"false": false,
|
||||||
|
"empty_list": [],
|
||||||
|
"empty_hash": {},
|
||||||
|
"objects": {"foo": "bar", "bar": "baz"},
|
||||||
|
"null_key": null
|
||||||
|
},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"expression": "abs(foo)",
|
||||||
|
"result": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "abs(foo)",
|
||||||
|
"result": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "abs(str)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "abs(array[1])",
|
||||||
|
"result": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "abs(array[1])",
|
||||||
|
"result": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "abs(`false`)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "abs(`-24`)",
|
||||||
|
"result": 24
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "abs(`-24`)",
|
||||||
|
"result": 24
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "abs(`1`, `2`)",
|
||||||
|
"error": "invalid-arity"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "abs()",
|
||||||
|
"error": "invalid-arity"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "unknown_function(`1`, `2`)",
|
||||||
|
"error": "unknown-function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "avg(numbers)",
|
||||||
|
"result": 2.75
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "avg(array)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "avg('abc')",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "avg(foo)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "avg(@)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "avg(strings)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "ceil(`1.2`)",
|
||||||
|
"result": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "ceil(decimals[0])",
|
||||||
|
"result": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "ceil(decimals[1])",
|
||||||
|
"result": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "ceil(decimals[2])",
|
||||||
|
"result": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "ceil('string')",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "contains('abc', 'a')",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "contains('abc', 'd')",
|
||||||
|
"result": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "contains(`false`, 'd')",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "contains(strings, 'a')",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "contains(decimals, `1.2`)",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "contains(decimals, `false`)",
|
||||||
|
"result": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "ends_with(str, 'r')",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "ends_with(str, 'tr')",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "ends_with(str, 'Str')",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "ends_with(str, 'SStr')",
|
||||||
|
"result": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "ends_with(str, 'foo')",
|
||||||
|
"result": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "ends_with(str, `0`)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "floor(`1.2`)",
|
||||||
|
"result": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "floor('string')",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "floor(decimals[0])",
|
||||||
|
"result": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "floor(foo)",
|
||||||
|
"result": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "floor(str)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "length('abc')",
|
||||||
|
"result": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "length('✓foo')",
|
||||||
|
"result": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "length('')",
|
||||||
|
"result": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "length(@)",
|
||||||
|
"result": 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "length(strings[0])",
|
||||||
|
"result": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "length(str)",
|
||||||
|
"result": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "length(array)",
|
||||||
|
"result": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "length(objects)",
|
||||||
|
"result": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "length(`false`)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "length(foo)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "length(strings[0])",
|
||||||
|
"result": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "max(numbers)",
|
||||||
|
"result": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "max(decimals)",
|
||||||
|
"result": 1.2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "max(strings)",
|
||||||
|
"result": "c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "max(abc)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "max(array)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "max(decimals)",
|
||||||
|
"result": 1.2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "max(empty_list)",
|
||||||
|
"result": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "merge(`{}`)",
|
||||||
|
"result": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "merge(`{}`, `{}`)",
|
||||||
|
"result": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "merge(`{\"a\": 1}`, `{\"b\": 2}`)",
|
||||||
|
"result": {"a": 1, "b": 2}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "merge(`{\"a\": 1}`, `{\"a\": 2}`)",
|
||||||
|
"result": {"a": 2}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "merge(`{\"a\": 1, \"b\": 2}`, `{\"a\": 2, \"c\": 3}`, `{\"d\": 4}`)",
|
||||||
|
"result": {"a": 2, "b": 2, "c": 3, "d": 4}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "min(numbers)",
|
||||||
|
"result": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "min(decimals)",
|
||||||
|
"result": -1.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "min(abc)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "min(array)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "min(empty_list)",
|
||||||
|
"result": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "min(decimals)",
|
||||||
|
"result": -1.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "min(strings)",
|
||||||
|
"result": "a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "type('abc')",
|
||||||
|
"result": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "type(`1.0`)",
|
||||||
|
"result": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "type(`2`)",
|
||||||
|
"result": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "type(`true`)",
|
||||||
|
"result": "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "type(`false`)",
|
||||||
|
"result": "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "type(`null`)",
|
||||||
|
"result": "null"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "type(`[0]`)",
|
||||||
|
"result": "array"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "type(`{\"a\": \"b\"}`)",
|
||||||
|
"result": "object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "type(@)",
|
||||||
|
"result": "object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sort(keys(objects))",
|
||||||
|
"result": ["bar", "foo"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "keys(foo)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "keys(strings)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "keys(`false`)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sort(values(objects))",
|
||||||
|
"result": ["bar", "baz"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "keys(empty_hash)",
|
||||||
|
"result": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "values(foo)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "join(', ', strings)",
|
||||||
|
"result": "a, b, c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "join(', ', strings)",
|
||||||
|
"result": "a, b, c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "join(',', `[\"a\", \"b\"]`)",
|
||||||
|
"result": "a,b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "join(',', `[\"a\", 0]`)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "join(', ', str)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "join('|', strings)",
|
||||||
|
"result": "a|b|c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "join(`2`, strings)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "join('|', decimals)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "join('|', decimals[].to_string(@))",
|
||||||
|
"result": "1.01|1.2|-1.5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "join('|', empty_list)",
|
||||||
|
"result": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "reverse(numbers)",
|
||||||
|
"result": [5, 4, 3, -1]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "reverse(array)",
|
||||||
|
"result": ["100", "a", 5, 4, 3, -1]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "reverse(`[]`)",
|
||||||
|
"result": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "reverse('')",
|
||||||
|
"result": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "reverse('hello world')",
|
||||||
|
"result": "dlrow olleh"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "starts_with(str, 'S')",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "starts_with(str, 'St')",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "starts_with(str, 'Str')",
|
||||||
|
"result": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "starts_with(str, 'String')",
|
||||||
|
"result": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "starts_with(str, `0`)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sum(numbers)",
|
||||||
|
"result": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sum(decimals)",
|
||||||
|
"result": 0.71
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sum(array)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sum(array[].to_number(@))",
|
||||||
|
"result": 111
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sum(`[]`)",
|
||||||
|
"result": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "to_array('foo')",
|
||||||
|
"result": ["foo"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "to_array(`0`)",
|
||||||
|
"result": [0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "to_array(objects)",
|
||||||
|
"result": [{"foo": "bar", "bar": "baz"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "to_array(`[1, 2, 3]`)",
|
||||||
|
"result": [1, 2, 3]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "to_array(false)",
|
||||||
|
"result": [false]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "to_string('foo')",
|
||||||
|
"result": "foo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "to_string(`1.2`)",
|
||||||
|
"result": "1.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "to_string(`[0, 1]`)",
|
||||||
|
"result": "[0,1]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "to_number('1.0')",
|
||||||
|
"result": 1.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "to_number('1.1')",
|
||||||
|
"result": 1.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "to_number('4')",
|
||||||
|
"result": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "to_number('notanumber')",
|
||||||
|
"result": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "to_number(`false`)",
|
||||||
|
"result": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "to_number(`null`)",
|
||||||
|
"result": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "to_number(`[0]`)",
|
||||||
|
"result": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "to_number(`{\"foo\": 0}`)",
|
||||||
|
"result": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "\"to_string\"(`1.0`)",
|
||||||
|
"error": "syntax"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sort(numbers)",
|
||||||
|
"result": [-1, 3, 4, 5]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sort(strings)",
|
||||||
|
"result": ["a", "b", "c"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sort(decimals)",
|
||||||
|
"result": [-1.5, 1.01, 1.2]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sort(array)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sort(abc)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sort(empty_list)",
|
||||||
|
"result": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sort(@)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "not_null(unknown_key, str)",
|
||||||
|
"result": "Str"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "not_null(unknown_key, foo.bar, empty_list, str)",
|
||||||
|
"result": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "not_null(unknown_key, null_key, empty_list, str)",
|
||||||
|
"result": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "not_null(all, expressions, are_null)",
|
||||||
|
"result": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "not_null()",
|
||||||
|
"error": "invalid-arity"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "function projection on single arg function",
|
||||||
|
"expression": "numbers[].to_string(@)",
|
||||||
|
"result": ["-1", "3", "4", "5"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "function projection on single arg function",
|
||||||
|
"expression": "array[].to_number(@)",
|
||||||
|
"result": [-1, 3, 4, 5, 100]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"given":
|
||||||
|
{
|
||||||
|
"foo": [
|
||||||
|
{"b": "b", "a": "a"},
|
||||||
|
{"c": "c", "b": "b"},
|
||||||
|
{"d": "d", "c": "c"},
|
||||||
|
{"e": "e", "d": "d"},
|
||||||
|
{"f": "f", "e": "e"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"description": "function projection on variadic function",
|
||||||
|
"expression": "foo[].not_null(f, e, d, c, b, a)",
|
||||||
|
"result": ["b", "c", "d", "e", "f"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"given":
|
||||||
|
{
|
||||||
|
"people": [
|
||||||
|
{"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"},
|
||||||
|
{"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"},
|
||||||
|
{"age": 30, "age_str": "30", "bool": true, "name": "c"},
|
||||||
|
{"age": 50, "age_str": "50", "bool": false, "name": "d"},
|
||||||
|
{"age": 10, "age_str": "10", "bool": true, "name": 3}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"description": "sort by field expression",
|
||||||
|
"expression": "sort_by(people, &age)",
|
||||||
|
"result": [
|
||||||
|
{"age": 10, "age_str": "10", "bool": true, "name": 3},
|
||||||
|
{"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"},
|
||||||
|
{"age": 30, "age_str": "30", "bool": true, "name": "c"},
|
||||||
|
{"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"},
|
||||||
|
{"age": 50, "age_str": "50", "bool": false, "name": "d"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sort_by(people, &age_str)",
|
||||||
|
"result": [
|
||||||
|
{"age": 10, "age_str": "10", "bool": true, "name": 3},
|
||||||
|
{"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"},
|
||||||
|
{"age": 30, "age_str": "30", "bool": true, "name": "c"},
|
||||||
|
{"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"},
|
||||||
|
{"age": 50, "age_str": "50", "bool": false, "name": "d"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "sort by function expression",
|
||||||
|
"expression": "sort_by(people, &to_number(age_str))",
|
||||||
|
"result": [
|
||||||
|
{"age": 10, "age_str": "10", "bool": true, "name": 3},
|
||||||
|
{"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"},
|
||||||
|
{"age": 30, "age_str": "30", "bool": true, "name": "c"},
|
||||||
|
{"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"},
|
||||||
|
{"age": 50, "age_str": "50", "bool": false, "name": "d"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "function projection on sort_by function",
|
||||||
|
"expression": "sort_by(people, &age)[].name",
|
||||||
|
"result": [3, "a", "c", "b", "d"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sort_by(people, &extra)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sort_by(people, &bool)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sort_by(people, &name)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sort_by(people, name)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sort_by(people, &age)[].extra",
|
||||||
|
"result": ["foo", "bar"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "sort_by(`[]`, &age)",
|
||||||
|
"result": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "max_by(people, &age)",
|
||||||
|
"result": {"age": 50, "age_str": "50", "bool": false, "name": "d"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "max_by(people, &age_str)",
|
||||||
|
"result": {"age": 50, "age_str": "50", "bool": false, "name": "d"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "max_by(people, &bool)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "max_by(people, &extra)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "max_by(people, &to_number(age_str))",
|
||||||
|
"result": {"age": 50, "age_str": "50", "bool": false, "name": "d"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "min_by(people, &age)",
|
||||||
|
"result": {"age": 10, "age_str": "10", "bool": true, "name": 3}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "min_by(people, &age_str)",
|
||||||
|
"result": {"age": 10, "age_str": "10", "bool": true, "name": 3}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "min_by(people, &bool)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "min_by(people, &extra)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "min_by(people, &to_number(age_str))",
|
||||||
|
"result": {"age": 10, "age_str": "10", "bool": true, "name": 3}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"given":
|
||||||
|
{
|
||||||
|
"people": [
|
||||||
|
{"age": 10, "order": "1"},
|
||||||
|
{"age": 10, "order": "2"},
|
||||||
|
{"age": 10, "order": "3"},
|
||||||
|
{"age": 10, "order": "4"},
|
||||||
|
{"age": 10, "order": "5"},
|
||||||
|
{"age": 10, "order": "6"},
|
||||||
|
{"age": 10, "order": "7"},
|
||||||
|
{"age": 10, "order": "8"},
|
||||||
|
{"age": 10, "order": "9"},
|
||||||
|
{"age": 10, "order": "10"},
|
||||||
|
{"age": 10, "order": "11"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"description": "stable sort order",
|
||||||
|
"expression": "sort_by(people, &age)",
|
||||||
|
"result": [
|
||||||
|
{"age": 10, "order": "1"},
|
||||||
|
{"age": 10, "order": "2"},
|
||||||
|
{"age": 10, "order": "3"},
|
||||||
|
{"age": 10, "order": "4"},
|
||||||
|
{"age": 10, "order": "5"},
|
||||||
|
{"age": 10, "order": "6"},
|
||||||
|
{"age": 10, "order": "7"},
|
||||||
|
{"age": 10, "order": "8"},
|
||||||
|
{"age": 10, "order": "9"},
|
||||||
|
{"age": 10, "order": "10"},
|
||||||
|
{"age": 10, "order": "11"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"given":
|
||||||
|
{
|
||||||
|
"people": [
|
||||||
|
{"a": 10, "b": 1, "c": "z"},
|
||||||
|
{"a": 10, "b": 2, "c": null},
|
||||||
|
{"a": 10, "b": 3},
|
||||||
|
{"a": 10, "b": 4, "c": "z"},
|
||||||
|
{"a": 10, "b": 5, "c": null},
|
||||||
|
{"a": 10, "b": 6},
|
||||||
|
{"a": 10, "b": 7, "c": "z"},
|
||||||
|
{"a": 10, "b": 8, "c": null},
|
||||||
|
{"a": 10, "b": 9}
|
||||||
|
],
|
||||||
|
"empty": []
|
||||||
|
},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"expression": "map(&a, people)",
|
||||||
|
"result": [10, 10, 10, 10, 10, 10, 10, 10, 10]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "map(&c, people)",
|
||||||
|
"result": ["z", null, null, "z", null, null, "z", null, null]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "map(&a, badkey)",
|
||||||
|
"error": "invalid-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "map(&foo, empty)",
|
||||||
|
"result": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"given": {
|
||||||
|
"array": [
|
||||||
|
{
|
||||||
|
"foo": {"bar": "yes1"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"foo": {"bar": "yes2"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"foo1": {"bar": "no"}
|
||||||
|
}
|
||||||
|
]},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"expression": "map(&foo.bar, array)",
|
||||||
|
"result": ["yes1", "yes2", null]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "map(&foo1.bar, array)",
|
||||||
|
"result": [null, null, "no"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "map(&foo.bar.baz, array)",
|
||||||
|
"result": [null, null, null]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"given": {
|
||||||
|
"array": [[1, 2, 3, [4]], [5, 6, 7, [8, 9]]]
|
||||||
|
},
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"expression": "map(&[], array)",
|
||||||
|
"result": [[1, 2, 3, 4], [5, 6, 7, 8, 9]]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue