github.com/stevenmatthewt/agent@v3.5.4+incompatible/agent/s3.go (about) 1 package agent 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/aws/credentials" 10 "github.com/aws/aws-sdk-go/aws/defaults" 11 "github.com/aws/aws-sdk-go/aws/endpoints" 12 "github.com/aws/aws-sdk-go/aws/session" 13 "github.com/aws/aws-sdk-go/service/s3" 14 "github.com/buildkite/agent/logger" 15 ) 16 17 type credentialsProvider struct { 18 retrieved bool 19 } 20 21 func (e *credentialsProvider) Retrieve() (creds credentials.Value, err error) { 22 e.retrieved = false 23 24 creds.AccessKeyID = os.Getenv("BUILDKITE_S3_ACCESS_KEY_ID") 25 if creds.AccessKeyID == "" { 26 creds.AccessKeyID = os.Getenv("BUILDKITE_S3_ACCESS_KEY") 27 } 28 29 creds.SecretAccessKey = os.Getenv("BUILDKITE_S3_SECRET_ACCESS_KEY") 30 if creds.SecretAccessKey == "" { 31 creds.SecretAccessKey = os.Getenv("BUILDKITE_S3_SECRET_KEY") 32 } 33 34 if creds.AccessKeyID == "" { 35 err = errors.New("BUILDKITE_S3_ACCESS_KEY_ID or BUILDKITE_S3_ACCESS_KEY not found in environment") 36 } 37 if creds.SecretAccessKey == "" { 38 err = errors.New("BUILDKITE_S3_SECRET_ACCESS_KEY or BUILDKITE_S3_SECRET_KEY not found in environment") 39 } 40 41 e.retrieved = true 42 return 43 } 44 45 func (e *credentialsProvider) IsExpired() bool { 46 return !e.retrieved 47 } 48 49 func awsS3RegionFromEnv() (region string, err error) { 50 regionName := "us-east-1" 51 if os.Getenv("BUILDKITE_S3_DEFAULT_REGION") != "" { 52 regionName = os.Getenv("BUILDKITE_S3_DEFAULT_REGION") 53 } else { 54 var err error 55 regionName, err = awsRegion() 56 if err != nil { 57 return "", err 58 } 59 } 60 61 // Check to make sure the region exists. 62 resolver := endpoints.DefaultResolver() 63 partitions := resolver.(endpoints.EnumPartitions).Partitions() 64 65 for _, p := range partitions { 66 for id := range p.Regions() { 67 if id == regionName { 68 return regionName, nil 69 } 70 } 71 } 72 73 return "", fmt.Errorf("Unknown AWS S3 Region %q", regionName) 74 } 75 76 func awsS3Session(region string) (*session.Session, error) { 77 // Chicken and egg... but this is kinda how they do it in the sdk 78 sess, err := session.NewSession() 79 if err != nil { 80 return nil, err 81 } 82 83 sess.Config.Region = aws.String(region) 84 85 sess.Config.Credentials = credentials.NewChainCredentials( 86 []credentials.Provider{ 87 &credentialsProvider{}, 88 &credentials.EnvProvider{}, 89 // EC2 and ECS meta-data providers 90 defaults.RemoteCredProvider(*sess.Config, sess.Handlers), 91 }) 92 93 return sess, nil 94 } 95 96 func newS3Client(bucket string) (*s3.S3, error) { 97 region, err := awsS3RegionFromEnv() 98 if err != nil { 99 return nil, err 100 } 101 102 sess, err := awsS3Session(region) 103 if err != nil { 104 return nil, err 105 } 106 107 logger.Debug("Authorizing S3 credentials and finding bucket `%s` in region `%s`...", bucket, region) 108 109 s3client := s3.New(sess) 110 111 // Test the authentication by trying to list the first 0 objects in the bucket. 112 _, err = s3client.ListObjects(&s3.ListObjectsInput{ 113 Bucket: aws.String(bucket), 114 MaxKeys: aws.Int64(0), 115 }) 116 if err != nil { 117 if err == credentials.ErrNoValidProvidersFoundInChain { 118 return nil, fmt.Errorf("Could not find a valid authentication strategy to connect to S3. Try setting BUILDKITE_S3_ACCESS_KEY and BUILDKITE_S3_SECRET_KEY") 119 } 120 return nil, fmt.Errorf("Failed to authenticate to bucket `%s` in region `%s` (%s)", bucket, region, err.Error()) 121 } 122 123 return s3client, nil 124 }