Skip to content

Commit

Permalink
cmd/scollector: Add AWS collector
Browse files Browse the repository at this point in the history
closes #767
  • Loading branch information
PeterGrace authored and maddyblue committed Mar 11, 2015
1 parent 753626a commit f035538
Show file tree
Hide file tree
Showing 25 changed files with 10,306 additions and 0 deletions.
270 changes: 270 additions & 0 deletions _third_party/github.com/awslabs/aws-sdk-go/aws/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
package aws

import (
"bufio"
"encoding/json"
"fmt"
"net/http"
"os"
"os/user"
"path"
"sync"
"time"

"bosun.org/_third_party/github.com/vaughan0/go-ini"
)

// Credentials are used to authenticate and authorize calls that you make to
// AWS.
type Credentials struct {
AccessKeyID string
SecretAccessKey string
SecurityToken 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")
)

// Context encapsulates the context of a client's connection to an AWS service.
type Context struct {
Service string
Region string
Credentials CredentialsProvider
}

var currentTime = func() time.Time {
return time.Now()
}

// 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, securityToken string) CredentialsProvider {
if accessKeyID != "" && secretAccessKey != "" {
return Creds(accessKeyID, secretAccessKey, securityToken)
}

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, securityToken string) CredentialsProvider {
return staticCredentialsProvider{
creds: Credentials{
AccessKeyID: accessKeyID,
SecretAccessKey: secretAccessKey,
SecurityToken: securityToken,
},
}
}

// 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 == "" {
u, err := user.Current()
if err != nil {
return nil, err
}

filename = path.Join(u.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)
}

securityToken := profile["aws_session_token"]

p.creds = Credentials{
AccessKeyID: accessKeyID,
SecretAccessKey: secretAccessKey,
SecurityToken: securityToken,
}
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,
SecurityToken: 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
}
Loading

0 comments on commit f035538

Please sign in to comment.