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  }