diff --git a/terraform/context_apply_test.go b/terraform/context_apply_test.go index 2bd97fd05..c9e09bedc 100644 --- a/terraform/context_apply_test.go +++ b/terraform/context_apply_test.go @@ -149,6 +149,39 @@ module.test: } } +func TestContext2Apply_refCount(t *testing.T) { + m := testModule(t, "apply-ref-count") + p := testProvider("aws") + p.ApplyFn = testApplyFn + p.DiffFn = testDiffFn + ctx := testContext2(t, &ContextOpts{ + Module: m, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + }) + + if _, err := ctx.Plan(); err != nil { + t.Fatalf("err: %s", err) + } + + state, err := ctx.Apply() + if err != nil { + t.Fatalf("err: %s", err) + } + + mod := state.RootModule() + if len(mod.Resources) < 2 { + t.Fatalf("bad: %#v", mod.Resources) + } + + actual := strings.TrimSpace(state.String()) + expected := strings.TrimSpace(testTerraformApplyRefCountStr) + if actual != expected { + t.Fatalf("bad: \n%s", actual) + } +} + func TestContext2Apply_providerAlias(t *testing.T) { m := testModule(t, "apply-provider-alias") p := testProvider("aws") @@ -1123,6 +1156,34 @@ func TestContext2Apply_countVariable(t *testing.T) { } } +func TestContext2Apply_countVariableRef(t *testing.T) { + m := testModule(t, "apply-count-variable-ref") + p := testProvider("aws") + p.ApplyFn = testApplyFn + p.DiffFn = testDiffFn + ctx := testContext2(t, &ContextOpts{ + Module: m, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + }) + + if _, err := ctx.Plan(); err != nil { + t.Fatalf("err: %s", err) + } + + state, err := ctx.Apply() + if err != nil { + t.Fatalf("err: %s", err) + } + + actual := strings.TrimSpace(state.String()) + expected := strings.TrimSpace(testTerraformApplyCountVariableRefStr) + if actual != expected { + t.Fatalf("bad: \n%s", actual) + } +} + func TestContext2Apply_mapVariableOverride(t *testing.T) { m := testModule(t, "apply-map-var-override") p := testProvider("aws") @@ -2405,6 +2466,59 @@ func TestContext2Apply_provisionerMultiSelfRef(t *testing.T) { } } +func TestContext2Apply_provisionerMultiSelfRefCount(t *testing.T) { + var lock sync.Mutex + commands := make([]string, 0, 5) + + m := testModule(t, "apply-provisioner-multi-self-ref-count") + p := testProvider("aws") + pr := testProvisioner() + p.ApplyFn = testApplyFn + p.DiffFn = testDiffFn + pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { + lock.Lock() + defer lock.Unlock() + + val, ok := c.Config["command"] + if !ok { + t.Fatalf("bad value for command: %v %#v", val, c) + } + + commands = append(commands, val.(string)) + return nil + } + + ctx := testContext2(t, &ContextOpts{ + Module: m, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + Provisioners: map[string]ResourceProvisionerFactory{ + "shell": testProvisionerFuncFixed(pr), + }, + }) + + if _, err := ctx.Plan(); err != nil { + t.Fatalf("err: %s", err) + } + + if _, err := ctx.Apply(); err != nil { + t.Fatalf("err: %s", err) + } + + // Verify apply was invoked + if !pr.ApplyCalled { + t.Fatalf("provisioner not invoked") + } + + // Verify our result + sort.Strings(commands) + expectedCommands := []string{"3", "3", "3"} + if !reflect.DeepEqual(commands, expectedCommands) { + t.Fatalf("bad: %#v", commands) + } +} + // Provisioner should NOT run on a diff, only create func TestContext2Apply_Provisioner_Diff(t *testing.T) { m := testModule(t, "apply-provisioner-diff") diff --git a/terraform/interpolate.go b/terraform/interpolate.go index b42ca8211..2117fc3f0 100644 --- a/terraform/interpolate.go +++ b/terraform/interpolate.go @@ -265,6 +265,7 @@ func (i *Interpolater) valueSelfVar( return fmt.Errorf( "%s: invalid scope, self variables are only valid on resources", n) } + rv, err := config.NewResourceVariable(fmt.Sprintf( "%s.%s.%d.%s", scope.Resource.Type, @@ -359,11 +360,25 @@ func (i *Interpolater) computeResourceVariable( // Get the information about this resource variable, and verify // that it exists and such. - module, _, err := i.resourceVariableInfo(scope, v) + module, cr, err := i.resourceVariableInfo(scope, v) if err != nil { return nil, err } + // If we're requesting "count" its a special variable that we grab + // directly from the config itself. + if v.Field == "count" { + count, err := cr.Count() + if err != nil { + return nil, fmt.Errorf( + "Error reading %s count: %s", + v.ResourceId(), + err) + } + + return &ast.Variable{Type: ast.TypeInt, Value: count}, nil + } + // If we have no module in the state yet or count, return empty if module == nil || len(module.Resources) == 0 { return nil, nil diff --git a/terraform/terraform_test.go b/terraform/terraform_test.go index e6dc575e5..f8244ce00 100644 --- a/terraform/terraform_test.go +++ b/terraform/terraform_test.go @@ -238,6 +238,22 @@ aws_instance.foo: type = aws_instance ` +const testTerraformApplyRefCountStr = ` +aws_instance.bar: + ID = foo + foo = 3 + type = aws_instance + + Dependencies: + aws_instance.foo +aws_instance.foo.0: + ID = foo +aws_instance.foo.1: + ID = foo +aws_instance.foo.2: + ID = foo +` + const testTerraformApplyProviderAliasStr = ` aws_instance.bar: ID = foo @@ -370,6 +386,20 @@ aws_instance.foo.1: type = aws_instance ` +const testTerraformApplyCountVariableRefStr = ` +aws_instance.bar: + ID = foo + foo = 2 + type = aws_instance + + Dependencies: + aws_instance.foo +aws_instance.foo.0: + ID = foo +aws_instance.foo.1: + ID = foo +` + const testTerraformApplyMinimalStr = ` aws_instance.bar: ID = foo diff --git a/terraform/test-fixtures/apply-count-variable-ref/main.tf b/terraform/test-fixtures/apply-count-variable-ref/main.tf new file mode 100644 index 000000000..52c70d90d --- /dev/null +++ b/terraform/test-fixtures/apply-count-variable-ref/main.tf @@ -0,0 +1,11 @@ +variable "foo" { + default = "2" +} + +resource "aws_instance" "foo" { + count = "${var.foo}" +} + +resource "aws_instance" "bar" { + foo = "${aws_instance.foo.count}" +} diff --git a/terraform/test-fixtures/apply-provisioner-multi-self-ref-count/main.tf b/terraform/test-fixtures/apply-provisioner-multi-self-ref-count/main.tf new file mode 100644 index 000000000..c8b83d7c0 --- /dev/null +++ b/terraform/test-fixtures/apply-provisioner-multi-self-ref-count/main.tf @@ -0,0 +1,7 @@ +resource "aws_instance" "foo" { + count = 3 + + provisioner "shell" { + command = "${self.count}" + } +} diff --git a/terraform/test-fixtures/apply-ref-count/main.tf b/terraform/test-fixtures/apply-ref-count/main.tf new file mode 100644 index 000000000..8669ba572 --- /dev/null +++ b/terraform/test-fixtures/apply-ref-count/main.tf @@ -0,0 +1,7 @@ +resource "aws_instance" "foo" { + count = 3 +} + +resource "aws_instance" "bar" { + foo = "${aws_instance.foo.count}" +}