diff --git a/command/apply_test.go b/command/apply_test.go index 70a6b03f4..2d6e71ea9 100644 --- a/command/apply_test.go +++ b/command/apply_test.go @@ -4,7 +4,6 @@ import ( "bytes" "fmt" "io/ioutil" - "log" "net" "net/http" "net/url" @@ -32,18 +31,7 @@ import ( func TestApply(t *testing.T) { statePath := testTempFile(t) - p := testProvider() - p.GetSchemaReturn = applyFixtureSchema() - p.PlanResourceChangeFn = func (req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { - return providers.PlanResourceChangeResponse{ - PlannedState: req.ProposedNewState, - } - } - p.ApplyResourceChangeFn = func (req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { - return providers.ApplyResourceChangeResponse{ - NewState: req.PlannedState, - } - } + p := applyFixtureProvider() ui := new(cli.MockUi) c := &ApplyCommand{ @@ -82,7 +70,7 @@ func TestApply_lockedState(t *testing.T) { } defer unlock() - p := testProvider() + p := applyFixtureProvider() ui := new(cli.MockUi) c := &ApplyCommand{ Meta: Meta{ @@ -108,7 +96,6 @@ func TestApply_lockedState(t *testing.T) { // test apply with locked state, waiting for unlock func TestApply_lockedStateWait(t *testing.T) { - t.Fatalf("FIXME: this test seems to be making the test program prematurely exit") statePath := testTempFile(t) unlock, err := testLockState("./testdata", statePath) @@ -122,7 +109,7 @@ func TestApply_lockedStateWait(t *testing.T) { unlock() }() - p := testProvider() + p := applyFixtureProvider() ui := new(cli.MockUi) c := &ApplyCommand{ Meta: Meta{ @@ -140,7 +127,7 @@ func TestApply_lockedStateWait(t *testing.T) { testFixturePath("apply"), } if code := c.Run(args); code != 0 { - log.Fatalf("lock should have succeed in less than 3s: %s", ui.ErrorWriter) + t.Fatalf("lock should have succeeded in less than 3s: %s", ui.ErrorWriter) } } @@ -173,12 +160,11 @@ func (t *hwm) Max() int { } func TestApply_parallelism(t *testing.T) { - provider := testProvider() statePath := testTempFile(t) par := 4 - // This blocks all the appy functions. We close it when we exit so + // This blocks all the apply functions. We close it when we exit so // they end quickly after this test finishes. block := make(chan struct{}) // signal how many goroutines have started @@ -186,24 +172,46 @@ func TestApply_parallelism(t *testing.T) { runCount := &hwm{} - provider.ApplyFn = func( - i *terraform.InstanceInfo, - s *terraform.InstanceState, - d *terraform.InstanceDiff) (*terraform.InstanceState, error) { - // Increment so we're counting parallelism - started <- 1 - runCount.Inc() - defer runCount.Dec() - // Block here to stage up our max number of parallel instances - <-block + // Since our mock provider has its own mutex preventing concurrent calls + // to ApplyResourceChange, we need to use a number of separate providers + // here. They will all have the same mock implementation function assigned + // but crucially they will each have their own mutex. + providerFactories := map[string]providers.Factory{} + for i := 0; i < 10; i++ { + name := fmt.Sprintf("test%d", i) + provider := &terraform.MockProvider{} + provider.GetSchemaReturn = &terraform.ProviderSchema{ + ResourceTypes: map[string]*configschema.Block{ + name+"_instance": {}, + }, + } + provider.PlanResourceChangeFn = func (req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { + return providers.PlanResourceChangeResponse{ + PlannedState: req.ProposedNewState, + } + } + provider.ApplyResourceChangeFn = func (req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { + // Increment so we're counting parallelism + started <- 1 + runCount.Inc() + defer runCount.Dec() + // Block here to stage up our max number of parallel instances + <-block - return nil, nil + return providers.ApplyResourceChangeResponse{ + NewState: cty.EmptyObjectVal, + } + } + providerFactories[name] = providers.FactoryFixed(provider) + } + testingOverrides := &testingOverrides{ + ProviderResolver: providers.ResolverFixed(providerFactories), } ui := new(cli.MockUi) c := &ApplyCommand{ Meta: Meta{ - testingOverrides: metaOverridesForProvider(provider), + testingOverrides: testingOverrides, Ui: ui, }, } @@ -286,7 +294,7 @@ func TestApply_defaultState(t *testing.T) { } defer os.Chdir(cwd) - p := testProvider() + p := applyFixtureProvider() ui := new(cli.MockUi) c := &ApplyCommand{ Meta: Meta{ @@ -774,7 +782,7 @@ func TestApply_refresh(t *testing.T) { }) statePath := testStateFile(t, originalState) - p := testProvider() + p := applyFixtureProvider() ui := new(cli.MockUi) c := &ApplyCommand{ Meta: Meta{ @@ -918,7 +926,7 @@ func TestApply_state(t *testing.T) { }) statePath := testStateFile(t, originalState) - p := testProvider() + p := applyFixtureProvider() p.PlanResourceChangeResponse = providers.PlanResourceChangeResponse{ PlannedState: cty.ObjectVal(map[string]cty.Value{ "ami": cty.StringVal("bar"), @@ -980,7 +988,7 @@ func TestApply_state(t *testing.T) { } func TestApply_stateNoExist(t *testing.T) { - p := testProvider() + p := applyFixtureProvider() ui := new(cli.MockUi) c := &ApplyCommand{ Meta: Meta{ @@ -1236,7 +1244,7 @@ func TestApply_backup(t *testing.T) { statePath := testStateFile(t, originalState) backupPath := testTempFile(t) - p := testProvider() + p := applyFixtureProvider() p.PlanResourceChangeResponse = providers.PlanResourceChangeResponse{ PlannedState: cty.ObjectVal(map[string]cty.Value{ "ami": cty.StringVal("bar"), @@ -1285,7 +1293,7 @@ func TestApply_disableBackup(t *testing.T) { originalState := testState() statePath := testStateFile(t, originalState) - p := testProvider() + p := applyFixtureProvider() p.PlanResourceChangeResponse = providers.PlanResourceChangeResponse{ PlannedState: cty.ObjectVal(map[string]cty.Value{ "ami": cty.StringVal("bar"), @@ -1478,6 +1486,27 @@ func applyFixtureSchema() *terraform.ProviderSchema { } } +// applyFixtureProvider returns a mock provider that is configured for basic +// operation with the configuration in test-fixtures/apply. This mock has +// GetSchemaReturn, PlanResourceChangeFn, and ApplyResourceChangeFn populated, +// with the plan/apply steps just passing through the data determined by +// Terraform Core. +func applyFixtureProvider() *terraform.MockProvider { + p := testProvider() + p.GetSchemaReturn = applyFixtureSchema() + p.PlanResourceChangeFn = func (req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { + return providers.PlanResourceChangeResponse{ + PlannedState: req.ProposedNewState, + } + } + p.ApplyResourceChangeFn = func (req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { + return providers.ApplyResourceChangeResponse{ + NewState: req.PlannedState, + } + } + return p +} + const applyVarFile = ` foo = "bar" ` diff --git a/command/command_test.go b/command/command_test.go index 9fa2d3500..38e81f7c0 100644 --- a/command/command_test.go +++ b/command/command_test.go @@ -154,7 +154,26 @@ func testModuleWithSnapshot(t *testing.T, name string) (*configs.Config, *config // testPlan returns a non-nil noop plan. func testPlan(t *testing.T) *plans.Plan { t.Helper() + + // This is what an empty configuration block would look like after being + // decoded with the schema of the "local" backend. + backendConfig := cty.ObjectVal(map[string]cty.Value{ + "path": cty.NullVal(cty.String), + "workspace_dir": cty.NullVal(cty.String), + }) + backendConfigRaw, err := plans.NewDynamicValue(backendConfig, backendConfig.Type()) + if err != nil { + t.Fatal(err) + } + return &plans.Plan{ + Backend: plans.Backend{ + // This is just a placeholder so that the plan file can be written + // out. Caller may wish to override it to something more "real" + // where the plan will actually be subsequently applied. + Type: "local", + Config: backendConfigRaw, + }, Changes: plans.NewChanges(), } } diff --git a/command/test-fixtures/apply-input-partial/main.tf b/command/test-fixtures/apply-input-partial/main.tf index 8c80dd09c..85ada15d2 100644 --- a/command/test-fixtures/apply-input-partial/main.tf +++ b/command/test-fixtures/apply-input-partial/main.tf @@ -1,5 +1,9 @@ variable "foo" {} variable "bar" {} -output "foo" { value = "${var.foo}" } -output "bar" { value = "${var.bar}" } +output "foo" { + value = "${var.foo}" +} +output "bar" { + value = "${var.bar}" +} diff --git a/command/test-fixtures/apply-terraform-env/main.tf b/command/test-fixtures/apply-terraform-env/main.tf index 6fc63dbc5..3866960d2 100644 --- a/command/test-fixtures/apply-terraform-env/main.tf +++ b/command/test-fixtures/apply-terraform-env/main.tf @@ -1 +1,3 @@ -output "output" { value = "${terraform.env}" } +output "output" { + value = terraform.workspace +} diff --git a/command/test-fixtures/parallelism/main.tf b/command/test-fixtures/parallelism/main.tf index dabb85a93..6032c62d1 100644 --- a/command/test-fixtures/parallelism/main.tf +++ b/command/test-fixtures/parallelism/main.tf @@ -1,10 +1,10 @@ -resource "test_instance" "foo1" {} -resource "test_instance" "foo2" {} -resource "test_instance" "foo3" {} -resource "test_instance" "foo4" {} -resource "test_instance" "foo5" {} -resource "test_instance" "foo6" {} -resource "test_instance" "foo7" {} -resource "test_instance" "foo8" {} -resource "test_instance" "foo9" {} -resource "test_instance" "foo10" {} +resource "test0_instance" "foo" {} +resource "test1_instance" "foo" {} +resource "test2_instance" "foo" {} +resource "test3_instance" "foo" {} +resource "test4_instance" "foo" {} +resource "test5_instance" "foo" {} +resource "test6_instance" "foo" {} +resource "test7_instance" "foo" {} +resource "test8_instance" "foo" {} +resource "test9_instance" "foo" {}