package terraform import ( "bytes" "fmt" "reflect" "sort" "strings" "sync" "testing" ) func TestContext2Plan(t *testing.T) { m := testModule(t, "plan-good") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } if len(plan.Diff.RootModule().Resources) < 2 { t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContext2Plan_emptyDiff(t *testing.T) { m := testModule(t, "plan-empty") p := testProvider("aws") p.DiffFn = func( info *InstanceInfo, s *InstanceState, c *ResourceConfig) (*InstanceDiff, error) { return nil, nil } ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanEmptyStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContext2Plan_minimal(t *testing.T) { m := testModule(t, "plan-empty") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanEmptyStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContext2Plan_modules(t *testing.T) { m := testModule(t, "plan-modules") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanModulesStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContext2Plan_moduleInput(t *testing.T) { m := testModule(t, "plan-module-input") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanModuleInputStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContext2Plan_moduleInputComputed(t *testing.T) { m := testModule(t, "plan-module-input-computed") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanModuleInputComputedStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContext2Plan_moduleInputFromVar(t *testing.T) { m := testModule(t, "plan-module-input-var") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Variables: map[string]string{ "foo": "52", }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanModuleInputVarStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContext2Plan_moduleMultiVar(t *testing.T) { m := testModule(t, "plan-module-multi-var") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanModuleMultiVarStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContext2Plan_moduleOrphans(t *testing.T) { m := testModule(t, "plan-modules-remove") p := testProvider("aws") p.DiffFn = testDiffFn s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: []string{"root", "child"}, Resources: map[string]*ResourceState{ "aws_instance.foo": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "baz", }, }, }, }, }, } ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanModuleOrphansStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContext2Plan_moduleProviderInherit(t *testing.T) { var l sync.Mutex var calls []string m := testModule(t, "plan-module-provider-inherit") ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": func() (ResourceProvider, error) { l.Lock() defer l.Unlock() p := testProvider("aws") p.ConfigureFn = func(c *ResourceConfig) error { if v, ok := c.Get("from"); !ok || v.(string) != "root" { return fmt.Errorf("bad") } return nil } p.DiffFn = func( info *InstanceInfo, state *InstanceState, c *ResourceConfig) (*InstanceDiff, error) { v, _ := c.Get("from") calls = append(calls, v.(string)) return testDiffFn(info, state, c) } return p, nil }, }, }) _, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := calls sort.Strings(actual) expected := []string{"child", "root"} if !reflect.DeepEqual(actual, expected) { t.Fatalf("bad: %#v", actual) } } func TestContext2Plan_moduleProviderDefaults(t *testing.T) { var l sync.Mutex var calls []string toCount := 0 m := testModule(t, "plan-module-provider-defaults") ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": func() (ResourceProvider, error) { l.Lock() defer l.Unlock() p := testProvider("aws") p.ConfigureFn = func(c *ResourceConfig) error { if v, ok := c.Get("from"); !ok || v.(string) != "root" { return fmt.Errorf("bad") } if v, ok := c.Get("to"); ok && v.(string) == "child" { toCount++ } return nil } p.DiffFn = func( info *InstanceInfo, state *InstanceState, c *ResourceConfig) (*InstanceDiff, error) { v, _ := c.Get("from") calls = append(calls, v.(string)) return testDiffFn(info, state, c) } return p, nil }, }, }) _, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } if toCount != 1 { t.Fatalf( "provider in child didn't set proper config\n\n"+ "toCount: %d", toCount) } actual := calls sort.Strings(actual) expected := []string{"child", "root"} if !reflect.DeepEqual(actual, expected) { t.Fatalf("bad: %#v", actual) } } func TestContext2Plan_moduleProviderDefaultsVar(t *testing.T) { var l sync.Mutex var calls []string m := testModule(t, "plan-module-provider-defaults-var") ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": func() (ResourceProvider, error) { l.Lock() defer l.Unlock() p := testProvider("aws") p.ConfigureFn = func(c *ResourceConfig) error { var buf bytes.Buffer if v, ok := c.Get("from"); ok { buf.WriteString(v.(string) + "\n") } if v, ok := c.Get("to"); ok { buf.WriteString(v.(string) + "\n") } calls = append(calls, buf.String()) return nil } p.DiffFn = testDiffFn return p, nil }, }, Variables: map[string]string{ "foo": "root", }, }) _, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } expected := []string{ "root\n", "root\nchild\n", } if !reflect.DeepEqual(calls, expected) { t.Fatalf("BAD: %#v", calls) } } func TestContext2Plan_moduleVar(t *testing.T) { m := testModule(t, "plan-module-var") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanModuleVarStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContext2Plan_moduleVarComputed(t *testing.T) { m := testModule(t, "plan-module-var-computed") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanModuleVarComputedStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContext2Plan_nil(t *testing.T) { m := testModule(t, "plan-nil") p := testProvider("aws") p.DiffFn = testDiffFn 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.foo": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", }, }, }, }, }, }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } if len(plan.Diff.RootModule().Resources) != 0 { t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources) } } func TestContext2Plan_computed(t *testing.T) { m := testModule(t, "plan-computed") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanComputedStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContext2Plan_computedList(t *testing.T) { m := testModule(t, "plan-computed-list") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanComputedListStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContext2Plan_count(t *testing.T) { m := testModule(t, "plan-count") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } if len(plan.Diff.RootModule().Resources) < 6 { t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanCountStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContext2Plan_countComputed(t *testing.T) { m := testModule(t, "plan-count-computed") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) _, err := ctx.Plan(nil) if err == nil { t.Fatal("should error") } } /* func TestContextPlan_countIndex(t *testing.T) { m := testModule(t, "plan-count-index") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanCountIndexStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContextPlan_countIndexZero(t *testing.T) { m := testModule(t, "plan-count-index-zero") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanCountIndexZeroStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContextPlan_countVar(t *testing.T) { m := testModule(t, "plan-count-var") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Variables: map[string]string{ "count": "3", }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanCountVarStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContextPlan_countZero(t *testing.T) { m := testModule(t, "plan-count-zero") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanCountZeroStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContextPlan_countOneIndex(t *testing.T) { m := testModule(t, "plan-count-one-index") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanCountOneIndexStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContextPlan_countDecreaseToOne(t *testing.T) { m := testModule(t, "plan-count-dec") p := testProvider("aws") p.DiffFn = testDiffFn s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.foo.0": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", Attributes: map[string]string{ "foo": "foo", "type": "aws_instance", }, }, }, "aws_instance.foo.1": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", }, }, "aws_instance.foo.2": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanCountDecreaseStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContextPlan_countIncreaseFromNotSet(t *testing.T) { m := testModule(t, "plan-count-inc") p := testProvider("aws") p.DiffFn = testDiffFn s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.foo": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", Attributes: map[string]string{ "foo": "foo", "type": "aws_instance", }, }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanCountIncreaseStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContextPlan_countIncreaseFromOne(t *testing.T) { m := testModule(t, "plan-count-inc") p := testProvider("aws") p.DiffFn = testDiffFn s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.foo.0": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", Attributes: map[string]string{ "foo": "foo", "type": "aws_instance", }, }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanCountIncreaseFromOneStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContextPlan_destroy(t *testing.T) { m := testModule(t, "plan-destroy") p := testProvider("aws") p.DiffFn = testDiffFn s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.one": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", }, }, "aws_instance.two": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "baz", }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) plan, err := ctx.Plan(&PlanOpts{Destroy: true}) if err != nil { t.Fatalf("err: %s", err) } if len(plan.Diff.RootModule().Resources) != 2 { t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanDestroyStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContextPlan_moduleDestroy(t *testing.T) { m := testModule(t, "plan-module-destroy") p := testProvider("aws") p.DiffFn = testDiffFn s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.foo": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", }, }, }, }, &ModuleState{ Path: []string{"root", "child"}, Resources: map[string]*ResourceState{ "aws_instance.foo": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) plan, err := ctx.Plan(&PlanOpts{Destroy: true}) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanModuleDestroyStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContextPlan_moduleDestroyMultivar(t *testing.T) { m := testModule(t, "plan-module-destroy-multivar") p := testProvider("aws") p.DiffFn = testDiffFn s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{}, }, &ModuleState{ Path: []string{"root", "child"}, Resources: map[string]*ResourceState{ "aws_instance.foo.0": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar0", }, }, "aws_instance.foo.1": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar1", }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) plan, err := ctx.Plan(&PlanOpts{Destroy: true}) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanModuleDestroyMultivarStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContextPlan_pathVar(t *testing.T) { cwd, err := os.Getwd() if err != nil { t.Fatalf("err: %s", err) } m := testModule(t, "plan-path-var") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanPathVarStr) // Warning: this ordering REALLY matters for this test. The // order is: cwd, module, root. expected = fmt.Sprintf( expected, cwd, m.Config().Dir, m.Config().Dir) if actual != expected { t.Fatalf("bad:\n%s\n\nexpected:\n\n%s", actual, expected) } } func TestContextPlan_diffVar(t *testing.T) { m := testModule(t, "plan-diffvar") p := testProvider("aws") s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.foo": &ResourceState{ Primary: &InstanceState{ ID: "bar", Attributes: map[string]string{ "num": "2", }, }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) p.DiffFn = func( info *InstanceInfo, s *InstanceState, c *ResourceConfig) (*InstanceDiff, error) { if s.ID != "bar" { return testDiffFn(info, s, c) } return &InstanceDiff{ Attributes: map[string]*ResourceAttrDiff{ "num": &ResourceAttrDiff{ Old: "2", New: "3", }, }, }, nil } plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanDiffVarStr) if actual != expected { t.Fatalf("actual:\n%s\n\nexpected:\n%s", actual, expected) } } func TestContextPlan_hook(t *testing.T) { m := testModule(t, "plan-good") h := new(MockHook) p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Hooks: []Hook{h}, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) _, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } if !h.PreDiffCalled { t.Fatal("should be called") } if !h.PostDiffCalled { t.Fatal("should be called") } } func TestContextPlan_orphan(t *testing.T) { m := testModule(t, "plan-orphan") p := testProvider("aws") p.DiffFn = testDiffFn s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.baz": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanOrphanStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContextPlan_state(t *testing.T) { m := testModule(t, "plan-good") p := testProvider("aws") p.DiffFn = testDiffFn s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.foo": &ResourceState{ Primary: &InstanceState{ ID: "bar", }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } if len(plan.Diff.RootModule().Resources) < 2 { t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanStateStr) if actual != expected { t.Fatalf("bad:\n%s\n\nexpected:\n\n%s", actual, expected) } } func TestContextPlan_taint(t *testing.T) { m := testModule(t, "plan-taint") p := testProvider("aws") p.DiffFn = testDiffFn s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.foo": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", Attributes: map[string]string{"num": "2"}, }, }, "aws_instance.bar": &ResourceState{ Type: "aws_instance", Tainted: []*InstanceState{ &InstanceState{ ID: "baz", }, }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanTaintStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } // Doing a Refresh (or any operation really, but Refresh usually // happens first) with a config with an unknown provider should result in // an error. The key bug this found was that this wasn't happening if // Providers was _empty_. func TestContextRefresh_unknownProvider(t *testing.T) { m := testModule(t, "refresh-unknown-provider") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{}, }) if _, err := ctx.Refresh(); err == nil { t.Fatal("should error") } } func TestContextPlan_multiple_taint(t *testing.T) { m := testModule(t, "plan-taint") p := testProvider("aws") p.DiffFn = testDiffFn s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.foo": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", Attributes: map[string]string{"num": "2"}, }, }, "aws_instance.bar": &ResourceState{ Type: "aws_instance", Tainted: []*InstanceState{ &InstanceState{ ID: "baz", }, &InstanceState{ ID: "zip", }, }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanMultipleTaintStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContextPlan_provider(t *testing.T) { m := testModule(t, "plan-provider") p := testProvider("aws") p.DiffFn = testDiffFn var value interface{} p.ConfigureFn = func(c *ResourceConfig) error { value, _ = c.Get("foo") return nil } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Variables: map[string]string{ "foo": "bar", }, }) if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } if value != "bar" { t.Fatalf("bad: %#v", value) } } func TestContextPlan_varMultiCountOne(t *testing.T) { m := testModule(t, "plan-var-multi-count-one") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) plan, err := ctx.Plan(nil) if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(plan.String()) expected := strings.TrimSpace(testTerraformPlanVarMultiCountOneStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContextPlan_varListErr(t *testing.T) { m := testModule(t, "plan-var-list-err") p := testProvider("aws") ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) _, err := ctx.Plan(nil) if err == nil { t.Fatal("should error") } } */ func TestContext2Refresh(t *testing.T) { p := testProvider("aws") m := testModule(t, "refresh-basic") 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", }, }, }, }, }, }, }) p.RefreshFn = nil p.RefreshReturn = &InstanceState{ ID: "foo", } s, err := ctx.Refresh() mod := s.RootModule() if err != nil { t.Fatalf("err: %s", err) } if !p.RefreshCalled { t.Fatal("refresh should be called") } if p.RefreshState.ID != "foo" { t.Fatalf("bad: %#v", p.RefreshState) } if !reflect.DeepEqual(mod.Resources["aws_instance.web"].Primary, p.RefreshReturn) { t.Fatalf("bad: %#v %#v", mod.Resources["aws_instance.web"], p.RefreshReturn) } for _, r := range mod.Resources { if r.Type == "" { t.Fatalf("no type: %#v", r) } } } func TestContext2Refresh_delete(t *testing.T) { p := testProvider("aws") m := testModule(t, "refresh-basic") 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", }, }, }, }, }, }, }) p.RefreshFn = nil p.RefreshReturn = nil s, err := ctx.Refresh() if err != nil { t.Fatalf("err: %s", err) } mod := s.RootModule() if len(mod.Resources) > 0 { t.Fatal("resources should be empty") } } func TestContext2Refresh_ignoreUncreated(t *testing.T) { p := testProvider("aws") m := testModule(t, "refresh-basic") ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: nil, }) p.RefreshFn = nil p.RefreshReturn = &InstanceState{ ID: "foo", } _, err := ctx.Refresh() if err != nil { t.Fatalf("err: %s", err) } if p.RefreshCalled { t.Fatal("refresh should not be called") } } func TestContext2Refresh_hook(t *testing.T) { h := new(MockHook) p := testProvider("aws") m := testModule(t, "refresh-basic") ctx := testContext2(t, &ContextOpts{ Module: m, Hooks: []Hook{h}, 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", }, }, }, }, }, }, }) if _, err := ctx.Refresh(); err != nil { t.Fatalf("err: %s", err) } if !h.PreRefreshCalled { t.Fatal("should be called") } if !h.PostRefreshCalled { t.Fatal("should be called") } } func TestContext2Refresh_modules(t *testing.T) { p := testProvider("aws") m := testModule(t, "refresh-modules") state := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.web": &ResourceState{ Type: "aws_instance", Tainted: []*InstanceState{ &InstanceState{ ID: "bar", }, }, }, }, }, &ModuleState{ Path: []string{"root", "child"}, Resources: map[string]*ResourceState{ "aws_instance.web": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "baz", }, }, }, }, }, } ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: state, }) p.RefreshFn = func(info *InstanceInfo, s *InstanceState) (*InstanceState, error) { if s.ID != "baz" { return s, nil } s.ID = "new" return s, nil } s, err := ctx.Refresh() if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(s.String()) expected := strings.TrimSpace(testContextRefreshModuleStr) if actual != expected { t.Fatalf("bad:\n\n%s\n\n%s", actual, expected) } } func TestContext2Refresh_moduleInputComputedOutput(t *testing.T) { m := testModule(t, "refresh-module-input-computed-output") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) if _, err := ctx.Refresh(); err != nil { t.Fatalf("err: %s", err) } } func TestContext2Refresh_moduleVarModule(t *testing.T) { m := testModule(t, "refresh-module-var-module") p := testProvider("aws") p.DiffFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) if _, err := ctx.Refresh(); err != nil { t.Fatalf("err: %s", err) } } // GH-70 func TestContext2Refresh_noState(t *testing.T) { p := testProvider("aws") m := testModule(t, "refresh-no-state") ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) p.RefreshFn = nil p.RefreshReturn = &InstanceState{ ID: "foo", } if _, err := ctx.Refresh(); err != nil { t.Fatalf("err: %s", err) } } func TestContext2Refresh_outputPartial(t *testing.T) { p := testProvider("aws") m := testModule(t, "refresh-output-partial") 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.foo": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "foo", }, }, }, }, }, }, }) p.RefreshFn = nil p.RefreshReturn = nil s, err := ctx.Refresh() if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(s.String()) expected := strings.TrimSpace(testContextRefreshOutputPartialStr) if actual != expected { t.Fatalf("bad:\n\n%s\n\n%s", actual, expected) } } func TestContext2Refresh_state(t *testing.T) { p := testProvider("aws") m := testModule(t, "refresh-basic") state := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.web": &ResourceState{ Primary: &InstanceState{ ID: "bar", }, }, }, }, }, } ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: state, }) p.RefreshFn = nil p.RefreshReturn = &InstanceState{ ID: "foo", } s, err := ctx.Refresh() if err != nil { t.Fatalf("err: %s", err) } originalMod := state.RootModule() mod := s.RootModule() if !p.RefreshCalled { t.Fatal("refresh should be called") } if !reflect.DeepEqual(p.RefreshState, originalMod.Resources["aws_instance.web"].Primary) { t.Fatalf( "bad:\n\n%#v\n\n%#v", p.RefreshState, originalMod.Resources["aws_instance.web"].Primary) } if !reflect.DeepEqual(mod.Resources["aws_instance.web"].Primary, p.RefreshReturn) { t.Fatalf("bad: %#v", mod.Resources) } } func TestContext2Refresh_tainted(t *testing.T) { p := testProvider("aws") m := testModule(t, "refresh-basic") state := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.web": &ResourceState{ Type: "aws_instance", Tainted: []*InstanceState{ &InstanceState{ ID: "bar", }, }, }, }, }, }, } ctx := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: state, }) p.RefreshFn = nil p.RefreshReturn = &InstanceState{ ID: "foo", } s, err := ctx.Refresh() if err != nil { t.Fatalf("err: %s", err) } if !p.RefreshCalled { t.Fatal("refresh should be called") } actual := strings.TrimSpace(s.String()) expected := strings.TrimSpace(testContextRefreshTaintedStr) if actual != expected { t.Fatalf("bad:\n\n%s\n\n%s", actual, expected) } } func TestContext2Refresh_vars(t *testing.T) { p := testProvider("aws") m := testModule(t, "refresh-vars") 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", }, }, }, }, }, }, }) p.RefreshFn = nil p.RefreshReturn = &InstanceState{ ID: "foo", } s, err := ctx.Refresh() if err != nil { t.Fatalf("err: %s", err) } mod := s.RootModule() if !p.RefreshCalled { t.Fatal("refresh should be called") } if p.RefreshState.ID != "foo" { t.Fatalf("bad: %#v", p.RefreshState) } if !reflect.DeepEqual(mod.Resources["aws_instance.web"].Primary, p.RefreshReturn) { t.Fatalf("bad: %#v", mod.Resources["aws_instance.web"]) } for _, r := range mod.Resources { if r.Type == "" { t.Fatalf("no type: %#v", r) } } } func TestContext2Validate(t *testing.T) { p := testProvider("aws") m := testModule(t, "validate-good") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) > 0 { t.Fatalf("bad: %s", e) } } func TestContext2Validate_badVar(t *testing.T) { p := testProvider("aws") m := testModule(t, "validate-bad-var") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) == 0 { t.Fatalf("bad: %#v", e) } } func TestContext2Validate_countNegative(t *testing.T) { p := testProvider("aws") m := testModule(t, "validate-count-negative") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) == 0 { t.Fatalf("bad: %#v", e) } } func TestContext2Validate_countVariable(t *testing.T) { p := testProvider("aws") m := testModule(t, "apply-count-variable") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) > 0 { t.Fatalf("bad: %s", e) } } func TestContext2Validate_countVariableNoDefault(t *testing.T) { p := testProvider("aws") m := testModule(t, "validate-count-variable") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) != 1 { t.Fatalf("bad: %s", e) } } func TestContext2Validate_moduleBadOutput(t *testing.T) { p := testProvider("aws") m := testModule(t, "validate-bad-module-output") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) == 0 { t.Fatalf("bad: %s", e) } } func TestContext2Validate_moduleGood(t *testing.T) { p := testProvider("aws") m := testModule(t, "validate-good-module") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) > 0 { t.Fatalf("bad: %#v", e) } } func TestContext2Validate_moduleBadResource(t *testing.T) { m := testModule(t, "validate-module-bad-rc") p := testProvider("aws") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) p.ValidateResourceReturnErrors = []error{fmt.Errorf("bad")} w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) == 0 { t.Fatalf("bad: %#v", e) } } func TestContext2Validate_moduleProviderInherit(t *testing.T) { m := testModule(t, "validate-module-pc-inherit") p := testProvider("aws") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) p.ValidateFn = func(c *ResourceConfig) ([]string, []error) { return nil, c.CheckSet([]string{"set"}) } w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) > 0 { t.Fatalf("bad: %s", e) } } func TestContext2Validate_orphans(t *testing.T) { p := testProvider("aws") m := testModule(t, "validate-good") state := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.web": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", }, }, }, }, }, } c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: state, }) p.ValidateResourceFn = func( t string, c *ResourceConfig) ([]string, []error) { return nil, c.CheckSet([]string{"foo"}) } w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) > 0 { t.Fatalf("bad: %s", e) } } func TestContext2Validate_providerConfig_bad(t *testing.T) { m := testModule(t, "validate-bad-pc") p := testProvider("aws") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) p.ValidateReturnErrors = []error{fmt.Errorf("bad")} w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) == 0 { t.Fatalf("bad: %s", e) } if !strings.Contains(fmt.Sprintf("%s", e), "bad") { t.Fatalf("bad: %s", e) } } func TestContext2Validate_providerConfig_badEmpty(t *testing.T) { m := testModule(t, "validate-bad-pc-empty") p := testProvider("aws") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) p.ValidateReturnErrors = []error{fmt.Errorf("bad")} w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) == 0 { t.Fatalf("bad: %#v", e) } } func TestContext2Validate_providerConfig_good(t *testing.T) { m := testModule(t, "validate-bad-pc") p := testProvider("aws") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) > 0 { t.Fatalf("bad: %#v", e) } } func TestContext2Validate_provisionerConfig_bad(t *testing.T) { m := testModule(t, "validate-bad-prov-conf") p := testProvider("aws") pr := testProvisioner() c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Provisioners: map[string]ResourceProvisionerFactory{ "shell": testProvisionerFuncFixed(pr), }, }) pr.ValidateReturnErrors = []error{fmt.Errorf("bad")} w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) == 0 { t.Fatalf("bad: %#v", e) } } func TestContext2Validate_provisionerConfig_good(t *testing.T) { m := testModule(t, "validate-bad-prov-conf") p := testProvider("aws") pr := testProvisioner() pr.ValidateFn = func(c *ResourceConfig) ([]string, []error) { if c == nil { t.Fatalf("missing resource config for provisioner") } return nil, nil } c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Provisioners: map[string]ResourceProvisionerFactory{ "shell": testProvisionerFuncFixed(pr), }, }) w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) > 0 { t.Fatalf("bad: %#v", e) } } func TestContext2Validate_requiredVar(t *testing.T) { m := testModule(t, "validate-required-var") p := testProvider("aws") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) == 0 { t.Fatalf("bad: %s", e) } } func TestContext2Validate_resourceConfig_bad(t *testing.T) { m := testModule(t, "validate-bad-rc") p := testProvider("aws") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) p.ValidateResourceReturnErrors = []error{fmt.Errorf("bad")} w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) == 0 { t.Fatalf("bad: %s", e) } } func TestContext2Validate_resourceConfig_good(t *testing.T) { m := testModule(t, "validate-bad-rc") p := testProvider("aws") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) > 0 { t.Fatalf("bad: %#v", e) } } func TestContext2Validate_resourceNameSymbol(t *testing.T) { p := testProvider("aws") m := testModule(t, "validate-resource-name-symbol") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) w, e := c.Validate() if len(w) == 0 { t.Fatalf("bad: %#v", w) } if len(e) > 0 { t.Fatalf("bad: %s", e) } } func TestContext2Validate_selfRef(t *testing.T) { p := testProvider("aws") m := testModule(t, "validate-self-ref") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) == 0 { t.Fatalf("bad: %s", e) } } func TestContext2Validate_selfRefMulti(t *testing.T) { p := testProvider("aws") m := testModule(t, "validate-self-ref-multi") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) == 0 { t.Fatalf("bad: %#v", e) } } func TestContext2Validate_selfRefMultiAll(t *testing.T) { p := testProvider("aws") m := testModule(t, "validate-self-ref-multi-all") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) == 0 { t.Fatalf("bad: %#v", e) } } func TestContext2Validate_tainted(t *testing.T) { p := testProvider("aws") m := testModule(t, "validate-good") state := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.foo": &ResourceState{ Type: "aws_instance", Tainted: []*InstanceState{ &InstanceState{ ID: "bar", }, }, }, }, }, }, } c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: state, }) p.ValidateResourceFn = func( t string, c *ResourceConfig) ([]string, []error) { return nil, c.CheckSet([]string{"foo"}) } w, e := c.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) > 0 { t.Fatalf("bad: %#v", e) } } func TestContext2Validate_varRef(t *testing.T) { m := testModule(t, "validate-variable-ref") p := testProvider("aws") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) computed := false p.ValidateResourceFn = func(t string, c *ResourceConfig) ([]string, []error) { computed = c.IsComputed("foo") return nil, nil } c.Validate() if !computed { t.Fatal("should be computed") } } func TestContext2Validate_varRefFilled(t *testing.T) { m := testModule(t, "validate-variable-ref") p := testProvider("aws") c := testContext2(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Variables: map[string]string{ "foo": "bar", }, }) var value interface{} p.ValidateResourceFn = func(t string, c *ResourceConfig) ([]string, []error) { value, _ = c.Get("foo") return nil, nil } c.Validate() if value != "bar" { t.Fatalf("bad: %#v", value) } } /* func TestContextInput(t *testing.T) { input := new(MockUIInput) m := testModule(t, "input-vars") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Variables: map[string]string{ "foo": "us-west-2", "amis.us-east-1": "override", }, UIInput: input, }) input.InputReturnMap = map[string]string{ "var.foo": "us-east-1", } if err := ctx.Input(InputModeStd); err != nil { t.Fatalf("err: %s", err) } if _, err := ctx.Plan(nil); 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(testTerraformInputVarsStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextInput_provider(t *testing.T) { m := testModule(t, "input-provider") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) var actual interface{} p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { c.Config["foo"] = "bar" return c, nil } p.ConfigureFn = func(c *ResourceConfig) error { actual = c.Config["foo"] return nil } if err := ctx.Input(InputModeStd); err != nil { t.Fatalf("err: %s", err) } if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } if _, err := ctx.Apply(); err != nil { t.Fatalf("err: %s", err) } if !reflect.DeepEqual(actual, "bar") { t.Fatalf("bad: %#v", actual) } } func TestContextInput_providerId(t *testing.T) { input := new(MockUIInput) m := testModule(t, "input-provider") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, UIInput: input, }) var actual interface{} p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { v, err := i.Input(&InputOpts{Id: "foo"}) if err != nil { return nil, err } c.Config["foo"] = v return c, nil } p.ConfigureFn = func(c *ResourceConfig) error { actual = c.Config["foo"] return nil } input.InputReturnMap = map[string]string{ "provider.aws.foo": "bar", } if err := ctx.Input(InputModeStd); err != nil { t.Fatalf("err: %s", err) } if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } if _, err := ctx.Apply(); err != nil { t.Fatalf("err: %s", err) } if !reflect.DeepEqual(actual, "bar") { t.Fatalf("bad: %#v", actual) } } func TestContextInput_providerOnly(t *testing.T) { input := new(MockUIInput) m := testModule(t, "input-provider-vars") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Variables: map[string]string{ "foo": "us-west-2", }, UIInput: input, }) input.InputReturnMap = map[string]string{ "var.foo": "us-east-1", } var actual interface{} p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { c.Config["foo"] = "bar" return c, nil } p.ConfigureFn = func(c *ResourceConfig) error { actual = c.Config["foo"] return nil } if err := ctx.Input(InputModeProvider); err != nil { t.Fatalf("err: %s", err) } if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } state, err := ctx.Apply() if err != nil { t.Fatalf("err: %s", err) } if !reflect.DeepEqual(actual, "bar") { t.Fatalf("bad: %#v", actual) } actualStr := strings.TrimSpace(state.String()) expectedStr := strings.TrimSpace(testTerraformInputProviderOnlyStr) if actualStr != expectedStr { t.Fatalf("bad: \n%s", actualStr) } } func TestContextInput_providerVars(t *testing.T) { input := new(MockUIInput) m := testModule(t, "input-provider-with-vars") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Variables: map[string]string{ "foo": "bar", }, UIInput: input, }) input.InputReturnMap = map[string]string{ "var.foo": "bar", } var actual interface{} p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { c.Config["bar"] = "baz" return c, nil } p.ConfigureFn = func(c *ResourceConfig) error { actual, _ = c.Get("foo") return nil } if err := ctx.Input(InputModeStd); err != nil { t.Fatalf("err: %s", err) } if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } if _, err := ctx.Apply(); err != nil { t.Fatalf("err: %s", err) } if !reflect.DeepEqual(actual, "bar") { t.Fatalf("bad: %#v", actual) } } func TestContextInput_varOnly(t *testing.T) { input := new(MockUIInput) m := testModule(t, "input-provider-vars") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Variables: map[string]string{ "foo": "us-west-2", }, UIInput: input, }) input.InputReturnMap = map[string]string{ "var.foo": "us-east-1", } var actual interface{} p.InputFn = func(i UIInput, c *ResourceConfig) (*ResourceConfig, error) { c.Raw["foo"] = "bar" return c, nil } p.ConfigureFn = func(c *ResourceConfig) error { actual = c.Raw["foo"] return nil } if err := ctx.Input(InputModeVar); err != nil { t.Fatalf("err: %s", err) } if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } state, err := ctx.Apply() if err != nil { t.Fatalf("err: %s", err) } if reflect.DeepEqual(actual, "bar") { t.Fatalf("bad: %#v", actual) } actualStr := strings.TrimSpace(state.String()) expectedStr := strings.TrimSpace(testTerraformInputVarOnlyStr) if actualStr != expectedStr { t.Fatalf("bad: \n%s", actualStr) } } func TestContextApply(t *testing.T) { m := testModule(t, "apply-good") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) if _, err := ctx.Plan(nil); 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(testTerraformApplyStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_emptyModule(t *testing.T) { m := testModule(t, "apply-empty-module") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } state, err := ctx.Apply() if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(state.String()) actual = strings.Replace(actual, " ", "", -1) expected := strings.TrimSpace(testTerraformApplyEmptyModuleStr) if actual != expected { t.Fatalf("bad: \n%s\nexpect:\n%s", actual, expected) } } func TestContextApply_createBeforeDestroy(t *testing.T) { m := testModule(t, "apply-good-create-before") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn state := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.bar": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", Attributes: map[string]string{ "require_new": "abc", }, }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: state, }) if _, err := ctx.Plan(nil); 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) != 1 { t.Fatalf("bad: %#v", mod.Resources) } actual := strings.TrimSpace(state.String()) expected := strings.TrimSpace(testTerraformApplyCreateBeforeStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_Minimal(t *testing.T) { m := testModule(t, "apply-minimal") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) if _, err := ctx.Plan(nil); 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(testTerraformApplyMinimalStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_badDiff(t *testing.T) { m := testModule(t, "apply-good") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { return &InstanceDiff{ Attributes: map[string]*ResourceAttrDiff{ "newp": nil, }, }, nil } if _, err := ctx.Apply(); err == nil { t.Fatal("should error") } } func TestContextApply_cancel(t *testing.T) { stopped := false m := testModule(t, "apply-cancel") p := testProvider("aws") ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) p.ApplyFn = func(*InstanceInfo, *InstanceState, *InstanceDiff) (*InstanceState, error) { if !stopped { stopped = true go ctx.Stop() for { if ctx.sh.Stopped() { break } } } return &InstanceState{ ID: "foo", Attributes: map[string]string{ "num": "2", }, }, nil } p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { return &InstanceDiff{ Attributes: map[string]*ResourceAttrDiff{ "num": &ResourceAttrDiff{ New: "bar", }, }, }, nil } if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } // Start the Apply in a goroutine stateCh := make(chan *State) go func() { state, err := ctx.Apply() if err != nil { panic(err) } stateCh <- state }() state := <-stateCh mod := state.RootModule() if len(mod.Resources) != 1 { t.Fatalf("bad: %#v", mod.Resources) } actual := strings.TrimSpace(state.String()) expected := strings.TrimSpace(testTerraformApplyCancelStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_compute(t *testing.T) { m := testModule(t, "apply-compute") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } ctx.variables = map[string]string{"value": "1"} state, err := ctx.Apply() if err != nil { t.Fatalf("err: %s", err) } actual := strings.TrimSpace(state.String()) expected := strings.TrimSpace(testTerraformApplyComputeStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_countDecrease(t *testing.T) { m := testModule(t, "apply-count-dec") p := testProvider("aws") p.DiffFn = testDiffFn s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.foo.0": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", Attributes: map[string]string{ "foo": "foo", "type": "aws_instance", }, }, }, "aws_instance.foo.1": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", Attributes: map[string]string{ "foo": "foo", "type": "aws_instance", }, }, }, "aws_instance.foo.2": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", Attributes: map[string]string{ "foo": "foo", "type": "aws_instance", }, }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) if _, err := ctx.Plan(nil); 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(testTerraformApplyCountDecStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_countDecreaseToOne(t *testing.T) { m := testModule(t, "apply-count-dec-one") p := testProvider("aws") p.DiffFn = testDiffFn s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.foo.0": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", Attributes: map[string]string{ "foo": "foo", "type": "aws_instance", }, }, }, "aws_instance.foo.1": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", }, }, "aws_instance.foo.2": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) if _, err := ctx.Plan(nil); 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(testTerraformApplyCountDecToOneStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_countTainted(t *testing.T) { m := testModule(t, "apply-count-tainted") p := testProvider("aws") p.DiffFn = testDiffFn s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.foo.0": &ResourceState{ Type: "aws_instance", Tainted: []*InstanceState{ &InstanceState{ ID: "bar", Attributes: map[string]string{ "foo": "foo", "type": "aws_instance", }, }, }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) if _, err := ctx.Plan(nil); 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(testTerraformApplyCountTaintedStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_countVariable(t *testing.T) { m := testModule(t, "apply-count-variable") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) if _, err := ctx.Plan(nil); 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(testTerraformApplyCountVariableStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_module(t *testing.T) { m := testModule(t, "apply-module") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) if _, err := ctx.Plan(nil); 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(testTerraformApplyModuleStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_nilDiff(t *testing.T) { m := testModule(t, "apply-good") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { return nil, nil } if _, err := ctx.Apply(); err == nil { t.Fatal("should error") } } func TestContextApply_Provisioner_compute(t *testing.T) { m := testModule(t, "apply-provisioner-compute") p := testProvider("aws") pr := testProvisioner() p.ApplyFn = testApplyFn p.DiffFn = testDiffFn pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { val, ok := c.Config["foo"] if !ok || val != "computed_dynamical" { t.Fatalf("bad value for foo: %v %#v", val, c) } return nil } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Provisioners: map[string]ResourceProvisionerFactory{ "shell": testProvisionerFuncFixed(pr), }, Variables: map[string]string{ "value": "1", }, }) if _, err := ctx.Plan(nil); 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(testTerraformApplyProvisionerStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } // Verify apply was invoked if !pr.ApplyCalled { t.Fatalf("provisioner not invoked") } } func TestContextApply_provisionerCreateFail(t *testing.T) { m := testModule(t, "apply-provisioner-fail-create") p := testProvider("aws") pr := testProvisioner() p.DiffFn = testDiffFn p.ApplyFn = func( info *InstanceInfo, is *InstanceState, id *InstanceDiff) (*InstanceState, error) { is.ID = "foo" return is, fmt.Errorf("error") } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Provisioners: map[string]ResourceProvisionerFactory{ "shell": testProvisionerFuncFixed(pr), }, }) if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } state, err := ctx.Apply() if err == nil { t.Fatal("should error") } actual := strings.TrimSpace(state.String()) expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_provisionerCreateFailNoId(t *testing.T) { m := testModule(t, "apply-provisioner-fail-create") p := testProvider("aws") pr := testProvisioner() p.DiffFn = testDiffFn p.ApplyFn = func( info *InstanceInfo, is *InstanceState, id *InstanceDiff) (*InstanceState, error) { return nil, fmt.Errorf("error") } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Provisioners: map[string]ResourceProvisionerFactory{ "shell": testProvisionerFuncFixed(pr), }, }) if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } state, err := ctx.Apply() if err == nil { t.Fatal("should error") } actual := strings.TrimSpace(state.String()) expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateNoIdStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_provisionerFail(t *testing.T) { m := testModule(t, "apply-provisioner-fail") p := testProvider("aws") pr := testProvisioner() p.ApplyFn = testApplyFn p.DiffFn = testDiffFn pr.ApplyFn = func(*InstanceState, *ResourceConfig) error { return fmt.Errorf("EXPLOSION") } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Provisioners: map[string]ResourceProvisionerFactory{ "shell": testProvisionerFuncFixed(pr), }, Variables: map[string]string{ "value": "1", }, }) if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } state, err := ctx.Apply() if err == nil { t.Fatal("should error") } actual := strings.TrimSpace(state.String()) expected := strings.TrimSpace(testTerraformApplyProvisionerFailStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_provisionerFail_createBeforeDestroy(t *testing.T) { m := testModule(t, "apply-provisioner-fail-create-before") p := testProvider("aws") pr := testProvisioner() p.ApplyFn = testApplyFn p.DiffFn = testDiffFn pr.ApplyFn = func(*InstanceState, *ResourceConfig) error { return fmt.Errorf("EXPLOSION") } state := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.bar": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", Attributes: map[string]string{ "require_new": "abc", }, }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Provisioners: map[string]ResourceProvisionerFactory{ "shell": testProvisionerFuncFixed(pr), }, State: state, }) if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } state, err := ctx.Apply() if err == nil { t.Fatal("should error") } actual := strings.TrimSpace(state.String()) expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateBeforeDestroyStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_error_createBeforeDestroy(t *testing.T) { m := testModule(t, "apply-error-create-before") p := testProvider("aws") state := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.bar": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", Attributes: map[string]string{ "require_new": "abc", }, }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: state, }) p.ApplyFn = func(info *InstanceInfo, is *InstanceState, id *InstanceDiff) (*InstanceState, error) { return nil, fmt.Errorf("error") } p.DiffFn = testDiffFn if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } state, err := ctx.Apply() if err == nil { t.Fatal("should have error") } actual := strings.TrimSpace(state.String()) expected := strings.TrimSpace(testTerraformApplyErrorCreateBeforeDestroyStr) if actual != expected { t.Fatalf("bad: \n%s\n\n\n%s", actual, expected) } } func TestContextApply_errorDestroy_createBeforeDestroy(t *testing.T) { m := testModule(t, "apply-error-create-before") p := testProvider("aws") state := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.bar": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", Attributes: map[string]string{ "require_new": "abc", }, }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: state, }) p.ApplyFn = func(info *InstanceInfo, is *InstanceState, id *InstanceDiff) (*InstanceState, error) { // Fail the destroy! if id.Destroy { return is, fmt.Errorf("error") } // Create should work is = &InstanceState{ ID: "foo", } return is, nil } p.DiffFn = testDiffFn if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } state, err := ctx.Apply() if err == nil { t.Fatal("should have error") } actual := strings.TrimSpace(state.String()) expected := strings.TrimSpace(testTerraformApplyErrorDestroyCreateBeforeDestroyStr) if actual != expected { t.Fatalf("bad: actual:\n%s\n\nexpected:\n%s", actual, expected) } } func TestContextApply_provisionerResourceRef(t *testing.T) { m := testModule(t, "apply-provisioner-resource-ref") p := testProvider("aws") pr := testProvisioner() p.ApplyFn = testApplyFn p.DiffFn = testDiffFn pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { val, ok := c.Config["foo"] if !ok || val != "2" { t.Fatalf("bad value for foo: %v %#v", val, c) } return nil } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Provisioners: map[string]ResourceProvisionerFactory{ "shell": testProvisionerFuncFixed(pr), }, }) if _, err := ctx.Plan(nil); 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(testTerraformApplyProvisionerResourceRefStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } // Verify apply was invoked if !pr.ApplyCalled { t.Fatalf("provisioner not invoked") } } // Provisioner should NOT run on a diff, only create func TestContextApply_Provisioner_Diff(t *testing.T) { m := testModule(t, "apply-provisioner-diff") p := testProvider("aws") pr := testProvisioner() p.ApplyFn = testApplyFn p.DiffFn = testDiffFn pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { return nil } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Provisioners: map[string]ResourceProvisionerFactory{ "shell": testProvisionerFuncFixed(pr), }, }) if _, err := ctx.Plan(nil); 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(testTerraformApplyProvisionerDiffStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } // Verify apply was invoked if !pr.ApplyCalled { t.Fatalf("provisioner not invoked") } pr.ApplyCalled = false // Change the state to force a diff mod := state.RootModule() mod.Resources["aws_instance.bar"].Primary.Attributes["foo"] = "baz" // Re-create context with state ctx = testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Provisioners: map[string]ResourceProvisionerFactory{ "shell": testProvisionerFuncFixed(pr), }, State: state, }) if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } state2, err := ctx.Apply() if err != nil { t.Fatalf("err: %s", err) } actual = strings.TrimSpace(state2.String()) if actual != expected { t.Fatalf("bad: \n%s", actual) } // Verify apply was NOT invoked if pr.ApplyCalled { t.Fatalf("provisioner invoked") } } func TestContextApply_outputDiffVars(t *testing.T) { m := testModule(t, "apply-good") p := testProvider("aws") s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.baz": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { for k, ad := range d.Attributes { if ad.NewComputed { return nil, fmt.Errorf("%s: computed", k) } } result := s.MergeDiff(d) result.ID = "foo" return result, nil } p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { return &InstanceDiff{ Attributes: map[string]*ResourceAttrDiff{ "foo": &ResourceAttrDiff{ NewComputed: true, Type: DiffAttrOutput, }, "bar": &ResourceAttrDiff{ New: "baz", }, }, }, nil } if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } if _, err := ctx.Apply(); err != nil { t.Fatalf("err: %s", err) } } func TestContextApply_Provisioner_ConnInfo(t *testing.T) { m := testModule(t, "apply-provisioner-conninfo") p := testProvider("aws") pr := testProvisioner() p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { if s.Ephemeral.ConnInfo == nil { t.Fatalf("ConnInfo not initialized") } result, _ := testApplyFn(info, s, d) result.Ephemeral.ConnInfo = map[string]string{ "type": "ssh", "host": "127.0.0.1", "port": "22", } return result, nil } p.DiffFn = testDiffFn pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { conn := rs.Ephemeral.ConnInfo if conn["type"] != "telnet" { t.Fatalf("Bad: %#v", conn) } if conn["host"] != "127.0.0.1" { t.Fatalf("Bad: %#v", conn) } if conn["port"] != "2222" { t.Fatalf("Bad: %#v", conn) } if conn["user"] != "superuser" { t.Fatalf("Bad: %#v", conn) } if conn["pass"] != "test" { t.Fatalf("Bad: %#v", conn) } return nil } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Provisioners: map[string]ResourceProvisionerFactory{ "shell": testProvisionerFuncFixed(pr), }, Variables: map[string]string{ "value": "1", "pass": "test", }, }) if _, err := ctx.Plan(nil); 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(testTerraformApplyProvisionerStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } // Verify apply was invoked if !pr.ApplyCalled { t.Fatalf("provisioner not invoked") } } func TestContextApply_destroy(t *testing.T) { m := testModule(t, "apply-destroy") h := new(HookRecordApplyOrder) p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Hooks: []Hook{h}, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) // First plan and apply a create operation if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } if _, err := ctx.Apply(); err != nil { t.Fatalf("err: %s", err) } // Next, plan and apply a destroy operation if _, err := ctx.Plan(&PlanOpts{Destroy: true}); err != nil { t.Fatalf("err: %s", err) } h.Active = true state, err := ctx.Apply() if err != nil { t.Fatalf("err: %s", err) } // Test that things were destroyed actual := strings.TrimSpace(state.String()) expected := strings.TrimSpace(testTerraformApplyDestroyStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } // Test that things were destroyed _in the right order_ expected2 := []string{"aws_instance.bar", "aws_instance.foo"} actual2 := h.IDs if !reflect.DeepEqual(actual2, expected2) { t.Fatalf("bad: %#v", actual2) } } func TestContextApply_destroyOutputs(t *testing.T) { m := testModule(t, "apply-destroy-outputs") h := new(HookRecordApplyOrder) p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Hooks: []Hook{h}, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) // First plan and apply a create operation if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } if _, err := ctx.Apply(); err != nil { t.Fatalf("err: %s", err) } // Next, plan and apply a destroy operation if _, err := ctx.Plan(&PlanOpts{Destroy: true}); err != nil { t.Fatalf("err: %s", err) } h.Active = true state, err := ctx.Apply() if err != nil { t.Fatalf("err: %s", err) } mod := state.RootModule() if len(mod.Resources) > 0 { t.Fatalf("bad: %#v", mod) } } func TestContextApply_destroyOrphan(t *testing.T) { m := testModule(t, "apply-error") p := testProvider("aws") s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.baz": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { if d.Destroy { return nil, nil } result := s.MergeDiff(d) result.ID = "foo" return result, nil } p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { return &InstanceDiff{ Attributes: map[string]*ResourceAttrDiff{ "num": &ResourceAttrDiff{ New: "bar", }, }, }, nil } if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } state, err := ctx.Apply() if err != nil { t.Fatalf("err: %s", err) } mod := state.RootModule() if _, ok := mod.Resources["aws_instance.baz"]; ok { t.Fatalf("bad: %#v", mod.Resources) } } func TestContextApply_destroyTaintedProvisioner(t *testing.T) { m := testModule(t, "apply-destroy-provisioner") p := testProvider("aws") pr := testProvisioner() p.ApplyFn = testApplyFn p.DiffFn = testDiffFn called := false pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error { called = true return nil } s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.foo": &ResourceState{ Type: "aws_instance", Tainted: []*InstanceState{ &InstanceState{ ID: "bar", Attributes: map[string]string{ "id": "bar", }, }, }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Provisioners: map[string]ResourceProvisionerFactory{ "shell": testProvisionerFuncFixed(pr), }, State: s, }) if _, err := ctx.Plan(&PlanOpts{Destroy: true}); err != nil { t.Fatalf("err: %s", err) } state, err := ctx.Apply() if err != nil { t.Fatalf("err: %s", err) } if called { t.Fatal("provisioner should not be called") } actual := strings.TrimSpace(state.String()) expected := strings.TrimSpace("") if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_error(t *testing.T) { errored := false m := testModule(t, "apply-error") p := testProvider("aws") ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) p.ApplyFn = func(*InstanceInfo, *InstanceState, *InstanceDiff) (*InstanceState, error) { if errored { state := &InstanceState{ ID: "bar", } return state, fmt.Errorf("error") } errored = true return &InstanceState{ ID: "foo", Attributes: map[string]string{ "num": "2", }, }, nil } p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { return &InstanceDiff{ Attributes: map[string]*ResourceAttrDiff{ "num": &ResourceAttrDiff{ New: "bar", }, }, }, nil } if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } state, err := ctx.Apply() if err == nil { t.Fatal("should have error") } actual := strings.TrimSpace(state.String()) expected := strings.TrimSpace(testTerraformApplyErrorStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_errorPartial(t *testing.T) { errored := false m := testModule(t, "apply-error") p := testProvider("aws") s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.bar": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { if errored { return s, fmt.Errorf("error") } errored = true return &InstanceState{ ID: "foo", Attributes: map[string]string{ "num": "2", }, }, nil } p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { return &InstanceDiff{ Attributes: map[string]*ResourceAttrDiff{ "num": &ResourceAttrDiff{ New: "bar", }, }, }, nil } if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } state, err := ctx.Apply() if err == nil { t.Fatal("should have error") } mod := state.RootModule() if len(mod.Resources) != 2 { t.Fatalf("bad: %#v", mod.Resources) } actual := strings.TrimSpace(state.String()) expected := strings.TrimSpace(testTerraformApplyErrorPartialStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_hook(t *testing.T) { m := testModule(t, "apply-good") h := new(MockHook) p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Hooks: []Hook{h}, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } if _, err := ctx.Apply(); err != nil { t.Fatalf("err: %s", err) } if !h.PreApplyCalled { t.Fatal("should be called") } if !h.PostApplyCalled { t.Fatal("should be called") } } func TestContextApply_idAttr(t *testing.T) { m := testModule(t, "apply-idattr") p := testProvider("aws") ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { result := s.MergeDiff(d) result.ID = "foo" result.Attributes = map[string]string{ "id": "bar", } return result, nil } p.DiffFn = func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) { return &InstanceDiff{ Attributes: map[string]*ResourceAttrDiff{ "num": &ResourceAttrDiff{ New: "bar", }, }, }, nil } if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } state, err := ctx.Apply() if err != nil { t.Fatalf("err: %s", err) } mod := state.RootModule() rs, ok := mod.Resources["aws_instance.foo"] if !ok { t.Fatal("not in state") } if rs.Primary.ID != "foo" { t.Fatalf("bad: %#v", rs.Primary.ID) } if rs.Primary.Attributes["id"] != "foo" { t.Fatalf("bad: %#v", rs.Primary.Attributes) } } func TestContextApply_output(t *testing.T) { m := testModule(t, "apply-output") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) if _, err := ctx.Plan(nil); 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(testTerraformApplyOutputStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_outputInvalid(t *testing.T) { m := testModule(t, "apply-output-invalid") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) _, err := ctx.Plan(nil) if err == nil { t.Fatalf("err: %s", err) } if !strings.Contains(err.Error(), "is not a string") { t.Fatalf("err: %s", err) } } func TestContextApply_outputList(t *testing.T) { m := testModule(t, "apply-output-list") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) if _, err := ctx.Plan(nil); 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(testTerraformApplyOutputListStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_outputMulti(t *testing.T) { m := testModule(t, "apply-output-multi") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) if _, err := ctx.Plan(nil); 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(testTerraformApplyOutputMultiStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_outputMultiIndex(t *testing.T) { m := testModule(t, "apply-output-multi-index") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) if _, err := ctx.Plan(nil); 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(testTerraformApplyOutputMultiIndexStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_taint(t *testing.T) { m := testModule(t, "apply-taint") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn s := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.bar": &ResourceState{ Type: "aws_instance", Tainted: []*InstanceState{ &InstanceState{ ID: "baz", Attributes: map[string]string{ "num": "2", "type": "aws_instance", }, }, }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: s, }) if _, err := ctx.Plan(nil); 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(testTerraformApplyTaintStr) if actual != expected { t.Fatalf("bad:\n%s", actual) } } func TestContextApply_unknownAttribute(t *testing.T) { m := testModule(t, "apply-unknown") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, }) if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } state, err := ctx.Apply() if err == nil { t.Fatal("should error") } actual := strings.TrimSpace(state.String()) expected := strings.TrimSpace(testTerraformApplyUnknownAttrStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_vars(t *testing.T) { m := testModule(t, "apply-vars") p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn ctx := testContext(t, &ContextOpts{ Module: m, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, Variables: map[string]string{ "foo": "us-west-2", "amis.us-east-1": "override", }, }) w, e := ctx.Validate() if len(w) > 0 { t.Fatalf("bad: %#v", w) } if len(e) > 0 { t.Fatalf("bad: %s", e) } if _, err := ctx.Plan(nil); 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(testTerraformApplyVarsStr) if actual != expected { t.Fatalf("bad: \n%s", actual) } } func TestContextApply_createBefore_depends(t *testing.T) { m := testModule(t, "apply-depends-create-before") h := new(HookRecordApplyOrder) p := testProvider("aws") p.ApplyFn = testApplyFn p.DiffFn = testDiffFn state := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.web": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", Attributes: map[string]string{ "require_new": "ami-old", }, }, }, "aws_instance.lb": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "baz", Attributes: map[string]string{ "instance": "bar", }, }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Hooks: []Hook{h}, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: state, }) if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } h.Active = true 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(testTerraformApplyDependsCreateBeforeStr) if actual != expected { t.Fatalf("bad: \n%s\n%s", actual, expected) } // Test that things were managed _in the right order_ order := h.States diffs := h.Diffs if order[0].ID != "" || diffs[0].Destroy { t.Fatalf("should create new instance first: %#v", order) } if order[1].ID != "baz" { t.Fatalf("update must happen after create: %#v", order) } if order[2].ID != "bar" || !diffs[2].Destroy { t.Fatalf("destroy must happen after update: %#v", order) } } func TestContextApply_singleDestroy(t *testing.T) { m := testModule(t, "apply-depends-create-before") h := new(HookRecordApplyOrder) p := testProvider("aws") invokeCount := 0 p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { invokeCount++ switch invokeCount { case 1: if d.Destroy { t.Fatalf("should not destroy") } if s.ID != "" { t.Fatalf("should not have ID") } case 2: if d.Destroy { t.Fatalf("should not destroy") } if s.ID != "baz" { t.Fatalf("should have id") } case 3: if !d.Destroy { t.Fatalf("should destroy") } if s.ID == "" { t.Fatalf("should have ID") } default: t.Fatalf("bad invoke count %d", invokeCount) } return testApplyFn(info, s, d) } p.DiffFn = testDiffFn state := &State{ Modules: []*ModuleState{ &ModuleState{ Path: rootModulePath, Resources: map[string]*ResourceState{ "aws_instance.web": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "bar", Attributes: map[string]string{ "require_new": "ami-old", }, }, }, "aws_instance.lb": &ResourceState{ Type: "aws_instance", Primary: &InstanceState{ ID: "baz", Attributes: map[string]string{ "instance": "bar", }, }, }, }, }, }, } ctx := testContext(t, &ContextOpts{ Module: m, Hooks: []Hook{h}, Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, State: state, }) if _, err := ctx.Plan(nil); err != nil { t.Fatalf("err: %s", err) } h.Active = true state, err := ctx.Apply() if err != nil { t.Fatalf("err: %s", err) } if invokeCount != 3 { t.Fatalf("bad: %d", invokeCount) } } func testContext(t *testing.T, opts *ContextOpts) *Context { return NewContext(opts) } func testApplyFn( info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) { if d.Destroy { return nil, nil } id := "foo" if idAttr, ok := d.Attributes["id"]; ok && !idAttr.NewComputed { id = idAttr.New } result := &InstanceState{ ID: id, } if d != nil { result = result.MergeDiff(d) } return result, nil } func testDiffFn( info *InstanceInfo, s *InstanceState, c *ResourceConfig) (*InstanceDiff, error) { var diff InstanceDiff diff.Attributes = make(map[string]*ResourceAttrDiff) for k, v := range c.Raw { if _, ok := v.(string); !ok { continue } if k == "nil" { return nil, nil } // This key is used for other purposes if k == "compute_value" { continue } if k == "compute" { attrDiff := &ResourceAttrDiff{ Old: "", New: "", NewComputed: true, } if cv, ok := c.Config["compute_value"]; ok { if cv.(string) == "1" { attrDiff.NewComputed = false attrDiff.New = fmt.Sprintf("computed_%s", v.(string)) } } diff.Attributes[v.(string)] = attrDiff continue } // If this key is not computed, then look it up in the // cleaned config. found := false for _, ck := range c.ComputedKeys { if ck == k { found = true break } } if !found { v = c.Config[k] } attrDiff := &ResourceAttrDiff{ Old: "", New: v.(string), } if k == "require_new" { attrDiff.RequiresNew = true } diff.Attributes[k] = attrDiff } for _, k := range c.ComputedKeys { diff.Attributes[k] = &ResourceAttrDiff{ Old: "", NewComputed: true, } } for k, v := range diff.Attributes { if v.NewComputed { continue } old, ok := s.Attributes[k] if !ok { continue } if old == v.New { delete(diff.Attributes, k) } } if !diff.Empty() { diff.Attributes["type"] = &ResourceAttrDiff{ Old: "", New: info.Type, } } return &diff, nil } func testProvider(prefix string) *MockResourceProvider { p := new(MockResourceProvider) p.RefreshFn = func(info *InstanceInfo, s *InstanceState) (*InstanceState, error) { return s, nil } p.ResourcesReturn = []ResourceType{ ResourceType{ Name: fmt.Sprintf("%s_instance", prefix), }, } return p } func testProvisioner() *MockResourceProvisioner { p := new(MockResourceProvisioner) return p } const testContextGraph = ` root: root aws_instance.bar aws_instance.bar -> provider.aws aws_instance.foo aws_instance.foo -> provider.aws provider.aws root root -> aws_instance.bar root -> aws_instance.foo ` const testContextRefreshModuleStr = ` aws_instance.web: (1 tainted) ID = Tainted ID 1 = bar module.child: aws_instance.web: ID = new ` const testContextRefreshOutputPartialStr = ` ` const testContextRefreshTaintedStr = ` aws_instance.web: (1 tainted) ID = Tainted ID 1 = foo ` */ func testContext2(t *testing.T, opts *ContextOpts) *Context2 { return NewContext2(opts) }