Commit bd9adaf8 authored by Trevor Pounds's avatar Trevor Pounds

Update Godeps to aws-sdk-go v1.0.2.

parent 9c5ede63
...@@ -49,53 +49,58 @@ ...@@ -49,53 +49,58 @@
}, },
{ {
"ImportPath": "github.com/aws/aws-sdk-go/aws", "ImportPath": "github.com/aws/aws-sdk-go/aws",
"Comment": "v0.9.16-3-g4944a94", "Comment": "v1.0.2",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a" "Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
}, },
{ {
"ImportPath": "github.com/aws/aws-sdk-go/private/endpoints", "ImportPath": "github.com/aws/aws-sdk-go/private/endpoints",
"Comment": "v0.9.16-3-g4944a94", "Comment": "v1.0.2",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a" "Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
}, },
{ {
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/ec2query", "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/ec2query",
"Comment": "v0.9.16-3-g4944a94", "Comment": "v1.0.2",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a" "Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
}, },
{ {
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query", "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query",
"Comment": "v0.9.16-3-g4944a94", "Comment": "v1.0.2",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a" "Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
}, },
{ {
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/rest", "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/rest",
"Comment": "v0.9.16-3-g4944a94", "Comment": "v1.0.2",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a" "Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
}, },
{ {
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil", "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil",
"Comment": "v0.9.16-3-g4944a94", "Comment": "v1.0.2",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a" "Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
}, },
{ {
"ImportPath": "github.com/aws/aws-sdk-go/private/signer/v4", "ImportPath": "github.com/aws/aws-sdk-go/private/signer/v4",
"Comment": "v0.9.16-3-g4944a94", "Comment": "v1.0.2",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a" "Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/waiter",
"Comment": "v1.0.2",
"Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
}, },
{ {
"ImportPath": "github.com/aws/aws-sdk-go/service/autoscaling", "ImportPath": "github.com/aws/aws-sdk-go/service/autoscaling",
"Comment": "v0.9.16-3-g4944a94", "Comment": "v1.0.2",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a" "Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
}, },
{ {
"ImportPath": "github.com/aws/aws-sdk-go/service/ec2", "ImportPath": "github.com/aws/aws-sdk-go/service/ec2",
"Comment": "v0.9.16-3-g4944a94", "Comment": "v1.0.2",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a" "Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
}, },
{ {
"ImportPath": "github.com/aws/aws-sdk-go/service/elb", "ImportPath": "github.com/aws/aws-sdk-go/service/elb",
"Comment": "v0.9.16-3-g4944a94", "Comment": "v1.0.2",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a" "Rev": "9d7bc2d6ca2ada0468f705f0e9725ca97f8550c8"
}, },
{ {
"ImportPath": "github.com/beorn7/perks/quantile", "ImportPath": "github.com/beorn7/perks/quantile",
...@@ -412,6 +417,11 @@ ...@@ -412,6 +417,11 @@
"Rev": "73d445a93680fa1a78ae23a5839bad48f32ba1ee" "Rev": "73d445a93680fa1a78ae23a5839bad48f32ba1ee"
}, },
{ {
"ImportPath": "github.com/go-ini/ini",
"Comment": "v0-54-g2e44421",
"Rev": "2e44421e256d82ebbf3d4d4fcabe8930b905eff3"
},
{
"ImportPath": "github.com/godbus/dbus", "ImportPath": "github.com/godbus/dbus",
"Comment": "v3", "Comment": "v3",
"Rev": "c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f" "Rev": "c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f"
...@@ -554,6 +564,11 @@ ...@@ -554,6 +564,11 @@
"Rev": "afde71eb1740fd763ab9450e1f700ba0e53c36d0" "Rev": "afde71eb1740fd763ab9450e1f700ba0e53c36d0"
}, },
{ {
"ImportPath": "github.com/jmespath/go-jmespath",
"Comment": "0.2.2",
"Rev": "3433f3ea46d9f8019119e7dd41274e112a2359a9"
},
{
"ImportPath": "github.com/jonboulle/clockwork", "ImportPath": "github.com/jonboulle/clockwork",
"Rev": "3f831b65b61282ba6bece21b91beea2edc4c887a" "Rev": "3f831b65b61282ba6bece21b91beea2edc4c887a"
}, },
...@@ -734,10 +749,6 @@ ...@@ -734,10 +749,6 @@
"Rev": "f1f1a805ed361a0e078bb537e4ea78cd37dcf065" "Rev": "f1f1a805ed361a0e078bb537e4ea78cd37dcf065"
}, },
{ {
"ImportPath": "github.com/vaughan0/go-ini",
"Rev": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1"
},
{
"ImportPath": "github.com/vishvananda/netlink", "ImportPath": "github.com/vishvananda/netlink",
"Rev": "1e2e08e8a2dcdacaae3f14ac44c5cfa31361f270" "Rev": "1e2e08e8a2dcdacaae3f14ac44c5cfa31361f270"
}, },
......
...@@ -57,16 +57,13 @@ func rcopy(dst, src reflect.Value, root bool) { ...@@ -57,16 +57,13 @@ func rcopy(dst, src reflect.Value, root bool) {
} }
} }
case reflect.Struct: case reflect.Struct:
if !root {
dst.Set(reflect.New(src.Type()).Elem())
}
t := dst.Type() t := dst.Type()
for i := 0; i < t.NumField(); i++ { for i := 0; i < t.NumField(); i++ {
name := t.Field(i).Name name := t.Field(i).Name
srcval := src.FieldByName(name) srcVal := src.FieldByName(name)
if srcval.IsValid() { dstVal := dst.FieldByName(name)
rcopy(dst.FieldByName(name), srcval, false) if srcVal.IsValid() && dstVal.CanSet() {
rcopy(dstVal, srcVal, false)
} }
} }
case reflect.Slice: case reflect.Slice:
......
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())
}
...@@ -5,18 +5,20 @@ import ( ...@@ -5,18 +5,20 @@ import (
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"github.com/jmespath/go-jmespath"
) )
var indexRe = regexp.MustCompile(`(.+)\[(-?\d+)?\]$`) var indexRe = regexp.MustCompile(`(.+)\[(-?\d+)?\]$`)
// rValuesAtPath returns a slice of values found in value v. The values // rValuesAtPath returns a slice of values found in value v. The values
// in v are explored recursively so all nested values are collected. // in v are explored recursively so all nested values are collected.
func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool) []reflect.Value { func rValuesAtPath(v interface{}, path string, createPath, caseSensitive, nilTerm bool) []reflect.Value {
pathparts := strings.Split(path, "||") pathparts := strings.Split(path, "||")
if len(pathparts) > 1 { if len(pathparts) > 1 {
for _, pathpart := range pathparts { for _, pathpart := range pathparts {
vals := rValuesAtPath(v, pathpart, create, caseSensitive) vals := rValuesAtPath(v, pathpart, createPath, caseSensitive, nilTerm)
if vals != nil && len(vals) > 0 { if len(vals) > 0 {
return vals return vals
} }
} }
...@@ -74,7 +76,16 @@ func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool) ...@@ -74,7 +76,16 @@ func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool)
return false return false
}) })
if create && value.Kind() == reflect.Ptr && value.IsNil() { 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.Set(reflect.New(value.Type().Elem()))
value = value.Elem() value = value.Elem()
} else { } else {
...@@ -82,7 +93,7 @@ func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool) ...@@ -82,7 +93,7 @@ func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool)
} }
if value.Kind() == reflect.Slice || value.Kind() == reflect.Map { if value.Kind() == reflect.Slice || value.Kind() == reflect.Map {
if !create && value.IsNil() { if !createPath && value.IsNil() {
value = reflect.ValueOf(nil) value = reflect.ValueOf(nil)
} }
} }
...@@ -114,7 +125,7 @@ func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool) ...@@ -114,7 +125,7 @@ func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool)
// pull out index // pull out index
i := int(*index) i := int(*index)
if i >= value.Len() { // check out of bounds if i >= value.Len() { // check out of bounds
if create { if createPath {
// TODO resize slice // TODO resize slice
} else { } else {
continue continue
...@@ -125,7 +136,7 @@ func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool) ...@@ -125,7 +136,7 @@ func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool)
value = reflect.Indirect(value.Index(i)) value = reflect.Indirect(value.Index(i))
if value.Kind() == reflect.Slice || value.Kind() == reflect.Map { if value.Kind() == reflect.Slice || value.Kind() == reflect.Map {
if !create && value.IsNil() { if !createPath && value.IsNil() {
value = reflect.ValueOf(nil) value = reflect.ValueOf(nil)
} }
} }
...@@ -142,46 +153,70 @@ func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool) ...@@ -142,46 +153,70 @@ func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool)
return values return values
} }
// ValuesAtPath returns a list of objects at the lexical path inside of a structure // ValuesAtPath returns a list of values at the case insensitive lexical
func ValuesAtPath(i interface{}, path string) []interface{} { // path inside of a structure.
if rvals := rValuesAtPath(i, path, false, true); rvals != nil { func ValuesAtPath(i interface{}, path string) ([]interface{}, error) {
vals := make([]interface{}, len(rvals)) result, err := jmespath.Search(path, i)
for i, rval := range rvals { if err != nil {
vals[i] = rval.Interface() return nil, err
}
return vals
} }
return nil
}
// ValuesAtAnyPath returns a list of objects at the case-insensitive lexical v := reflect.ValueOf(result)
// path inside of a structure if !v.IsValid() || (v.Kind() == reflect.Ptr && v.IsNil()) {
func ValuesAtAnyPath(i interface{}, path string) []interface{} { return nil, nil
if rvals := rValuesAtPath(i, path, false, false); rvals != nil {
vals := make([]interface{}, len(rvals))
for i, rval := range rvals {
vals[i] = rval.Interface()
} }
return vals if s, ok := result.([]interface{}); ok {
return s, err
} }
return nil 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 an object at the lexical path inside of a structure // SetValueAtPath sets a value at the case insensitive lexical path inside
// of a structure.
func SetValueAtPath(i interface{}, path string, v interface{}) { func SetValueAtPath(i interface{}, path string, v interface{}) {
if rvals := rValuesAtPath(i, path, true, true); rvals != nil { if rvals := rValuesAtPath(i, path, true, false, v == nil); rvals != nil {
for _, rval := range rvals { for _, rval := range rvals {
rval.Set(reflect.ValueOf(v)) if rval.Kind() == reflect.Ptr && rval.IsNil() {
continue
}
setValue(rval, v)
} }
} }
} }
// SetValueAtAnyPath sets an object at the case insensitive lexical path inside func setValue(dstVal reflect.Value, src interface{}) {
// of a structure if dstVal.Kind() == reflect.Ptr {
func SetValueAtAnyPath(i interface{}, path string, v interface{}) { dstVal = reflect.Indirect(dstVal)
if rvals := rValuesAtPath(i, path, true, false); rvals != nil { }
for _, rval := range rvals { srcVal := reflect.ValueOf(src)
rval.Set(reflect.ValueOf(v))
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)
} }
} }
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())
}
}
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 reponse 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))
}
package service package client
import ( import (
"math" "math"
"math/rand" "math/rand"
"time" "time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/request"
) )
...@@ -22,16 +21,13 @@ import ( ...@@ -22,16 +21,13 @@ import (
// // This implementation always has 100 max retries // // This implementation always has 100 max retries
// func (d retryer) MaxRetries() uint { return 100 } // func (d retryer) MaxRetries() uint { return 100 }
type DefaultRetryer struct { type DefaultRetryer struct {
*Service NumMaxRetries int
} }
// MaxRetries returns the number of maximum returns the service will use to make // MaxRetries returns the number of maximum returns the service will use to make
// an individual API request. // an individual API request.
func (d DefaultRetryer) MaxRetries() uint { func (d DefaultRetryer) MaxRetries() int {
if aws.IntValue(d.Service.Config.MaxRetries) < 0 { return d.NumMaxRetries
return d.DefaultMaxRetries
}
return uint(aws.IntValue(d.Service.Config.MaxRetries))
} }
// RetryRules returns the delay duration before retrying this request again // RetryRules returns the delay duration before retrying this request again
......
package serviceinfo package metadata
import "github.com/aws/aws-sdk-go/aws" // ClientInfo wraps immutable data from the client.Client structure.
type ClientInfo struct {
// ServiceInfo wraps immutable data from the service.Service structure.
type ServiceInfo struct {
Config *aws.Config
ServiceName string ServiceName string
APIVersion string APIVersion string
Endpoint string Endpoint string
......
...@@ -7,15 +7,20 @@ import ( ...@@ -7,15 +7,20 @@ import (
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
) )
// DefaultRetries is the default number of retries for a service. The value of // UseServiceDefaultRetries instructs the config to use the service's own default
// -1 indicates that the service specific retry default will be used. // number of retries. This will be the default action if Config.MaxRetries
const DefaultRetries = -1 // 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, // A Config provides service configuration for service clients. By default,
// all clients will use the {defaults.DefaultConfig} structure. // all clients will use the {defaults.DefaultConfig} structure.
type Config struct { type Config struct {
// The credentials object to use when signing requests. Defaults to // The credentials object to use when signing requests. Defaults to
// {defaults.DefaultChainCredentials}. // a chain of credential providers to search for credentials in environment
// variables, shared credential file, and EC2 Instance Roles.
Credentials *credentials.Credentials Credentials *credentials.Credentials
// An optional endpoint URL (hostname only or fully qualified URI) // An optional endpoint URL (hostname only or fully qualified URI)
...@@ -57,6 +62,21 @@ type Config struct { ...@@ -57,6 +62,21 @@ type Config struct {
// configuration. // configuration.
MaxRetries *int 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 // Disables semantic parameter validation, which validates input for missing
// required fields and/or other semantic request input errors. // required fields and/or other semantic request input errors.
DisableParamValidation *bool DisableParamValidation *bool
...@@ -171,15 +191,17 @@ func (c *Config) WithSleepDelay(fn func(time.Duration)) *Config { ...@@ -171,15 +191,17 @@ func (c *Config) WithSleepDelay(fn func(time.Duration)) *Config {
return c return c
} }
// Merge returns a new Config with the other Config's attribute values merged into // MergeIn merges the passed in configs into the existing config object.
// this Config. If the other Config's attribute is nil it will not be merged into func (c *Config) MergeIn(cfgs ...*Config) {
// the new Config to be returned. for _, other := range cfgs {
func (c Config) Merge(other *Config) *Config { mergeInConfig(c, other)
if other == nil {
return &c
} }
}
dst := c func mergeInConfig(dst *Config, other *Config) {
if other == nil {
return
}
if other.Credentials != nil { if other.Credentials != nil {
dst.Credentials = other.Credentials dst.Credentials = other.Credentials
...@@ -213,6 +235,10 @@ func (c Config) Merge(other *Config) *Config { ...@@ -213,6 +235,10 @@ func (c Config) Merge(other *Config) *Config {
dst.MaxRetries = other.MaxRetries dst.MaxRetries = other.MaxRetries
} }
if other.Retryer != nil {
dst.Retryer = other.Retryer
}
if other.DisableParamValidation != nil { if other.DisableParamValidation != nil {
dst.DisableParamValidation = other.DisableParamValidation dst.DisableParamValidation = other.DisableParamValidation
} }
...@@ -228,12 +254,17 @@ func (c Config) Merge(other *Config) *Config { ...@@ -228,12 +254,17 @@ func (c Config) Merge(other *Config) *Config {
if other.SleepDelay != nil { if other.SleepDelay != nil {
dst.SleepDelay = other.SleepDelay dst.SleepDelay = other.SleepDelay
} }
return &dst
} }
// Copy will return a shallow copy of the Config object. // Copy will return a shallow copy of the Config object. If any additional
func (c Config) Copy() *Config { // configurations are provided they will be merged into the new config returned.
dst := c func (c *Config) Copy(cfgs ...*Config) *Config {
return &dst dst := &Config{}
dst.MergeIn(c)
for _, cfg := range cfgs {
dst.MergeIn(cfg)
}
return dst
} }
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"regexp" "regexp"
"runtime"
"strconv" "strconv"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
...@@ -23,7 +24,7 @@ type lener interface { ...@@ -23,7 +24,7 @@ type lener interface {
// BuildContentLengthHandler builds the content length of a request based on the body, // 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 // 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. // to determine request body length and no "Content-Length" was specified it will panic.
var BuildContentLengthHandler = request.NamedHandler{"core.BuildContentLengthHandler", func(r *request.Request) { var BuildContentLengthHandler = request.NamedHandler{Name: "core.BuildContentLengthHandler", Fn: func(r *request.Request) {
if slength := r.HTTPRequest.Header.Get("Content-Length"); slength != "" { if slength := r.HTTPRequest.Header.Get("Content-Length"); slength != "" {
length, _ := strconv.ParseInt(slength, 10, 64) length, _ := strconv.ParseInt(slength, 10, 64)
r.HTTPRequest.ContentLength = length r.HTTPRequest.ContentLength = length
...@@ -49,17 +50,19 @@ var BuildContentLengthHandler = request.NamedHandler{"core.BuildContentLengthHan ...@@ -49,17 +50,19 @@ var BuildContentLengthHandler = request.NamedHandler{"core.BuildContentLengthHan
r.HTTPRequest.Header.Set("Content-Length", fmt.Sprintf("%d", length)) r.HTTPRequest.Header.Set("Content-Length", fmt.Sprintf("%d", length))
}} }}
// UserAgentHandler is a request handler for injecting User agent into requests. // SDKVersionUserAgentHandler is a request handler for adding the SDK Version to the user agent.
var UserAgentHandler = request.NamedHandler{"core.UserAgentHandler", func(r *request.Request) { var SDKVersionUserAgentHandler = request.NamedHandler{
r.HTTPRequest.Header.Set("User-Agent", aws.SDKName+"/"+aws.SDKVersion) Name: "core.SDKVersionUserAgentHandler",
}} Fn: request.MakeAddToUserAgentHandler(aws.SDKName, aws.SDKVersion,
runtime.Version(), runtime.GOOS, runtime.GOARCH),
}
var reStatusCode = regexp.MustCompile(`^(\d{3})`) var reStatusCode = regexp.MustCompile(`^(\d{3})`)
// SendHandler is a request handler to send service request using HTTP client. // SendHandler is a request handler to send service request using HTTP client.
var SendHandler = request.NamedHandler{"core.SendHandler", func(r *request.Request) { var SendHandler = request.NamedHandler{Name: "core.SendHandler", Fn: func(r *request.Request) {
var err error var err error
r.HTTPResponse, err = r.Service.Config.HTTPClient.Do(r.HTTPRequest) r.HTTPResponse, err = r.Config.HTTPClient.Do(r.HTTPRequest)
if err != nil { if err != nil {
// Capture the case where url.Error is returned for error processing // Capture the case where url.Error is returned for error processing
// response. e.g. 301 without location header comes back as string // response. e.g. 301 without location header comes back as string
...@@ -92,7 +95,7 @@ var SendHandler = request.NamedHandler{"core.SendHandler", func(r *request.Reque ...@@ -92,7 +95,7 @@ var SendHandler = request.NamedHandler{"core.SendHandler", func(r *request.Reque
}} }}
// ValidateResponseHandler is a request handler to validate service response. // ValidateResponseHandler is a request handler to validate service response.
var ValidateResponseHandler = request.NamedHandler{"core.ValidateResponseHandler", func(r *request.Request) { var ValidateResponseHandler = request.NamedHandler{Name: "core.ValidateResponseHandler", Fn: func(r *request.Request) {
if r.HTTPResponse.StatusCode == 0 || r.HTTPResponse.StatusCode >= 300 { if r.HTTPResponse.StatusCode == 0 || r.HTTPResponse.StatusCode >= 300 {
// this may be replaced by an UnmarshalError handler // this may be replaced by an UnmarshalError handler
r.Error = awserr.New("UnknownError", "unknown error", nil) r.Error = awserr.New("UnknownError", "unknown error", nil)
...@@ -101,7 +104,7 @@ var ValidateResponseHandler = request.NamedHandler{"core.ValidateResponseHandler ...@@ -101,7 +104,7 @@ var ValidateResponseHandler = request.NamedHandler{"core.ValidateResponseHandler
// AfterRetryHandler performs final checks to determine if the request should // AfterRetryHandler performs final checks to determine if the request should
// be retried and how long to delay. // be retried and how long to delay.
var AfterRetryHandler = request.NamedHandler{"core.AfterRetryHandler", func(r *request.Request) { var AfterRetryHandler = request.NamedHandler{Name: "core.AfterRetryHandler", Fn: func(r *request.Request) {
// If one of the other handlers already set the retry state // If one of the other handlers already set the retry state
// we don't want to override it based on the service's state // we don't want to override it based on the service's state
if r.Retryable == nil { if r.Retryable == nil {
...@@ -110,13 +113,13 @@ var AfterRetryHandler = request.NamedHandler{"core.AfterRetryHandler", func(r *r ...@@ -110,13 +113,13 @@ var AfterRetryHandler = request.NamedHandler{"core.AfterRetryHandler", func(r *r
if r.WillRetry() { if r.WillRetry() {
r.RetryDelay = r.RetryRules(r) r.RetryDelay = r.RetryRules(r)
r.Service.Config.SleepDelay(r.RetryDelay) r.Config.SleepDelay(r.RetryDelay)
// when the expired token exception occurs the credentials // when the expired token exception occurs the credentials
// need to be expired locally so that the next request to // need to be expired locally so that the next request to
// get credentials will trigger a credentials refresh. // get credentials will trigger a credentials refresh.
if r.IsErrorExpired() { if r.IsErrorExpired() {
r.Service.Config.Credentials.Expire() r.Config.Credentials.Expire()
} }
r.RetryCount++ r.RetryCount++
...@@ -127,10 +130,10 @@ var AfterRetryHandler = request.NamedHandler{"core.AfterRetryHandler", func(r *r ...@@ -127,10 +130,10 @@ var AfterRetryHandler = request.NamedHandler{"core.AfterRetryHandler", func(r *r
// ValidateEndpointHandler is a request handler to validate a request had the // ValidateEndpointHandler is a request handler to validate a request had the
// appropriate Region and Endpoint set. Will set r.Error if the endpoint or // appropriate Region and Endpoint set. Will set r.Error if the endpoint or
// region is not valid. // region is not valid.
var ValidateEndpointHandler = request.NamedHandler{"core.ValidateEndpointHandler", func(r *request.Request) { var ValidateEndpointHandler = request.NamedHandler{Name: "core.ValidateEndpointHandler", Fn: func(r *request.Request) {
if r.Service.SigningRegion == "" && aws.StringValue(r.Service.Config.Region) == "" { if r.ClientInfo.SigningRegion == "" && aws.StringValue(r.Config.Region) == "" {
r.Error = aws.ErrMissingRegion r.Error = aws.ErrMissingRegion
} else if r.Service.Endpoint == "" { } else if r.ClientInfo.Endpoint == "" {
r.Error = aws.ErrMissingEndpoint r.Error = aws.ErrMissingEndpoint
} }
}} }}
...@@ -12,7 +12,7 @@ import ( ...@@ -12,7 +12,7 @@ import (
// ValidateParametersHandler is a request handler to validate the input parameters. // ValidateParametersHandler is a request handler to validate the input parameters.
// Validating parameters only has meaning if done prior to the request being sent. // Validating parameters only has meaning if done prior to the request being sent.
var ValidateParametersHandler = request.NamedHandler{"core.ValidateParametersHandler", func(r *request.Request) { var ValidateParametersHandler = request.NamedHandler{Name: "core.ValidateParametersHandler", Fn: func(r *request.Request) {
if r.ParamsFilled() { if r.ParamsFilled() {
v := validator{errors: []string{}} v := validator{errors: []string{}}
v.validateAny(reflect.ValueOf(r.Params), "") v.validateAny(reflect.ValueOf(r.Params), "")
......
...@@ -36,7 +36,9 @@ var ( ...@@ -36,7 +36,9 @@ var (
// creds := NewChainCredentials( // creds := NewChainCredentials(
// []Provider{ // []Provider{
// &EnvProvider{}, // &EnvProvider{},
// &EC2RoleProvider{}, // &EC2RoleProvider{
// Client: ec2metadata.New(sess),
// },
// }) // })
// //
// // Usage of ChainCredentials with aws.Config // // Usage of ChainCredentials with aws.Config
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"time" "time"
"github.com/aws/aws-sdk-go/aws/awserr" "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/credentials"
"github.com/aws/aws-sdk-go/aws/ec2metadata" "github.com/aws/aws-sdk-go/aws/ec2metadata"
) )
...@@ -22,12 +23,10 @@ import ( ...@@ -22,12 +23,10 @@ import (
// p := &ec2rolecreds.EC2RoleProvider{ // p := &ec2rolecreds.EC2RoleProvider{
// // Pass in a custom timeout to be used when requesting // // Pass in a custom timeout to be used when requesting
// // IAM EC2 Role credentials. // // IAM EC2 Role credentials.
// Client: &http.Client{ // Client: ec2metadata.New(sess, aws.Config{
// Timeout: 10 * time.Second, // HTTPClient: &http.Client{Timeout: 10 * time.Second},
// }, // }),
// // Use default EC2 Role metadata endpoint, Alternate endpoints can be //
// // specified setting Endpoint to something else.
// Endpoint: "",
// // Do not use early expiry of credentials. If a non zero value is // // Do not use early expiry of credentials. If a non zero value is
// // specified the credentials will be expired early // // specified the credentials will be expired early
// ExpiryWindow: 0, // ExpiryWindow: 0,
...@@ -35,8 +34,8 @@ import ( ...@@ -35,8 +34,8 @@ import (
type EC2RoleProvider struct { type EC2RoleProvider struct {
credentials.Expiry credentials.Expiry
// EC2Metadata client to use when connecting to EC2 metadata service // Required EC2Metadata client to use when connecting to EC2 metadata service.
Client *ec2metadata.Client Client *ec2metadata.EC2Metadata
// ExpiryWindow will allow the credentials to trigger refreshing prior to // ExpiryWindow will allow the credentials to trigger refreshing prior to
// the credentials actually expiring. This is beneficial so race conditions // the credentials actually expiring. This is beneficial so race conditions
...@@ -50,33 +49,40 @@ type EC2RoleProvider struct { ...@@ -50,33 +49,40 @@ type EC2RoleProvider struct {
ExpiryWindow time.Duration ExpiryWindow time.Duration
} }
// NewCredentials returns a pointer to a new Credentials object // NewCredentials returns a pointer to a new Credentials object wrapping
// wrapping the EC2RoleProvider. // the EC2RoleProvider. Takes a ConfigProvider to create a EC2Metadata client.
// // The ConfigProvider is satisfied by the session.Session type.
// Takes a custom http.Client which can be configured for custom handling of func NewCredentials(c client.ConfigProvider, options ...func(*EC2RoleProvider)) *credentials.Credentials {
// things such as timeout. p := &EC2RoleProvider{
// Client: ec2metadata.New(c),
// Endpoint is the URL that the EC2RoleProvider will connect to when retrieving }
// role and credentials.
// for _, option := range options {
// Window is the expiry window that will be subtracted from the expiry returned option(p)
// by the role credential request. This is done so that the credentials will }
// expire sooner than their actual lifespan.
func NewCredentials(client *ec2metadata.Client, window time.Duration) *credentials.Credentials { return credentials.NewCredentials(p)
return credentials.NewCredentials(&EC2RoleProvider{ }
// 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, Client: client,
ExpiryWindow: window, }
})
for _, option := range options {
option(p)
}
return credentials.NewCredentials(p)
} }
// Retrieve retrieves credentials from the EC2 service. // Retrieve retrieves credentials from the EC2 service.
// Error will be returned if the request fails, or unable to extract // Error will be returned if the request fails, or unable to extract
// the desired credentials. // the desired credentials.
func (m *EC2RoleProvider) Retrieve() (credentials.Value, error) { func (m *EC2RoleProvider) Retrieve() (credentials.Value, error) {
if m.Client == nil {
m.Client = ec2metadata.New(nil)
}
credsList, err := requestCredList(m.Client) credsList, err := requestCredList(m.Client)
if err != nil { if err != nil {
return credentials.Value{}, err return credentials.Value{}, err
...@@ -101,7 +107,7 @@ func (m *EC2RoleProvider) Retrieve() (credentials.Value, error) { ...@@ -101,7 +107,7 @@ func (m *EC2RoleProvider) Retrieve() (credentials.Value, error) {
}, nil }, nil
} }
// A ec2RoleCredRespBody provides the shape for deserializing credential // A ec2RoleCredRespBody provides the shape for unmarshalling credential
// request responses. // request responses.
type ec2RoleCredRespBody struct { type ec2RoleCredRespBody struct {
// Success State // Success State
...@@ -119,7 +125,7 @@ const iamSecurityCredsPath = "/iam/security-credentials" ...@@ -119,7 +125,7 @@ const iamSecurityCredsPath = "/iam/security-credentials"
// requestCredList requests a list of credentials from the EC2 service. // 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 // If there are no credentials, or there is an error making or receiving the request
func requestCredList(client *ec2metadata.Client) ([]string, error) { func requestCredList(client *ec2metadata.EC2Metadata) ([]string, error) {
resp, err := client.GetMetadata(iamSecurityCredsPath) resp, err := client.GetMetadata(iamSecurityCredsPath)
if err != nil { if err != nil {
return nil, awserr.New("EC2RoleRequestError", "failed to list EC2 Roles", err) return nil, awserr.New("EC2RoleRequestError", "failed to list EC2 Roles", err)
...@@ -142,7 +148,7 @@ func requestCredList(client *ec2metadata.Client) ([]string, error) { ...@@ -142,7 +148,7 @@ func requestCredList(client *ec2metadata.Client) ([]string, error) {
// //
// If the credentials cannot be found, or there is an error reading the response // If the credentials cannot be found, or there is an error reading the response
// and error will be returned. // and error will be returned.
func requestCred(client *ec2metadata.Client, credsName string) (ec2RoleCredRespBody, error) { func requestCred(client *ec2metadata.EC2Metadata, credsName string) (ec2RoleCredRespBody, error) {
resp, err := client.GetMetadata(path.Join(iamSecurityCredsPath, credsName)) resp, err := client.GetMetadata(path.Join(iamSecurityCredsPath, credsName))
if err != nil { if err != nil {
return ec2RoleCredRespBody{}, return ec2RoleCredRespBody{},
......
...@@ -6,3 +6,7 @@ aws_session_token = token ...@@ -6,3 +6,7 @@ aws_session_token = token
[no_token] [no_token]
aws_access_key_id = accessKey aws_access_key_id = accessKey
aws_secret_access_key = secret aws_secret_access_key = secret
[with_colon]
aws_access_key_id: accessKey
aws_secret_access_key: secret
...@@ -5,7 +5,7 @@ import ( ...@@ -5,7 +5,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/vaughan0/go-ini" "github.com/go-ini/ini"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
) )
...@@ -76,32 +76,36 @@ func (p *SharedCredentialsProvider) IsExpired() bool { ...@@ -76,32 +76,36 @@ func (p *SharedCredentialsProvider) IsExpired() bool {
// The credentials retrieved from the profile will be returned or error. Error will be // 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. // returned if it fails to read from the file, or the data is invalid.
func loadProfile(filename, profile string) (Value, error) { func loadProfile(filename, profile string) (Value, error) {
config, err := ini.LoadFile(filename) config, err := ini.Load(filename)
if err != nil { if err != nil {
return Value{}, awserr.New("SharedCredsLoad", "failed to load shared credentials file", err) return Value{}, awserr.New("SharedCredsLoad", "failed to load shared credentials file", err)
} }
iniProfile := config.Section(profile) iniProfile, err := config.GetSection(profile)
if err != nil {
return Value{}, awserr.New("SharedCredsLoad", "failed to get profile", err)
}
id, ok := iniProfile["aws_access_key_id"] id, err := iniProfile.GetKey("aws_access_key_id")
if !ok { if err != nil {
return Value{}, awserr.New("SharedCredsAccessKey", return Value{}, awserr.New("SharedCredsAccessKey",
fmt.Sprintf("shared credentials %s in %s did not contain aws_access_key_id", profile, filename), fmt.Sprintf("shared credentials %s in %s did not contain aws_access_key_id", profile, filename),
nil) err)
} }
secret, ok := iniProfile["aws_secret_access_key"] secret, err := iniProfile.GetKey("aws_secret_access_key")
if !ok { if err != nil {
return Value{}, awserr.New("SharedCredsSecret", return Value{}, awserr.New("SharedCredsSecret",
fmt.Sprintf("shared credentials %s in %s did not contain aws_secret_access_key", profile, filename), fmt.Sprintf("shared credentials %s in %s did not contain aws_secret_access_key", profile, filename),
nil) nil)
} }
token := iniProfile["aws_session_token"] // Default to empty string if not found
token := iniProfile.Key("aws_session_token")
return Value{ return Value{
AccessKeyID: id, AccessKeyID: id.String(),
SecretAccessKey: secret, SecretAccessKey: secret.String(),
SessionToken: token, SessionToken: token.String(),
}, nil }, nil
} }
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"time" "time"
"github.com/aws/aws-sdk-go/aws" "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/aws/credentials"
"github.com/aws/aws-sdk-go/service/sts" "github.com/aws/aws-sdk-go/service/sts"
) )
...@@ -18,31 +19,17 @@ type AssumeRoler interface { ...@@ -18,31 +19,17 @@ type AssumeRoler interface {
AssumeRole(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) 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 // AssumeRoleProvider retrieves temporary credentials from the STS service, and
// keeps track of their expiration time. This provider must be used explicitly, // keeps track of their expiration time. This provider must be used explicitly,
// as it is not included in the credentials chain. // as it is not included in the credentials chain.
//
// Example how to configure a service to use this provider:
//
// config := &aws.Config{
// Credentials: stscreds.NewCredentials(nil, "arn-of-the-role-to-assume", 10*time.Second),
// })
// // Use config for creating your AWS service.
//
// Example how to obtain customised credentials:
//
// provider := &stscreds.Provider{
// // Extend the duration to 1 hour.
// Duration: time.Hour,
// // Custom role name.
// RoleSessionName: "custom-session-name",
// }
// creds := credentials.NewCredentials(provider)
//
type AssumeRoleProvider struct { type AssumeRoleProvider struct {
credentials.Expiry credentials.Expiry
// Custom STS client. If not set the default STS client will be used. // STS client to make assume role request with.
Client AssumeRoler Client AssumeRoler
// Role to be assumed. // Role to be assumed.
...@@ -73,34 +60,52 @@ type AssumeRoleProvider struct { ...@@ -73,34 +60,52 @@ type AssumeRoleProvider struct {
// AssumeRoleProvider. The credentials will expire every 15 minutes and the // AssumeRoleProvider. The credentials will expire every 15 minutes and the
// role will be named after a nanosecond timestamp of this operation. // role will be named after a nanosecond timestamp of this operation.
// //
// The sts and roleARN parameters are used for building the "AssumeRole" call. // Takes a Config provider to create the STS client. The ConfigProvider is
// Pass nil as sts to use the default client. // 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.
// //
// Window is the expiry window that will be subtracted from the expiry returned // Takes an AssumeRoler which can be satisfiede by the STS client.
// by the role credential request. This is done so that the credentials will func NewCredentialsWithClient(svc AssumeRoler, roleARN string, options ...func(*AssumeRoleProvider)) *credentials.Credentials {
// expire sooner than their actual lifespan. p := &AssumeRoleProvider{
func NewCredentials(client AssumeRoler, roleARN string, window time.Duration) *credentials.Credentials { Client: svc,
return credentials.NewCredentials(&AssumeRoleProvider{
Client: client,
RoleARN: roleARN, RoleARN: roleARN,
ExpiryWindow: window, Duration: DefaultDuration,
}) }
for _, option := range options {
option(p)
}
return credentials.NewCredentials(p)
} }
// Retrieve generates a new set of temporary credentials using STS. // Retrieve generates a new set of temporary credentials using STS.
func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) { func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) {
// Apply defaults where parameters are not set. // Apply defaults where parameters are not set.
if p.Client == nil {
p.Client = sts.New(nil)
}
if p.RoleSessionName == "" { if p.RoleSessionName == "" {
// Try to work out a role name that will hopefully end up unique. // Try to work out a role name that will hopefully end up unique.
p.RoleSessionName = fmt.Sprintf("%d", time.Now().UTC().UnixNano()) p.RoleSessionName = fmt.Sprintf("%d", time.Now().UTC().UnixNano())
} }
if p.Duration == 0 { if p.Duration == 0 {
// Expire as often as AWS permits. // Expire as often as AWS permits.
p.Duration = 15 * time.Minute p.Duration = DefaultDuration
} }
roleOutput, err := p.Client.AssumeRole(&sts.AssumeRoleInput{ roleOutput, err := p.Client.AssumeRole(&sts.AssumeRoleInput{
......
// 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 package defaults
import ( import (
...@@ -6,34 +13,83 @@ import ( ...@@ -6,34 +13,83 @@ import (
"time" "time"
"github.com/aws/aws-sdk-go/aws" "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"
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" "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"
) )
// DefaultChainCredentials is a Credentials which will find the first available // A Defaults provides a collection of default values for SDK clients.
// credentials Value from the list of Providers. type Defaults struct {
// Config *aws.Config
// This should be used in the default case. Once the type of credentials are Handlers request.Handlers
// known switching to the specific Credentials will be more efficient. }
var DefaultChainCredentials = credentials.NewChainCredentials(
[]credentials.Provider{ // Get returns the SDK's default values with Config and handlers pre-configured.
&credentials.EnvProvider{}, func Get() Defaults {
&credentials.SharedCredentialsProvider{Filename: "", Profile: ""}, cfg := Config()
&ec2rolecreds.EC2RoleProvider{ExpiryWindow: 5 * time.Minute}, handlers := Handlers()
}) cfg.Credentials = CredChain(cfg, handlers)
return Defaults{
Config: cfg,
Handlers: handlers,
}
}
// DefaultConfig is the default all service configuration will be based off of. // Config returns the default configuration without credentials.
// By default, all clients use this structure for initialization options unless // To retrieve a config with credentials also included use
// a custom configuration object is passed in. // `defaults.Get().Config` instead.
// //
// You may modify this global structure to change all default configuration // Generally you shouldn't need to use this method directly, but
// in the SDK. Note that configuration options are copied by value, so any // is available if you need to reset the configuration of an
// modifications must happen before constructing a client. // existing service client or session.
var DefaultConfig = aws.NewConfig(). func Config() *aws.Config {
WithCredentials(DefaultChainCredentials). return aws.NewConfig().
WithCredentials(credentials.AnonymousCredentials).
WithRegion(os.Getenv("AWS_REGION")). WithRegion(os.Getenv("AWS_REGION")).
WithHTTPClient(http.DefaultClient). WithHTTPClient(http.DefaultClient).
WithMaxRetries(aws.DefaultRetries). WithMaxRetries(aws.UseServiceDefaultRetries).
WithLogger(aws.NewDefaultLogger()). WithLogger(aws.NewDefaultLogger()).
WithLogLevel(aws.LogOff). WithLogLevel(aws.LogOff).
WithSleepDelay(time.Sleep) 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.NewChainCredentials(
[]credentials.Provider{
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: ""},
&ec2rolecreds.EC2RoleProvider{
Client: ec2metadata.NewClient(*cfg, handlers, endpoint, signingRegion),
ExpiryWindow: 5 * time.Minute,
},
})
}
...@@ -7,7 +7,7 @@ import ( ...@@ -7,7 +7,7 @@ import (
) )
// GetMetadata uses the path provided to request // GetMetadata uses the path provided to request
func (c *Client) GetMetadata(p string) (string, error) { func (c *EC2Metadata) GetMetadata(p string) (string, error) {
op := &request.Operation{ op := &request.Operation{
Name: "GetMetadata", Name: "GetMetadata",
HTTPMethod: "GET", HTTPMethod: "GET",
...@@ -15,13 +15,13 @@ func (c *Client) GetMetadata(p string) (string, error) { ...@@ -15,13 +15,13 @@ func (c *Client) GetMetadata(p string) (string, error) {
} }
output := &metadataOutput{} output := &metadataOutput{}
req := request.New(c.Service.ServiceInfo, c.Service.Handlers, c.Service.Retryer, op, nil, output) req := c.NewRequest(op, nil, output)
return output.Content, req.Send() return output.Content, req.Send()
} }
// Region returns the region the instance is running in. // Region returns the region the instance is running in.
func (c *Client) Region() (string, error) { func (c *EC2Metadata) Region() (string, error) {
resp, err := c.GetMetadata("placement/availability-zone") resp, err := c.GetMetadata("placement/availability-zone")
if err != nil { if err != nil {
return "", err return "", err
...@@ -34,7 +34,7 @@ func (c *Client) Region() (string, error) { ...@@ -34,7 +34,7 @@ func (c *Client) Region() (string, error) {
// Available returns if the application has access to the EC2 Metadata service. // 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 // Can be used to determine if application is running within an EC2 Instance and
// the metadata service is available. // the metadata service is available.
func (c *Client) Available() bool { func (c *EC2Metadata) Available() bool {
if _, err := c.GetMetadata("instance-id"); err != nil { if _, err := c.GetMetadata("instance-id"); err != nil {
return false return false
} }
......
// Package ec2metadata provides the client for making API calls to the
// EC2 Metadata service.
package ec2metadata package ec2metadata
import ( import (
...@@ -8,89 +10,41 @@ import ( ...@@ -8,89 +10,41 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials" "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/aws/request"
"github.com/aws/aws-sdk-go/aws/service"
"github.com/aws/aws-sdk-go/aws/service/serviceinfo"
) )
// DefaultRetries states the default number of times the service client will // ServiceName is the name of the service.
// attempt to retry a failed request before failing. const ServiceName = "ec2metadata"
const DefaultRetries = 3
// A Config provides the configuration for the EC2 Metadata service.
type Config struct {
// An optional endpoint URL (hostname only or fully qualified URI)
// that overrides the default service endpoint for a client. Set this
// to nil, or `""` to use the default service endpoint.
Endpoint *string
// 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.
Logger aws.Logger
// The logger writer interface to write logging messages to. Defaults to
// standard out.
LogLevel *aws.LogLevelType
// The maximum number of times that a request will be retried for failures.
// Defaults to DefaultRetries for the number of retries to be performed
// per request.
MaxRetries *int
}
// A Client is an EC2 Metadata service Client. // A EC2Metadata is an EC2 Metadata service Client.
type Client struct { type EC2Metadata struct {
*service.Service *client.Client
} }
// New creates a new instance of the EC2 Metadata service client. // New creates a new instance of the EC2Metadata client with a session.
// This client is safe to use across multiple goroutines.
// //
// In the general use case the configuration for this service client should not // Example:
// be needed and `nil` can be provided. Configuration is only needed if the // // Create a EC2Metadata client from just a session.
// `ec2metadata.Config` defaults need to be overridden. Eg. Setting LogLevel. // svc := ec2metadata.New(mySession)
// //
// @note This configuration will NOT be merged with the default AWS service // // Create a EC2Metadata client with additional configuration
// client configuration `defaults.DefaultConfig`. Due to circular dependencies // svc := ec2metadata.New(mySession, aws.NewConfig().WithLogLevel(aws.LogDebugHTTPBody))
// with the defaults package and credentials EC2 Role Provider. func New(p client.ConfigProvider, cfgs ...*aws.Config) *EC2Metadata {
func New(config *Config) *Client { c := p.ClientConfig(ServiceName, cfgs...)
service := &service.Service{ return NewClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
ServiceInfo: serviceinfo.ServiceInfo{
Config: copyConfig(config),
ServiceName: "Client",
Endpoint: "http://169.254.169.254/latest",
APIVersion: "latest",
},
}
service.Initialize()
service.Handlers.Unmarshal.PushBack(unmarshalHandler)
service.Handlers.UnmarshalError.PushBack(unmarshalError)
service.Handlers.Validate.Clear()
service.Handlers.Validate.PushBack(validateEndpointHandler)
return &Client{service}
} }
func copyConfig(config *Config) *aws.Config { // NewClient returns a new EC2Metadata client. Should be used to create
if config == nil { // a client when not using a session. Generally using just New with a session
config = &Config{} // is preferred.
} func NewClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string, opts ...func(*client.Client)) *EC2Metadata {
c := &aws.Config{ // If the default http client is provided, replace it with a custom
Credentials: credentials.AnonymousCredentials, // client using default timeouts.
Endpoint: config.Endpoint, if cfg.HTTPClient == http.DefaultClient {
HTTPClient: config.HTTPClient, cfg.HTTPClient = &http.Client{
Logger: config.Logger,
LogLevel: config.LogLevel,
MaxRetries: config.MaxRetries,
}
if c.HTTPClient == nil {
c.HTTPClient = &http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment, Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{ Dial: (&net.Dialer{
...@@ -104,17 +58,30 @@ func copyConfig(config *Config) *aws.Config { ...@@ -104,17 +58,30 @@ func copyConfig(config *Config) *aws.Config {
}, },
} }
} }
if c.Logger == nil {
c.Logger = aws.NewDefaultLogger() svc := &EC2Metadata{
} Client: client.New(
if c.LogLevel == nil { cfg,
c.LogLevel = aws.LogLevel(aws.LogOff) metadata.ClientInfo{
ServiceName: ServiceName,
Endpoint: endpoint,
APIVersion: "latest",
},
handlers,
),
} }
if c.MaxRetries == nil {
c.MaxRetries = aws.Int(DefaultRetries) 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 c return svc
} }
type metadataOutput struct { type metadataOutput struct {
...@@ -143,7 +110,7 @@ func unmarshalError(r *request.Request) { ...@@ -143,7 +110,7 @@ func unmarshalError(r *request.Request) {
} }
func validateEndpointHandler(r *request.Request) { func validateEndpointHandler(r *request.Request) {
if r.Service.Endpoint == "" { if r.ClientInfo.Endpoint == "" {
r.Error = aws.ErrMissingEndpoint r.Error = aws.ErrMissingEndpoint
} }
} }
...@@ -7,11 +7,11 @@ var ( ...@@ -7,11 +7,11 @@ var (
// not found. // not found.
// //
// @readonly // @readonly
ErrMissingRegion error = awserr.New("MissingRegion", "could not find region configuration", nil) ErrMissingRegion = awserr.New("MissingRegion", "could not find region configuration", nil)
// ErrMissingEndpoint is an error that is returned if an endpoint cannot be // ErrMissingEndpoint is an error that is returned if an endpoint cannot be
// resolved for a service. // resolved for a service.
// //
// @readonly // @readonly
ErrMissingEndpoint error = awserr.New("MissingEndpoint", "'Endpoint' configuration is required for this service", nil) ErrMissingEndpoint = awserr.New("MissingEndpoint", "'Endpoint' configuration is required for this service", nil)
) )
package request package request
import (
"fmt"
"strings"
)
// A Handlers provides a collection of request handlers for various // A Handlers provides a collection of request handlers for various
// stages of handling requests. // stages of handling requests.
type Handlers struct { type Handlers struct {
...@@ -110,3 +115,26 @@ func (l *HandlerList) Run(r *Request) { ...@@ -110,3 +115,26 @@ func (l *HandlerList) Run(r *Request) {
f.Fn(r) 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)
}
}
...@@ -12,15 +12,16 @@ import ( ...@@ -12,15 +12,16 @@ import (
"time" "time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awsutil" "github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/service/serviceinfo"
) )
// A Request is the service request to be made. // A Request is the service request to be made.
type Request struct { type Request struct {
Retryer Config aws.Config
Service serviceinfo.ServiceInfo ClientInfo metadata.ClientInfo
Handlers Handlers Handlers Handlers
Retryer
Time time.Time Time time.Time
ExpireTime time.Duration ExpireTime time.Duration
Operation *Operation Operation *Operation
...@@ -32,7 +33,7 @@ type Request struct { ...@@ -32,7 +33,7 @@ type Request struct {
Error error Error error
Data interface{} Data interface{}
RequestID string RequestID string
RetryCount uint RetryCount int
Retryable *bool Retryable *bool
RetryDelay time.Duration RetryDelay time.Duration
...@@ -61,7 +62,9 @@ type Paginator struct { ...@@ -61,7 +62,9 @@ type Paginator struct {
// Params is any value of input parameters to be the request payload. // Params is any value of input parameters to be the request payload.
// Data is pointer value to an object which the request's response // Data is pointer value to an object which the request's response
// payload will be deserialized to. // payload will be deserialized to.
func New(service serviceinfo.ServiceInfo, handlers Handlers, retryer Retryer, operation *Operation, params interface{}, data interface{}) *Request { func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
retryer Retryer, operation *Operation, params interface{}, data interface{}) *Request {
method := operation.HTTPMethod method := operation.HTTPMethod
if method == "" { if method == "" {
method = "POST" method = "POST"
...@@ -72,12 +75,14 @@ func New(service serviceinfo.ServiceInfo, handlers Handlers, retryer Retryer, op ...@@ -72,12 +75,14 @@ func New(service serviceinfo.ServiceInfo, handlers Handlers, retryer Retryer, op
} }
httpReq, _ := http.NewRequest(method, "", nil) httpReq, _ := http.NewRequest(method, "", nil)
httpReq.URL, _ = url.Parse(service.Endpoint + p) httpReq.URL, _ = url.Parse(clientInfo.Endpoint + p)
r := &Request{ r := &Request{
Retryer: retryer, Config: cfg,
Service: service, ClientInfo: clientInfo,
Handlers: handlers.Copy(), Handlers: handlers.Copy(),
Retryer: retryer,
Time: time.Now(), Time: time.Now(),
ExpireTime: 0, ExpireTime: 0,
Operation: operation, Operation: operation,
...@@ -140,7 +145,7 @@ func (r *Request) Presign(expireTime time.Duration) (string, error) { ...@@ -140,7 +145,7 @@ func (r *Request) Presign(expireTime time.Duration) (string, error) {
} }
func debugLogReqError(r *Request, stage string, retrying bool, err error) { func debugLogReqError(r *Request, stage string, retrying bool, err error) {
if !r.Service.Config.LogLevel.Matches(aws.LogDebugWithRequestErrors) { if !r.Config.LogLevel.Matches(aws.LogDebugWithRequestErrors) {
return return
} }
...@@ -149,8 +154,8 @@ func debugLogReqError(r *Request, stage string, retrying bool, err error) { ...@@ -149,8 +154,8 @@ func debugLogReqError(r *Request, stage string, retrying bool, err error) {
retryStr = "will retry" retryStr = "will retry"
} }
r.Service.Config.Logger.Log(fmt.Sprintf("DEBUG: %s %s/%s failed, %s, error %v", r.Config.Logger.Log(fmt.Sprintf("DEBUG: %s %s/%s failed, %s, error %v",
stage, r.Service.ServiceName, r.Operation.Name, retryStr, err)) stage, r.ClientInfo.ServiceName, r.Operation.Name, retryStr, err))
} }
// Build will build the request's object so it can be signed and sent // Build will build the request's object so it can be signed and sent
...@@ -205,9 +210,9 @@ func (r *Request) Send() error { ...@@ -205,9 +210,9 @@ func (r *Request) Send() error {
} }
if aws.BoolValue(r.Retryable) { if aws.BoolValue(r.Retryable) {
if r.Service.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) { if r.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) {
r.Service.Config.Logger.Log(fmt.Sprintf("DEBUG: Retrying Request %s/%s, attempt %d", r.Config.Logger.Log(fmt.Sprintf("DEBUG: Retrying Request %s/%s, attempt %d",
r.Service.ServiceName, r.Operation.Name, r.RetryCount)) r.ClientInfo.ServiceName, r.Operation.Name, r.RetryCount))
} }
// Re-seek the body back to the original point in for a retry so that // Re-seek the body back to the original point in for a retry so that
...@@ -264,85 +269,11 @@ func (r *Request) Send() error { ...@@ -264,85 +269,11 @@ func (r *Request) Send() error {
return nil return nil
} }
// HasNextPage returns true if this request has more pages of data available. // AddToUserAgent adds the string to the end of the request's current user agent.
func (r *Request) HasNextPage() bool { func AddToUserAgent(r *Request, s string) {
return r.nextPageTokens() != nil curUA := r.HTTPRequest.Header.Get("User-Agent")
} if len(curUA) > 0 {
s = curUA + " " + s
// nextPageTokens returns the tokens to use when asking for the next page of
// data.
func (r *Request) nextPageTokens() []interface{} {
if r.Operation.Paginator == nil {
return nil
}
if r.Operation.TruncationToken != "" {
tr := awsutil.ValuesAtAnyPath(r.Data, r.Operation.TruncationToken)
if tr == nil || len(tr) == 0 {
return nil
}
switch v := tr[0].(type) {
case bool:
if v == false {
return nil
}
}
}
found := false
tokens := make([]interface{}, len(r.Operation.OutputTokens))
for i, outtok := range r.Operation.OutputTokens {
v := awsutil.ValuesAtAnyPath(r.Data, outtok)
if v != nil && len(v) > 0 {
found = true
tokens[i] = v[0]
}
}
if found {
return tokens
}
return nil
}
// NextPage returns a new Request that can be executed to return the next
// page of result data. Call .Send() on this request to execute it.
func (r *Request) NextPage() *Request {
tokens := r.nextPageTokens()
if tokens == nil {
return nil
} }
r.HTTPRequest.Header.Set("User-Agent", s)
data := reflect.New(reflect.TypeOf(r.Data).Elem()).Interface()
nr := New(r.Service, r.Handlers, r.Retryer, r.Operation, awsutil.CopyOf(r.Params), data)
for i, intok := range nr.Operation.InputTokens {
awsutil.SetValueAtAnyPath(nr.Params, intok, tokens[i])
}
return nr
}
// EachPage iterates over each page of a paginated request object. The fn
// parameter should be a function with the following sample signature:
//
// func(page *T, lastPage bool) bool {
// return true // return false to stop iterating
// }
//
// Where "T" is the structure type matching the output structure of the given
// operation. For example, a request object generated by
// DynamoDB.ListTablesRequest() would expect to see dynamodb.ListTablesOutput
// as the structure "T". The lastPage value represents whether the page is
// the last page of data or not. The return value of this function should
// return true to keep iterating or false to stop.
func (r *Request) EachPage(fn func(data interface{}, isLastPage bool) (shouldContinue bool)) error {
for page := r; page != nil; page = page.NextPage() {
page.Send()
shouldContinue := fn(page.Data, !page.HasNextPage())
if page.Error != nil || !shouldContinue {
return page.Error
}
}
return nil
} }
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
}
...@@ -3,6 +3,7 @@ package request ...@@ -3,6 +3,7 @@ package request
import ( import (
"time" "time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
) )
...@@ -12,13 +13,21 @@ import ( ...@@ -12,13 +13,21 @@ import (
type Retryer interface { type Retryer interface {
RetryRules(*Request) time.Duration RetryRules(*Request) time.Duration
ShouldRetry(*Request) bool ShouldRetry(*Request) bool
MaxRetries() uint 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 // retryableCodes is a collection of service response codes which are retry-able
// without any further action. // without any further action.
var retryableCodes = map[string]struct{}{ var retryableCodes = map[string]struct{}{
"RequestError": {}, "RequestError": {},
"RequestTimeout": {},
"ProvisionedThroughputExceededException": {}, "ProvisionedThroughputExceededException": {},
"Throttling": {}, "Throttling": {},
"ThrottlingException": {}, "ThrottlingException": {},
......
package service
import (
"fmt"
"io/ioutil"
"net/http"
"net/http/httputil"
"regexp"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/corehandlers"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/service/serviceinfo"
"github.com/aws/aws-sdk-go/private/endpoints"
)
// A Service implements the base service request and response handling
// used by all services.
type Service struct {
serviceinfo.ServiceInfo
request.Retryer
DefaultMaxRetries uint
Handlers request.Handlers
}
var schemeRE = regexp.MustCompile("^([^:]+)://")
// New will return a pointer to a new Server object initialized.
func New(config *aws.Config) *Service {
svc := &Service{ServiceInfo: serviceinfo.ServiceInfo{Config: config}}
svc.Initialize()
return svc
}
// Initialize initializes the service.
func (s *Service) Initialize() {
if s.Config == nil {
s.Config = &aws.Config{}
}
if s.Config.HTTPClient == nil {
s.Config.HTTPClient = http.DefaultClient
}
if s.Config.SleepDelay == nil {
s.Config.SleepDelay = time.Sleep
}
s.Retryer = DefaultRetryer{s}
s.DefaultMaxRetries = 3
s.Handlers.Validate.PushBackNamed(corehandlers.ValidateEndpointHandler)
s.Handlers.Build.PushBackNamed(corehandlers.UserAgentHandler)
s.Handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
s.Handlers.Send.PushBackNamed(corehandlers.SendHandler)
s.Handlers.AfterRetry.PushBackNamed(corehandlers.AfterRetryHandler)
s.Handlers.ValidateResponse.PushBackNamed(corehandlers.ValidateResponseHandler)
if !aws.BoolValue(s.Config.DisableParamValidation) {
s.Handlers.Validate.PushBackNamed(corehandlers.ValidateParametersHandler)
}
s.AddDebugHandlers()
s.buildEndpoint()
}
// NewRequest returns a new Request pointer for the service API
// operation and parameters.
func (s *Service) NewRequest(operation *request.Operation, params interface{}, data interface{}) *request.Request {
return request.New(s.ServiceInfo, s.Handlers, s.Retryer, operation, params, data)
}
// buildEndpoint builds the endpoint values the service will use to make requests with.
func (s *Service) buildEndpoint() {
if aws.StringValue(s.Config.Endpoint) != "" {
s.Endpoint = *s.Config.Endpoint
} else if s.Endpoint == "" {
s.Endpoint, s.SigningRegion =
endpoints.EndpointForRegion(s.ServiceName, aws.StringValue(s.Config.Region))
}
if s.Endpoint != "" && !schemeRE.MatchString(s.Endpoint) {
scheme := "https"
if aws.BoolValue(s.Config.DisableSSL) {
scheme = "http"
}
s.Endpoint = scheme + "://" + s.Endpoint
}
}
// AddDebugHandlers injects debug logging handlers into the service to log request
// debug information.
func (s *Service) AddDebugHandlers() {
if !s.Config.LogLevel.AtLeast(aws.LogDebug) {
return
}
s.Handlers.Send.PushFront(logRequest)
s.Handlers.Send.PushBack(logResponse)
}
const logReqMsg = `DEBUG: Request %s/%s Details:
---[ REQUEST POST-SIGN ]-----------------------------
%s
-----------------------------------------------------`
func logRequest(r *request.Request) {
logBody := r.Service.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.Service.Config.Logger.Log(fmt.Sprintf(logReqMsg, r.Service.ServiceName, r.Operation.Name, string(dumpedBody)))
}
const logRespMsg = `DEBUG: Response %s/%s Details:
---[ RESPONSE ]--------------------------------------
%s
-----------------------------------------------------`
func logResponse(r *request.Request) {
var msg = "no reponse data"
if r.HTTPResponse != nil {
logBody := r.Service.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
dumpedBody, _ := httputil.DumpResponse(r.HTTPResponse, logBody)
msg = string(dumpedBody)
} else if r.Error != nil {
msg = r.Error.Error()
}
r.Service.Config.Logger.Log(fmt.Sprintf(logRespMsg, r.Service.ServiceName, r.Operation.Name, msg))
}
// 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 {
def := defaults.Get()
s := &Session{
Config: def.Config,
Handlers: def.Handlers,
}
s.Config.MergeIn(cfgs...)
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,
}
}
...@@ -5,7 +5,7 @@ import ( ...@@ -5,7 +5,7 @@ import (
"sync" "sync"
) )
// ReadSeekCloser wraps a io.Reader returning a ReaderSeakerCloser // ReadSeekCloser wraps a io.Reader returning a ReaderSeekerCloser
func ReadSeekCloser(r io.Reader) ReaderSeekerCloser { func ReadSeekCloser(r io.Reader) ReaderSeekerCloser {
return ReaderSeekerCloser{r} return ReaderSeekerCloser{r}
} }
......
...@@ -5,4 +5,4 @@ package aws ...@@ -5,4 +5,4 @@ package aws
const SDKName = "aws-sdk-go" const SDKName = "aws-sdk-go"
// SDKVersion is the version of this SDK // SDKVersion is the version of this SDK
const SDKVersion = "0.9.16" const SDKVersion = "1.0.2"
...@@ -4,11 +4,27 @@ package endpoints ...@@ -4,11 +4,27 @@ package endpoints
//go:generate go run ../model/cli/gen-endpoints/main.go endpoints.json endpoints_map.go //go:generate go run ../model/cli/gen-endpoints/main.go endpoints.json endpoints_map.go
//go:generate gofmt -s -w endpoints_map.go //go:generate gofmt -s -w endpoints_map.go
import "strings" 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. // 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. // if the service and region pair are not found endpoint and signingRegion will be empty.
func EndpointForRegion(svcName, region string) (endpoint, signingRegion string) { func EndpointForRegion(svcName, region string, disableSSL bool) (endpoint, signingRegion string) {
derivedKeys := []string{ derivedKeys := []string{
region + "/" + svcName, region + "/" + svcName,
region + "/*", region + "/*",
...@@ -24,8 +40,26 @@ func EndpointForRegion(svcName, region string) (endpoint, signingRegion string) ...@@ -24,8 +40,26 @@ func EndpointForRegion(svcName, region string) (endpoint, signingRegion string)
endpoint = ep endpoint = ep
signingRegion = val.SigningRegion signingRegion = val.SigningRegion
return break
} }
} }
return
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
} }
...@@ -29,6 +29,10 @@ ...@@ -29,6 +29,10 @@
"endpoint": "", "endpoint": "",
"signingRegion": "us-east-1" "signingRegion": "us-east-1"
}, },
"*/ec2metadata": {
"endpoint": "http://169.254.169.254/latest",
"signingRegion": "us-east-1"
},
"*/iam": { "*/iam": {
"endpoint": "iam.amazonaws.com", "endpoint": "iam.amazonaws.com",
"signingRegion": "us-east-1" "signingRegion": "us-east-1"
...@@ -45,6 +49,10 @@ ...@@ -45,6 +49,10 @@
"endpoint": "sts.amazonaws.com", "endpoint": "sts.amazonaws.com",
"signingRegion": "us-east-1" "signingRegion": "us-east-1"
}, },
"*/waf": {
"endpoint": "waf.amazonaws.com",
"signingRegion": "us-east-1"
},
"us-east-1/sdb": { "us-east-1/sdb": {
"endpoint": "sdb.amazonaws.com", "endpoint": "sdb.amazonaws.com",
"signingRegion": "us-east-1" "signingRegion": "us-east-1"
......
...@@ -30,6 +30,10 @@ var endpointsMap = endpointStruct{ ...@@ -30,6 +30,10 @@ var endpointsMap = endpointStruct{
Endpoint: "", Endpoint: "",
SigningRegion: "us-east-1", SigningRegion: "us-east-1",
}, },
"*/ec2metadata": {
Endpoint: "http://169.254.169.254/latest",
SigningRegion: "us-east-1",
},
"*/iam": { "*/iam": {
Endpoint: "iam.amazonaws.com", Endpoint: "iam.amazonaws.com",
SigningRegion: "us-east-1", SigningRegion: "us-east-1",
...@@ -46,6 +50,10 @@ var endpointsMap = endpointStruct{ ...@@ -46,6 +50,10 @@ var endpointsMap = endpointStruct{
Endpoint: "sts.amazonaws.com", Endpoint: "sts.amazonaws.com",
SigningRegion: "us-east-1", SigningRegion: "us-east-1",
}, },
"*/waf": {
Endpoint: "waf.amazonaws.com",
SigningRegion: "us-east-1",
},
"ap-northeast-1/s3": { "ap-northeast-1/s3": {
Endpoint: "s3-{region}.amazonaws.com", Endpoint: "s3-{region}.amazonaws.com",
}, },
......
...@@ -15,7 +15,7 @@ import ( ...@@ -15,7 +15,7 @@ import (
func Build(r *request.Request) { func Build(r *request.Request) {
body := url.Values{ body := url.Values{
"Action": {r.Operation.Name}, "Action": {r.Operation.Name},
"Version": {r.Service.APIVersion}, "Version": {r.ClientInfo.APIVersion},
} }
if err := queryutil.Parse(body, r.Params, true); err != nil { if err := queryutil.Parse(body, r.Params, true); err != nil {
r.Error = awserr.New("SerializationError", "failed encoding EC2 Query request", err) r.Error = awserr.New("SerializationError", "failed encoding EC2 Query request", err)
......
...@@ -15,7 +15,7 @@ import ( ...@@ -15,7 +15,7 @@ import (
func Build(r *request.Request) { func Build(r *request.Request) {
body := url.Values{ body := url.Values{
"Action": {r.Operation.Name}, "Action": {r.Operation.Name},
"Version": {r.Service.APIVersion}, "Version": {r.ClientInfo.APIVersion},
} }
if err := queryutil.Parse(body, r.Params, false); err != nil { if err := queryutil.Parse(body, r.Params, false); err != nil {
r.Error = awserr.New("SerializationError", "failed encoding Query request", err) r.Error = awserr.New("SerializationError", "failed encoding Query request", err)
......
...@@ -72,7 +72,7 @@ func (q *queryParser) parseStruct(v url.Values, value reflect.Value, prefix stri ...@@ -72,7 +72,7 @@ func (q *queryParser) parseStruct(v url.Values, value reflect.Value, prefix stri
continue // ignore unexported fields continue // ignore unexported fields
} }
value := elemOf(value.Field(i)) elemValue := elemOf(value.Field(i))
field := t.Field(i) field := t.Field(i)
var name string var name string
...@@ -97,7 +97,7 @@ func (q *queryParser) parseStruct(v url.Values, value reflect.Value, prefix stri ...@@ -97,7 +97,7 @@ func (q *queryParser) parseStruct(v url.Values, value reflect.Value, prefix stri
name = prefix + "." + name name = prefix + "." + name
} }
if err := q.parseValue(v, value, name, field.Tag); err != nil { if err := q.parseValue(v, elemValue, name, field.Tag); err != nil {
return err return err
} }
} }
......
...@@ -67,18 +67,18 @@ type signer struct { ...@@ -67,18 +67,18 @@ type signer struct {
func Sign(req *request.Request) { func Sign(req *request.Request) {
// If the request does not need to be signed ignore the signing of the // If the request does not need to be signed ignore the signing of the
// request if the AnonymousCredentials object is used. // request if the AnonymousCredentials object is used.
if req.Service.Config.Credentials == credentials.AnonymousCredentials { if req.Config.Credentials == credentials.AnonymousCredentials {
return return
} }
region := req.Service.SigningRegion region := req.ClientInfo.SigningRegion
if region == "" { if region == "" {
region = aws.StringValue(req.Service.Config.Region) region = aws.StringValue(req.Config.Region)
} }
name := req.Service.SigningName name := req.ClientInfo.SigningName
if name == "" { if name == "" {
name = req.Service.ServiceName name = req.ClientInfo.ServiceName
} }
s := signer{ s := signer{
...@@ -89,9 +89,9 @@ func Sign(req *request.Request) { ...@@ -89,9 +89,9 @@ func Sign(req *request.Request) {
Body: req.Body, Body: req.Body,
ServiceName: name, ServiceName: name,
Region: region, Region: region,
Credentials: req.Service.Config.Credentials, Credentials: req.Config.Credentials,
Debug: req.Service.Config.LogLevel.Value(), Debug: req.Config.LogLevel.Value(),
Logger: req.Service.Config.Logger, Logger: req.Config.Logger,
} }
req.Error = s.sign() req.Error = s.sign()
......
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 {
if err != nil && a.Matcher != "error" {
// Only matcher error is valid if there is a request error
continue
}
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)
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...))
}
}
...@@ -519,6 +519,7 @@ func (c *AutoScaling) DescribeAutoScalingGroups(input *DescribeAutoScalingGroups ...@@ -519,6 +519,7 @@ func (c *AutoScaling) DescribeAutoScalingGroups(input *DescribeAutoScalingGroups
func (c *AutoScaling) DescribeAutoScalingGroupsPages(input *DescribeAutoScalingGroupsInput, fn func(p *DescribeAutoScalingGroupsOutput, lastPage bool) (shouldContinue bool)) error { func (c *AutoScaling) DescribeAutoScalingGroupsPages(input *DescribeAutoScalingGroupsInput, fn func(p *DescribeAutoScalingGroupsOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeAutoScalingGroupsRequest(input) page, _ := c.DescribeAutoScalingGroupsRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool { return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeAutoScalingGroupsOutput), lastPage) return fn(p.(*DescribeAutoScalingGroupsOutput), lastPage)
}) })
...@@ -560,6 +561,7 @@ func (c *AutoScaling) DescribeAutoScalingInstances(input *DescribeAutoScalingIns ...@@ -560,6 +561,7 @@ func (c *AutoScaling) DescribeAutoScalingInstances(input *DescribeAutoScalingIns
func (c *AutoScaling) DescribeAutoScalingInstancesPages(input *DescribeAutoScalingInstancesInput, fn func(p *DescribeAutoScalingInstancesOutput, lastPage bool) (shouldContinue bool)) error { func (c *AutoScaling) DescribeAutoScalingInstancesPages(input *DescribeAutoScalingInstancesInput, fn func(p *DescribeAutoScalingInstancesOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeAutoScalingInstancesRequest(input) page, _ := c.DescribeAutoScalingInstancesRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool { return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeAutoScalingInstancesOutput), lastPage) return fn(p.(*DescribeAutoScalingInstancesOutput), lastPage)
}) })
...@@ -628,6 +630,7 @@ func (c *AutoScaling) DescribeLaunchConfigurations(input *DescribeLaunchConfigur ...@@ -628,6 +630,7 @@ func (c *AutoScaling) DescribeLaunchConfigurations(input *DescribeLaunchConfigur
func (c *AutoScaling) DescribeLaunchConfigurationsPages(input *DescribeLaunchConfigurationsInput, fn func(p *DescribeLaunchConfigurationsOutput, lastPage bool) (shouldContinue bool)) error { func (c *AutoScaling) DescribeLaunchConfigurationsPages(input *DescribeLaunchConfigurationsInput, fn func(p *DescribeLaunchConfigurationsOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeLaunchConfigurationsRequest(input) page, _ := c.DescribeLaunchConfigurationsRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool { return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeLaunchConfigurationsOutput), lastPage) return fn(p.(*DescribeLaunchConfigurationsOutput), lastPage)
}) })
...@@ -780,6 +783,7 @@ func (c *AutoScaling) DescribeNotificationConfigurations(input *DescribeNotifica ...@@ -780,6 +783,7 @@ func (c *AutoScaling) DescribeNotificationConfigurations(input *DescribeNotifica
func (c *AutoScaling) DescribeNotificationConfigurationsPages(input *DescribeNotificationConfigurationsInput, fn func(p *DescribeNotificationConfigurationsOutput, lastPage bool) (shouldContinue bool)) error { func (c *AutoScaling) DescribeNotificationConfigurationsPages(input *DescribeNotificationConfigurationsInput, fn func(p *DescribeNotificationConfigurationsOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeNotificationConfigurationsRequest(input) page, _ := c.DescribeNotificationConfigurationsRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool { return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeNotificationConfigurationsOutput), lastPage) return fn(p.(*DescribeNotificationConfigurationsOutput), lastPage)
}) })
...@@ -820,6 +824,7 @@ func (c *AutoScaling) DescribePolicies(input *DescribePoliciesInput) (*DescribeP ...@@ -820,6 +824,7 @@ func (c *AutoScaling) DescribePolicies(input *DescribePoliciesInput) (*DescribeP
func (c *AutoScaling) DescribePoliciesPages(input *DescribePoliciesInput, fn func(p *DescribePoliciesOutput, lastPage bool) (shouldContinue bool)) error { func (c *AutoScaling) DescribePoliciesPages(input *DescribePoliciesInput, fn func(p *DescribePoliciesOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribePoliciesRequest(input) page, _ := c.DescribePoliciesRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool { return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribePoliciesOutput), lastPage) return fn(p.(*DescribePoliciesOutput), lastPage)
}) })
...@@ -863,6 +868,7 @@ func (c *AutoScaling) DescribeScalingActivities(input *DescribeScalingActivities ...@@ -863,6 +868,7 @@ func (c *AutoScaling) DescribeScalingActivities(input *DescribeScalingActivities
func (c *AutoScaling) DescribeScalingActivitiesPages(input *DescribeScalingActivitiesInput, fn func(p *DescribeScalingActivitiesOutput, lastPage bool) (shouldContinue bool)) error { func (c *AutoScaling) DescribeScalingActivitiesPages(input *DescribeScalingActivitiesInput, fn func(p *DescribeScalingActivitiesOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeScalingActivitiesRequest(input) page, _ := c.DescribeScalingActivitiesRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool { return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeScalingActivitiesOutput), lastPage) return fn(p.(*DescribeScalingActivitiesOutput), lastPage)
}) })
...@@ -931,6 +937,7 @@ func (c *AutoScaling) DescribeScheduledActions(input *DescribeScheduledActionsIn ...@@ -931,6 +937,7 @@ func (c *AutoScaling) DescribeScheduledActions(input *DescribeScheduledActionsIn
func (c *AutoScaling) DescribeScheduledActionsPages(input *DescribeScheduledActionsInput, fn func(p *DescribeScheduledActionsOutput, lastPage bool) (shouldContinue bool)) error { func (c *AutoScaling) DescribeScheduledActionsPages(input *DescribeScheduledActionsInput, fn func(p *DescribeScheduledActionsOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeScheduledActionsRequest(input) page, _ := c.DescribeScheduledActionsRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool { return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeScheduledActionsOutput), lastPage) return fn(p.(*DescribeScheduledActionsOutput), lastPage)
}) })
...@@ -980,6 +987,7 @@ func (c *AutoScaling) DescribeTags(input *DescribeTagsInput) (*DescribeTagsOutpu ...@@ -980,6 +987,7 @@ func (c *AutoScaling) DescribeTags(input *DescribeTagsInput) (*DescribeTagsOutpu
func (c *AutoScaling) DescribeTagsPages(input *DescribeTagsInput, fn func(p *DescribeTagsOutput, lastPage bool) (shouldContinue bool)) error { func (c *AutoScaling) DescribeTagsPages(input *DescribeTagsInput, fn func(p *DescribeTagsOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeTagsRequest(input) page, _ := c.DescribeTagsRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool { return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeTagsOutput), lastPage) return fn(p.(*DescribeTagsOutput), lastPage)
}) })
......
...@@ -218,3 +218,5 @@ type AutoScalingAPI interface { ...@@ -218,3 +218,5 @@ type AutoScalingAPI interface {
UpdateAutoScalingGroup(*autoscaling.UpdateAutoScalingGroupInput) (*autoscaling.UpdateAutoScalingGroupOutput, error) UpdateAutoScalingGroup(*autoscaling.UpdateAutoScalingGroupInput) (*autoscaling.UpdateAutoScalingGroupOutput, error)
} }
var _ AutoScalingAPI = (*autoscaling.AutoScaling)(nil)
...@@ -4,10 +4,9 @@ package autoscaling ...@@ -4,10 +4,9 @@ package autoscaling
import ( import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/defaults" "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/aws/request"
"github.com/aws/aws-sdk-go/aws/service"
"github.com/aws/aws-sdk-go/aws/service/serviceinfo"
"github.com/aws/aws-sdk-go/private/protocol/query" "github.com/aws/aws-sdk-go/private/protocol/query"
"github.com/aws/aws-sdk-go/private/signer/v4" "github.com/aws/aws-sdk-go/private/signer/v4"
) )
...@@ -15,40 +14,64 @@ import ( ...@@ -15,40 +14,64 @@ import (
// Auto Scaling is designed to automatically launch or terminate EC2 instances // Auto Scaling is designed to automatically launch or terminate EC2 instances
// based on user-defined policies, schedules, and health checks. Use this service // based on user-defined policies, schedules, and health checks. Use this service
// in conjunction with the Amazon CloudWatch and Elastic Load Balancing services. // in conjunction with the Amazon CloudWatch and Elastic Load Balancing services.
//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 AutoScaling struct { type AutoScaling struct {
*service.Service *client.Client
} }
// Used for custom service initialization logic // Used for custom client initialization logic
var initService func(*service.Service) var initClient func(*client.Client)
// Used for custom request initialization logic // Used for custom request initialization logic
var initRequest func(*request.Request) var initRequest func(*request.Request)
// New returns a new AutoScaling client. // A ServiceName is the name of the service the client will make API calls to.
func New(config *aws.Config) *AutoScaling { const ServiceName = "autoscaling"
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{ // New creates a new instance of the AutoScaling client with a session.
Config: defaults.DefaultConfig.Merge(config), // If additional configuration is needed for the client instance use the optional
ServiceName: "autoscaling", // aws.Config parameter to add your extra config.
//
// Example:
// // Create a AutoScaling client from just a session.
// svc := autoscaling.New(mySession)
//
// // Create a AutoScaling client with additional configuration
// svc := autoscaling.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func New(p client.ConfigProvider, cfgs ...*aws.Config) *AutoScaling {
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) *AutoScaling {
svc := &AutoScaling{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: ServiceName,
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "2011-01-01", APIVersion: "2011-01-01",
}, },
handlers,
),
} }
service.Initialize()
// Handlers // Handlers
service.Handlers.Sign.PushBack(v4.Sign) svc.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(query.Build) svc.Handlers.Build.PushBack(query.Build)
service.Handlers.Unmarshal.PushBack(query.Unmarshal) svc.Handlers.Unmarshal.PushBack(query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(query.UnmarshalMeta) svc.Handlers.UnmarshalMeta.PushBack(query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(query.UnmarshalError) svc.Handlers.UnmarshalError.PushBack(query.UnmarshalError)
// Run custom service initialization if present // Run custom client initialization if present
if initService != nil { if initClient != nil {
initService(service) initClient(svc.Client)
} }
return &AutoScaling{service} return svc
} }
// newRequest creates a new request for a AutoScaling operation and runs any // newRequest creates a new request for a AutoScaling operation and runs any
......
...@@ -3,8 +3,10 @@ package ec2 ...@@ -3,8 +3,10 @@ package ec2
import ( import (
"time" "time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awsutil" "github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/private/endpoints"
) )
func init() { func init() {
...@@ -20,38 +22,34 @@ func fillPresignedURL(r *request.Request) { ...@@ -20,38 +22,34 @@ func fillPresignedURL(r *request.Request) {
return return
} }
params := r.Params.(*CopySnapshotInput) origParams := r.Params.(*CopySnapshotInput)
// Stop if PresignedURL/DestinationRegion is set // Stop if PresignedURL/DestinationRegion is set
if params.PresignedUrl != nil || params.DestinationRegion != nil { if origParams.PresignedUrl != nil || origParams.DestinationRegion != nil {
return return
} }
// First generate a copy of parameters origParams.DestinationRegion = r.Config.Region
r.Params = awsutil.CopyOf(r.Params) newParams := awsutil.CopyOf(r.Params).(*CopySnapshotInput)
params = r.Params.(*CopySnapshotInput)
// Set destination region. Avoids infinite handler loop. // Create a new request based on the existing request. We will use this to
// Also needed to sign sub-request. // presign the CopySnapshot request against the source region.
params.DestinationRegion = r.Service.Config.Region cfg := r.Config.Copy(aws.NewConfig().
// Create a new client pointing at source region.
// We will use this to presign the CopySnapshot request against
// the source region
config := r.Service.Config.Copy().
WithEndpoint(""). WithEndpoint("").
WithRegion(*params.SourceRegion) WithRegion(aws.StringValue(origParams.SourceRegion)))
client := New(config) clientInfo := r.ClientInfo
clientInfo.Endpoint, clientInfo.SigningRegion = endpoints.EndpointForRegion(
clientInfo.ServiceName, aws.StringValue(cfg.Region), aws.BoolValue(cfg.DisableSSL))
// Presign a CopySnapshot request with modified params // Presign a CopySnapshot request with modified params
req, _ := client.CopySnapshotRequest(params) req := request.New(*cfg, clientInfo, r.Handlers, r.Retryer, r.Operation, newParams, r.Data)
url, err := req.Presign(300 * time.Second) // 5 minutes should be enough. url, err := req.Presign(5 * time.Minute) // 5 minutes should be enough.
if err != nil { // bubble error back up to original request if err != nil { // bubble error back up to original request
r.Error = err r.Error = err
return
} }
// We have our URL, set it on params // We have our URL, set it on params
params.PresignedUrl = &url origParams.PresignedUrl = &url
} }
...@@ -18,6 +18,10 @@ type EC2API interface { ...@@ -18,6 +18,10 @@ type EC2API interface {
AllocateAddress(*ec2.AllocateAddressInput) (*ec2.AllocateAddressOutput, error) AllocateAddress(*ec2.AllocateAddressInput) (*ec2.AllocateAddressOutput, error)
AllocateHostsRequest(*ec2.AllocateHostsInput) (*request.Request, *ec2.AllocateHostsOutput)
AllocateHosts(*ec2.AllocateHostsInput) (*ec2.AllocateHostsOutput, error)
AssignPrivateIpAddressesRequest(*ec2.AssignPrivateIpAddressesInput) (*request.Request, *ec2.AssignPrivateIpAddressesOutput) AssignPrivateIpAddressesRequest(*ec2.AssignPrivateIpAddressesInput) (*request.Request, *ec2.AssignPrivateIpAddressesOutput)
AssignPrivateIpAddresses(*ec2.AssignPrivateIpAddressesInput) (*ec2.AssignPrivateIpAddressesOutput, error) AssignPrivateIpAddresses(*ec2.AssignPrivateIpAddressesInput) (*ec2.AssignPrivateIpAddressesOutput, error)
...@@ -346,6 +350,14 @@ type EC2API interface { ...@@ -346,6 +350,14 @@ type EC2API interface {
DescribeFlowLogs(*ec2.DescribeFlowLogsInput) (*ec2.DescribeFlowLogsOutput, error) DescribeFlowLogs(*ec2.DescribeFlowLogsInput) (*ec2.DescribeFlowLogsOutput, error)
DescribeHostsRequest(*ec2.DescribeHostsInput) (*request.Request, *ec2.DescribeHostsOutput)
DescribeHosts(*ec2.DescribeHostsInput) (*ec2.DescribeHostsOutput, error)
DescribeIdFormatRequest(*ec2.DescribeIdFormatInput) (*request.Request, *ec2.DescribeIdFormatOutput)
DescribeIdFormat(*ec2.DescribeIdFormatInput) (*ec2.DescribeIdFormatOutput, error)
DescribeImageAttributeRequest(*ec2.DescribeImageAttributeInput) (*request.Request, *ec2.DescribeImageAttributeOutput) DescribeImageAttributeRequest(*ec2.DescribeImageAttributeInput) (*request.Request, *ec2.DescribeImageAttributeOutput)
DescribeImageAttribute(*ec2.DescribeImageAttributeInput) (*ec2.DescribeImageAttributeOutput, error) DescribeImageAttribute(*ec2.DescribeImageAttributeInput) (*ec2.DescribeImageAttributeOutput, error)
...@@ -486,6 +498,8 @@ type EC2API interface { ...@@ -486,6 +498,8 @@ type EC2API interface {
DescribeTags(*ec2.DescribeTagsInput) (*ec2.DescribeTagsOutput, error) DescribeTags(*ec2.DescribeTagsInput) (*ec2.DescribeTagsOutput, error)
DescribeTagsPages(*ec2.DescribeTagsInput, func(*ec2.DescribeTagsOutput, bool) bool) error
DescribeVolumeAttributeRequest(*ec2.DescribeVolumeAttributeInput) (*request.Request, *ec2.DescribeVolumeAttributeOutput) DescribeVolumeAttributeRequest(*ec2.DescribeVolumeAttributeInput) (*request.Request, *ec2.DescribeVolumeAttributeOutput)
DescribeVolumeAttribute(*ec2.DescribeVolumeAttributeInput) (*ec2.DescribeVolumeAttributeOutput, error) DescribeVolumeAttribute(*ec2.DescribeVolumeAttributeInput) (*ec2.DescribeVolumeAttributeOutput, error)
...@@ -610,6 +624,14 @@ type EC2API interface { ...@@ -610,6 +624,14 @@ type EC2API interface {
ImportVolume(*ec2.ImportVolumeInput) (*ec2.ImportVolumeOutput, error) ImportVolume(*ec2.ImportVolumeInput) (*ec2.ImportVolumeOutput, error)
ModifyHostsRequest(*ec2.ModifyHostsInput) (*request.Request, *ec2.ModifyHostsOutput)
ModifyHosts(*ec2.ModifyHostsInput) (*ec2.ModifyHostsOutput, error)
ModifyIdFormatRequest(*ec2.ModifyIdFormatInput) (*request.Request, *ec2.ModifyIdFormatOutput)
ModifyIdFormat(*ec2.ModifyIdFormatInput) (*ec2.ModifyIdFormatOutput, error)
ModifyImageAttributeRequest(*ec2.ModifyImageAttributeInput) (*request.Request, *ec2.ModifyImageAttributeOutput) ModifyImageAttributeRequest(*ec2.ModifyImageAttributeInput) (*request.Request, *ec2.ModifyImageAttributeOutput)
ModifyImageAttribute(*ec2.ModifyImageAttributeInput) (*ec2.ModifyImageAttributeOutput, error) ModifyImageAttribute(*ec2.ModifyImageAttributeInput) (*ec2.ModifyImageAttributeOutput, error)
...@@ -618,6 +640,10 @@ type EC2API interface { ...@@ -618,6 +640,10 @@ type EC2API interface {
ModifyInstanceAttribute(*ec2.ModifyInstanceAttributeInput) (*ec2.ModifyInstanceAttributeOutput, error) ModifyInstanceAttribute(*ec2.ModifyInstanceAttributeInput) (*ec2.ModifyInstanceAttributeOutput, error)
ModifyInstancePlacementRequest(*ec2.ModifyInstancePlacementInput) (*request.Request, *ec2.ModifyInstancePlacementOutput)
ModifyInstancePlacement(*ec2.ModifyInstancePlacementInput) (*ec2.ModifyInstancePlacementOutput, error)
ModifyNetworkInterfaceAttributeRequest(*ec2.ModifyNetworkInterfaceAttributeInput) (*request.Request, *ec2.ModifyNetworkInterfaceAttributeOutput) ModifyNetworkInterfaceAttributeRequest(*ec2.ModifyNetworkInterfaceAttributeInput) (*request.Request, *ec2.ModifyNetworkInterfaceAttributeOutput)
ModifyNetworkInterfaceAttribute(*ec2.ModifyNetworkInterfaceAttributeInput) (*ec2.ModifyNetworkInterfaceAttributeOutput, error) ModifyNetworkInterfaceAttribute(*ec2.ModifyNetworkInterfaceAttributeInput) (*ec2.ModifyNetworkInterfaceAttributeOutput, error)
...@@ -678,6 +704,10 @@ type EC2API interface { ...@@ -678,6 +704,10 @@ type EC2API interface {
ReleaseAddress(*ec2.ReleaseAddressInput) (*ec2.ReleaseAddressOutput, error) ReleaseAddress(*ec2.ReleaseAddressInput) (*ec2.ReleaseAddressOutput, error)
ReleaseHostsRequest(*ec2.ReleaseHostsInput) (*request.Request, *ec2.ReleaseHostsOutput)
ReleaseHosts(*ec2.ReleaseHostsInput) (*ec2.ReleaseHostsOutput, error)
ReplaceNetworkAclAssociationRequest(*ec2.ReplaceNetworkAclAssociationInput) (*request.Request, *ec2.ReplaceNetworkAclAssociationOutput) ReplaceNetworkAclAssociationRequest(*ec2.ReplaceNetworkAclAssociationInput) (*request.Request, *ec2.ReplaceNetworkAclAssociationOutput)
ReplaceNetworkAclAssociation(*ec2.ReplaceNetworkAclAssociationInput) (*ec2.ReplaceNetworkAclAssociationOutput, error) ReplaceNetworkAclAssociation(*ec2.ReplaceNetworkAclAssociationInput) (*ec2.ReplaceNetworkAclAssociationOutput, error)
...@@ -758,3 +788,5 @@ type EC2API interface { ...@@ -758,3 +788,5 @@ type EC2API interface {
UnmonitorInstances(*ec2.UnmonitorInstancesInput) (*ec2.UnmonitorInstancesOutput, error) UnmonitorInstances(*ec2.UnmonitorInstancesInput) (*ec2.UnmonitorInstancesOutput, error)
} }
var _ EC2API = (*ec2.EC2)(nil)
...@@ -4,10 +4,9 @@ package ec2 ...@@ -4,10 +4,9 @@ package ec2
import ( import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/defaults" "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/aws/request"
"github.com/aws/aws-sdk-go/aws/service"
"github.com/aws/aws-sdk-go/aws/service/serviceinfo"
"github.com/aws/aws-sdk-go/private/protocol/ec2query" "github.com/aws/aws-sdk-go/private/protocol/ec2query"
"github.com/aws/aws-sdk-go/private/signer/v4" "github.com/aws/aws-sdk-go/private/signer/v4"
) )
...@@ -16,40 +15,64 @@ import ( ...@@ -16,40 +15,64 @@ import (
// in the Amazon Web Services (AWS) cloud. Using Amazon EC2 eliminates your // in the Amazon Web Services (AWS) cloud. Using Amazon EC2 eliminates your
// need to invest in hardware up front, so you can develop and deploy applications // need to invest in hardware up front, so you can develop and deploy applications
// faster. // faster.
//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 EC2 struct { type EC2 struct {
*service.Service *client.Client
} }
// Used for custom service initialization logic // Used for custom client initialization logic
var initService func(*service.Service) var initClient func(*client.Client)
// Used for custom request initialization logic // Used for custom request initialization logic
var initRequest func(*request.Request) var initRequest func(*request.Request)
// New returns a new EC2 client. // A ServiceName is the name of the service the client will make API calls to.
func New(config *aws.Config) *EC2 { const ServiceName = "ec2"
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{ // New creates a new instance of the EC2 client with a session.
Config: defaults.DefaultConfig.Merge(config), // If additional configuration is needed for the client instance use the optional
ServiceName: "ec2", // aws.Config parameter to add your extra config.
//
// Example:
// // Create a EC2 client from just a session.
// svc := ec2.New(mySession)
//
// // Create a EC2 client with additional configuration
// svc := ec2.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func New(p client.ConfigProvider, cfgs ...*aws.Config) *EC2 {
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) *EC2 {
svc := &EC2{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: ServiceName,
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "2015-10-01", APIVersion: "2015-10-01",
}, },
handlers,
),
} }
service.Initialize()
// Handlers // Handlers
service.Handlers.Sign.PushBack(v4.Sign) svc.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(ec2query.Build) svc.Handlers.Build.PushBack(ec2query.Build)
service.Handlers.Unmarshal.PushBack(ec2query.Unmarshal) svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta) svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError) svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
// Run custom service initialization if present // Run custom client initialization if present
if initService != nil { if initClient != nil {
initService(service) initClient(svc.Client)
} }
return &EC2{service} return svc
} }
// newRequest creates a new request for a EC2 operation and runs any // newRequest creates a new request for a EC2 operation and runs any
......
...@@ -472,10 +472,12 @@ func (c *ELB) DescribeInstanceHealthRequest(input *DescribeInstanceHealthInput) ...@@ -472,10 +472,12 @@ func (c *ELB) DescribeInstanceHealthRequest(input *DescribeInstanceHealthInput)
return return
} }
// Describes the state of the specified instances registered with the specified // Describes the state of the specified instances with respect to the specified
// load balancer. If no instances are specified, the call describes the state // load balancer. If no instances are specified, the call describes the state
// of all instances registered with the load balancer, not including any terminated // of all instances that are currently registered with the load balancer. If
// instances. // instances are specified, their state is returned even if they are no longer
// registered with the load balancer. The state of terminated instances is not
// returned.
func (c *ELB) DescribeInstanceHealth(input *DescribeInstanceHealthInput) (*DescribeInstanceHealthOutput, error) { func (c *ELB) DescribeInstanceHealth(input *DescribeInstanceHealthInput) (*DescribeInstanceHealthOutput, error) {
req, out := c.DescribeInstanceHealthRequest(input) req, out := c.DescribeInstanceHealthRequest(input)
err := req.Send() err := req.Send()
...@@ -609,6 +611,7 @@ func (c *ELB) DescribeLoadBalancers(input *DescribeLoadBalancersInput) (*Describ ...@@ -609,6 +611,7 @@ func (c *ELB) DescribeLoadBalancers(input *DescribeLoadBalancersInput) (*Describ
func (c *ELB) DescribeLoadBalancersPages(input *DescribeLoadBalancersInput, fn func(p *DescribeLoadBalancersOutput, lastPage bool) (shouldContinue bool)) error { func (c *ELB) DescribeLoadBalancersPages(input *DescribeLoadBalancersInput, fn func(p *DescribeLoadBalancersOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeLoadBalancersRequest(input) page, _ := c.DescribeLoadBalancersRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool { return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeLoadBalancersOutput), lastPage) return fn(p.(*DescribeLoadBalancersOutput), lastPage)
}) })
...@@ -814,8 +817,9 @@ func (c *ELB) RegisterInstancesWithLoadBalancerRequest(input *RegisterInstancesW ...@@ -814,8 +817,9 @@ func (c *ELB) RegisterInstancesWithLoadBalancerRequest(input *RegisterInstancesW
// with the load balancer in the VPC. // with the load balancer in the VPC.
// //
// Note that RegisterInstanceWithLoadBalancer completes when the request has // Note that RegisterInstanceWithLoadBalancer completes when the request has
// been registered. Instance registration happens shortly afterwards. To check // been registered. Instance registration takes a little time to complete. To
// the state of the registered instances, use DescribeLoadBalancers or DescribeInstanceHealth. // check the state of the registered instances, use DescribeLoadBalancers or
// DescribeInstanceHealth.
// //
// After the instance is registered, it starts receiving traffic and requests // After the instance is registered, it starts receiving traffic and requests
// from the load balancer. Any instance that is not in one of the Availability // from the load balancer. Any instance that is not in one of the Availability
...@@ -1310,8 +1314,9 @@ type CreateAppCookieStickinessPolicyInput struct { ...@@ -1310,8 +1314,9 @@ type CreateAppCookieStickinessPolicyInput struct {
// The name of the load balancer. // The name of the load balancer.
LoadBalancerName *string `type:"string" required:"true"` LoadBalancerName *string `type:"string" required:"true"`
// The name of the policy being created. This name must be unique within the // The name of the policy being created. Policy names must consist of alphanumeric
// set of policies for this load balancer. // characters and dashes (-). This name must be unique within the set of policies
// for this load balancer.
PolicyName *string `type:"string" required:"true"` PolicyName *string `type:"string" required:"true"`
metadataCreateAppCookieStickinessPolicyInput `json:"-" xml:"-"` metadataCreateAppCookieStickinessPolicyInput `json:"-" xml:"-"`
...@@ -1358,8 +1363,9 @@ type CreateLBCookieStickinessPolicyInput struct { ...@@ -1358,8 +1363,9 @@ type CreateLBCookieStickinessPolicyInput struct {
// The name of the load balancer. // The name of the load balancer.
LoadBalancerName *string `type:"string" required:"true"` LoadBalancerName *string `type:"string" required:"true"`
// The name of the policy being created. This name must be unique within the // The name of the policy being created. Policy names must consist of alphanumeric
// set of policies for this load balancer. // characters and dashes (-). This name must be unique within the set of policies
// for this load balancer.
PolicyName *string `type:"string" required:"true"` PolicyName *string `type:"string" required:"true"`
metadataCreateLBCookieStickinessPolicyInput `json:"-" xml:"-"` metadataCreateLBCookieStickinessPolicyInput `json:"-" xml:"-"`
...@@ -1415,9 +1421,9 @@ type CreateLoadBalancerInput struct { ...@@ -1415,9 +1421,9 @@ type CreateLoadBalancerInput struct {
// The name of the load balancer. // The name of the load balancer.
// //
// This name must be unique within your AWS account, must have a maximum of // This name must be unique within your set of load balancers for the region,
// 32 characters, must contain only alphanumeric characters or hyphens, and // must have a maximum of 32 characters, must contain only alphanumeric characters
// cannot begin or end with a hyphen. // or hyphens, and cannot begin or end with a hyphen.
LoadBalancerName *string `type:"string" required:"true"` LoadBalancerName *string `type:"string" required:"true"`
// The type of a load balancer. Valid only for load balancers in a VPC. // The type of a load balancer. Valid only for load balancers in a VPC.
...@@ -2364,8 +2370,9 @@ type Listener struct { ...@@ -2364,8 +2370,9 @@ type Listener struct {
// is HTTP or TCP, the listener's InstanceProtocol must be HTTP or TCP. // is HTTP or TCP, the listener's InstanceProtocol must be HTTP or TCP.
InstanceProtocol *string `type:"string"` InstanceProtocol *string `type:"string"`
// The port on which the load balancer is listening. The supported ports are: // The port on which the load balancer is listening. On EC2-VPC, you can specify
// 25, 80, 443, 465, 587, and 1024-65535. // any port from the range 1-65535. On EC2-Classic, you can specify any port
// from the following list: 25, 80, 443, 465, 587, 1024-65535.
LoadBalancerPort *int64 `type:"integer" required:"true"` LoadBalancerPort *int64 `type:"integer" required:"true"`
// The load balancer transport protocol to use for routing: HTTP, HTTPS, TCP, // The load balancer transport protocol to use for routing: HTTP, HTTPS, TCP,
......
...@@ -124,3 +124,5 @@ type ELBAPI interface { ...@@ -124,3 +124,5 @@ type ELBAPI interface {
SetLoadBalancerPoliciesOfListener(*elb.SetLoadBalancerPoliciesOfListenerInput) (*elb.SetLoadBalancerPoliciesOfListenerOutput, error) SetLoadBalancerPoliciesOfListener(*elb.SetLoadBalancerPoliciesOfListenerInput) (*elb.SetLoadBalancerPoliciesOfListenerOutput, error)
} }
var _ ELBAPI = (*elb.ELB)(nil)
...@@ -4,10 +4,9 @@ package elb ...@@ -4,10 +4,9 @@ package elb
import ( import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/defaults" "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/aws/request"
"github.com/aws/aws-sdk-go/aws/service"
"github.com/aws/aws-sdk-go/aws/service/serviceinfo"
"github.com/aws/aws-sdk-go/private/protocol/query" "github.com/aws/aws-sdk-go/private/protocol/query"
"github.com/aws/aws-sdk-go/private/signer/v4" "github.com/aws/aws-sdk-go/private/signer/v4"
) )
...@@ -25,40 +24,64 @@ import ( ...@@ -25,40 +24,64 @@ import (
// All Elastic Load Balancing operations are idempotent, which means that they // All Elastic Load Balancing operations are idempotent, which means that they
// complete at most one time. If you repeat an operation, it succeeds with a // complete at most one time. If you repeat an operation, it succeeds with a
// 200 OK response code. // 200 OK response code.
//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 ELB struct { type ELB struct {
*service.Service *client.Client
} }
// Used for custom service initialization logic // Used for custom client initialization logic
var initService func(*service.Service) var initClient func(*client.Client)
// Used for custom request initialization logic // Used for custom request initialization logic
var initRequest func(*request.Request) var initRequest func(*request.Request)
// New returns a new ELB client. // A ServiceName is the name of the service the client will make API calls to.
func New(config *aws.Config) *ELB { const ServiceName = "elasticloadbalancing"
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{ // New creates a new instance of the ELB client with a session.
Config: defaults.DefaultConfig.Merge(config), // If additional configuration is needed for the client instance use the optional
ServiceName: "elasticloadbalancing", // aws.Config parameter to add your extra config.
//
// Example:
// // Create a ELB client from just a session.
// svc := elb.New(mySession)
//
// // Create a ELB client with additional configuration
// svc := elb.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func New(p client.ConfigProvider, cfgs ...*aws.Config) *ELB {
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) *ELB {
svc := &ELB{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: ServiceName,
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "2012-06-01", APIVersion: "2012-06-01",
}, },
handlers,
),
} }
service.Initialize()
// Handlers // Handlers
service.Handlers.Sign.PushBack(v4.Sign) svc.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(query.Build) svc.Handlers.Build.PushBack(query.Build)
service.Handlers.Unmarshal.PushBack(query.Unmarshal) svc.Handlers.Unmarshal.PushBack(query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(query.UnmarshalMeta) svc.Handlers.UnmarshalMeta.PushBack(query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(query.UnmarshalError) svc.Handlers.UnmarshalError.PushBack(query.UnmarshalError)
// Run custom service initialization if present // Run custom client initialization if present
if initService != nil { if initClient != nil {
initService(service) initClient(svc.Client)
} }
return &ELB{service} return svc
} }
// newRequest creates a new request for a ELB operation and runs any // newRequest creates a new request for a ELB operation and runs any
......
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
package elb
import (
"github.com/aws/aws-sdk-go/private/waiter"
)
func (c *ELB) WaitUntilAnyInstanceInService(input *DescribeInstanceHealthInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeInstanceHealth",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAny",
Argument: "InstanceStates[].State",
Expected: "InService",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *ELB) WaitUntilInstanceInService(input *DescribeInstanceHealthInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeInstanceHealth",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "InstanceStates[].State",
Expected: "InService",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
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.
testdata/conf_out.ini
ini.sublime-project
ini.sublime-workspace
testdata/conf_reflect.ini
// 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()
if err == nil {
field.Set(reflect.ValueOf(durationVal))
return nil
}
intVal, err := key.Int64()
if err != nil {
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)
}
jpgo
jmespath-fuzz.zip
cpu.out
go-jmespath.test
language: go
sudo: false
go:
- 1.4
install: go get -v -t ./...
script: make test
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.
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/corpus
bench:
go test -bench . -cpuprofile cpu.out
pprof-cpu:
go tool pprof ./go-jmespath.test ./cpu.out
# 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.
package jmespath
// 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)
}
// 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]]
}
/*Basic command line interface for debug and testing purposes.
Examples:
Only print the AST for the expression:
jp.go -ast "foo.bar.baz"
Evaluate the JMESPath expression against JSON data from a file:
jp.go -input /tmp/data.json "foo.bar.baz"
This program can also be used as an executable to the jp-compliance
runner (github.com/jmespath/jmespath.test).
*/
package main
import (
"flag"
"fmt"
"io/ioutil"
"os"
)
import (
"encoding/json"
"github.com/jmespath/go-jmespath"
)
func errMsg(msg string, a ...interface{}) int {
fmt.Fprintf(os.Stderr, msg, a...)
fmt.Fprintln(os.Stderr)
return 1
}
func run() int {
astOnly := flag.Bool("ast", false, "Print the AST for the input expression and exit.")
inputFile := flag.String("input", "", "Filename containing JSON data to search. If not provided, data is read from stdin.")
flag.Parse()
args := flag.Args()
if len(args) != 1 {
fmt.Fprintf(os.Stderr, "Usage:\n\n")
flag.PrintDefaults()
return errMsg("\nError: expected a single argument (the JMESPath expression).")
}
expression := args[0]
parser := jmespath.NewParser()
parsed, err := parser.Parse(expression)
if err != nil {
if syntaxError, ok := err.(jmespath.SyntaxError); ok {
return errMsg("%s\n%s\n", syntaxError, syntaxError.HighlightLocation())
}
return errMsg("%s", err)
}
if *astOnly {
fmt.Println("")
fmt.Printf("%s\n", parsed)
return 0
}
var inputData []byte
if *inputFile != "" {
inputData, err = ioutil.ReadFile(*inputFile)
if err != nil {
return errMsg("Error loading file %s: %s", *inputFile, err)
}
} else {
// If an input data file is not provided then we read the
// data from stdin.
inputData, err = ioutil.ReadAll(os.Stdin)
if err != nil {
return errMsg("Error reading from stdin: %s", err)
}
}
var data interface{}
json.Unmarshal(inputData, &data)
result, err := jmespath.Search(expression, data)
if err != nil {
return errMsg("Error executing expression: %s", err)
}
toJSON, err := json.MarshalIndent(result, "", " ")
if err != nil {
return errMsg("Error serializing result to JSON: %s", err)
}
fmt.Println(string(toJSON))
return 0
}
func main() {
os.Exit(run())
}
[{
"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"
}
]
}
]
[
{
"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
}
]
}
]
[
{
"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"}
}
]
}
]
[{
"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"
}
]
}]
[{
"given":
{"foo": {"bar": ["zero", "one", "two"]}},
"cases": [
{
"expression": "foo.bar[0]",
"result": "zero"
},
{
"expression": "foo.bar[1]",
"result": "one"
},
{
"expression": "foo.bar[2]",
"result": "two"
},
{
"expression": "foo.bar[3]",
"result": null
},
{
"expression": "foo.bar[-1]",
"result": "two"
},
{
"expression": "foo.bar[-2]",
"result": "one"
},
{
"expression": "foo.bar[-3]",
"result": "zero"
},
{
"expression": "foo.bar[-4]",
"result": null
}
]
},
{
"given":
{"foo": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}]},
"cases": [
{
"expression": "foo.bar",
"result": null
},
{
"expression": "foo[0].bar",
"result": "one"
},
{
"expression": "foo[1].bar",
"result": "two"
},
{
"expression": "foo[2].bar",
"result": "three"
},
{
"expression": "foo[3].notbar",
"result": "four"
},
{
"expression": "foo[3].bar",
"result": null
},
{
"expression": "foo[0]",
"result": {"bar": "one"}
},
{
"expression": "foo[1]",
"result": {"bar": "two"}
},
{
"expression": "foo[2]",
"result": {"bar": "three"}
},
{
"expression": "foo[3]",
"result": {"notbar": "four"}
},
{
"expression": "foo[4]",
"result": null
}
]
},
{
"given": [
"one", "two", "three"
],
"cases": [
{
"expression": "[0]",
"result": "one"
},
{
"expression": "[1]",
"result": "two"
},
{
"expression": "[2]",
"result": "three"
},
{
"expression": "[-1]",
"result": "three"
},
{
"expression": "[-2]",
"result": "two"
},
{
"expression": "[-3]",
"result": "one"
}
]
},
{
"given": {"reservations": [
{"instances": [{"foo": 1}, {"foo": 2}]}
]},
"cases": [
{
"expression": "reservations[].instances[].foo",
"result": [1, 2]
},
{
"expression": "reservations[].instances[].bar",
"result": []
},
{
"expression": "reservations[].notinstances[].foo",
"result": []
},
{
"expression": "reservations[].notinstances[].foo",
"result": []
}
]
},
{
"given": {"reservations": [{
"instances": [
{"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]},
{"foo": [{"bar": 5}, {"bar": 6}, {"notbar": [7]}, {"bar": 8}]},
{"foo": "bar"},
{"notfoo": [{"bar": 20}, {"bar": 21}, {"notbar": [7]}, {"bar": 22}]},
{"bar": [{"baz": [1]}, {"baz": [2]}, {"baz": [3]}, {"baz": [4]}]},
{"baz": [{"baz": [1, 2]}, {"baz": []}, {"baz": []}, {"baz": [3, 4]}]},
{"qux": [{"baz": []}, {"baz": [1, 2, 3]}, {"baz": [4]}, {"baz": []}]}
],
"otherkey": {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]}
}, {
"instances": [
{"a": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]},
{"b": [{"bar": 5}, {"bar": 6}, {"notbar": [7]}, {"bar": 8}]},
{"c": "bar"},
{"notfoo": [{"bar": 23}, {"bar": 24}, {"notbar": [7]}, {"bar": 25}]},
{"qux": [{"baz": []}, {"baz": [1, 2, 3]}, {"baz": [4]}, {"baz": []}]}
],
"otherkey": {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]}
}
]},
"cases": [
{
"expression": "reservations[].instances[].foo[].bar",
"result": [1, 2, 4, 5, 6, 8]
},
{
"expression": "reservations[].instances[].foo[].baz",
"result": []
},
{
"expression": "reservations[].instances[].notfoo[].bar",
"result": [20, 21, 22, 23, 24, 25]
},
{
"expression": "reservations[].instances[].notfoo[].notbar",
"result": [[7], [7]]
},
{
"expression": "reservations[].notinstances[].foo",
"result": []
},
{
"expression": "reservations[].instances[].foo[].notbar",
"result": [3, [7]]
},
{
"expression": "reservations[].instances[].bar[].baz",
"result": [[1], [2], [3], [4]]
},
{
"expression": "reservations[].instances[].baz[].baz",
"result": [[1, 2], [], [], [3, 4]]
},
{
"expression": "reservations[].instances[].qux[].baz",
"result": [[], [1, 2, 3], [4], [], [], [1, 2, 3], [4], []]
},
{
"expression": "reservations[].instances[].qux[].baz[]",
"result": [1, 2, 3, 4, 1, 2, 3, 4]
}
]
},
{
"given": {
"foo": [
[["one", "two"], ["three", "four"]],
[["five", "six"], ["seven", "eight"]],
[["nine"], ["ten"]]
]
},
"cases": [
{
"expression": "foo[]",
"result": [["one", "two"], ["three", "four"], ["five", "six"],
["seven", "eight"], ["nine"], ["ten"]]
},
{
"expression": "foo[][0]",
"result": ["one", "three", "five", "seven", "nine", "ten"]
},
{
"expression": "foo[][1]",
"result": ["two", "four", "six", "eight"]
},
{
"expression": "foo[][0][0]",
"result": []
},
{
"expression": "foo[][2][2]",
"result": []
},
{
"expression": "foo[][0][0][100]",
"result": []
}
]
},
{
"given": {
"foo": [{
"bar": [
{
"qux": 2,
"baz": 1
},
{
"qux": 4,
"baz": 3
}
]
},
{
"bar": [
{
"qux": 6,
"baz": 5
},
{
"qux": 8,
"baz": 7
}
]
}
]
},
"cases": [
{
"expression": "foo",
"result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]},
{"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}]
},
{
"expression": "foo[]",
"result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]},
{"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}]
},
{
"expression": "foo[].bar",
"result": [[{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}],
[{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]]
},
{
"expression": "foo[].bar[]",
"result": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3},
{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]
},
{
"expression": "foo[].bar[].baz",
"result": [1, 3, 5, 7]
}
]
},
{
"given": {
"string": "string",
"hash": {"foo": "bar", "bar": "baz"},
"number": 23,
"nullvalue": null
},
"cases": [
{
"expression": "string[]",
"result": null
},
{
"expression": "hash[]",
"result": null
},
{
"expression": "number[]",
"result": null
},
{
"expression": "nullvalue[]",
"result": null
},
{
"expression": "string[].foo",
"result": null
},
{
"expression": "hash[].foo",
"result": null
},
{
"expression": "number[].foo",
"result": null
},
{
"expression": "nullvalue[].foo",
"result": null
},
{
"expression": "nullvalue[].foo[].bar",
"result": null
}
]
}
]
[
{
"given": {
"foo": [{"name": "a"}, {"name": "b"}],
"bar": {"baz": "qux"}
},
"cases": [
{
"expression": "`\"foo\"`",
"result": "foo"
},
{
"comment": "Interpret escaped unicode.",
"expression": "`\"\\u03a6\"`",
"result": "Φ"
},
{
"expression": "`\"\"`",
"result": "✓"
},
{
"expression": "`[1, 2, 3]`",
"result": [1, 2, 3]
},
{
"expression": "`{\"a\": \"b\"}`",
"result": {"a": "b"}
},
{
"expression": "`true`",
"result": true
},
{
"expression": "`false`",
"result": false
},
{
"expression": "`null`",
"result": null
},
{
"expression": "`0`",
"result": 0
},
{
"expression": "`1`",
"result": 1
},
{
"expression": "`2`",
"result": 2
},
{
"expression": "`3`",
"result": 3
},
{
"expression": "`4`",
"result": 4
},
{
"expression": "`5`",
"result": 5
},
{
"expression": "`6`",
"result": 6
},
{
"expression": "`7`",
"result": 7
},
{
"expression": "`8`",
"result": 8
},
{
"expression": "`9`",
"result": 9
},
{
"comment": "Escaping a backtick in quotes",
"expression": "`\"foo\\`bar\"`",
"result": "foo`bar"
},
{
"comment": "Double quote in literal",
"expression": "`\"foo\\\"bar\"`",
"result": "foo\"bar"
},
{
"expression": "`\"1\\`\"`",
"result": "1`"
},
{
"comment": "Multiple literal expressions with escapes",
"expression": "`\"\\\\\"`.{a:`\"b\"`}",
"result": {"a": "b"}
},
{
"comment": "literal . identifier",
"expression": "`{\"a\": \"b\"}`.a",
"result": "b"
},
{
"comment": "literal . identifier . identifier",
"expression": "`{\"a\": {\"b\": \"c\"}}`.a.b",
"result": "c"
},
{
"comment": "literal . identifier bracket-expr",
"expression": "`[0, 1, 2]`[1]",
"result": 1
}
]
},
{
"comment": "Literals",
"given": {"type": "object"},
"cases": [
{
"comment": "Literal with leading whitespace",
"expression": "` {\"foo\": true}`",
"result": {"foo": true}
},
{
"comment": "Literal with trailing whitespace",
"expression": "`{\"foo\": true} `",
"result": {"foo": true}
},
{
"comment": "Literal on RHS of subexpr not allowed",
"expression": "foo.`\"bar\"`",
"error": "syntax"
}
]
},
{
"comment": "Raw String Literals",
"given": {},
"cases": [
{
"expression": "'foo'",
"result": "foo"
},
{
"expression": "' foo '",
"result": " foo "
},
{
"expression": "'0'",
"result": "0"
},
{
"expression": "'newline\n'",
"result": "newline\n"
},
{
"expression": "'\n'",
"result": "\n"
},
{
"expression": "'✓'",
"result": "✓"
},
{
"expression": "'𝄞'",
"result": "𝄞"
},
{
"expression": "' [foo] '",
"result": " [foo] "
},
{
"expression": "'[foo]'",
"result": "[foo]"
},
{
"comment": "Do not interpret escaped unicode.",
"expression": "'\\u03a6'",
"result": "\\u03a6"
}
]
}
]
[{
"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": {
"foo": {
"bar": {
"baz": "subkey"
},
"other": {
"baz": "subkey"
},
"other2": {
"baz": "subkey"
},
"other3": {
"notbaz": ["a", "b", "c"]
},
"other4": {
"notbaz": ["a", "b", "c"]
}
}
},
"cases": [
{
"expression": "foo.*.baz | [0]",
"result": "subkey"
},
{
"expression": "foo.*.baz | [1]",
"result": "subkey"
},
{
"expression": "foo.*.baz | [2]",
"result": "subkey"
},
{
"expression": "foo.bar.* | [0]",
"result": "subkey"
},
{
"expression": "foo.*.notbaz | [*]",
"result": [["a", "b", "c"], ["a", "b", "c"]]
},
{
"expression": "{\"a\": foo.bar, \"b\": foo.other} | *.baz",
"result": ["subkey", "subkey"]
}
]
}, {
"given": {
"foo": {
"bar": {
"baz": "one"
},
"other": {
"baz": "two"
},
"other2": {
"baz": "three"
},
"other3": {
"notbaz": ["a", "b", "c"]
},
"other4": {
"notbaz": ["d", "e", "f"]
}
}
},
"cases": [
{
"expression": "foo | bar",
"result": {"baz": "one"}
},
{
"expression": "foo | bar | baz",
"result": "one"
},
{
"expression": "foo|bar| baz",
"result": "one"
},
{
"expression": "not_there | [0]",
"result": null
},
{
"expression": "not_there | [0]",
"result": null
},
{
"expression": "[foo.bar, foo.other] | [0]",
"result": {"baz": "one"}
},
{
"expression": "{\"a\": foo.bar, \"b\": foo.other} | a",
"result": {"baz": "one"}
},
{
"expression": "{\"a\": foo.bar, \"b\": foo.other} | b",
"result": {"baz": "two"}
},
{
"expression": "foo.bam || foo.bar | baz",
"result": "one"
},
{
"expression": "foo | not_there || bar",
"result": {"baz": "one"}
}
]
}, {
"given": {
"foo": [{
"bar": [{
"baz": "one"
}, {
"baz": "two"
}]
}, {
"bar": [{
"baz": "three"
}, {
"baz": "four"
}]
}]
},
"cases": [
{
"expression": "foo[*].bar[*] | [0][0]",
"result": {"baz": "one"}
}
]
}]
[{
"given": {
"foo": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"bar": {
"baz": 1
}
},
"cases": [
{
"expression": "bar[0:10]",
"result": null
},
{
"expression": "foo[0:10:1]",
"result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
},
{
"expression": "foo[0:10]",
"result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
},
{
"expression": "foo[0:10:]",
"result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
},
{
"expression": "foo[0::1]",
"result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
},
{
"expression": "foo[0::]",
"result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
},
{
"expression": "foo[0:]",
"result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
},
{
"expression": "foo[:10:1]",
"result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
},
{
"expression": "foo[::1]",
"result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
},
{
"expression": "foo[:10:]",
"result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
},
{
"expression": "foo[::]",
"result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
},
{
"expression": "foo[:]",
"result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
},
{
"expression": "foo[1:9]",
"result": [1, 2, 3, 4, 5, 6, 7, 8]
},
{
"expression": "foo[0:10:2]",
"result": [0, 2, 4, 6, 8]
},
{
"expression": "foo[5:]",
"result": [5, 6, 7, 8, 9]
},
{
"expression": "foo[5::2]",
"result": [5, 7, 9]
},
{
"expression": "foo[::2]",
"result": [0, 2, 4, 6, 8]
},
{
"expression": "foo[::-1]",
"result": [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
},
{
"expression": "foo[1::2]",
"result": [1, 3, 5, 7, 9]
},
{
"expression": "foo[10:0:-1]",
"result": [9, 8, 7, 6, 5, 4, 3, 2, 1]
},
{
"expression": "foo[10:5:-1]",
"result": [9, 8, 7, 6]
},
{
"expression": "foo[8:2:-2]",
"result": [8, 6, 4]
},
{
"expression": "foo[0:20]",
"result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
},
{
"expression": "foo[10:-20:-1]",
"result": [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
},
{
"expression": "foo[10:-20]",
"result": []
},
{
"expression": "foo[-4:-1]",
"result": [6, 7, 8]
},
{
"expression": "foo[:-5:-1]",
"result": [9, 8, 7, 6]
},
{
"expression": "foo[8:2:0]",
"error": "invalid-value"
},
{
"expression": "foo[8:2:0:1]",
"error": "syntax"
},
{
"expression": "foo[8:2&]",
"error": "syntax"
},
{
"expression": "foo[2:a:3]",
"error": "syntax"
}
]
}, {
"given": {
"foo": [{"a": 1}, {"a": 2}, {"a": 3}],
"bar": [{"a": {"b": 1}}, {"a": {"b": 2}},
{"a": {"b": 3}}],
"baz": 50
},
"cases": [
{
"expression": "foo[:2].a",
"result": [1, 2]
},
{
"expression": "foo[:2].b",
"result": []
},
{
"expression": "foo[:2].a.b",
"result": []
},
{
"expression": "bar[::-1].a.b",
"result": [3, 2, 1]
},
{
"expression": "bar[:2].a.b",
"result": [1, 2]
},
{
"expression": "baz[:2].a",
"result": null
}
]
}, {
"given": [{"a": 1}, {"a": 2}, {"a": 3}],
"cases": [
{
"expression": "[:]",
"result": [{"a": 1}, {"a": 2}, {"a": 3}]
},
{
"expression": "[:2].a",
"result": [1, 2]
},
{
"expression": "[::-1].a",
"result": [3, 2, 1]
},
{
"expression": "[:2].b",
"result": []
}
]
}]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment