2017-01-19 05:47:56 +01:00
|
|
|
package local
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2017-05-18 00:26:21 +02:00
|
|
|
"errors"
|
2017-01-19 05:47:56 +01:00
|
|
|
"fmt"
|
2017-02-16 01:00:59 +01:00
|
|
|
"os"
|
2017-05-18 00:26:21 +02:00
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
2017-01-19 05:47:56 +01:00
|
|
|
"sync"
|
|
|
|
"testing"
|
|
|
|
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 23:24:45 +02:00
|
|
|
"github.com/mitchellh/cli"
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
|
2017-01-19 05:47:56 +01:00
|
|
|
"github.com/hashicorp/terraform/backend"
|
2018-03-21 02:43:02 +01:00
|
|
|
"github.com/hashicorp/terraform/configs/configload"
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 23:24:45 +02:00
|
|
|
"github.com/hashicorp/terraform/configs/configschema"
|
|
|
|
"github.com/hashicorp/terraform/states"
|
|
|
|
"github.com/hashicorp/terraform/states/statemgr"
|
2017-01-19 05:47:56 +01:00
|
|
|
"github.com/hashicorp/terraform/terraform"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestLocal_applyBasic(t *testing.T) {
|
2018-03-28 16:54:08 +02:00
|
|
|
b, cleanup := TestLocal(t)
|
|
|
|
defer cleanup()
|
2018-05-23 04:57:04 +02:00
|
|
|
p := TestLocalProvider(t, b, "test", applyFixtureSchema())
|
2017-01-19 05:47:56 +01:00
|
|
|
|
|
|
|
p.ApplyReturn = &terraform.InstanceState{ID: "yes"}
|
|
|
|
|
2018-03-21 02:43:02 +01:00
|
|
|
op, configCleanup := testOperationApply(t, "./test-fixtures/apply")
|
|
|
|
defer configCleanup()
|
2017-01-19 05:47:56 +01:00
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
<-run.Done()
|
2018-03-21 02:43:02 +01:00
|
|
|
if run.Result != backend.OperationSuccess {
|
|
|
|
t.Fatal("operation failed")
|
2017-01-19 05:47:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if p.RefreshCalled {
|
|
|
|
t.Fatal("refresh should not be called")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !p.DiffCalled {
|
|
|
|
t.Fatal("diff should be called")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !p.ApplyCalled {
|
|
|
|
t.Fatal("apply should be called")
|
|
|
|
}
|
|
|
|
|
|
|
|
checkState(t, b.StateOutPath, `
|
|
|
|
test_instance.foo:
|
|
|
|
ID = yes
|
2017-11-08 03:23:08 +01:00
|
|
|
provider = provider.test
|
2017-01-19 05:47:56 +01:00
|
|
|
`)
|
|
|
|
}
|
|
|
|
|
2017-02-16 01:00:59 +01:00
|
|
|
func TestLocal_applyEmptyDir(t *testing.T) {
|
2018-03-28 16:54:08 +02:00
|
|
|
b, cleanup := TestLocal(t)
|
|
|
|
defer cleanup()
|
|
|
|
|
2018-05-23 04:57:04 +02:00
|
|
|
p := TestLocalProvider(t, b, "test", &terraform.ProviderSchema{})
|
2017-02-16 01:00:59 +01:00
|
|
|
|
|
|
|
p.ApplyReturn = &terraform.InstanceState{ID: "yes"}
|
|
|
|
|
2018-03-21 02:43:02 +01:00
|
|
|
op, configCleanup := testOperationApply(t, "./test-fixtures/empty")
|
|
|
|
defer configCleanup()
|
2017-02-16 01:00:59 +01:00
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
<-run.Done()
|
2018-03-21 02:43:02 +01:00
|
|
|
if run.Result == backend.OperationSuccess {
|
|
|
|
t.Fatal("operation succeeded; want error")
|
2017-02-16 01:00:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if p.ApplyCalled {
|
|
|
|
t.Fatal("apply should not be called")
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := os.Stat(b.StateOutPath); err == nil {
|
|
|
|
t.Fatal("should not exist")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestLocal_applyEmptyDirDestroy(t *testing.T) {
|
2018-03-28 16:54:08 +02:00
|
|
|
b, cleanup := TestLocal(t)
|
|
|
|
defer cleanup()
|
2018-05-23 04:57:04 +02:00
|
|
|
p := TestLocalProvider(t, b, "test", &terraform.ProviderSchema{})
|
2017-02-16 01:00:59 +01:00
|
|
|
|
|
|
|
p.ApplyReturn = nil
|
|
|
|
|
2018-03-21 02:43:02 +01:00
|
|
|
op, configCleanup := testOperationApply(t, "./test-fixtures/empty")
|
|
|
|
defer configCleanup()
|
2017-02-16 01:00:59 +01:00
|
|
|
op.Destroy = true
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
<-run.Done()
|
2018-03-21 02:43:02 +01:00
|
|
|
if run.Result != backend.OperationSuccess {
|
|
|
|
t.Fatalf("apply operation failed")
|
2017-02-16 01:00:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if p.ApplyCalled {
|
|
|
|
t.Fatal("apply should not be called")
|
|
|
|
}
|
|
|
|
|
|
|
|
checkState(t, b.StateOutPath, `<no state>`)
|
|
|
|
}
|
|
|
|
|
2017-01-19 05:47:56 +01:00
|
|
|
func TestLocal_applyError(t *testing.T) {
|
2018-03-28 16:54:08 +02:00
|
|
|
b, cleanup := TestLocal(t)
|
|
|
|
defer cleanup()
|
2018-05-23 04:57:04 +02:00
|
|
|
p := TestLocalProvider(t, b, "test", nil)
|
2017-01-19 05:47:56 +01:00
|
|
|
|
|
|
|
var lock sync.Mutex
|
|
|
|
errored := false
|
2018-05-23 04:57:04 +02:00
|
|
|
p.GetSchemaReturn = &terraform.ProviderSchema{
|
|
|
|
ResourceTypes: map[string]*configschema.Block{
|
|
|
|
"test_instance": {
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
"error": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2017-01-19 05:47:56 +01:00
|
|
|
p.ApplyFn = func(
|
|
|
|
info *terraform.InstanceInfo,
|
|
|
|
s *terraform.InstanceState,
|
|
|
|
d *terraform.InstanceDiff) (*terraform.InstanceState, error) {
|
|
|
|
lock.Lock()
|
|
|
|
defer lock.Unlock()
|
|
|
|
|
|
|
|
if !errored && info.Id == "test_instance.bar" {
|
|
|
|
errored = true
|
|
|
|
return nil, fmt.Errorf("error")
|
|
|
|
}
|
|
|
|
|
|
|
|
return &terraform.InstanceState{ID: "foo"}, nil
|
|
|
|
}
|
|
|
|
p.DiffFn = func(
|
|
|
|
*terraform.InstanceInfo,
|
|
|
|
*terraform.InstanceState,
|
|
|
|
*terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
|
|
|
|
return &terraform.InstanceDiff{
|
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"ami": &terraform.ResourceAttrDiff{
|
|
|
|
New: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2018-03-21 02:43:02 +01:00
|
|
|
op, configCleanup := testOperationApply(t, "./test-fixtures/apply-error")
|
|
|
|
defer configCleanup()
|
2017-01-19 05:47:56 +01:00
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
<-run.Done()
|
2018-03-21 02:43:02 +01:00
|
|
|
if run.Result == backend.OperationSuccess {
|
|
|
|
t.Fatal("operation succeeded; want failure")
|
2017-01-19 05:47:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
checkState(t, b.StateOutPath, `
|
|
|
|
test_instance.foo:
|
|
|
|
ID = foo
|
2017-11-08 03:23:08 +01:00
|
|
|
provider = provider.test
|
2017-01-19 05:47:56 +01:00
|
|
|
`)
|
|
|
|
}
|
|
|
|
|
2017-05-18 00:26:21 +02:00
|
|
|
func TestLocal_applyBackendFail(t *testing.T) {
|
2018-03-21 02:43:02 +01:00
|
|
|
op, configCleanup := testOperationApply(t, "./test-fixtures/apply")
|
|
|
|
defer configCleanup()
|
2017-05-18 00:26:21 +02:00
|
|
|
|
2018-03-28 16:54:08 +02:00
|
|
|
b, cleanup := TestLocal(t)
|
|
|
|
defer cleanup()
|
|
|
|
|
2017-05-18 00:26:21 +02:00
|
|
|
wd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("failed to get current working directory")
|
|
|
|
}
|
|
|
|
err = os.Chdir(filepath.Dir(b.StatePath))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("failed to set temporary working directory")
|
|
|
|
}
|
|
|
|
defer os.Chdir(wd)
|
|
|
|
|
|
|
|
b.Backend = &backendWithFailingState{}
|
|
|
|
b.CLI = new(cli.MockUi)
|
2018-05-23 04:57:04 +02:00
|
|
|
p := TestLocalProvider(t, b, "test", applyFixtureSchema())
|
2017-05-18 00:26:21 +02:00
|
|
|
|
|
|
|
p.ApplyReturn = &terraform.InstanceState{ID: "yes"}
|
|
|
|
|
|
|
|
run, err := b.Operation(context.Background(), op)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
<-run.Done()
|
2018-03-21 02:43:02 +01:00
|
|
|
if run.Result == backend.OperationSuccess {
|
2017-05-18 00:26:21 +02:00
|
|
|
t.Fatalf("apply succeeded; want error")
|
|
|
|
}
|
|
|
|
|
|
|
|
msgStr := b.CLI.(*cli.MockUi).ErrorWriter.String()
|
|
|
|
if !strings.Contains(msgStr, "Failed to save state: fake failure") {
|
2018-05-23 04:57:04 +02:00
|
|
|
t.Fatalf("missing \"fake failure\" message in output:\n%s", msgStr)
|
2017-05-18 00:26:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// The fallback behavior should've created a file errored.tfstate in the
|
|
|
|
// current working directory.
|
|
|
|
checkState(t, "errored.tfstate", `
|
|
|
|
test_instance.foo:
|
|
|
|
ID = yes
|
2017-11-08 03:23:08 +01:00
|
|
|
provider = provider.test
|
2017-05-18 00:26:21 +02:00
|
|
|
`)
|
|
|
|
}
|
|
|
|
|
|
|
|
type backendWithFailingState struct {
|
|
|
|
Local
|
|
|
|
}
|
|
|
|
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 23:24:45 +02:00
|
|
|
func (b *backendWithFailingState) StateMgr(name string) (statemgr.Full, error) {
|
2017-05-18 00:26:21 +02:00
|
|
|
return &failingState{
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 23:24:45 +02:00
|
|
|
statemgr.NewFilesystem("failing-state.tfstate"),
|
2017-05-18 00:26:21 +02:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type failingState struct {
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 23:24:45 +02:00
|
|
|
*statemgr.Filesystem
|
2017-05-18 00:26:21 +02:00
|
|
|
}
|
|
|
|
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 23:24:45 +02:00
|
|
|
func (s failingState) WriteState(state *states.State) error {
|
2017-05-18 00:26:21 +02:00
|
|
|
return errors.New("fake failure")
|
|
|
|
}
|
|
|
|
|
2018-03-21 02:43:02 +01:00
|
|
|
func testOperationApply(t *testing.T, configDir string) (*backend.Operation, func()) {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
_, configLoader, configCleanup := configload.MustLoadConfigForTests(t, configDir)
|
|
|
|
|
2017-01-19 05:47:56 +01:00
|
|
|
return &backend.Operation{
|
2018-03-21 02:43:02 +01:00
|
|
|
Type: backend.OperationTypeApply,
|
|
|
|
ConfigDir: configDir,
|
|
|
|
ConfigLoader: configLoader,
|
|
|
|
}, configCleanup
|
2017-01-19 05:47:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// testApplyState is just a common state that we use for testing refresh.
|
|
|
|
func testApplyState() *terraform.State {
|
|
|
|
return &terraform.State{
|
|
|
|
Version: 2,
|
|
|
|
Modules: []*terraform.ModuleState{
|
|
|
|
&terraform.ModuleState{
|
|
|
|
Path: []string{"root"},
|
|
|
|
Resources: map[string]*terraform.ResourceState{
|
|
|
|
"test_instance.foo": &terraform.ResourceState{
|
|
|
|
Type: "test_instance",
|
|
|
|
Primary: &terraform.InstanceState{
|
|
|
|
ID: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2018-05-23 04:57:04 +02:00
|
|
|
|
|
|
|
// applyFixtureSchema returns a schema suitable for processing the
|
|
|
|
// configuration in test-fixtures/apply . This schema should be
|
|
|
|
// assigned to a mock provider named "test".
|
|
|
|
func applyFixtureSchema() *terraform.ProviderSchema {
|
|
|
|
return &terraform.ProviderSchema{
|
|
|
|
ResourceTypes: map[string]*configschema.Block{
|
|
|
|
"test_instance": {
|
|
|
|
Attributes: map[string]*configschema.Attribute{
|
|
|
|
"ami": {Type: cty.String, Optional: true},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|