Use New() instead of `once.Do(b.init)`
This commit is contained in:
parent
97d1c46602
commit
495d1ea350
|
@ -39,59 +39,14 @@ type Backend struct {
|
||||||
|
|
||||||
// schema is the schema for configuration, set by init
|
// schema is the schema for configuration, set by init
|
||||||
schema *schema.Backend
|
schema *schema.Backend
|
||||||
once sync.Once
|
|
||||||
|
|
||||||
// opLock locks operations
|
// opLock locks operations
|
||||||
opLock sync.Mutex
|
opLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) Input(
|
// New returns a new initialized Atlas backend.
|
||||||
ui terraform.UIInput, c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) {
|
func New() *Backend {
|
||||||
b.once.Do(b.init)
|
b := &Backend{}
|
||||||
return b.schema.Input(ui, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Backend) Validate(c *terraform.ResourceConfig) ([]string, []error) {
|
|
||||||
b.once.Do(b.init)
|
|
||||||
return b.schema.Validate(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Backend) Configure(c *terraform.ResourceConfig) error {
|
|
||||||
b.once.Do(b.init)
|
|
||||||
return b.schema.Configure(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
return &remote.State{Client: b.stateClient}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Colorize returns the Colorize structure that can be used for colorizing
|
|
||||||
// output. This is gauranteed to always return a non-nil value and so is useful
|
|
||||||
// as a helper to wrap any potentially colored strings.
|
|
||||||
func (b *Backend) Colorize() *colorstring.Colorize {
|
|
||||||
if b.CLIColor != nil {
|
|
||||||
return b.CLIColor
|
|
||||||
}
|
|
||||||
|
|
||||||
return &colorstring.Colorize{
|
|
||||||
Colors: colorstring.DefaultColors,
|
|
||||||
Disable: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Backend) init() {
|
|
||||||
b.schema = &schema.Backend{
|
b.schema = &schema.Backend{
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"name": &schema.Schema{
|
"name": &schema.Schema{
|
||||||
|
@ -115,11 +70,13 @@ func (b *Backend) init() {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
ConfigureFunc: b.schemaConfigure,
|
ConfigureFunc: b.configure,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) schemaConfigure(ctx context.Context) error {
|
func (b *Backend) configure(ctx context.Context) error {
|
||||||
d := schema.FromContextBackendConfig(ctx)
|
d := schema.FromContextBackendConfig(ctx)
|
||||||
|
|
||||||
// Parse the address
|
// Parse the address
|
||||||
|
@ -153,6 +110,47 @@ func (b *Backend) schemaConfigure(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Backend) Input(ui terraform.UIInput, c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) {
|
||||||
|
return b.schema.Input(ui, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Backend) Validate(c *terraform.ResourceConfig) ([]string, []error) {
|
||||||
|
return b.schema.Validate(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Backend) Configure(c *terraform.ResourceConfig) error {
|
||||||
|
return b.schema.Configure(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Backend) State(name string) (state.State, error) {
|
||||||
|
if name != backend.DefaultStateName {
|
||||||
|
return nil, backend.ErrNamedStatesNotSupported
|
||||||
|
}
|
||||||
|
return &remote.State{Client: b.stateClient}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Backend) DeleteState(name string) error {
|
||||||
|
return backend.ErrNamedStatesNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Backend) States() ([]string, error) {
|
||||||
|
return nil, backend.ErrNamedStatesNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// Colorize returns the Colorize structure that can be used for colorizing
|
||||||
|
// output. This is gauranteed to always return a non-nil value and so is useful
|
||||||
|
// as a helper to wrap any potentially colored strings.
|
||||||
|
func (b *Backend) Colorize() *colorstring.Colorize {
|
||||||
|
if b.CLIColor != nil {
|
||||||
|
return b.CLIColor
|
||||||
|
}
|
||||||
|
|
||||||
|
return &colorstring.Colorize{
|
||||||
|
Colors: colorstring.DefaultColors,
|
||||||
|
Disable: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var schemaDescriptions = map[string]string{
|
var schemaDescriptions = map[string]string{
|
||||||
"name": "Full name of the environment in Atlas, such as 'hashicorp/myenv'",
|
"name": "Full name of the environment in Atlas, such as 'hashicorp/myenv'",
|
||||||
"access_token": "Access token to use to access Atlas. If ATLAS_TOKEN is set then\n" +
|
"access_token": "Access token to use to access Atlas. If ATLAS_TOKEN is set then\n" +
|
||||||
|
|
|
@ -18,7 +18,7 @@ func TestConfigure_envAddr(t *testing.T) {
|
||||||
defer os.Setenv("ATLAS_ADDRESS", os.Getenv("ATLAS_ADDRESS"))
|
defer os.Setenv("ATLAS_ADDRESS", os.Getenv("ATLAS_ADDRESS"))
|
||||||
os.Setenv("ATLAS_ADDRESS", "http://foo.com")
|
os.Setenv("ATLAS_ADDRESS", "http://foo.com")
|
||||||
|
|
||||||
b := &Backend{}
|
b := New()
|
||||||
err := b.Configure(terraform.NewResourceConfig(config.TestRawConfig(t, map[string]interface{}{
|
err := b.Configure(terraform.NewResourceConfig(config.TestRawConfig(t, map[string]interface{}{
|
||||||
"name": "foo/bar",
|
"name": "foo/bar",
|
||||||
})))
|
})))
|
||||||
|
@ -35,7 +35,7 @@ func TestConfigure_envToken(t *testing.T) {
|
||||||
defer os.Setenv("ATLAS_TOKEN", os.Getenv("ATLAS_TOKEN"))
|
defer os.Setenv("ATLAS_TOKEN", os.Getenv("ATLAS_TOKEN"))
|
||||||
os.Setenv("ATLAS_TOKEN", "foo")
|
os.Setenv("ATLAS_TOKEN", "foo")
|
||||||
|
|
||||||
b := &Backend{}
|
b := New()
|
||||||
err := b.Configure(terraform.NewResourceConfig(config.TestRawConfig(t, map[string]interface{}{
|
err := b.Configure(terraform.NewResourceConfig(config.TestRawConfig(t, map[string]interface{}{
|
||||||
"name": "foo/bar",
|
"name": "foo/bar",
|
||||||
})))
|
})))
|
||||||
|
|
|
@ -20,7 +20,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func testStateClient(t *testing.T, c map[string]interface{}) remote.Client {
|
func testStateClient(t *testing.T, c map[string]interface{}) remote.Client {
|
||||||
b := backend.TestBackendConfig(t, &Backend{}, c)
|
b := backend.TestBackendConfig(t, New(), c)
|
||||||
raw, err := b.State(backend.DefaultStateName)
|
raw, err := b.State(backend.DefaultStateName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
|
|
|
@ -39,8 +39,8 @@ func init() {
|
||||||
// Our hardcoded backends. We don't need to acquire a lock here
|
// Our hardcoded backends. We don't need to acquire a lock here
|
||||||
// since init() code is serial and can't spawn goroutines.
|
// since init() code is serial and can't spawn goroutines.
|
||||||
backends = map[string]func() backend.Backend{
|
backends = map[string]func() backend.Backend{
|
||||||
"local": func() backend.Backend { return &backendLocal.Local{} },
|
"local": func() backend.Backend { return backendLocal.New() },
|
||||||
"atlas": func() backend.Backend { return &backendAtlas.Backend{} },
|
"atlas": func() backend.Backend { return backendAtlas.New() },
|
||||||
"azure": deprecateBackend(backendAzure.New(),
|
"azure": deprecateBackend(backendAzure.New(),
|
||||||
`Warning: "azure" name is deprecated, please use "azurerm"`),
|
`Warning: "azure" name is deprecated, please use "azurerm"`),
|
||||||
"azurerm": func() backend.Backend { return backendAzure.New() },
|
"azurerm": func() backend.Backend { return backendAzure.New() },
|
||||||
|
|
|
@ -91,94 +91,106 @@ type Local struct {
|
||||||
|
|
||||||
schema *schema.Backend
|
schema *schema.Backend
|
||||||
opLock sync.Mutex
|
opLock sync.Mutex
|
||||||
once sync.Once
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Local) Input(
|
// New returns a new initialized local backend.
|
||||||
ui terraform.UIInput, c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) {
|
func New() *Local {
|
||||||
b.once.Do(b.init)
|
return NewWithBackend(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWithBackend returns a new local backend initialized with a
|
||||||
|
// dedicated backend for non-enhanced behavior.
|
||||||
|
func NewWithBackend(backend backend.Backend) *Local {
|
||||||
|
b := &Local{
|
||||||
|
Backend: backend,
|
||||||
|
}
|
||||||
|
|
||||||
|
b.schema = &schema.Backend{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"path": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "",
|
||||||
|
},
|
||||||
|
|
||||||
|
"workspace_dir": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "",
|
||||||
|
},
|
||||||
|
|
||||||
|
"environment_dir": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "",
|
||||||
|
ConflictsWith: []string{"workspace_dir"},
|
||||||
|
Deprecated: "workspace_dir should be used instead, with the same meaning",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
ConfigureFunc: b.configure,
|
||||||
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Local) configure(ctx context.Context) error {
|
||||||
|
d := schema.FromContextBackendConfig(ctx)
|
||||||
|
|
||||||
|
// Set the path if it is set
|
||||||
|
pathRaw, ok := d.GetOk("path")
|
||||||
|
if ok {
|
||||||
|
path := pathRaw.(string)
|
||||||
|
if path == "" {
|
||||||
|
return fmt.Errorf("configured path is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
b.StatePath = path
|
||||||
|
b.StateOutPath = path
|
||||||
|
}
|
||||||
|
|
||||||
|
if raw, ok := d.GetOk("workspace_dir"); ok {
|
||||||
|
path := raw.(string)
|
||||||
|
if path != "" {
|
||||||
|
b.StateWorkspaceDir = path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy name, which ConflictsWith workspace_dir
|
||||||
|
if raw, ok := d.GetOk("environment_dir"); ok {
|
||||||
|
path := raw.(string)
|
||||||
|
if path != "" {
|
||||||
|
b.StateWorkspaceDir = path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Local) Input(ui terraform.UIInput, c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) {
|
||||||
f := b.schema.Input
|
f := b.schema.Input
|
||||||
if b.Backend != nil {
|
if b.Backend != nil {
|
||||||
f = b.Backend.Input
|
f = b.Backend.Input
|
||||||
}
|
}
|
||||||
|
|
||||||
return f(ui, c)
|
return f(ui, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Local) Validate(c *terraform.ResourceConfig) ([]string, []error) {
|
func (b *Local) Validate(c *terraform.ResourceConfig) ([]string, []error) {
|
||||||
b.once.Do(b.init)
|
|
||||||
|
|
||||||
f := b.schema.Validate
|
f := b.schema.Validate
|
||||||
if b.Backend != nil {
|
if b.Backend != nil {
|
||||||
f = b.Backend.Validate
|
f = b.Backend.Validate
|
||||||
}
|
}
|
||||||
|
|
||||||
return f(c)
|
return f(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Local) Configure(c *terraform.ResourceConfig) error {
|
func (b *Local) Configure(c *terraform.ResourceConfig) error {
|
||||||
b.once.Do(b.init)
|
|
||||||
|
|
||||||
f := b.schema.Configure
|
f := b.schema.Configure
|
||||||
if b.Backend != nil {
|
if b.Backend != nil {
|
||||||
f = b.Backend.Configure
|
f = b.Backend.Configure
|
||||||
}
|
}
|
||||||
|
|
||||||
return f(c)
|
return f(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Local) States() ([]string, error) {
|
|
||||||
// If we have a backend handling state, defer to that.
|
|
||||||
if b.Backend != nil {
|
|
||||||
return b.Backend.States()
|
|
||||||
}
|
|
||||||
|
|
||||||
// the listing always start with "default"
|
|
||||||
envs := []string{backend.DefaultStateName}
|
|
||||||
|
|
||||||
entries, err := ioutil.ReadDir(b.stateWorkspaceDir())
|
|
||||||
// no error if there's no envs configured
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return envs, nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var listed []string
|
|
||||||
for _, entry := range entries {
|
|
||||||
if entry.IsDir() {
|
|
||||||
listed = append(listed, filepath.Base(entry.Name()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(listed)
|
|
||||||
envs = append(envs, listed...)
|
|
||||||
|
|
||||||
return envs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteState removes a named state.
|
|
||||||
// The "default" state cannot be removed.
|
|
||||||
func (b *Local) DeleteState(name string) error {
|
|
||||||
// If we have a backend handling state, defer to that.
|
|
||||||
if b.Backend != nil {
|
|
||||||
return b.Backend.DeleteState(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if name == "" {
|
|
||||||
return errors.New("empty state name")
|
|
||||||
}
|
|
||||||
|
|
||||||
if name == backend.DefaultStateName {
|
|
||||||
return errors.New("cannot delete default state")
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(b.states, name)
|
|
||||||
return os.RemoveAll(filepath.Join(b.stateWorkspaceDir(), name))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Local) State(name string) (state.State, error) {
|
func (b *Local) State(name string) (state.State, error) {
|
||||||
statePath, stateOutPath, backupPath := b.StatePaths(name)
|
statePath, stateOutPath, backupPath := b.StatePaths(name)
|
||||||
|
|
||||||
|
@ -216,6 +228,57 @@ func (b *Local) State(name string) (state.State, error) {
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteState removes a named state.
|
||||||
|
// The "default" state cannot be removed.
|
||||||
|
func (b *Local) DeleteState(name string) error {
|
||||||
|
// If we have a backend handling state, defer to that.
|
||||||
|
if b.Backend != nil {
|
||||||
|
return b.Backend.DeleteState(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
return errors.New("empty state name")
|
||||||
|
}
|
||||||
|
|
||||||
|
if name == backend.DefaultStateName {
|
||||||
|
return errors.New("cannot delete default state")
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(b.states, name)
|
||||||
|
return os.RemoveAll(filepath.Join(b.stateWorkspaceDir(), name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Local) States() ([]string, error) {
|
||||||
|
// If we have a backend handling state, defer to that.
|
||||||
|
if b.Backend != nil {
|
||||||
|
return b.Backend.States()
|
||||||
|
}
|
||||||
|
|
||||||
|
// the listing always start with "default"
|
||||||
|
envs := []string{backend.DefaultStateName}
|
||||||
|
|
||||||
|
entries, err := ioutil.ReadDir(b.stateWorkspaceDir())
|
||||||
|
// no error if there's no envs configured
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return envs, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var listed []string
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() {
|
||||||
|
listed = append(listed, filepath.Base(entry.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(listed)
|
||||||
|
envs = append(envs, listed...)
|
||||||
|
|
||||||
|
return envs, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Operation implements backend.Enhanced
|
// Operation implements backend.Enhanced
|
||||||
//
|
//
|
||||||
// This will initialize an in-memory terraform.Context to perform the
|
// This will initialize an in-memory terraform.Context to perform the
|
||||||
|
@ -348,68 +411,6 @@ func (b *Local) Colorize() *colorstring.Colorize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Local) init() {
|
|
||||||
b.schema = &schema.Backend{
|
|
||||||
Schema: map[string]*schema.Schema{
|
|
||||||
"path": &schema.Schema{
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Default: "",
|
|
||||||
},
|
|
||||||
|
|
||||||
"workspace_dir": &schema.Schema{
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Default: "",
|
|
||||||
},
|
|
||||||
|
|
||||||
"environment_dir": &schema.Schema{
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Default: "",
|
|
||||||
ConflictsWith: []string{"workspace_dir"},
|
|
||||||
|
|
||||||
Deprecated: "workspace_dir should be used instead, with the same meaning",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
ConfigureFunc: b.schemaConfigure,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Local) schemaConfigure(ctx context.Context) error {
|
|
||||||
d := schema.FromContextBackendConfig(ctx)
|
|
||||||
|
|
||||||
// Set the path if it is set
|
|
||||||
pathRaw, ok := d.GetOk("path")
|
|
||||||
if ok {
|
|
||||||
path := pathRaw.(string)
|
|
||||||
if path == "" {
|
|
||||||
return fmt.Errorf("configured path is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
b.StatePath = path
|
|
||||||
b.StateOutPath = path
|
|
||||||
}
|
|
||||||
|
|
||||||
if raw, ok := d.GetOk("workspace_dir"); ok {
|
|
||||||
path := raw.(string)
|
|
||||||
if path != "" {
|
|
||||||
b.StateWorkspaceDir = path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Legacy name, which ConflictsWith workspace_dir
|
|
||||||
if raw, ok := d.GetOk("environment_dir"); ok {
|
|
||||||
path := raw.(string)
|
|
||||||
if path != "" {
|
|
||||||
b.StateWorkspaceDir = path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatePaths returns the StatePath, StateOutPath, and StateBackupPath as
|
// StatePaths returns the StatePath, StateOutPath, and StateBackupPath as
|
||||||
// configured from the CLI.
|
// configured from the CLI.
|
||||||
func (b *Local) StatePaths(name string) (string, string, string) {
|
func (b *Local) StatePaths(name string) (string, string, string) {
|
||||||
|
|
|
@ -5,15 +5,13 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/command/clistate"
|
|
||||||
"github.com/hashicorp/terraform/command/format"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/tfdiags"
|
|
||||||
|
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
"github.com/hashicorp/terraform/backend"
|
"github.com/hashicorp/terraform/backend"
|
||||||
|
"github.com/hashicorp/terraform/command/clistate"
|
||||||
|
"github.com/hashicorp/terraform/command/format"
|
||||||
"github.com/hashicorp/terraform/state"
|
"github.com/hashicorp/terraform/state"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
"github.com/hashicorp/terraform/tfdiags"
|
||||||
)
|
)
|
||||||
|
|
||||||
// backend.Local implementation.
|
// backend.Local implementation.
|
||||||
|
|
|
@ -15,14 +15,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLocal_impl(t *testing.T) {
|
func TestLocal_impl(t *testing.T) {
|
||||||
var _ backend.Enhanced = new(Local)
|
var _ backend.Enhanced = New()
|
||||||
var _ backend.Local = new(Local)
|
var _ backend.Local = New()
|
||||||
var _ backend.CLI = new(Local)
|
var _ backend.CLI = New()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocal_backend(t *testing.T) {
|
func TestLocal_backend(t *testing.T) {
|
||||||
defer testTmpDir(t)()
|
defer testTmpDir(t)()
|
||||||
b := &Local{}
|
b := New()
|
||||||
backend.TestBackendStates(t, b)
|
backend.TestBackendStates(t, b)
|
||||||
backend.TestBackendStateLocks(t, b, b)
|
backend.TestBackendStateLocks(t, b, b)
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ func checkState(t *testing.T, path, expected string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocal_StatePaths(t *testing.T) {
|
func TestLocal_StatePaths(t *testing.T) {
|
||||||
b := &Local{}
|
b := New()
|
||||||
|
|
||||||
// Test the defaults
|
// Test the defaults
|
||||||
path, out, back := b.StatePaths("")
|
path, out, back := b.StatePaths("")
|
||||||
|
@ -94,7 +94,7 @@ func TestLocal_addAndRemoveStates(t *testing.T) {
|
||||||
dflt := backend.DefaultStateName
|
dflt := backend.DefaultStateName
|
||||||
expectedStates := []string{dflt}
|
expectedStates := []string{dflt}
|
||||||
|
|
||||||
b := &Local{}
|
b := New()
|
||||||
states, err := b.States()
|
states, err := b.States()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -210,13 +210,11 @@ func (b *testDelegateBackend) DeleteState(name string) error {
|
||||||
// verify that the MultiState methods are dispatched to the correct Backend.
|
// verify that the MultiState methods are dispatched to the correct Backend.
|
||||||
func TestLocal_multiStateBackend(t *testing.T) {
|
func TestLocal_multiStateBackend(t *testing.T) {
|
||||||
// assign a separate backend where we can read the state
|
// assign a separate backend where we can read the state
|
||||||
b := &Local{
|
b := NewWithBackend(&testDelegateBackend{
|
||||||
Backend: &testDelegateBackend{
|
|
||||||
stateErr: true,
|
stateErr: true,
|
||||||
statesErr: true,
|
statesErr: true,
|
||||||
deleteErr: true,
|
deleteErr: true,
|
||||||
},
|
})
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := b.State("test"); err != errTestDelegateState {
|
if _, err := b.State("test"); err != errTestDelegateState {
|
||||||
t.Fatal("expected errTestDelegateState, got:", err)
|
t.Fatal("expected errTestDelegateState, got:", err)
|
||||||
|
|
|
@ -18,13 +18,14 @@ import (
|
||||||
// public fields without any locks.
|
// public fields without any locks.
|
||||||
func TestLocal(t *testing.T) (*Local, func()) {
|
func TestLocal(t *testing.T) (*Local, func()) {
|
||||||
tempDir := testTempDir(t)
|
tempDir := testTempDir(t)
|
||||||
local := &Local{
|
|
||||||
StatePath: filepath.Join(tempDir, "state.tfstate"),
|
local := New()
|
||||||
StateOutPath: filepath.Join(tempDir, "state.tfstate"),
|
local.StatePath = filepath.Join(tempDir, "state.tfstate")
|
||||||
StateBackupPath: filepath.Join(tempDir, "state.tfstate.bak"),
|
local.StateOutPath = filepath.Join(tempDir, "state.tfstate")
|
||||||
StateWorkspaceDir: filepath.Join(tempDir, "state.tfstate.d"),
|
local.StateBackupPath = filepath.Join(tempDir, "state.tfstate.bak")
|
||||||
ContextOpts: &terraform.ContextOpts{},
|
local.StateWorkspaceDir = filepath.Join(tempDir, "state.tfstate.d")
|
||||||
}
|
local.ContextOpts = &terraform.ContextOpts{}
|
||||||
|
|
||||||
cleanup := func() {
|
cleanup := func() {
|
||||||
if err := os.RemoveAll(tempDir); err != nil {
|
if err := os.RemoveAll(tempDir); err != nil {
|
||||||
t.Fatal("error clecanup up test:", err)
|
t.Fatal("error clecanup up test:", err)
|
||||||
|
@ -69,7 +70,7 @@ func TestLocalProvider(t *testing.T, b *Local, name string) *terraform.MockResou
|
||||||
// TestNewLocalSingle is a factory for creating a TestLocalSingleState.
|
// TestNewLocalSingle is a factory for creating a TestLocalSingleState.
|
||||||
// This function matches the signature required for backend/init.
|
// This function matches the signature required for backend/init.
|
||||||
func TestNewLocalSingle() backend.Backend {
|
func TestNewLocalSingle() backend.Backend {
|
||||||
return &TestLocalSingleState{}
|
return &TestLocalSingleState{Local: New()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestLocalSingleState is a backend implementation that wraps Local
|
// TestLocalSingleState is a backend implementation that wraps Local
|
||||||
|
@ -79,7 +80,7 @@ func TestNewLocalSingle() backend.Backend {
|
||||||
// This isn't an actual use case, this is exported just to provide a
|
// This isn't an actual use case, this is exported just to provide a
|
||||||
// easy way to test that behavior.
|
// easy way to test that behavior.
|
||||||
type TestLocalSingleState struct {
|
type TestLocalSingleState struct {
|
||||||
Local
|
*Local
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *TestLocalSingleState) State(name string) (state.State, error) {
|
func (b *TestLocalSingleState) State(name string) (state.State, error) {
|
||||||
|
|
|
@ -18,10 +18,10 @@ import (
|
||||||
"google.golang.org/api/option"
|
"google.golang.org/api/option"
|
||||||
)
|
)
|
||||||
|
|
||||||
// gcsBackend implements "backend".Backend for GCS.
|
// Backend implements "backend".Backend for GCS.
|
||||||
// Input(), Validate() and Configure() are implemented by embedding *schema.Backend.
|
// Input(), Validate() and Configure() are implemented by embedding *schema.Backend.
|
||||||
// State(), DeleteState() and States() are implemented explicitly.
|
// State(), DeleteState() and States() are implemented explicitly.
|
||||||
type gcsBackend struct {
|
type Backend struct {
|
||||||
*schema.Backend
|
*schema.Backend
|
||||||
|
|
||||||
storageClient *storage.Client
|
storageClient *storage.Client
|
||||||
|
@ -38,9 +38,9 @@ type gcsBackend struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() backend.Backend {
|
func New() backend.Backend {
|
||||||
be := &gcsBackend{}
|
b := &Backend{}
|
||||||
be.Backend = &schema.Backend{
|
b.Backend = &schema.Backend{
|
||||||
ConfigureFunc: be.configure,
|
ConfigureFunc: b.configure,
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"bucket": {
|
"bucket": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
|
@ -91,10 +91,10 @@ func New() backend.Backend {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return be
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *gcsBackend) configure(ctx context.Context) error {
|
func (b *Backend) configure(ctx context.Context) error {
|
||||||
if b.storageClient != nil {
|
if b.storageClient != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ const (
|
||||||
|
|
||||||
// States returns a list of names for the states found on GCS. The default
|
// States returns a list of names for the states found on GCS. The default
|
||||||
// state is always returned as the first element in the slice.
|
// state is always returned as the first element in the slice.
|
||||||
func (b *gcsBackend) States() ([]string, error) {
|
func (b *Backend) States() ([]string, error) {
|
||||||
states := []string{backend.DefaultStateName}
|
states := []string{backend.DefaultStateName}
|
||||||
|
|
||||||
bucket := b.storageClient.Bucket(b.bucketName)
|
bucket := b.storageClient.Bucket(b.bucketName)
|
||||||
|
@ -54,7 +54,7 @@ func (b *gcsBackend) States() ([]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteState deletes the named state. The "default" state cannot be deleted.
|
// DeleteState deletes the named state. The "default" state cannot be deleted.
|
||||||
func (b *gcsBackend) DeleteState(name string) error {
|
func (b *Backend) DeleteState(name string) error {
|
||||||
if name == backend.DefaultStateName {
|
if name == backend.DefaultStateName {
|
||||||
return fmt.Errorf("cowardly refusing to delete the %q state", name)
|
return fmt.Errorf("cowardly refusing to delete the %q state", name)
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ func (b *gcsBackend) DeleteState(name string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// client returns a remoteClient for the named state.
|
// client returns a remoteClient for the named state.
|
||||||
func (b *gcsBackend) client(name string) (*remoteClient, error) {
|
func (b *Backend) client(name string) (*remoteClient, error) {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return nil, fmt.Errorf("%q is not a valid state name", name)
|
return nil, fmt.Errorf("%q is not a valid state name", name)
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ func (b *gcsBackend) client(name string) (*remoteClient, error) {
|
||||||
|
|
||||||
// State reads and returns the named state from GCS. If the named state does
|
// State reads and returns the named state from GCS. If the named state does
|
||||||
// not yet exist, a new state file is created.
|
// not yet exist, a new state file is created.
|
||||||
func (b *gcsBackend) State(name string) (state.State, error) {
|
func (b *Backend) State(name string) (state.State, error) {
|
||||||
c, err := b.client(name)
|
c, err := b.client(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -144,14 +144,14 @@ func (b *gcsBackend) State(name string) (state.State, error) {
|
||||||
return st, nil
|
return st, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *gcsBackend) stateFile(name string) string {
|
func (b *Backend) stateFile(name string) string {
|
||||||
if name == backend.DefaultStateName && b.defaultStateFile != "" {
|
if name == backend.DefaultStateName && b.defaultStateFile != "" {
|
||||||
return b.defaultStateFile
|
return b.defaultStateFile
|
||||||
}
|
}
|
||||||
return path.Join(b.prefix, name+stateFileSuffix)
|
return path.Join(b.prefix, name+stateFileSuffix)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *gcsBackend) lockFile(name string) string {
|
func (b *Backend) lockFile(name string) string {
|
||||||
if name == backend.DefaultStateName && b.defaultStateFile != "" {
|
if name == backend.DefaultStateName && b.defaultStateFile != "" {
|
||||||
return strings.TrimSuffix(b.defaultStateFile, stateFileSuffix) + lockFileSuffix
|
return strings.TrimSuffix(b.defaultStateFile, stateFileSuffix) + lockFileSuffix
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ func TestStateFile(t *testing.T) {
|
||||||
{"state", "legacy.state", "test", "state/test.tfstate", "state/test.tflock"},
|
{"state", "legacy.state", "test", "state/test.tfstate", "state/test.tflock"},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
b := &gcsBackend{
|
b := &Backend{
|
||||||
prefix: c.prefix,
|
prefix: c.prefix,
|
||||||
defaultStateFile: c.defaultStateFile,
|
defaultStateFile: c.defaultStateFile,
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ func setupBackend(t *testing.T, bucket, prefix, key string) backend.Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
b := backend.TestBackendConfig(t, New(), config)
|
b := backend.TestBackendConfig(t, New(), config)
|
||||||
be := b.(*gcsBackend)
|
be := b.(*Backend)
|
||||||
|
|
||||||
// create the bucket if it doesn't exist
|
// create the bucket if it doesn't exist
|
||||||
bkt := be.storageClient.Bucket(bucket)
|
bkt := be.storageClient.Bucket(bucket)
|
||||||
|
@ -213,7 +213,7 @@ func setupBackend(t *testing.T, bucket, prefix, key string) backend.Backend {
|
||||||
// teardownBackend deletes all states from be except the default state.
|
// teardownBackend deletes all states from be except the default state.
|
||||||
func teardownBackend(t *testing.T, be backend.Backend, prefix string) {
|
func teardownBackend(t *testing.T, be backend.Backend, prefix string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
gcsBE, ok := be.(*gcsBackend)
|
gcsBE, ok := be.(*Backend)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("be is a %T, want a *gcsBackend", be)
|
t.Fatalf("be is a %T, want a *gcsBackend", be)
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,11 +138,13 @@ func (c *InitCommand) Run(args []string) int {
|
||||||
|
|
||||||
// If our directory is empty, then we're done. We can't get or setup
|
// If our directory is empty, then we're done. We can't get or setup
|
||||||
// the backend with an empty directory.
|
// the backend with an empty directory.
|
||||||
if empty, err := config.IsEmptyDir(path); err != nil {
|
empty, err := config.IsEmptyDir(path)
|
||||||
|
if err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf(
|
c.Ui.Error(fmt.Sprintf(
|
||||||
"Error checking configuration: %s", err))
|
"Error checking configuration: %s", err))
|
||||||
return 1
|
return 1
|
||||||
} else if empty {
|
}
|
||||||
|
if empty {
|
||||||
c.Ui.Output(c.Colorize().Color(strings.TrimSpace(outputInitEmpty)))
|
c.Ui.Output(c.Colorize().Color(strings.TrimSpace(outputInitEmpty)))
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,7 +136,7 @@ func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the local backend
|
// Build the local backend
|
||||||
local := &backendLocal.Local{Backend: b}
|
local := backendLocal.NewWithBackend(b)
|
||||||
if err := local.CLIInit(cliOpts); err != nil {
|
if err := local.CLIInit(cliOpts); err != nil {
|
||||||
// Local backend isn't allowed to fail. It would be a bug.
|
// Local backend isn't allowed to fail. It would be a bug.
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
Loading…
Reference in New Issue