backend/consul: build your own backend

This commit is contained in:
Mitchell Hashimoto 2017-03-01 22:15:08 -08:00
parent 73006a243a
commit 3db55cf747
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
3 changed files with 102 additions and 56 deletions

View File

@ -6,67 +6,78 @@ import (
consulapi "github.com/hashicorp/consul/api" consulapi "github.com/hashicorp/consul/api"
"github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/backend/remote-state"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/state/remote"
) )
// New creates a new backend for Consul remote state. // New creates a new backend for Consul remote state.
func New() backend.Backend { func New() backend.Backend {
return &remotestate.Backend{ s := &schema.Backend{
ConfigureFunc: configure, Schema: map[string]*schema.Schema{
"path": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "Path to store state in Consul",
},
// Set the schema "access_token": &schema.Schema{
Backend: &schema.Backend{ Type: schema.TypeString,
Schema: map[string]*schema.Schema{ Optional: true,
"path": &schema.Schema{ Description: "Access token for a Consul ACL",
Type: schema.TypeString, Default: "", // To prevent input
Required: true, },
Description: "Path to store state in Consul",
},
"access_token": &schema.Schema{ "address": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
Description: "Access token for a Consul ACL", Description: "Address to the Consul Cluster",
Default: "", // To prevent input Default: "", // To prevent input
}, },
"address": &schema.Schema{ "scheme": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
Description: "Address to the Consul Cluster", Description: "Scheme to communicate to Consul with",
Default: "", // To prevent input Default: "", // To prevent input
}, },
"scheme": &schema.Schema{ "datacenter": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
Description: "Scheme to communicate to Consul with", Description: "Datacenter to communicate with",
Default: "", // To prevent input Default: "", // To prevent input
}, },
"datacenter": &schema.Schema{ "http_auth": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
Description: "Datacenter to communicate with", Description: "HTTP Auth in the format of 'username:password'",
Default: "", // To prevent input Default: "", // To prevent input
},
"http_auth": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "HTTP Auth in the format of 'username:password'",
Default: "", // To prevent input
},
}, },
}, },
} }
result := &Backend{Backend: s}
result.Backend.ConfigureFunc = result.configure
return result
} }
func configure(ctx context.Context) (remote.Client, error) { type Backend struct {
*schema.Backend
configData *schema.ResourceData
}
func (b *Backend) configure(ctx context.Context) error {
// Grab the resource data // Grab the resource data
data := schema.FromContextBackendConfig(ctx) b.configData = schema.FromContextBackendConfig(ctx)
// Initialize a client to test config
_, err := b.clientRaw()
return err
}
func (b *Backend) clientRaw() (*consulapi.Client, error) {
data := b.configData
// Configure the client // Configure the client
config := consulapi.DefaultConfig() config := consulapi.DefaultConfig()
@ -100,13 +111,5 @@ func configure(ctx context.Context) (remote.Client, error) {
} }
} }
client, err := consulapi.NewClient(config) return consulapi.NewClient(config)
if err != nil {
return nil, err
}
return &RemoteClient{
Client: client,
Path: data.Get("path").(string),
}, nil
} }

View File

@ -0,0 +1,38 @@
package consul
import (
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/state"
"github.com/hashicorp/terraform/state/remote"
)
func (b *Backend) States() ([]string, error) {
return nil, backend.ErrNamedStatesNotSupported
}
func (b *Backend) DeleteState(name string) error {
return backend.ErrNamedStatesNotSupported
}
func (b *Backend) State(name string) (state.State, error) {
if name != backend.DefaultStateName {
return nil, backend.ErrNamedStatesNotSupported
}
// Get the Consul API client
client, err := b.clientRaw()
if err != nil {
return nil, err
}
// Determine the path of the data
path := b.configData.Get("path").(string)
// Build the remote state client
return &remote.State{
Client: &RemoteClient{
Client: client,
Path: path,
},
}, nil
}

View File

@ -7,7 +7,6 @@ import (
"time" "time"
"github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/backend/remote-state"
"github.com/hashicorp/terraform/state/remote" "github.com/hashicorp/terraform/state/remote"
) )
@ -29,8 +28,14 @@ func TestRemoteClient(t *testing.T) {
"path": fmt.Sprintf("tf-unit/%s", time.Now().String()), "path": fmt.Sprintf("tf-unit/%s", time.Now().String()),
}) })
// Grab the client
state, err := b.State(backend.DefaultStateName)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test // Test
remotestate.TestClient(t, b) remote.TestClient(t, state.(*remote.State).Client)
} }
func TestConsul_stateLock(t *testing.T) { func TestConsul_stateLock(t *testing.T) {