From 8027fe9e08a9ac3d2f63aa093c27c3e18f15059f Mon Sep 17 00:00:00 2001 From: James Bardin Date: Mon, 27 Mar 2017 17:11:26 -0400 Subject: [PATCH 1/2] Don't Validate if we have an execution plan The plan file should contain all data required to execute the apply operation. Validation requires interpolation, and the `file()` interpolation function may fail if the module files are not present. This is the case currently with how TFE executes plans. --- command/meta_backend.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/command/meta_backend.go b/command/meta_backend.go index 5019c0242c..b30659dc0e 100644 --- a/command/meta_backend.go +++ b/command/meta_backend.go @@ -104,9 +104,13 @@ func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, error) { StateBackupPath: m.backupPath, ContextOpts: m.contextOpts(), Input: m.Input(), - Validation: true, } + // Don't validate if we have a plan. Validation is normally harmless here, + // but validation requires interpolation, and `file()` function calls may + // not have the original files in the current execution context. + cliOpts.Validation = opts.Plan == nil + // If the backend supports CLI initialization, do it. if cli, ok := b.(backend.CLI); ok { if err := cli.CLIInit(cliOpts); err != nil { From 2cffa25235b07ef997821b54634e31c3f6fba5ab Mon Sep 17 00:00:00 2001 From: James Bardin Date: Mon, 27 Mar 2017 18:39:18 -0400 Subject: [PATCH 2/2] Add test to verify that Validation isn't called The apply won't succeed because we don't have a valid plan, but this verifies that providing a plan file prevents Validation. --- command/apply_test.go | 33 +++++++++++++++++++ .../apply-plan-no-module/main.tf | 7 ++++ 2 files changed, 40 insertions(+) create mode 100644 command/test-fixtures/apply-plan-no-module/main.tf diff --git a/command/apply_test.go b/command/apply_test.go index 01c230326e..661d88c76c 100644 --- a/command/apply_test.go +++ b/command/apply_test.go @@ -802,6 +802,39 @@ func TestApply_planVars(t *testing.T) { } } +// we should be able to apply a plan file with no other file dependencies +func TestApply_planNoModuleFiles(t *testing.T) { + // temprary data directory which we can remove between commands + td, err := ioutil.TempDir("", "tf") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(td) + + defer testChdir(t, td)() + + p := testProvider() + planFile := testPlanFile(t, &terraform.Plan{ + Module: testModule(t, "apply-plan-no-module"), + }) + + contextOpts := testCtxConfig(p) + + apply := &ApplyCommand{ + Meta: Meta{ + ContextOpts: contextOpts, + Ui: new(cli.MockUi), + }, + } + args := []string{ + planFile, + } + apply.Run(args) + if p.ValidateCalled { + t.Fatal("Validate should not be called with a plan") + } +} + func TestApply_refresh(t *testing.T) { originalState := &terraform.State{ Modules: []*terraform.ModuleState{ diff --git a/command/test-fixtures/apply-plan-no-module/main.tf b/command/test-fixtures/apply-plan-no-module/main.tf new file mode 100644 index 0000000000..deea30d669 --- /dev/null +++ b/command/test-fixtures/apply-plan-no-module/main.tf @@ -0,0 +1,7 @@ +resource "test_instance" "tmpl" { + foo = "${file("${path.module}/template.txt")}" +} + +output "template" { + value = "${test_instance.tmpl.foo}" +}