From 3db55cf747eb07656e4d7860c5330894e366f40f Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 1 Mar 2017 22:15:08 -0800 Subject: [PATCH] backend/consul: build your own backend --- backend/remote-state/consul/backend.go | 111 ++++++++++--------- backend/remote-state/consul/backend_state.go | 38 +++++++ backend/remote-state/consul/client_test.go | 9 +- 3 files changed, 102 insertions(+), 56 deletions(-) create mode 100644 backend/remote-state/consul/backend_state.go diff --git a/backend/remote-state/consul/backend.go b/backend/remote-state/consul/backend.go index 1a5a93c640..7193bf8a96 100644 --- a/backend/remote-state/consul/backend.go +++ b/backend/remote-state/consul/backend.go @@ -6,67 +6,78 @@ import ( consulapi "github.com/hashicorp/consul/api" "github.com/hashicorp/terraform/backend" - "github.com/hashicorp/terraform/backend/remote-state" "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/state/remote" ) // New creates a new backend for Consul remote state. func New() backend.Backend { - return &remotestate.Backend{ - ConfigureFunc: configure, + s := &schema.Backend{ + Schema: map[string]*schema.Schema{ + "path": &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "Path to store state in Consul", + }, - // Set the schema - Backend: &schema.Backend{ - Schema: map[string]*schema.Schema{ - "path": &schema.Schema{ - Type: schema.TypeString, - Required: true, - Description: "Path to store state in Consul", - }, + "access_token": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "Access token for a Consul ACL", + Default: "", // To prevent input + }, - "access_token": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Description: "Access token for a Consul ACL", - Default: "", // To prevent input - }, + "address": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "Address to the Consul Cluster", + Default: "", // To prevent input + }, - "address": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Description: "Address to the Consul Cluster", - Default: "", // To prevent input - }, + "scheme": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "Scheme to communicate to Consul with", + Default: "", // To prevent input + }, - "scheme": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Description: "Scheme to communicate to Consul with", - Default: "", // To prevent input - }, + "datacenter": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "Datacenter to communicate with", + Default: "", // To prevent input + }, - "datacenter": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Description: "Datacenter to communicate with", - 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 - }, + "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 - 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 config := consulapi.DefaultConfig() @@ -100,13 +111,5 @@ func configure(ctx context.Context) (remote.Client, error) { } } - client, err := consulapi.NewClient(config) - if err != nil { - return nil, err - } - - return &RemoteClient{ - Client: client, - Path: data.Get("path").(string), - }, nil + return consulapi.NewClient(config) } diff --git a/backend/remote-state/consul/backend_state.go b/backend/remote-state/consul/backend_state.go new file mode 100644 index 0000000000..ac8fa4091b --- /dev/null +++ b/backend/remote-state/consul/backend_state.go @@ -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 +} diff --git a/backend/remote-state/consul/client_test.go b/backend/remote-state/consul/client_test.go index 2cb11f0d91..d123e39c78 100644 --- a/backend/remote-state/consul/client_test.go +++ b/backend/remote-state/consul/client_test.go @@ -7,7 +7,6 @@ import ( "time" "github.com/hashicorp/terraform/backend" - "github.com/hashicorp/terraform/backend/remote-state" "github.com/hashicorp/terraform/state/remote" ) @@ -29,8 +28,14 @@ func TestRemoteClient(t *testing.T) { "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 - remotestate.TestClient(t, b) + remote.TestClient(t, state.(*remote.State).Client) } func TestConsul_stateLock(t *testing.T) {