diff options
author | =Corey Hulen <corey@hulen.com> | 2015-06-14 23:53:32 -0800 |
---|---|---|
committer | =Corey Hulen <corey@hulen.com> | 2015-06-14 23:53:32 -0800 |
commit | cf7a05f80f68b5b1c8bcc0089679dd497cec2506 (patch) | |
tree | 70007378570a6962d7c175ca96af732b71aeb6da /Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws | |
download | chat-cf7a05f80f68b5b1c8bcc0089679dd497cec2506.tar.gz chat-cf7a05f80f68b5b1c8bcc0089679dd497cec2506.tar.bz2 chat-cf7a05f80f68b5b1c8bcc0089679dd497cec2506.zip |
first commit
Diffstat (limited to 'Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws')
18 files changed, 1758 insertions, 0 deletions
diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/awsutil/path_value.go b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/awsutil/path_value.go new file mode 100644 index 000000000..ce4c5e27a --- /dev/null +++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/awsutil/path_value.go @@ -0,0 +1,142 @@ +package awsutil + +import ( + "reflect" + "regexp" + "strconv" + "strings" +) + +var indexRe = regexp.MustCompile(`(.+)\[(-?\d+)?\]$`) + +func rValuesAtPath(v interface{}, path string, create bool) []reflect.Value { + pathparts := strings.Split(path, "||") + if len(pathparts) > 1 { + for _, pathpart := range pathparts { + vals := rValuesAtPath(v, pathpart, create) + if vals != nil && len(vals) > 0 { + return vals + } + } + return nil + } + + values := []reflect.Value{reflect.Indirect(reflect.ValueOf(v))} + components := strings.Split(path, ".") + for len(values) > 0 && len(components) > 0 { + var index *int64 + var indexStar bool + c := strings.TrimSpace(components[0]) + if c == "" { // no actual component, illegal syntax + return nil + } else if c != "*" && strings.ToLower(c[0:1]) == c[0:1] { + // TODO normalize case for user + return nil // don't support unexported fields + } + + // parse this component + if m := indexRe.FindStringSubmatch(c); m != nil { + c = m[1] + if m[2] == "" { + index = nil + indexStar = true + } else { + i, _ := strconv.ParseInt(m[2], 10, 32) + index = &i + indexStar = false + } + } + + nextvals := []reflect.Value{} + for _, value := range values { + // pull component name out of struct member + if value.Kind() != reflect.Struct { + continue + } + + if c == "*" { // pull all members + for i := 0; i < value.NumField(); i++ { + if f := reflect.Indirect(value.Field(i)); f.IsValid() { + nextvals = append(nextvals, f) + } + } + continue + } + + value = value.FieldByName(c) + if create && value.Kind() == reflect.Ptr && value.IsNil() { + value.Set(reflect.New(value.Type().Elem())) + value = value.Elem() + } else { + value = reflect.Indirect(value) + } + + if value.IsValid() { + nextvals = append(nextvals, value) + } + } + values = nextvals + + if indexStar || index != nil { + nextvals = []reflect.Value{} + for _, value := range values { + value := reflect.Indirect(value) + if value.Kind() != reflect.Slice { + continue + } + + if indexStar { // grab all indices + for i := 0; i < value.Len(); i++ { + idx := reflect.Indirect(value.Index(i)) + if idx.IsValid() { + nextvals = append(nextvals, idx) + } + } + continue + } + + // pull out index + i := int(*index) + if i >= value.Len() { // check out of bounds + if create { + // TODO resize slice + } else { + continue + } + } else if i < 0 { // support negative indexing + i = value.Len() + i + } + value = reflect.Indirect(value.Index(i)) + + if value.IsValid() { + nextvals = append(nextvals, value) + } + } + values = nextvals + } + + components = components[1:] + } + return values +} + +// ValuesAtPath returns a list of objects at the lexical path inside of a structure +func ValuesAtPath(i interface{}, path string) []interface{} { + if rvals := rValuesAtPath(i, path, false); rvals != nil { + vals := make([]interface{}, len(rvals)) + for i, rval := range rvals { + vals[i] = rval.Interface() + } + return vals + } + return nil +} + +// SetValueAtPath sets an object at the lexical path inside of a structure +func SetValueAtPath(i interface{}, path string, v interface{}) { + if rvals := rValuesAtPath(i, path, true); rvals != nil { + for _, rval := range rvals { + rval.Set(reflect.ValueOf(v)) + } + } +} diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/awsutil/path_value_test.go b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/awsutil/path_value_test.go new file mode 100644 index 000000000..881927365 --- /dev/null +++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/awsutil/path_value_test.go @@ -0,0 +1,60 @@ +package awsutil_test + +import ( + "testing" + + "github.com/awslabs/aws-sdk-go/aws/awsutil" + "github.com/stretchr/testify/assert" +) + +type Struct struct { + A []Struct + a []Struct + B *Struct + D *Struct + C string +} + +var data = Struct{ + A: []Struct{Struct{C: "value1"}, Struct{C: "value2"}, Struct{C: "value3"}}, + a: []Struct{Struct{C: "value1"}, Struct{C: "value2"}, Struct{C: "value3"}}, + B: &Struct{B: &Struct{C: "terminal"}, D: &Struct{C: "terminal2"}}, + C: "initial", +} + +func TestValueAtPathSuccess(t *testing.T) { + assert.Equal(t, []interface{}{"initial"}, awsutil.ValuesAtPath(data, "C")) + assert.Equal(t, []interface{}{"value1"}, awsutil.ValuesAtPath(data, "A[0].C")) + assert.Equal(t, []interface{}{"value2"}, awsutil.ValuesAtPath(data, "A[1].C")) + assert.Equal(t, []interface{}{"value3"}, awsutil.ValuesAtPath(data, "A[2].C")) + assert.Equal(t, []interface{}{"value3"}, awsutil.ValuesAtPath(data, "A[-1].C")) + assert.Equal(t, []interface{}{"value1", "value2", "value3"}, awsutil.ValuesAtPath(data, "A[].C")) + assert.Equal(t, []interface{}{"terminal"}, awsutil.ValuesAtPath(data, "B . B . C")) + assert.Equal(t, []interface{}{"terminal", "terminal2"}, awsutil.ValuesAtPath(data, "B.*.C")) + assert.Equal(t, []interface{}{"initial"}, awsutil.ValuesAtPath(data, "A.D.X || C")) +} + +func TestValueAtPathFailure(t *testing.T) { + assert.Equal(t, []interface{}(nil), awsutil.ValuesAtPath(data, "C.x")) + assert.Equal(t, []interface{}(nil), awsutil.ValuesAtPath(data, ".x")) + assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(data, "X.Y.Z")) + assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(data, "A[100].C")) + assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(data, "A[3].C")) + assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(data, "B.B.C.Z")) + assert.Equal(t, []interface{}(nil), awsutil.ValuesAtPath(data, "a[-1].C")) + assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(nil, "A.B.C")) +} + +func TestSetValueAtPathSuccess(t *testing.T) { + var s Struct + awsutil.SetValueAtPath(&s, "C", "test1") + awsutil.SetValueAtPath(&s, "B.B.C", "test2") + awsutil.SetValueAtPath(&s, "B.D.C", "test3") + assert.Equal(t, "test1", s.C) + assert.Equal(t, "test2", s.B.B.C) + assert.Equal(t, "test3", s.B.D.C) + + awsutil.SetValueAtPath(&s, "B.*.C", "test0") + assert.Equal(t, "test0", s.B.B.C) + assert.Equal(t, "test0", s.B.D.C) +} diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/awsutil/string_value.go b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/awsutil/string_value.go new file mode 100644 index 000000000..2e90f8da4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/awsutil/string_value.go @@ -0,0 +1,88 @@ +package awsutil + +import ( + "bytes" + "fmt" + "reflect" + "strings" +) + +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()) + } +} diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/config.go b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/config.go new file mode 100644 index 000000000..6cc6da42e --- /dev/null +++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/config.go @@ -0,0 +1,101 @@ +package aws + +import ( + "io" + "net/http" + "os" +) + +const DEFAULT_RETRIES = -1 + +var DefaultConfig = &Config{ + Credentials: DefaultCreds(), + Endpoint: "", + Region: os.Getenv("AWS_REGION"), + DisableSSL: false, + ManualSend: false, + HTTPClient: http.DefaultClient, + LogLevel: 0, + Logger: os.Stdout, + MaxRetries: DEFAULT_RETRIES, + DisableParamValidation: false, +} + +type Config struct { + Credentials CredentialsProvider + Endpoint string + Region string + DisableSSL bool + ManualSend bool + HTTPClient *http.Client + LogLevel uint + Logger io.Writer + MaxRetries int + DisableParamValidation bool +} + +func (c Config) Merge(newcfg *Config) *Config { + cfg := Config{} + + if newcfg != nil && newcfg.Credentials != nil { + cfg.Credentials = newcfg.Credentials + } else { + cfg.Credentials = c.Credentials + } + + if newcfg != nil && newcfg.Endpoint != "" { + cfg.Endpoint = newcfg.Endpoint + } else { + cfg.Endpoint = c.Endpoint + } + + if newcfg != nil && newcfg.Region != "" { + cfg.Region = newcfg.Region + } else { + cfg.Region = c.Region + } + + if newcfg != nil && newcfg.DisableSSL { + cfg.DisableSSL = newcfg.DisableSSL + } else { + cfg.DisableSSL = c.DisableSSL + } + + if newcfg != nil && newcfg.ManualSend { + cfg.ManualSend = newcfg.ManualSend + } else { + cfg.ManualSend = c.ManualSend + } + + if newcfg != nil && newcfg.HTTPClient != nil { + cfg.HTTPClient = newcfg.HTTPClient + } else { + cfg.HTTPClient = c.HTTPClient + } + + if newcfg != nil && newcfg.LogLevel != 0 { + cfg.LogLevel = newcfg.LogLevel + } else { + cfg.LogLevel = c.LogLevel + } + + if newcfg != nil && newcfg.Logger != nil { + cfg.Logger = newcfg.Logger + } else { + cfg.Logger = c.Logger + } + + if newcfg != nil && newcfg.MaxRetries != DEFAULT_RETRIES { + cfg.MaxRetries = newcfg.MaxRetries + } else { + cfg.MaxRetries = c.MaxRetries + } + + if newcfg != nil && newcfg.DisableParamValidation { + cfg.DisableParamValidation = newcfg.DisableParamValidation + } else { + cfg.DisableParamValidation = c.DisableParamValidation + } + + return &cfg +} diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/credentials.go b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/credentials.go new file mode 100644 index 000000000..4490c674a --- /dev/null +++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/credentials.go @@ -0,0 +1,288 @@ +package aws + +import ( + "bufio" + "encoding/json" + "errors" + "fmt" + "net/http" + "os" + "path/filepath" + "sync" + "time" + + "github.com/vaughan0/go-ini" +) + +var currentTime = time.Now + +// Credentials are used to authenticate and authorize calls that you make to +// AWS. +type Credentials struct { + AccessKeyID string + SecretAccessKey string + SessionToken string +} + +// A CredentialsProvider is a provider of credentials. +type CredentialsProvider interface { + // Credentials returns a set of credentials (or an error if no credentials + // could be provided). + Credentials() (*Credentials, error) +} + +var ( + // ErrAccessKeyIDNotFound is returned when the AWS Access Key ID can't be + // found in the process's environment. + ErrAccessKeyIDNotFound = fmt.Errorf("AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY not found in environment") + // ErrSecretAccessKeyNotFound is returned when the AWS Secret Access Key + // can't be found in the process's environment. + ErrSecretAccessKeyNotFound = fmt.Errorf("AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY not found in environment") +) + +type DefaultCredentialsProvider struct { +} + +func (p *DefaultCredentialsProvider) Credentials() (*Credentials, error) { + env, err := EnvCreds() + if err == nil { + return env.Credentials() + } + + profile, err := ProfileCreds("", "", 10*time.Minute) + if err == nil { + profileCreds, err := profile.Credentials() + if err == nil { + return profileCreds, nil + } + } + + return IAMCreds().Credentials() +} + +func DefaultCreds() CredentialsProvider { + return &DefaultCredentialsProvider{} +} + +// DetectCreds returns a CredentialsProvider based on the available information. +// +// If the access key ID and secret access key are provided, it returns a basic +// provider. +// +// If credentials are available via environment variables, it returns an +// environment provider. +// +// If a profile configuration file is available in the default location and has +// a default profile configured, it returns a profile provider. +// +// Otherwise, it returns an IAM instance provider. +func DetectCreds(accessKeyID, secretAccessKey, sessionToken string) CredentialsProvider { + if accessKeyID != "" && secretAccessKey != "" { + return Creds(accessKeyID, secretAccessKey, sessionToken) + } + + env, err := EnvCreds() + if err == nil { + return env + } + + profile, err := ProfileCreds("", "", 10*time.Minute) + if err != nil { + return IAMCreds() + } + + _, err = profile.Credentials() + if err != nil { + return IAMCreds() + } + + return profile +} + +// EnvCreds returns a static provider of AWS credentials from the process's +// environment, or an error if none are found. +func EnvCreds() (CredentialsProvider, error) { + id := os.Getenv("AWS_ACCESS_KEY_ID") + if id == "" { + id = os.Getenv("AWS_ACCESS_KEY") + } + + secret := os.Getenv("AWS_SECRET_ACCESS_KEY") + if secret == "" { + secret = os.Getenv("AWS_SECRET_KEY") + } + + if id == "" { + return nil, ErrAccessKeyIDNotFound + } + + if secret == "" { + return nil, ErrSecretAccessKeyNotFound + } + + return Creds(id, secret, os.Getenv("AWS_SESSION_TOKEN")), nil +} + +// Creds returns a static provider of credentials. +func Creds(accessKeyID, secretAccessKey, sessionToken string) CredentialsProvider { + return staticCredentialsProvider{ + creds: Credentials{ + AccessKeyID: accessKeyID, + SecretAccessKey: secretAccessKey, + SessionToken: sessionToken, + }, + } +} + +// IAMCreds returns a provider which pulls credentials from the local EC2 +// instance's IAM roles. +func IAMCreds() CredentialsProvider { + return &iamProvider{} +} + +// ProfileCreds returns a provider which pulls credentials from the profile +// configuration file. +func ProfileCreds(filename, profile string, expiry time.Duration) (CredentialsProvider, error) { + if filename == "" { + homeDir := os.Getenv("HOME") // *nix + if homeDir == "" { // Windows + homeDir = os.Getenv("USERPROFILE") + } + if homeDir == "" { + return nil, errors.New("User home directory not found.") + } + + filename = filepath.Join(homeDir, ".aws", "credentials") + } + + if profile == "" { + profile = "default" + } + + return &profileProvider{ + filename: filename, + profile: profile, + expiry: expiry, + }, nil +} + +type profileProvider struct { + filename string + profile string + expiry time.Duration + + creds Credentials + m sync.Mutex + expiration time.Time +} + +func (p *profileProvider) Credentials() (*Credentials, error) { + p.m.Lock() + defer p.m.Unlock() + + if p.expiration.After(currentTime()) { + return &p.creds, nil + } + + config, err := ini.LoadFile(p.filename) + if err != nil { + return nil, err + } + profile := config.Section(p.profile) + + accessKeyID, ok := profile["aws_access_key_id"] + if !ok { + return nil, fmt.Errorf("profile %s in %s did not contain aws_access_key_id", p.profile, p.filename) + } + + secretAccessKey, ok := profile["aws_secret_access_key"] + if !ok { + return nil, fmt.Errorf("profile %s in %s did not contain aws_secret_access_key", p.profile, p.filename) + } + + sessionToken := profile["aws_session_token"] + + p.creds = Credentials{ + AccessKeyID: accessKeyID, + SecretAccessKey: secretAccessKey, + SessionToken: sessionToken, + } + p.expiration = currentTime().Add(p.expiry) + + return &p.creds, nil +} + +type iamProvider struct { + creds Credentials + m sync.Mutex + expiration time.Time +} + +var metadataCredentialsEndpoint = "http://169.254.169.254/latest/meta-data/iam/security-credentials/" + +// IAMClient is the HTTP client used to query the metadata endpoint for IAM +// credentials. +var IAMClient = http.Client{ + Timeout: 1 * time.Second, +} + +func (p *iamProvider) Credentials() (*Credentials, error) { + p.m.Lock() + defer p.m.Unlock() + + if p.expiration.After(currentTime()) { + return &p.creds, nil + } + + var body struct { + Expiration time.Time + AccessKeyID string + SecretAccessKey string + Token string + } + + resp, err := IAMClient.Get(metadataCredentialsEndpoint) + if err != nil { + return nil, fmt.Errorf("listing IAM credentials") + } + defer func() { + _ = resp.Body.Close() + }() + + // Take the first line of the body of the metadata endpoint + s := bufio.NewScanner(resp.Body) + if !s.Scan() { + return nil, fmt.Errorf("unable to find default IAM credentials") + } else if s.Err() != nil { + return nil, fmt.Errorf("%s listing IAM credentials", s.Err()) + } + + resp, err = IAMClient.Get(metadataCredentialsEndpoint + s.Text()) + if err != nil { + return nil, fmt.Errorf("getting %s IAM credentials", s.Text()) + } + defer func() { + _ = resp.Body.Close() + }() + + if err := json.NewDecoder(resp.Body).Decode(&body); err != nil { + return nil, fmt.Errorf("decoding %s IAM credentials", s.Text()) + } + + p.creds = Credentials{ + AccessKeyID: body.AccessKeyID, + SecretAccessKey: body.SecretAccessKey, + SessionToken: body.Token, + } + p.expiration = body.Expiration + + return &p.creds, nil +} + +type staticCredentialsProvider struct { + creds Credentials +} + +func (p staticCredentialsProvider) Credentials() (*Credentials, error) { + return &p.creds, nil +} diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/credentials_test.go b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/credentials_test.go new file mode 100644 index 000000000..3143cebd6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/credentials_test.go @@ -0,0 +1,236 @@ +package aws + +import ( + "fmt" + "net/http" + "net/http/httptest" + "os" + "testing" + "time" +) + +func TestEnvCreds(t *testing.T) { + os.Clearenv() + os.Setenv("AWS_ACCESS_KEY_ID", "access") + os.Setenv("AWS_SECRET_ACCESS_KEY", "secret") + os.Setenv("AWS_SESSION_TOKEN", "token") + + prov, err := EnvCreds() + if err != nil { + t.Fatal(err) + } + + creds, err := prov.Credentials() + if err != nil { + t.Fatal(err) + } + + if v, want := creds.AccessKeyID, "access"; v != want { + t.Errorf("Access key ID was %v, expected %v", v, want) + } + + if v, want := creds.SecretAccessKey, "secret"; v != want { + t.Errorf("Secret access key was %v, expected %v", v, want) + } + + if v, want := creds.SessionToken, "token"; v != want { + t.Errorf("Security token was %v, expected %v", v, want) + } +} + +func TestEnvCredsNoAccessKeyID(t *testing.T) { + os.Clearenv() + os.Setenv("AWS_SECRET_ACCESS_KEY", "secret") + + prov, err := EnvCreds() + if err != ErrAccessKeyIDNotFound { + t.Fatalf("ErrAccessKeyIDNotFound expected, but was %#v/%#v", prov, err) + } +} + +func TestEnvCredsNoSecretAccessKey(t *testing.T) { + os.Clearenv() + os.Setenv("AWS_ACCESS_KEY_ID", "access") + + prov, err := EnvCreds() + if err != ErrSecretAccessKeyNotFound { + t.Fatalf("ErrSecretAccessKeyNotFound expected, but was %#v/%#v", prov, err) + } +} + +func TestEnvCredsAlternateNames(t *testing.T) { + os.Clearenv() + os.Setenv("AWS_ACCESS_KEY", "access") + os.Setenv("AWS_SECRET_KEY", "secret") + + prov, err := EnvCreds() + if err != nil { + t.Fatal(err) + } + + creds, err := prov.Credentials() + if err != nil { + t.Fatal(err) + } + + if v, want := creds.AccessKeyID, "access"; v != want { + t.Errorf("Access key ID was %v, expected %v", v, want) + } + + if v, want := creds.SecretAccessKey, "secret"; v != want { + t.Errorf("Secret access key was %v, expected %v", v, want) + } +} + +func TestIAMCreds(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.RequestURI == "/" { + fmt.Fprintln(w, "/creds") + } else { + fmt.Fprintln(w, `{ + "AccessKeyId" : "accessKey", + "SecretAccessKey" : "secret", + "Token" : "token", + "Expiration" : "2014-12-16T01:51:37Z" +}`) + } + })) + defer server.Close() + + defer func(s string) { + metadataCredentialsEndpoint = s + }(metadataCredentialsEndpoint) + metadataCredentialsEndpoint = server.URL + + defer func() { + currentTime = time.Now + }() + currentTime = func() time.Time { + return time.Date(2014, 12, 15, 21, 26, 0, 0, time.UTC) + } + + prov := IAMCreds() + creds, err := prov.Credentials() + if err != nil { + t.Fatal(err) + } + + if v, want := creds.AccessKeyID, "accessKey"; v != want { + t.Errorf("AcccessKeyID was %v, but expected %v", v, want) + } + + if v, want := creds.SecretAccessKey, "secret"; v != want { + t.Errorf("SecretAccessKey was %v, but expected %v", v, want) + } + + if v, want := creds.SessionToken, "token"; v != want { + t.Errorf("SessionToken was %v, but expected %v", v, want) + } +} + +func TestProfileCreds(t *testing.T) { + prov, err := ProfileCreds("example.ini", "", 10*time.Minute) + if err != nil { + t.Fatal(err) + } + + creds, err := prov.Credentials() + if err != nil { + t.Fatal(err) + } + + if v, want := creds.AccessKeyID, "accessKey"; v != want { + t.Errorf("AcccessKeyID was %v, but expected %v", v, want) + } + + if v, want := creds.SecretAccessKey, "secret"; v != want { + t.Errorf("SecretAccessKey was %v, but expected %v", v, want) + } + + if v, want := creds.SessionToken, "token"; v != want { + t.Errorf("SessionToken was %v, but expected %v", v, want) + } +} + +func TestProfileCredsWithoutToken(t *testing.T) { + prov, err := ProfileCreds("example.ini", "no_token", 10*time.Minute) + if err != nil { + t.Fatal(err) + } + + creds, err := prov.Credentials() + if err != nil { + t.Fatal(err) + } + + if v, want := creds.AccessKeyID, "accessKey"; v != want { + t.Errorf("AcccessKeyID was %v, but expected %v", v, want) + } + + if v, want := creds.SecretAccessKey, "secret"; v != want { + t.Errorf("SecretAccessKey was %v, but expected %v", v, want) + } + + if v, want := creds.SessionToken, ""; v != want { + t.Errorf("SessionToken was %v, but expected %v", v, want) + } +} + +func BenchmarkProfileCreds(b *testing.B) { + prov, err := ProfileCreds("example.ini", "", 10*time.Minute) + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _, err := prov.Credentials() + if err != nil { + b.Fatal(err) + } + } + }) +} + +func BenchmarkIAMCreds(b *testing.B) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.RequestURI == "/" { + fmt.Fprintln(w, "/creds") + } else { + fmt.Fprintln(w, `{ + "AccessKeyId" : "accessKey", + "SecretAccessKey" : "secret", + "Token" : "token", + "Expiration" : "2014-12-16T01:51:37Z" +}`) + } + })) + defer server.Close() + + defer func(s string) { + metadataCredentialsEndpoint = s + }(metadataCredentialsEndpoint) + metadataCredentialsEndpoint = server.URL + + defer func() { + currentTime = time.Now + }() + currentTime = func() time.Time { + return time.Date(2014, 12, 15, 21, 26, 0, 0, time.UTC) + } + + b.ResetTimer() + + prov := IAMCreds() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _, err := prov.Credentials() + if err != nil { + b.Fatal(err) + } + } + }) +} diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/error.go b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/error.go new file mode 100644 index 000000000..6b8989911 --- /dev/null +++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/error.go @@ -0,0 +1,26 @@ +package aws + +import "time" + +// An APIError is an error returned by an AWS API. +type APIError struct { + StatusCode int // HTTP status code e.g. 200 + Code string + Message string + RequestID string + Retryable bool + RetryDelay time.Duration + RetryCount uint +} + +func (e APIError) Error() string { + return e.Message +} + +func Error(e error) *APIError { + if err, ok := e.(APIError); ok { + return &err + } else { + return nil + } +} diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/example.ini b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/example.ini new file mode 100644 index 000000000..aa2dc506a --- /dev/null +++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/example.ini @@ -0,0 +1,8 @@ +[default] +aws_access_key_id = accessKey +aws_secret_access_key = secret +aws_session_token = token + +[no_token] +aws_access_key_id = accessKey +aws_secret_access_key = secret diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/handler_functions.go b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/handler_functions.go new file mode 100644 index 000000000..4de0f4a11 --- /dev/null +++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/handler_functions.go @@ -0,0 +1,78 @@ +package aws + +import ( + "fmt" + "io" + "time" +) + +var sleepDelay = func(delay time.Duration) { + time.Sleep(delay) +} + +type lener interface { + Len() int +} + +func BuildContentLength(r *Request) { + if r.HTTPRequest.Header.Get("Content-Length") != "" { + return + } + + var length int64 + switch body := r.Body.(type) { + case nil: + length = 0 + case lener: + length = int64(body.Len()) + case io.Seeker: + cur, _ := body.Seek(0, 1) + end, _ := body.Seek(0, 2) + body.Seek(cur, 0) // make sure to seek back to original location + length = end - cur + default: + panic("Cannot get length of body, must provide `ContentLength`") + } + + r.HTTPRequest.ContentLength = length + r.HTTPRequest.Header.Set("Content-Length", fmt.Sprintf("%d", length)) +} + +func UserAgentHandler(r *Request) { + r.HTTPRequest.Header.Set("User-Agent", SDKName+"/"+SDKVersion) +} + +func SendHandler(r *Request) { + r.HTTPResponse, r.Error = r.Service.Config.HTTPClient.Do(r.HTTPRequest) +} + +func ValidateResponseHandler(r *Request) { + if r.HTTPResponse.StatusCode == 0 || r.HTTPResponse.StatusCode >= 400 { + err := APIError{ + StatusCode: r.HTTPResponse.StatusCode, + RetryCount: r.RetryCount, + } + r.Error = err + err.Retryable = r.Service.ShouldRetry(r) + err.RetryDelay = r.Service.RetryRules(r) + r.Error = err + } +} + +func AfterRetryHandler(r *Request) { + delay := 0 * time.Second + willRetry := false + + if err := Error(r.Error); err != nil { + delay = err.RetryDelay + if err.Retryable && r.RetryCount < r.Service.MaxRetries() { + r.RetryCount++ + willRetry = true + } + } + + if willRetry { + r.Error = nil + sleepDelay(delay) + } +} diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/handlers.go b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/handlers.go new file mode 100644 index 000000000..f7c135fed --- /dev/null +++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/handlers.go @@ -0,0 +1,65 @@ +package aws + +import "container/list" + +type Handlers struct { + Validate HandlerList + Build HandlerList + Sign HandlerList + Send HandlerList + ValidateResponse HandlerList + Unmarshal HandlerList + UnmarshalMeta HandlerList + UnmarshalError HandlerList + Retry HandlerList + AfterRetry HandlerList +} + +func (h *Handlers) copy() Handlers { + return Handlers{ + Validate: h.Validate.copy(), + Build: h.Build.copy(), + Sign: h.Sign.copy(), + Send: h.Send.copy(), + ValidateResponse: h.ValidateResponse.copy(), + Unmarshal: h.Unmarshal.copy(), + UnmarshalError: h.UnmarshalError.copy(), + UnmarshalMeta: h.UnmarshalMeta.copy(), + Retry: h.Retry.copy(), + AfterRetry: h.AfterRetry.copy(), + } +} + +// Clear removes callback functions for all handlers +func (h *Handlers) Clear() { + h.Validate.Init() + h.Build.Init() + h.Send.Init() + h.Sign.Init() + h.Unmarshal.Init() + h.UnmarshalMeta.Init() + h.UnmarshalError.Init() + h.ValidateResponse.Init() + h.Retry.Init() + h.AfterRetry.Init() +} + +type HandlerList struct { + list.List +} + +func (l HandlerList) copy() HandlerList { + var n HandlerList + for e := l.Front(); e != nil; e = e.Next() { + h := e.Value.(func(*Request)) + n.PushBack(h) + } + return n +} + +func (l *HandlerList) Run(r *Request) { + for e := l.Front(); e != nil; e = e.Next() { + h := e.Value.(func(*Request)) + h(r) + } +} diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/handlers_test.go b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/handlers_test.go new file mode 100644 index 000000000..89e87cc49 --- /dev/null +++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/handlers_test.go @@ -0,0 +1,24 @@ +package aws + +import "testing" + +func TestHandlerList(t *testing.T) { + r := &Request{} + l := HandlerList{} + l.PushBack(func(r *Request) { r.Data = Boolean(true) }) + l.Run(r) + if r.Data == nil { + t.Error("Expected handler to execute") + } +} + +func TestMultipleHandlers(t *testing.T) { + r := &Request{} + l := HandlerList{} + l.PushBack(func(r *Request) { r.Data = Boolean(true) }) + l.PushBack(func(r *Request) { r.Data = nil }) + l.Run(r) + if r.Data != nil { + t.Error("Expected handler to execute") + } +} diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/param_validator.go b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/param_validator.go new file mode 100644 index 000000000..e1cb5cea5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/param_validator.go @@ -0,0 +1,80 @@ +package aws + +import ( + "fmt" + "reflect" + "strings" +) + +func ValidateParameters(r *Request) { + if r.ParamsFilled() { + v := validator{errors: []string{}} + v.validateAny(reflect.ValueOf(r.Params), "") + + if count := len(v.errors); count > 0 { + format := "%d validation errors:\n- %s" + msg := fmt.Sprintf(format, count, strings.Join(v.errors, "\n- ")) + r.Error = APIError{Code: "InvalidParameter", Message: msg} + } + } +} + +type validator struct { + errors []string +} + +func (v *validator) validateAny(value reflect.Value, path string) { + value = reflect.Indirect(value) + if !value.IsValid() { + return + } + + switch value.Kind() { + case reflect.Struct: + v.validateStruct(value, path) + case reflect.Slice: + for i := 0; i < value.Len(); i++ { + v.validateAny(value.Index(i), path+fmt.Sprintf("[%d]", i)) + } + case reflect.Map: + for _, n := range value.MapKeys() { + v.validateAny(value.MapIndex(n), path+fmt.Sprintf("[%q]", n.String())) + } + } +} + +func (v *validator) validateStruct(value reflect.Value, path string) { + prefix := "." + if path == "" { + prefix = "" + } + + for i := 0; i < value.Type().NumField(); i++ { + f := value.Type().Field(i) + if strings.ToLower(f.Name[0:1]) == f.Name[0:1] { + continue + } + fvalue := value.FieldByName(f.Name) + + notset := false + if f.Tag.Get("required") != "" { + switch fvalue.Kind() { + case reflect.Ptr, reflect.Slice: + if fvalue.IsNil() { + notset = true + } + default: + if !fvalue.IsValid() { + notset = true + } + } + } + + if notset { + msg := "missing required parameter: " + path + prefix + f.Name + v.errors = append(v.errors, msg) + } else { + v.validateAny(fvalue, path+prefix+f.Name) + } + } +} diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/param_validator_test.go b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/param_validator_test.go new file mode 100644 index 000000000..08deca15a --- /dev/null +++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/param_validator_test.go @@ -0,0 +1,85 @@ +package aws_test + +import ( + "testing" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/stretchr/testify/assert" +) + +var service = func() *aws.Service { + s := &aws.Service{ + Config: &aws.Config{}, + ServiceName: "mock-service", + APIVersion: "2015-01-01", + } + return s +}() + +type StructShape struct { + RequiredList []*ConditionalStructShape `required:"true"` + RequiredMap *map[string]*ConditionalStructShape `required:"true"` + RequiredBool *bool `required:"true"` + OptionalStruct *ConditionalStructShape + + hiddenParameter *string + + metadataStructureShape +} + +type metadataStructureShape struct { + SDKShapeTraits bool +} + +type ConditionalStructShape struct { + Name *string `required:"true"` + SDKShapeTraits bool +} + +func TestNoErrors(t *testing.T) { + input := &StructShape{ + RequiredList: []*ConditionalStructShape{}, + RequiredMap: &map[string]*ConditionalStructShape{ + "key1": &ConditionalStructShape{Name: aws.String("Name")}, + "key2": &ConditionalStructShape{Name: aws.String("Name")}, + }, + RequiredBool: aws.Boolean(true), + OptionalStruct: &ConditionalStructShape{Name: aws.String("Name")}, + } + + req := aws.NewRequest(service, &aws.Operation{}, input, nil) + aws.ValidateParameters(req) + assert.NoError(t, req.Error) +} + +func TestMissingRequiredParameters(t *testing.T) { + input := &StructShape{} + req := aws.NewRequest(service, &aws.Operation{}, input, nil) + aws.ValidateParameters(req) + err := aws.Error(req.Error) + + assert.Error(t, err) + assert.Equal(t, "InvalidParameter", err.Code) + assert.Equal(t, "3 validation errors:\n- missing required parameter: RequiredList\n- missing required parameter: RequiredMap\n- missing required parameter: RequiredBool", err.Message) +} + +func TestNestedMissingRequiredParameters(t *testing.T) { + input := &StructShape{ + RequiredList: []*ConditionalStructShape{&ConditionalStructShape{}}, + RequiredMap: &map[string]*ConditionalStructShape{ + "key1": &ConditionalStructShape{Name: aws.String("Name")}, + "key2": &ConditionalStructShape{}, + }, + RequiredBool: aws.Boolean(true), + OptionalStruct: &ConditionalStructShape{}, + } + + req := aws.NewRequest(service, &aws.Operation{}, input, nil) + aws.ValidateParameters(req) + err := aws.Error(req.Error) + + assert.Error(t, err) + assert.Equal(t, "InvalidParameter", err.Code) + assert.Equal(t, "3 validation errors:\n- missing required parameter: RequiredList[0].Name\n- missing required parameter: RequiredMap[\"key2\"].Name\n- missing required parameter: OptionalStruct.Name", err.Message) + +} diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/request.go b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/request.go new file mode 100644 index 000000000..1c442b1cd --- /dev/null +++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/request.go @@ -0,0 +1,149 @@ +package aws + +import ( + "bytes" + "io" + "io/ioutil" + "net/http" + "net/url" + "reflect" + "time" +) + +type Request struct { + *Service + Handlers Handlers + Time time.Time + ExpireTime time.Duration + Operation *Operation + HTTPRequest *http.Request + HTTPResponse *http.Response + Body io.ReadSeeker + Params interface{} + Error error + Data interface{} + RequestID string + RetryCount uint + + built bool +} + +type Operation struct { + Name string + HTTPMethod string + HTTPPath string +} + +func NewRequest(service *Service, operation *Operation, params interface{}, data interface{}) *Request { + method := operation.HTTPMethod + if method == "" { + method = "POST" + } + p := operation.HTTPPath + if p == "" { + p = "/" + } + + httpReq, _ := http.NewRequest(method, "", nil) + httpReq.URL, _ = url.Parse(service.Endpoint + p) + + r := &Request{ + Service: service, + Handlers: service.Handlers.copy(), + Time: time.Now(), + ExpireTime: 0, + Operation: operation, + HTTPRequest: httpReq, + Body: nil, + Params: params, + Error: nil, + Data: data, + } + r.SetBufferBody([]byte{}) + + return r +} + +func (r *Request) ParamsFilled() bool { + return r.Params != nil && reflect.ValueOf(r.Params).Elem().IsValid() +} + +func (r *Request) DataFilled() bool { + return r.Data != nil && reflect.ValueOf(r.Data).Elem().IsValid() +} + +func (r *Request) SetBufferBody(buf []byte) { + r.SetReaderBody(bytes.NewReader(buf)) +} + +func (r *Request) SetReaderBody(reader io.ReadSeeker) { + r.HTTPRequest.Body = ioutil.NopCloser(reader) + r.Body = reader +} + +func (r *Request) Presign(expireTime time.Duration) (string, error) { + r.ExpireTime = expireTime + r.Sign() + if r.Error != nil { + return "", r.Error + } else { + return r.HTTPRequest.URL.String(), nil + } +} + +func (r *Request) Build() error { + if !r.built { + r.Error = nil + r.Handlers.Validate.Run(r) + if r.Error != nil { + return r.Error + } + r.Handlers.Build.Run(r) + r.built = true + } + + return r.Error +} + +func (r *Request) Sign() error { + r.Build() + if r.Error != nil { + return r.Error + } + + r.Handlers.Sign.Run(r) + return r.Error +} + +func (r *Request) Send() error { + r.Sign() + if r.Error != nil { + return r.Error + } + + for { + r.Handlers.Send.Run(r) + if r.Error != nil { + return r.Error + } + + r.Handlers.UnmarshalMeta.Run(r) + r.Handlers.ValidateResponse.Run(r) + if r.Error != nil { + r.Handlers.Retry.Run(r) + r.Handlers.AfterRetry.Run(r) + if r.Error != nil { + r.Handlers.UnmarshalError.Run(r) + return r.Error + } + continue + } + + r.Handlers.Unmarshal.Run(r) + if r.Error != nil { + return r.Error + } + + return nil + } +} diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/request_test.go b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/request_test.go new file mode 100644 index 000000000..b27b55067 --- /dev/null +++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/request_test.go @@ -0,0 +1,118 @@ +package aws + +import ( + "bytes" + "encoding/json" + "io" + "io/ioutil" + "net/http" + "reflect" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +type testData struct { + Data string +} + +func body(str string) io.ReadCloser { + return ioutil.NopCloser(bytes.NewReader([]byte(str))) +} + +func unmarshal(req *Request) { + defer req.HTTPResponse.Body.Close() + if req.Data != nil { + json.NewDecoder(req.HTTPResponse.Body).Decode(req.Data) + } + return +} + +func unmarshalError(req *Request) { + bodyBytes, err := ioutil.ReadAll(req.HTTPResponse.Body) + if err != nil { + req.Error = err + return + } + if len(bodyBytes) == 0 { + req.Error = APIError{ + StatusCode: req.HTTPResponse.StatusCode, + Message: req.HTTPResponse.Status, + } + return + } + var jsonErr jsonErrorResponse + if err := json.Unmarshal(bodyBytes, &jsonErr); err != nil { + req.Error = err + return + } + req.Error = APIError{ + StatusCode: req.HTTPResponse.StatusCode, + Code: jsonErr.Code, + Message: jsonErr.Message, + } +} + +type jsonErrorResponse struct { + Code string `json:"__type"` + Message string `json:"message"` +} + +func TestRequestRecoverRetry(t *testing.T) { + reqNum := 0 + reqs := []http.Response{ + http.Response{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)}, + http.Response{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)}, + http.Response{StatusCode: 200, Body: body(`{"data":"valid"}`)}, + } + + s := NewService(&Config{MaxRetries: -1}) + s.Handlers.Unmarshal.PushBack(unmarshal) + s.Handlers.UnmarshalError.PushBack(unmarshalError) + s.Handlers.Send.Init() // mock sending + s.Handlers.Send.PushBack(func(r *Request) { + r.HTTPResponse = &reqs[reqNum] + reqNum++ + }) + out := &testData{} + r := NewRequest(s, &Operation{Name: "Operation"}, nil, out) + err := r.Send() + assert.Nil(t, err) + assert.Equal(t, 2, int(r.RetryCount)) + assert.Equal(t, "valid", out.Data) +} + +func TestRequestExhaustRetries(t *testing.T) { + delays := []time.Duration{} + sleepDelay = func(delay time.Duration) { + delays = append(delays, delay) + } + + reqNum := 0 + reqs := []http.Response{ + http.Response{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)}, + http.Response{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)}, + http.Response{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)}, + http.Response{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)}, + } + + s := NewService(&Config{MaxRetries: -1}) + s.Handlers.Unmarshal.PushBack(unmarshal) + s.Handlers.UnmarshalError.PushBack(unmarshalError) + s.Handlers.Send.Init() // mock sending + s.Handlers.Send.PushBack(func(r *Request) { + r.HTTPResponse = &reqs[reqNum] + reqNum++ + }) + r := NewRequest(s, &Operation{Name: "Operation"}, nil, nil) + err := r.Send() + apiErr := Error(err) + assert.NotNil(t, err) + assert.NotNil(t, apiErr) + assert.Equal(t, 500, apiErr.StatusCode) + assert.Equal(t, "UnknownError", apiErr.Code) + assert.Equal(t, "An error occurred.", apiErr.Message) + assert.Equal(t, 3, int(r.RetryCount)) + assert.True(t, reflect.DeepEqual([]time.Duration{30 * time.Millisecond, 60 * time.Millisecond, 120 * time.Millisecond}, delays)) +} diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/service.go b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/service.go new file mode 100644 index 000000000..95d590b91 --- /dev/null +++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/service.go @@ -0,0 +1,142 @@ +package aws + +import ( + "fmt" + "math" + "net/http" + "net/http/httputil" + "regexp" + "time" + + "github.com/awslabs/aws-sdk-go/internal/endpoints" +) + +type Service struct { + Config *Config + Handlers Handlers + ManualSend bool + ServiceName string + APIVersion string + Endpoint string + JSONVersion string + TargetPrefix string + RetryRules func(*Request) time.Duration + ShouldRetry func(*Request) bool + DefaultMaxRetries uint +} + +var schemeRE = regexp.MustCompile("^([^:]+)://") + +func NewService(config *Config) *Service { + svc := &Service{Config: config} + svc.Initialize() + return svc +} + +func (s *Service) Initialize() { + if s.Config == nil { + s.Config = &Config{} + } + if s.Config.HTTPClient == nil { + s.Config.HTTPClient = http.DefaultClient + } + + if s.RetryRules == nil { + s.RetryRules = retryRules + } + + if s.ShouldRetry == nil { + s.ShouldRetry = shouldRetry + } + + s.DefaultMaxRetries = 3 + s.Handlers.Build.PushBack(UserAgentHandler) + s.Handlers.Sign.PushBack(BuildContentLength) + s.Handlers.Send.PushBack(SendHandler) + s.Handlers.AfterRetry.PushBack(AfterRetryHandler) + s.Handlers.ValidateResponse.PushBack(ValidateResponseHandler) + s.AddDebugHandlers() + s.buildEndpoint() + + if !s.Config.DisableParamValidation { + s.Handlers.Validate.PushBack(ValidateParameters) + } +} + +func (s *Service) buildEndpoint() { + if s.Config.Endpoint != "" { + s.Endpoint = s.Config.Endpoint + } else { + s.Endpoint = endpoints.EndpointForRegion(s.ServiceName, s.Config.Region) + } + + if !schemeRE.MatchString(s.Endpoint) { + scheme := "https" + if s.Config.DisableSSL { + scheme = "http" + } + s.Endpoint = scheme + "://" + s.Endpoint + } +} + +func (s *Service) AddDebugHandlers() { + out := s.Config.Logger + if s.Config.LogLevel == 0 { + return + } + + s.Handlers.Sign.PushBack(func(r *Request) { + dumpedBody, _ := httputil.DumpRequest(r.HTTPRequest, true) + + fmt.Fprintf(out, "=> [%s] %s.%s(%+v)\n", r.Time, + r.Service.ServiceName, r.Operation.Name, r.Params) + fmt.Fprintf(out, "---[ REQUEST PRE-SIGN ]------------------------------\n") + fmt.Fprintf(out, "%s\n", string(dumpedBody)) + fmt.Fprintf(out, "-----------------------------------------------------\n") + }) + s.Handlers.Send.PushFront(func(r *Request) { + dumpedBody, _ := httputil.DumpRequest(r.HTTPRequest, true) + + fmt.Fprintf(out, "---[ REQUEST POST-SIGN ]-----------------------------\n") + fmt.Fprintf(out, "%s\n", string(dumpedBody)) + fmt.Fprintf(out, "-----------------------------------------------------\n") + }) + s.Handlers.Send.PushBack(func(r *Request) { + fmt.Fprintf(out, "---[ RESPONSE ]--------------------------------------\n") + if r.HTTPResponse != nil { + dumpedBody, _ := httputil.DumpResponse(r.HTTPResponse, true) + fmt.Fprintf(out, "%s\n", string(dumpedBody)) + } else if r.Error != nil { + fmt.Fprintf(out, "%s\n", r.Error) + } + fmt.Fprintf(out, "-----------------------------------------------------\n") + }) +} + +func (s *Service) MaxRetries() uint { + if s.Config.MaxRetries < 0 { + return s.DefaultMaxRetries + } else { + return uint(s.Config.MaxRetries) + } +} + +func retryRules(r *Request) time.Duration { + delay := time.Duration(math.Pow(2, float64(r.RetryCount))) * 30 + return delay * time.Millisecond +} + +func shouldRetry(r *Request) bool { + if err := Error(r.Error); err != nil { + if err.StatusCode >= 500 { + return true + } + + switch err.Code { + case "ExpiredTokenException": + case "ProvisionedThroughputExceededException", "Throttling": + return true + } + } + return false +} diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/types.go b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/types.go new file mode 100644 index 000000000..5da09114c --- /dev/null +++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/types.go @@ -0,0 +1,63 @@ +package aws + +import ( + "io" + "time" +) + +// String converts a Go string into a string pointer. +func String(v string) *string { + return &v +} + +// Boolean converts a Go bool into a boolean pointer. +func Boolean(v bool) *bool { + return &v +} + +// Long converts a Go int64 into a long pointer. +func Long(v int64) *int64 { + return &v +} + +// Double converts a Go float64 into a double pointer. +func Double(v float64) *float64 { + return &v +} + +// Time converts a Go Time into a Time pointer +func Time(t time.Time) *time.Time { + return &t +} + +func ReadSeekCloser(r io.Reader) ReaderSeekerCloser { + return ReaderSeekerCloser{r} +} + +type ReaderSeekerCloser struct { + r io.Reader +} + +func (r ReaderSeekerCloser) Read(p []byte) (int, error) { + switch t := r.r.(type) { + case io.Reader: + return t.Read(p) + } + return 0, nil +} + +func (r ReaderSeekerCloser) Seek(offset int64, whence int) (int64, error) { + switch t := r.r.(type) { + case io.Seeker: + return t.Seek(offset, whence) + } + return int64(0), nil +} + +func (r ReaderSeekerCloser) Close() error { + switch t := r.r.(type) { + case io.Closer: + return t.Close() + } + return nil +} diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/version.go b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/version.go new file mode 100644 index 000000000..f673d470a --- /dev/null +++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/version.go @@ -0,0 +1,5 @@ +// Package aws provides core functionality for making requests to AWS services. +package aws + +const SDKName = "aws-sdk-go" +const SDKVersion = "0.5.0" |