terraform/backend/remote-state/swift/client.go

116 lines
2.8 KiB
Go

package swift
import (
"bytes"
"crypto/md5"
"log"
"os"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers"
"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects"
"github.com/hashicorp/terraform/state/remote"
)
const (
TFSTATE_NAME = "tfstate.tf"
TFSTATE_LOCK_NAME = "tfstate.lock"
)
// RemoteClient implements the Client interface for an Openstack Swift server.
type RemoteClient struct {
client *gophercloud.ServiceClient
container string
archive bool
archiveContainer string
expireSecs int
}
func (c *RemoteClient) Get() (*remote.Payload, error) {
log.Printf("[DEBUG] Getting object %s in container %s", TFSTATE_NAME, c.container)
result := objects.Download(c.client, c.container, TFSTATE_NAME, nil)
// Extract any errors from result
_, err := result.Extract()
// 404 response is to be expected if the object doesn't already exist!
if _, ok := err.(gophercloud.ErrDefault404); ok {
log.Println("[DEBUG] Object doesn't exist to download.")
return nil, nil
}
bytes, err := result.ExtractContent()
if err != nil {
return nil, err
}
hash := md5.Sum(bytes)
payload := &remote.Payload{
Data: bytes,
MD5: hash[:md5.Size],
}
return payload, nil
}
func (c *RemoteClient) Put(data []byte) error {
if err := c.ensureContainerExists(); err != nil {
return err
}
log.Printf("[DEBUG] Putting object %s in container %s", TFSTATE_NAME, c.container)
reader := bytes.NewReader(data)
createOpts := objects.CreateOpts{
Content: reader,
}
if c.expireSecs != 0 {
log.Printf("[DEBUG] ExpireSecs = %d", c.expireSecs)
createOpts.DeleteAfter = c.expireSecs
}
result := objects.Create(c.client, c.container, TFSTATE_NAME, createOpts)
return result.Err
}
func (c *RemoteClient) Delete() error {
log.Printf("[DEBUG] Deleting object %s in container %s", TFSTATE_NAME, c.container)
result := objects.Delete(c.client, c.container, TFSTATE_NAME, nil)
return result.Err
}
func (c *RemoteClient) ensureContainerExists() error {
containerOpts := &containers.CreateOpts{}
if c.archive {
log.Printf("[DEBUG] Creating archive container %s", c.archiveContainer)
result := containers.Create(c.client, c.archiveContainer, nil)
if result.Err != nil {
log.Printf("[DEBUG] Error creating archive container %s: %s", c.archiveContainer, result.Err)
return result.Err
}
log.Printf("[DEBUG] Enabling Versioning on container %s", c.container)
containerOpts.VersionsLocation = c.archiveContainer
}
log.Printf("[DEBUG] Creating container %s", c.container)
result := containers.Create(c.client, c.container, containerOpts)
if result.Err != nil {
return result.Err
}
return nil
}
func multiEnv(ks []string) string {
for _, k := range ks {
if v := os.Getenv(k); v != "" {
return v
}
}
return ""
}