From 4c3923e32a0cea6d294c5f94717918f10b31add6 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 10 Apr 2015 13:51:22 -0700 Subject: [PATCH] terraform: return value for resource interpolation on refresh Instead of returning UnknownVariableValue every time, attempt to return the real value. If we don't find it, return unknown value. This fixes removing outputs from state on refresh. --- terraform/context_test.go | 58 +++++++++++++++++++ terraform/interpolate.go | 10 +++- .../test-fixtures/refresh-output/main.tf | 5 ++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 terraform/test-fixtures/refresh-output/main.tf diff --git a/terraform/context_test.go b/terraform/context_test.go index beb257e3c..56dd6ea6e 100644 --- a/terraform/context_test.go +++ b/terraform/context_test.go @@ -1845,6 +1845,54 @@ func TestContext2Refresh_noState(t *testing.T) { } } +func TestContext2Refresh_output(t *testing.T) { + p := testProvider("aws") + m := testModule(t, "refresh-output") + ctx := testContext2(t, &ContextOpts{ + Module: m, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + State: &State{ + Modules: []*ModuleState{ + &ModuleState{ + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "aws_instance.web": &ResourceState{ + Type: "aws_instance", + Primary: &InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "foo": "bar", + }, + }, + }, + }, + + Outputs: map[string]string{ + "foo": "foo", + }, + }, + }, + }, + }) + + p.RefreshFn = func(info *InstanceInfo, s *InstanceState) (*InstanceState, error) { + return s, nil + } + + s, err := ctx.Refresh() + if err != nil { + t.Fatalf("err: %s", err) + } + + actual := strings.TrimSpace(s.String()) + expected := strings.TrimSpace(testContextRefreshOutputStr) + if actual != expected { + t.Fatalf("bad:\n\n%s\n\n%s", actual, expected) + } +} + func TestContext2Refresh_outputPartial(t *testing.T) { p := testProvider("aws") m := testModule(t, "refresh-output-partial") @@ -5995,6 +6043,16 @@ module.child: ID = new ` +const testContextRefreshOutputStr = ` +aws_instance.web: + ID = foo + foo = bar + +Outputs: + +foo = bar +` + const testContextRefreshOutputPartialStr = ` ` diff --git a/terraform/interpolate.go b/terraform/interpolate.go index cf88ad825..a1e6d37af 100644 --- a/terraform/interpolate.go +++ b/terraform/interpolate.go @@ -193,7 +193,7 @@ func (i *Interpolater) valueResourceVar( result map[string]ast.Variable) error { // If we're computing all dynamic fields, then module vars count // and we mark it as computed. - if i.Operation == walkValidate || i.Operation == walkRefresh { + if i.Operation == walkValidate { result[n] = ast.Variable{ Value: config.UnknownVariableValue, Type: ast.TypeString, @@ -353,6 +353,14 @@ func (i *Interpolater) computeResourceVariable( } MISSING: + // If the operation is refresh, it isn't an error for a value to + // be unknown. Instead, we return that the value is computed so + // that the graph can continue to refresh other nodes. It doesn't + // matter because the config isn't interpolated anyways. + if i.Operation == walkRefresh { + return config.UnknownVariableValue, nil + } + return "", fmt.Errorf( "Resource '%s' does not have attribute '%s' "+ "for variable '%s'", diff --git a/terraform/test-fixtures/refresh-output/main.tf b/terraform/test-fixtures/refresh-output/main.tf new file mode 100644 index 000000000..42a01bd5c --- /dev/null +++ b/terraform/test-fixtures/refresh-output/main.tf @@ -0,0 +1,5 @@ +resource "aws_instance" "web" {} + +output "foo" { + value = "${aws_instance.web.foo}" +}