Etcd remote state backend
This commit is contained in:
parent
5930771615
commit
bb51882f33
|
@ -0,0 +1,78 @@
|
|||
package remote
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
|
||||
etcdapi "github.com/coreos/etcd/client"
|
||||
)
|
||||
|
||||
func etcdFactory(conf map[string]string) (Client, error) {
|
||||
path, ok := conf["path"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing 'path' configuration")
|
||||
}
|
||||
|
||||
endpoints, ok := conf["endpoints"]
|
||||
if !ok || endpoints == "" {
|
||||
return nil, fmt.Errorf("missing 'endpoints' configuration")
|
||||
}
|
||||
|
||||
config := etcdapi.Config{
|
||||
Endpoints: strings.Split(endpoints, " "),
|
||||
}
|
||||
if username, ok := conf["username"]; ok && username != "" {
|
||||
config.Username = username
|
||||
}
|
||||
if password, ok := conf["password"]; ok && password != "" {
|
||||
config.Password = password
|
||||
}
|
||||
|
||||
client, err := etcdapi.New(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &EtcdClient{
|
||||
Client: client,
|
||||
Path: path,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// EtcdClient is a remote client that stores data in etcd.
|
||||
type EtcdClient struct {
|
||||
Client etcdapi.Client
|
||||
Path string
|
||||
}
|
||||
|
||||
func (c *EtcdClient) Get() (*Payload, error) {
|
||||
resp, err := etcdapi.NewKeysAPI(c.Client).Get(context.Background(), c.Path, &etcdapi.GetOptions{Quorum: true})
|
||||
if err != nil {
|
||||
if err, ok := err.(etcdapi.Error); ok && err.Code == etcdapi.ErrorCodeKeyNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if resp.Node.Dir {
|
||||
return nil, fmt.Errorf("path is a directory")
|
||||
}
|
||||
|
||||
data := []byte(resp.Node.Value)
|
||||
md5 := md5.Sum(data)
|
||||
return &Payload{
|
||||
Data: data,
|
||||
MD5: md5[:],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *EtcdClient) Put(data []byte) error {
|
||||
_, err := etcdapi.NewKeysAPI(c.Client).Set(context.Background(), c.Path, string(data), nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *EtcdClient) Delete() error {
|
||||
_, err := etcdapi.NewKeysAPI(c.Client).Delete(context.Background(), c.Path, nil)
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package remote
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestEtcdClient_impl(t *testing.T) {
|
||||
var _ Client = new(EtcdClient)
|
||||
}
|
||||
|
||||
func TestEtcdClient(t *testing.T) {
|
||||
endpoint := os.Getenv("ETCD_ENDPOINT")
|
||||
if endpoint == "" {
|
||||
t.Skipf("skipping; ETCD_ENDPOINT must be set")
|
||||
}
|
||||
|
||||
config := map[string]string{
|
||||
"endpoints": endpoint,
|
||||
"path": fmt.Sprintf("tf-unit/%s", time.Now().String()),
|
||||
}
|
||||
|
||||
if username := os.Getenv("ETCD_USERNAME"); username != "" {
|
||||
config["username"] = username
|
||||
}
|
||||
if password := os.Getenv("ETCD_PASSWORD"); password != "" {
|
||||
config["password"] = password
|
||||
}
|
||||
|
||||
client, err := etcdFactory(config)
|
||||
if err != nil {
|
||||
t.Fatalf("Error for valid config: %s", err)
|
||||
}
|
||||
|
||||
testClient(t, client)
|
||||
}
|
|
@ -38,6 +38,7 @@ func NewClient(t string, conf map[string]string) (Client, error) {
|
|||
var BuiltinClients = map[string]Factory{
|
||||
"atlas": atlasFactory,
|
||||
"consul": consulFactory,
|
||||
"etcd": etcdFactory,
|
||||
"http": httpFactory,
|
||||
"s3": s3Factory,
|
||||
"swift": swiftFactory,
|
||||
|
|
Loading…
Reference in New Issue