backend/legacy
This allows using legacy remote state backends with the new backend interface.
This commit is contained in:
parent
397e1b3132
commit
13c34b16e8
|
@ -0,0 +1,62 @@
|
||||||
|
package legacy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/state"
|
||||||
|
"github.com/hashicorp/terraform/state/remote"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Backend is an implementation of backend.Backend for legacy remote state
|
||||||
|
// clients.
|
||||||
|
type Backend struct {
|
||||||
|
// Type is the type of remote state client to support
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// client is set after Configure is called and client is initialized.
|
||||||
|
client remote.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Backend) Input(
|
||||||
|
ui terraform.UIInput, c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) {
|
||||||
|
// Return the config as-is, legacy doesn't support input
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Backend) Validate(*terraform.ResourceConfig) ([]string, []error) {
|
||||||
|
// No validation was supported for old clients
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Backend) Configure(c *terraform.ResourceConfig) error {
|
||||||
|
// Legacy remote state was only map[string]string config
|
||||||
|
var conf map[string]string
|
||||||
|
if err := mapstructure.Decode(c.Raw, &conf); err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Failed to decode %q configuration: %s\n\n"+
|
||||||
|
"This backend expects all configuration keys and values to be\n"+
|
||||||
|
"strings. Please verify your configuration and try again.",
|
||||||
|
b.Type, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := remote.NewClient(b.Type, conf)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Failed to configure remote backend %q: %s",
|
||||||
|
b.Type, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set our client
|
||||||
|
b.client = client
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Backend) State() (state.State, error) {
|
||||||
|
if b.client == nil {
|
||||||
|
panic("State called with nil remote state client")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &remote.State{Client: b.client}, nil
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package legacy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/backend"
|
||||||
|
"github.com/hashicorp/terraform/config"
|
||||||
|
"github.com/hashicorp/terraform/state"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBackend_impl(t *testing.T) {
|
||||||
|
var _ backend.Backend = new(Backend)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackend(t *testing.T) {
|
||||||
|
td, err := ioutil.TempDir("", "tf")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(td)
|
||||||
|
|
||||||
|
b := &Backend{Type: "local"}
|
||||||
|
conf := terraform.NewResourceConfig(config.TestRawConfig(t, map[string]interface{}{
|
||||||
|
"path": filepath.Join(td, "data"),
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Config
|
||||||
|
if err := b.Configure(conf); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab state
|
||||||
|
s, err := b.State()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
if s == nil {
|
||||||
|
t.Fatalf("state is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test it
|
||||||
|
s.WriteState(state.TestStateInitial())
|
||||||
|
s.PersistState()
|
||||||
|
state.TestState(t, s)
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Package legacy contains a backend implementation that can be used
|
||||||
|
// with the legacy remote state clients.
|
||||||
|
package legacy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/backend"
|
||||||
|
"github.com/hashicorp/terraform/state/remote"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init updates the backend/init package map of initializers to support
|
||||||
|
// all the remote state types.
|
||||||
|
//
|
||||||
|
// If a type is already in the map, it will not be added. This will allow
|
||||||
|
// us to slowly convert the legacy types to first-class backends.
|
||||||
|
func Init(m map[string]func() backend.Backend) {
|
||||||
|
for k, _ := range remote.BuiltinClients {
|
||||||
|
if _, ok := m[k]; !ok {
|
||||||
|
// Copy the "k" value since the variable "k" is reused for
|
||||||
|
// each key (address doesn't change).
|
||||||
|
typ := k
|
||||||
|
|
||||||
|
// Build the factory function to return a backend of typ
|
||||||
|
m[k] = func() backend.Backend {
|
||||||
|
return &Backend{Type: typ}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package legacy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/backend"
|
||||||
|
"github.com/hashicorp/terraform/state/remote"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInit(t *testing.T) {
|
||||||
|
m := make(map[string]func() backend.Backend)
|
||||||
|
Init(m)
|
||||||
|
|
||||||
|
for k, _ := range remote.BuiltinClients {
|
||||||
|
b, ok := m[k]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("missing: %s", k)
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ := b().(*Backend).Type; typ != k {
|
||||||
|
t.Fatalf("bad type: %s", typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInit_ignoreExisting(t *testing.T) {
|
||||||
|
m := make(map[string]func() backend.Backend)
|
||||||
|
m["local"] = nil
|
||||||
|
Init(m)
|
||||||
|
|
||||||
|
if v, ok := m["local"]; !ok || v != nil {
|
||||||
|
t.Fatalf("bad: %#v", m)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue