state/remote/swift: Add support for versioning state file in swift, and expiring object after period of time.

Update website docs with changes
This commit is contained in:
Gavin Williams 2016-11-11 15:13:10 +00:00
parent 73e191442d
commit c77ed69a4e
2 changed files with 79 additions and 17 deletions

View File

@ -11,6 +11,8 @@ import (
"net/http" "net/http"
"os" "os"
"strconv" "strconv"
"strings"
"time"
"github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack"
@ -22,21 +24,24 @@ const TFSTATE_NAME = "tfstate.tf"
// SwiftClient implements the Client interface for an Openstack Swift server. // SwiftClient implements the Client interface for an Openstack Swift server.
type SwiftClient struct { type SwiftClient struct {
client *gophercloud.ServiceClient client *gophercloud.ServiceClient
authurl string authurl string
cacert string cacert string
cert string cert string
domainid string domainid string
domainname string domainname string
insecure bool insecure bool
key string key string
password string password string
path string path string
region string region string
tenantid string tenantid string
tenantname string tenantname string
userid string userid string
username string username string
archive bool
archivepath string
expireSecs int
} }
func swiftFactory(conf map[string]string) (Client, error) { func swiftFactory(conf map[string]string) (Client, error) {
@ -131,6 +136,35 @@ func (c *SwiftClient) validateConfig(conf map[string]string) (err error) {
} }
c.path = path c.path = path
if archivepath, ok := conf["archive_path"]; ok {
log.Printf("[DEBUG] Archivepath set, enabling object versioning")
c.archive = true
c.archivepath = archivepath
}
if expire, ok := conf["expire_after"]; ok {
log.Printf("[DEBUG] Requested that remote state expires after %s", expire)
if strings.HasSuffix(expire, "d") {
log.Printf("[DEBUG] Got a days expire after duration. Converting to hours")
days, err := strconv.Atoi(expire[:len(expire)-1])
if err != nil {
return fmt.Errorf("Error converting expire_after value %s to int: %s", expire, err)
}
expire = fmt.Sprintf("%dh", days*24)
log.Printf("[DEBUG] Expire after %s hours", expire)
}
expireDur, err := time.ParseDuration(expire)
if err != nil {
log.Printf("[DEBUG] Error parsing duration %s: %s", expire, err)
return fmt.Errorf("Error parsing expire_after duration '%s': %s", expire, err)
}
log.Printf("[DEBUG] Seconds duration = %d", int(expireDur.Seconds()))
c.expireSecs = int(expireDur.Seconds())
}
c.insecure = false c.insecure = false
raw, ok := conf["insecure"] raw, ok := conf["insecure"]
if !ok { if !ok {
@ -252,10 +286,17 @@ func (c *SwiftClient) Put(data []byte) error {
return err return err
} }
log.Printf("[DEBUG] Creating object %s at path %s", TFSTATE_NAME, c.path)
reader := bytes.NewReader(data) reader := bytes.NewReader(data)
createOpts := objects.CreateOpts{ createOpts := objects.CreateOpts{
Content: reader, Content: reader,
} }
if c.expireSecs != 0 {
log.Printf("[DEBUG] ExpireSecs = %d", c.expireSecs)
createOpts.DeleteAfter = c.expireSecs
}
result := objects.Create(c.client, c.path, TFSTATE_NAME, createOpts) result := objects.Create(c.client, c.path, TFSTATE_NAME, createOpts)
return result.Err return result.Err
@ -267,7 +308,22 @@ func (c *SwiftClient) Delete() error {
} }
func (c *SwiftClient) ensureContainerExists() error { func (c *SwiftClient) ensureContainerExists() error {
result := containers.Create(c.client, c.path, nil) containerOpts := &containers.CreateOpts{}
if c.archive {
log.Printf("[DEBUG] Creating container %s", c.archivepath)
result := containers.Create(c.client, c.archivepath, nil)
if result.Err != nil {
log.Printf("[DEBUG] Error creating container %s: %s", c.archivepath, result.Err)
return result.Err
}
log.Printf("[DEBUG] Enabling Versioning on container %s", c.path)
containerOpts.VersionsLocation = c.archivepath
}
log.Printf("[DEBUG] Creating container %s", c.path)
result := containers.Create(c.client, c.path, containerOpts)
if result.Err != nil { if result.Err != nil {
return result.Err return result.Err
} }

View File

@ -80,4 +80,10 @@ The following configuration options are supported:
authentication. If omitted the `OS_CERT` environment variable is used. authentication. If omitted the `OS_CERT` environment variable is used.
* `key` - (Optional) Specify client private key file for SSL client * `key` - (Optional) Specify client private key file for SSL client
authentication. If omitted the `OS_KEY` environment variable is used. authentication. If omitted the `OS_KEY` environment variable is used.
* `archive_path` - (Optional) The path to store archived copied of `terraform.tfstate`.
If specified, Swift object versioning is enabled on the container created at `path`.
* `expire_after` - (Optional) How long should the `terraform.tfstate` created at `path`
be retained for? Supported durations: `m` - Minutes, `h` - Hours, `d` - Days.