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