178 lines
5.0 KiB
Go
178 lines
5.0 KiB
Go
package local
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/terraform/backend"
|
|
"github.com/hashicorp/terraform/providers"
|
|
"github.com/hashicorp/terraform/states"
|
|
"github.com/hashicorp/terraform/states/statemgr"
|
|
"github.com/hashicorp/terraform/terraform"
|
|
"github.com/hashicorp/terraform/tfdiags"
|
|
)
|
|
|
|
// TestLocal returns a configured Local struct with temporary paths and
|
|
// in-memory ContextOpts.
|
|
//
|
|
// No operations will be called on the returned value, so you can still set
|
|
// public fields without any locks.
|
|
func TestLocal(t *testing.T) (*Local, func()) {
|
|
t.Helper()
|
|
|
|
tempDir := testTempDir(t)
|
|
var local *Local
|
|
local = &Local{
|
|
StatePath: filepath.Join(tempDir, "state.tfstate"),
|
|
StateOutPath: filepath.Join(tempDir, "state.tfstate"),
|
|
StateBackupPath: filepath.Join(tempDir, "state.tfstate.bak"),
|
|
StateWorkspaceDir: filepath.Join(tempDir, "state.tfstate.d"),
|
|
ContextOpts: &terraform.ContextOpts{},
|
|
ShowDiagnostics: func(vals ...interface{}) {
|
|
var diags tfdiags.Diagnostics
|
|
diags = diags.Append(vals...)
|
|
for _, diag := range diags {
|
|
// NOTE: Since the caller here is not directly the TestLocal
|
|
// function, t.Helper doesn't apply and so the log source
|
|
// isn't correctly shown in the test log output. This seems
|
|
// unavoidable as long as this is happening so indirectly.
|
|
t.Log(diag.Description().Summary)
|
|
if local.CLI != nil {
|
|
local.CLI.Error(diag.Description().Summary)
|
|
}
|
|
}
|
|
},
|
|
}
|
|
cleanup := func() {
|
|
if err := os.RemoveAll(tempDir); err != nil {
|
|
t.Fatal("error cleanup up test:", err)
|
|
}
|
|
}
|
|
|
|
return local, cleanup
|
|
}
|
|
|
|
// TestLocalProvider modifies the ContextOpts of the *Local parameter to
|
|
// have a provider with the given name.
|
|
func TestLocalProvider(t *testing.T, b *Local, name string, schema *terraform.ProviderSchema) *terraform.MockProvider {
|
|
// Build a mock resource provider for in-memory operations
|
|
p := new(terraform.MockProvider)
|
|
p.GetSchemaReturn = schema
|
|
p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
|
|
return providers.PlanResourceChangeResponse{
|
|
PlannedState: req.ProposedNewState,
|
|
PlannedPrivate: req.PriorPrivate,
|
|
}
|
|
}
|
|
p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse {
|
|
return providers.ReadResourceResponse{NewState: req.PriorState}
|
|
}
|
|
|
|
// Initialize the opts
|
|
if b.ContextOpts == nil {
|
|
b.ContextOpts = &terraform.ContextOpts{}
|
|
}
|
|
|
|
// Setup our provider
|
|
b.ContextOpts.ProviderResolver = providers.ResolverFixed(
|
|
map[string]providers.Factory{
|
|
name: providers.FactoryFixed(p),
|
|
},
|
|
)
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
// TestNewLocalSingle is a factory for creating a TestLocalSingleState.
|
|
// This function matches the signature required for backend/init.
|
|
func TestNewLocalSingle() backend.Backend {
|
|
return &TestLocalSingleState{Local: &Local{}}
|
|
}
|
|
|
|
// TestLocalSingleState is a backend implementation that wraps Local
|
|
// and modifies it to only support single states (returns
|
|
// ErrNamedStatesNotSupported for multi-state operations).
|
|
//
|
|
// This isn't an actual use case, this is exported just to provide a
|
|
// easy way to test that behavior.
|
|
type TestLocalSingleState struct {
|
|
*Local
|
|
}
|
|
|
|
func (b *TestLocalSingleState) State(name string) (statemgr.Full, error) {
|
|
if name != backend.DefaultStateName {
|
|
return nil, backend.ErrNamedStatesNotSupported
|
|
}
|
|
|
|
return b.Local.StateMgr(name)
|
|
}
|
|
|
|
func (b *TestLocalSingleState) States() ([]string, error) {
|
|
return nil, backend.ErrNamedStatesNotSupported
|
|
}
|
|
|
|
func (b *TestLocalSingleState) DeleteState(string) error {
|
|
return backend.ErrNamedStatesNotSupported
|
|
}
|
|
|
|
// TestNewLocalNoDefault is a factory for creating a TestLocalNoDefaultState.
|
|
// This function matches the signature required for backend/init.
|
|
func TestNewLocalNoDefault() backend.Backend {
|
|
return &TestLocalNoDefaultState{Local: &Local{}}
|
|
}
|
|
|
|
// TestLocalNoDefaultState is a backend implementation that wraps
|
|
// Local and modifies it to support named states, but not the
|
|
// default state. It returns ErrDefaultStateNotSupported when the
|
|
// DefaultStateName is used.
|
|
type TestLocalNoDefaultState struct {
|
|
*Local
|
|
}
|
|
|
|
func (b *TestLocalNoDefaultState) State(name string) (state.State, error) {
|
|
if name == backend.DefaultStateName {
|
|
return nil, backend.ErrDefaultStateNotSupported
|
|
}
|
|
return b.Local.State(name)
|
|
}
|
|
|
|
func (b *TestLocalNoDefaultState) States() ([]string, error) {
|
|
states, err := b.Local.States()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
filtered := states[:0]
|
|
for _, name := range states {
|
|
if name != backend.DefaultStateName {
|
|
filtered = append(filtered, name)
|
|
}
|
|
}
|
|
|
|
return filtered, nil
|
|
}
|
|
|
|
func (b *TestLocalNoDefaultState) DeleteState(name string) error {
|
|
if name == backend.DefaultStateName {
|
|
return backend.ErrDefaultStateNotSupported
|
|
}
|
|
return b.Local.DeleteState(name)
|
|
}
|
|
|
|
func testTempDir(t *testing.T) string {
|
|
d, err := ioutil.TempDir("", "tf")
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
return d
|
|
}
|
|
|
|
func testStateFile(t *testing.T, path string, s *states.State) {
|
|
stateFile := statemgr.NewFilesystem(path)
|
|
stateFile.WriteState(s)
|
|
}
|