Merge pull request #2254 from hartzell/swift-remote

Rework devcamcar's OpenStack Swift remote for new style state/remote.
This commit is contained in:
Mitchell Hashimoto 2015-06-07 22:14:46 -07:00
commit 6836bd59e7
3 changed files with 146 additions and 0 deletions

View File

@ -40,6 +40,7 @@ var BuiltinClients = map[string]Factory{
"consul": consulFactory, "consul": consulFactory,
"http": httpFactory, "http": httpFactory,
"s3": s3Factory, "s3": s3Factory,
"swift": swiftFactory,
// This is used for development purposes only. // This is used for development purposes only.
"_local": fileFactory, "_local": fileFactory,

114
state/remote/swift.go Normal file
View File

@ -0,0 +1,114 @@
package remote
import (
"bytes"
"crypto/md5"
"fmt"
"os"
"strings"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack"
"github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
"github.com/rackspace/gophercloud/openstack/objectstorage/v1/objects"
)
const TFSTATE_NAME = "tfstate.tf"
// SwiftClient implements the Client interface for an Openstack Swift server.
type SwiftClient struct {
client *gophercloud.ServiceClient
path string
}
func swiftFactory(conf map[string]string) (Client, error) {
client := &SwiftClient{}
if err := client.validateConfig(conf); err != nil {
return nil, err
}
return client, nil
}
func (c *SwiftClient) validateConfig(conf map[string]string) (err error) {
if val := os.Getenv("OS_AUTH_URL"); val == "" {
return fmt.Errorf("missing OS_AUTH_URL environment variable")
}
if val := os.Getenv("OS_USERNAME"); val == "" {
return fmt.Errorf("missing OS_USERNAME environment variable")
}
if val := os.Getenv("OS_TENANT_NAME"); val == "" {
return fmt.Errorf("missing OS_TENANT_NAME environment variable")
}
if val := os.Getenv("OS_PASSWORD"); val == "" {
return fmt.Errorf("missing OS_PASSWORD environment variable")
}
path, ok := conf["path"]
if !ok || path == "" {
return fmt.Errorf("missing 'path' configuration")
}
provider, err := openstack.AuthenticatedClient(gophercloud.AuthOptions{
IdentityEndpoint: os.Getenv("OS_AUTH_URL"),
Username: os.Getenv("OS_USERNAME"),
TenantName: os.Getenv("OS_TENANT_NAME"),
Password: os.Getenv("OS_PASSWORD"),
})
if err != nil {
return err
}
c.path = path
c.client, err = openstack.NewObjectStorageV1(provider, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
return err
}
func (c *SwiftClient) Get() (*Payload, error) {
result := objects.Download(c.client, c.path, TFSTATE_NAME, nil)
bytes, err := result.ExtractContent()
if err != nil {
if strings.Contains(err.Error(), "but got 404 instead") {
return nil, nil
}
return nil, err
}
hash := md5.Sum(bytes)
payload := &Payload{
Data: bytes,
MD5: hash[:md5.Size],
}
return payload, nil
}
func (c *SwiftClient) Put(data []byte) error {
if err := c.ensureContainerExists(); err != nil {
return err
}
reader := bytes.NewReader(data)
result := objects.Create(c.client, c.path, TFSTATE_NAME, reader, nil)
return result.Err
}
func (c *SwiftClient) Delete() error {
result := objects.Delete(c.client, c.path, TFSTATE_NAME, nil)
return result.Err
}
func (c *SwiftClient) ensureContainerExists() error {
result := containers.Create(c.client, c.path, nil)
if result.Err != nil {
return result.Err
}
return nil
}

View File

@ -0,0 +1,31 @@
package remote
import (
"net/http"
"testing"
"os"
)
func TestSwiftClient_impl(t *testing.T) {
var _ Client = new(SwiftClient)
}
func TestSwiftClient(t *testing.T) {
os_auth_url := os.Getenv("OS_AUTH_URL")
if os_auth_url == "" {
t.Skipf("skipping, OS_AUTH_URL and friends must be set")
}
if _, err := http.Get(os_auth_url); err != nil {
t.Skipf("skipping, unable to reach %s: %s", os_auth_url, err)
}
client, err := swiftFactory(map[string]string{
"path": "swift_test",
})
if err != nil {
t.Fatalf("bad: %s", err)
}
testClient(t, client)
}