github.com/leeprovoost/terraform@v0.6.10-0.20160119085442-96f3f76118e7/state/remote/s3.go (about) 1 package remote 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "log" 8 "os" 9 "strconv" 10 11 "github.com/aws/aws-sdk-go/aws" 12 "github.com/aws/aws-sdk-go/aws/awserr" 13 "github.com/aws/aws-sdk-go/aws/credentials" 14 "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" 15 "github.com/aws/aws-sdk-go/aws/ec2metadata" 16 "github.com/aws/aws-sdk-go/aws/session" 17 "github.com/aws/aws-sdk-go/service/s3" 18 "github.com/hashicorp/go-cleanhttp" 19 ) 20 21 func s3Factory(conf map[string]string) (Client, error) { 22 bucketName, ok := conf["bucket"] 23 if !ok { 24 return nil, fmt.Errorf("missing 'bucket' configuration") 25 } 26 27 keyName, ok := conf["key"] 28 if !ok { 29 return nil, fmt.Errorf("missing 'key' configuration") 30 } 31 32 endpoint, ok := conf["endpoint"] 33 if !ok { 34 endpoint = os.Getenv("AWS_S3_ENDPOINT") 35 } 36 37 regionName, ok := conf["region"] 38 if !ok { 39 regionName = os.Getenv("AWS_DEFAULT_REGION") 40 if regionName == "" { 41 return nil, fmt.Errorf( 42 "missing 'region' configuration or AWS_DEFAULT_REGION environment variable") 43 } 44 } 45 46 serverSideEncryption := false 47 if raw, ok := conf["encrypt"]; ok { 48 v, err := strconv.ParseBool(raw) 49 if err != nil { 50 return nil, fmt.Errorf( 51 "'encrypt' field couldn't be parsed as bool: %s", err) 52 } 53 54 serverSideEncryption = v 55 } 56 57 acl := "" 58 if raw, ok := conf["acl"]; ok { 59 acl = raw 60 } 61 62 accessKeyId := conf["access_key"] 63 secretAccessKey := conf["secret_key"] 64 65 credentialsProvider := credentials.NewChainCredentials([]credentials.Provider{ 66 &credentials.StaticProvider{Value: credentials.Value{ 67 AccessKeyID: accessKeyId, 68 SecretAccessKey: secretAccessKey, 69 SessionToken: "", 70 }}, 71 &credentials.EnvProvider{}, 72 &credentials.SharedCredentialsProvider{Filename: "", Profile: ""}, 73 &ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(session.New())}, 74 }) 75 76 // Make sure we got some sort of working credentials. 77 _, err := credentialsProvider.Get() 78 if err != nil { 79 return nil, fmt.Errorf("Unable to determine AWS credentials. Set the AWS_ACCESS_KEY_ID and "+ 80 "AWS_SECRET_ACCESS_KEY environment variables.\n(error was: %s)", err) 81 } 82 83 awsConfig := &aws.Config{ 84 Credentials: credentialsProvider, 85 Endpoint: aws.String(endpoint), 86 Region: aws.String(regionName), 87 HTTPClient: cleanhttp.DefaultClient(), 88 } 89 sess := session.New(awsConfig) 90 nativeClient := s3.New(sess) 91 92 return &S3Client{ 93 nativeClient: nativeClient, 94 bucketName: bucketName, 95 keyName: keyName, 96 serverSideEncryption: serverSideEncryption, 97 acl: acl, 98 }, nil 99 } 100 101 type S3Client struct { 102 nativeClient *s3.S3 103 bucketName string 104 keyName string 105 serverSideEncryption bool 106 acl string 107 } 108 109 func (c *S3Client) Get() (*Payload, error) { 110 output, err := c.nativeClient.GetObject(&s3.GetObjectInput{ 111 Bucket: &c.bucketName, 112 Key: &c.keyName, 113 }) 114 115 if err != nil { 116 if awserr := err.(awserr.Error); awserr != nil { 117 if awserr.Code() == "NoSuchKey" { 118 return nil, nil 119 } else { 120 return nil, err 121 } 122 } else { 123 return nil, err 124 } 125 } 126 127 defer output.Body.Close() 128 129 buf := bytes.NewBuffer(nil) 130 if _, err := io.Copy(buf, output.Body); err != nil { 131 return nil, fmt.Errorf("Failed to read remote state: %s", err) 132 } 133 134 payload := &Payload{ 135 Data: buf.Bytes(), 136 } 137 138 // If there was no data, then return nil 139 if len(payload.Data) == 0 { 140 return nil, nil 141 } 142 143 return payload, nil 144 } 145 146 func (c *S3Client) Put(data []byte) error { 147 contentType := "application/json" 148 contentLength := int64(len(data)) 149 150 i := &s3.PutObjectInput{ 151 ContentType: &contentType, 152 ContentLength: &contentLength, 153 Body: bytes.NewReader(data), 154 Bucket: &c.bucketName, 155 Key: &c.keyName, 156 } 157 158 if c.serverSideEncryption { 159 i.ServerSideEncryption = aws.String("AES256") 160 } 161 162 if c.acl != "" { 163 i.ACL = aws.String(c.acl) 164 } 165 166 log.Printf("[DEBUG] Uploading remote state to S3: %#v", i) 167 168 if _, err := c.nativeClient.PutObject(i); err == nil { 169 return nil 170 } else { 171 return fmt.Errorf("Failed to upload state: %v", err) 172 } 173 } 174 175 func (c *S3Client) Delete() error { 176 _, err := c.nativeClient.DeleteObject(&s3.DeleteObjectInput{ 177 Bucket: &c.bucketName, 178 Key: &c.keyName, 179 }) 180 181 return err 182 }