terraform/backend/remote-state/s3/backend_state.go

113 lines
2.3 KiB
Go

package s3
import (
"fmt"
"sort"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/state"
"github.com/hashicorp/terraform/state/remote"
)
const (
// This will be used as directory name, the odd looking colon is simply to
// reduce the chance of name conflicts with existing objects.
keyEnvPrefix = "env:"
)
func (b *Backend) States() ([]string, error) {
params := &s3.ListObjectsInput{
Bucket: &b.bucketName,
Prefix: aws.String(keyEnvPrefix + "/"),
}
resp, err := b.s3Client.ListObjects(params)
if err != nil {
return nil, err
}
var envs []string
for _, obj := range resp.Contents {
env := keyEnv(*obj.Key)
if env != "" {
envs = append(envs, env)
}
}
sort.Strings(envs)
envs = append([]string{backend.DefaultStateName}, envs...)
return envs, nil
}
// extract the env name from the S3 key
func keyEnv(key string) string {
parts := strings.Split(key, "/")
if len(parts) < 3 {
// no env here
return ""
}
if parts[0] != keyEnvPrefix {
// not our key, so ignore
return ""
}
return parts[1]
}
func (b *Backend) DeleteState(name string) error {
if name == backend.DefaultStateName || name == "" {
return fmt.Errorf("can't delete default state")
}
params := &s3.DeleteObjectInput{
Bucket: &b.bucketName,
Key: aws.String(b.path(name)),
}
_, err := b.s3Client.DeleteObject(params)
if err != nil {
return err
}
return nil
}
func (b *Backend) State(name string) (state.State, error) {
client := &RemoteClient{
s3Client: b.s3Client,
dynClient: b.dynClient,
bucketName: b.bucketName,
path: b.path(name),
serverSideEncryption: b.serverSideEncryption,
acl: b.acl,
kmsKeyID: b.kmsKeyID,
lockTable: b.lockTable,
}
// if this isn't the default state name, we need to create the object so
// it's listed by States.
if name != backend.DefaultStateName {
if err := client.Put([]byte{}); err != nil {
return nil, err
}
}
return &remote.State{Client: client}, nil
}
func (b *Backend) client() *RemoteClient {
return &RemoteClient{}
}
func (b *Backend) path(name string) string {
if name == backend.DefaultStateName {
return b.keyName
}
return strings.Join([]string{keyEnvPrefix, name, b.keyName}, "/")
}