diff --git a/terraform/context.go b/terraform/context.go index 54a1f5b0f..e640454c1 100644 --- a/terraform/context.go +++ b/terraform/context.go @@ -83,6 +83,9 @@ func (c *Context2) Refresh() (*State, []error) { return nil, multierror.Append(errs, err).Errors } + // Clean out any unused things + c.state.prune() + return c.state, nil } diff --git a/terraform/context_test.go b/terraform/context_test.go index 8ce034670..92e13c0bc 100644 --- a/terraform/context_test.go +++ b/terraform/context_test.go @@ -7,6 +7,464 @@ import ( "testing" ) +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 TestContextRefresh_ignoreUncreated(t *testing.T) { + p := testProvider("aws") + m := testModule(t, "refresh-basic") + ctx := testContext(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 TestContextRefresh_hook(t *testing.T) { + h := new(MockHook) + p := testProvider("aws") + m := testModule(t, "refresh-basic") + ctx := testContext(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 TestContextRefresh_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 := testContext(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 TestContextRefresh_moduleInputComputedOutput(t *testing.T) { + m := testModule(t, "refresh-module-input-computed-output") + p := testProvider("aws") + p.DiffFn = testDiffFn + ctx := testContext(t, &ContextOpts{ + Module: m, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + }) + + if _, err := ctx.Refresh(); err != nil { + t.Fatalf("err: %s", err) + } +} + +func TestContextRefresh_moduleVarModule(t *testing.T) { + m := testModule(t, "refresh-module-var-module") + p := testProvider("aws") + p.DiffFn = testDiffFn + ctx := testContext(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 TestContextRefresh_noState(t *testing.T) { + p := testProvider("aws") + m := testModule(t, "refresh-no-state") + ctx := testContext(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 TestContextRefresh_outputPartial(t *testing.T) { + p := testProvider("aws") + m := testModule(t, "refresh-output-partial") + ctx := testContext(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 TestContextRefresh_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 := testContext(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: %#v %#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 TestContextRefresh_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 := testContext(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 TestContextRefresh_vars(t *testing.T) { + p := testProvider("aws") + m := testModule(t, "refresh-vars") + ctx := testContext(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") @@ -566,58 +1024,6 @@ func TestContext2Validate_varRefFilled(t *testing.T) { } } -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 TestContextInput(t *testing.T) { input := new(MockUIInput) @@ -4211,409 +4617,7 @@ func TestContextPlan_varListErr(t *testing.T) { } } -func TestContextRefresh_delete(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "refresh-basic") - ctx := testContext(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 TestContextRefresh_ignoreUncreated(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "refresh-basic") - ctx := testContext(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 TestContextRefresh_hook(t *testing.T) { - h := new(MockHook) - p := testProvider("aws") - m := testModule(t, "refresh-basic") - ctx := testContext(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 TestContextRefresh_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 := testContext(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 TestContextRefresh_moduleInputComputedOutput(t *testing.T) { - m := testModule(t, "refresh-module-input-computed-output") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(t, &ContextOpts{ - Module: m, - Providers: map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(p), - }, - }) - - if _, err := ctx.Refresh(); err != nil { - t.Fatalf("err: %s", err) - } -} - -func TestContextRefresh_moduleVarModule(t *testing.T) { - m := testModule(t, "refresh-module-var-module") - p := testProvider("aws") - p.DiffFn = testDiffFn - ctx := testContext(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 TestContextRefresh_noState(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "refresh-no-state") - ctx := testContext(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 TestContextRefresh_outputPartial(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "refresh-output-partial") - ctx := testContext(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 TestContextRefresh_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 := testContext(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: %#v %#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 TestContextRefresh_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 := testContext(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 TestContextRefresh_vars(t *testing.T) { - p := testProvider("aws") - m := testModule(t, "refresh-vars") - ctx := testContext(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 testContext(t *testing.T, opts *ContextOpts) *Context { return NewContext(opts)