diff --git a/terraform/graph_config_node_resource.go b/terraform/graph_config_node_resource.go index 70f83bb47..52e89fc5e 100644 --- a/terraform/graph_config_node_resource.go +++ b/terraform/graph_config_node_resource.go @@ -165,15 +165,6 @@ func (n *GraphNodeConfigResource) DynamicExpand(ctx EvalContext) (*Graph, error) // Additional destroy modifications. if n.Destroy { - // If we're destroying a primary or tainted resource, we want to - // expand orphans, which have all the same semantics in a destroy - // as a primary or tainted resource. - steps = append(steps, &OrphanTransformer{ - Resource: n.Resource, - State: state, - View: n.Resource.Id(), - }) - steps = append(steps, &DeposedTransformer{ State: state, View: n.Resource.Id(), diff --git a/terraform/transform_orphan.go b/terraform/transform_orphan.go deleted file mode 100644 index 1b288b39d..000000000 --- a/terraform/transform_orphan.go +++ /dev/null @@ -1,437 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform/config" - "github.com/hashicorp/terraform/config/module" - "github.com/hashicorp/terraform/dag" -) - -// GraphNodeStateRepresentative is an interface that can be implemented by -// a node to say that it is representing a resource in the state. -type GraphNodeStateRepresentative interface { - StateId() []string -} - -// OrphanTransformer is a GraphTransformer that adds orphans to the -// graph. This transformer adds both resource and module orphans. -type OrphanTransformer struct { - // Resource is resource configuration. This is only non-nil when - // expanding a resource that is in the configuration. It can't be - // dependend on. - Resource *config.Resource - - // State is the global state. We require the global state to - // properly find module orphans at our path. - State *State - - // Module is the root module. We'll look up the proper configuration - // using the graph path. - Module *module.Tree - - // View, if non-nil will set a view on the module state. - View string -} - -func (t *OrphanTransformer) Transform(g *Graph) error { - if t.State == nil { - // If the entire state is nil, there can't be any orphans - return nil - } - - // Build up all our state representatives - resourceRep := make(map[string]struct{}) - for _, v := range g.Vertices() { - if sr, ok := v.(GraphNodeStateRepresentative); ok { - for _, k := range sr.StateId() { - resourceRep[k] = struct{}{} - } - } - } - - var config *config.Config - if t.Module != nil { - if module := t.Module.Child(g.Path[1:]); module != nil { - config = module.Config() - } - } - - var resourceVertexes []dag.Vertex - if state := t.State.ModuleByPath(g.Path); state != nil { - // If we have state, then we can have orphan resources - - // If we have a view, get the view - if t.View != "" { - state = state.View(t.View) - } - - resourceOrphans := state.Orphans(config) - - resourceVertexes = make([]dag.Vertex, len(resourceOrphans)) - for i, k := range resourceOrphans { - // If this orphan is represented by some other node somehow, - // then ignore it. - if _, ok := resourceRep[k]; ok { - continue - } - - rs := state.Resources[k] - - rsk, err := ParseResourceStateKey(k) - if err != nil { - return err - } - resourceVertexes[i] = g.Add(&graphNodeOrphanResource{ - Path: g.Path, - ResourceKey: rsk, - Resource: t.Resource, - Provider: rs.Provider, - dependentOn: rs.Dependencies, - }) - } - } - - // Go over each module orphan and add it to the graph. We store the - // vertexes and states outside so that we can connect dependencies later. - moduleOrphans := t.State.ModuleOrphans(g.Path, config) - moduleVertexes := make([]dag.Vertex, len(moduleOrphans)) - for i, path := range moduleOrphans { - var deps []string - if s := t.State.ModuleByPath(path); s != nil { - deps = s.Dependencies - } - - moduleVertexes[i] = g.Add(&graphNodeOrphanModule{ - Path: path, - dependentOn: deps, - }) - } - - // Now do the dependencies. We do this _after_ adding all the orphan - // nodes above because there are cases in which the orphans themselves - // depend on other orphans. - - // Resource dependencies - for _, v := range resourceVertexes { - g.ConnectDependent(v) - } - - // Module dependencies - for _, v := range moduleVertexes { - g.ConnectDependent(v) - } - - return nil -} - -// graphNodeOrphanModule is the graph vertex representing an orphan resource.. -type graphNodeOrphanModule struct { - Path []string - - dependentOn []string -} - -func (n *graphNodeOrphanModule) DependableName() []string { - return []string{n.dependableName()} -} - -func (n *graphNodeOrphanModule) DependentOn() []string { - return n.dependentOn -} - -func (n *graphNodeOrphanModule) Name() string { - return fmt.Sprintf("%s (orphan)", n.dependableName()) -} - -func (n *graphNodeOrphanModule) dependableName() string { - return fmt.Sprintf("module.%s", n.Path[len(n.Path)-1]) -} - -// GraphNodeExpandable -func (n *graphNodeOrphanModule) Expand(b GraphBuilder) (GraphNodeSubgraph, error) { - g, err := b.Build(n.Path) - if err != nil { - return nil, err - } - - return &GraphNodeBasicSubgraph{ - NameValue: n.Name(), - Graph: g, - }, nil -} - -// graphNodeOrphanResource is the graph vertex representing an orphan resource.. -type graphNodeOrphanResource struct { - Path []string - ResourceKey *ResourceStateKey - Resource *config.Resource - Provider string - - dependentOn []string -} - -func (n *graphNodeOrphanResource) ConfigType() GraphNodeConfigType { - return GraphNodeConfigTypeResource -} - -func (n *graphNodeOrphanResource) ResourceAddress() *ResourceAddress { - return &ResourceAddress{ - Index: n.ResourceKey.Index, - InstanceType: TypePrimary, - Name: n.ResourceKey.Name, - Path: n.Path[1:], - Type: n.ResourceKey.Type, - Mode: n.ResourceKey.Mode, - } -} - -func (n *graphNodeOrphanResource) DependableName() []string { - return []string{n.dependableName()} -} - -func (n *graphNodeOrphanResource) DependentOn() []string { - return n.dependentOn -} - -func (n *graphNodeOrphanResource) Flatten(p []string) (dag.Vertex, error) { - return &graphNodeOrphanResourceFlat{ - graphNodeOrphanResource: n, - PathValue: p, - }, nil -} - -func (n *graphNodeOrphanResource) Name() string { - return fmt.Sprintf("%s (orphan)", n.ResourceKey) -} - -func (n *graphNodeOrphanResource) ProvidedBy() []string { - return []string{resourceProvider(n.ResourceKey.Type, n.Provider)} -} - -// GraphNodeEvalable impl. -func (n *graphNodeOrphanResource) EvalTree() EvalNode { - - seq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)} - - // Build instance info - info := &InstanceInfo{Id: n.ResourceKey.String(), Type: n.ResourceKey.Type} - info.uniqueExtra = "destroy" - - seq.Nodes = append(seq.Nodes, &EvalInstanceInfo{Info: info}) - - // Each resource mode has its own lifecycle - switch n.ResourceKey.Mode { - case config.ManagedResourceMode: - seq.Nodes = append( - seq.Nodes, - n.managedResourceEvalNodes(info)..., - ) - case config.DataResourceMode: - seq.Nodes = append( - seq.Nodes, - n.dataResourceEvalNodes(info)..., - ) - default: - panic(fmt.Errorf("unsupported resource mode %s", n.ResourceKey.Mode)) - } - - return seq -} - -func (n *graphNodeOrphanResource) managedResourceEvalNodes(info *InstanceInfo) []EvalNode { - var provider ResourceProvider - var state *InstanceState - - nodes := make([]EvalNode, 0, 3) - - // Refresh the resource - nodes = append(nodes, &EvalOpFilter{ - Ops: []walkOperation{walkRefresh}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Name: n.ProvidedBy()[0], - Output: &provider, - }, - &EvalReadState{ - Name: n.ResourceKey.String(), - Output: &state, - }, - &EvalRefresh{ - Info: info, - Provider: &provider, - State: &state, - Output: &state, - }, - &EvalWriteState{ - Name: n.ResourceKey.String(), - ResourceType: n.ResourceKey.Type, - Provider: n.Provider, - Dependencies: n.DependentOn(), - State: &state, - }, - }, - }, - }) - - // Diff the resource - var diff *InstanceDiff - nodes = append(nodes, &EvalOpFilter{ - Ops: []walkOperation{walkPlan, walkPlanDestroy}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - &EvalReadState{ - Name: n.ResourceKey.String(), - Output: &state, - }, - &EvalDiffDestroy{ - Info: info, - State: &state, - Output: &diff, - }, - &EvalCheckPreventDestroy{ - Resource: n.Resource, - ResourceId: n.ResourceKey.String(), - Diff: &diff, - }, - &EvalWriteDiff{ - Name: n.ResourceKey.String(), - Diff: &diff, - }, - }, - }, - }) - - // Apply - var err error - nodes = append(nodes, &EvalOpFilter{ - Ops: []walkOperation{walkApply, walkDestroy}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - &EvalReadDiff{ - Name: n.ResourceKey.String(), - Diff: &diff, - }, - &EvalGetProvider{ - Name: n.ProvidedBy()[0], - Output: &provider, - }, - &EvalReadState{ - Name: n.ResourceKey.String(), - Output: &state, - }, - &EvalApplyPre{ - Info: info, - State: &state, - Diff: &diff, - }, - &EvalApply{ - Info: info, - State: &state, - Diff: &diff, - Provider: &provider, - Output: &state, - Error: &err, - }, - &EvalWriteState{ - Name: n.ResourceKey.String(), - ResourceType: n.ResourceKey.Type, - Provider: n.Provider, - Dependencies: n.DependentOn(), - State: &state, - }, - &EvalApplyPost{ - Info: info, - State: &state, - Error: &err, - }, - &EvalUpdateStateHook{}, - }, - }, - }) - - return nodes -} - -func (n *graphNodeOrphanResource) dataResourceEvalNodes(info *InstanceInfo) []EvalNode { - nodes := make([]EvalNode, 0, 3) - - // This will remain nil, since we don't retain states for orphaned - // data resources. - var state *InstanceState - - // On both refresh and apply we just drop our state altogether, - // since the config resource validation pass will have proven that the - // resources remaining in the configuration don't need it. - nodes = append(nodes, &EvalOpFilter{ - Ops: []walkOperation{walkRefresh, walkApply}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - &EvalWriteState{ - Name: n.ResourceKey.String(), - ResourceType: n.ResourceKey.Type, - Provider: n.Provider, - Dependencies: n.DependentOn(), - State: &state, // state is nil - }, - }, - }, - }) - - return nodes -} - -func (n *graphNodeOrphanResource) dependableName() string { - return n.ResourceKey.String() -} - -// GraphNodeDestroyable impl. -func (n *graphNodeOrphanResource) DestroyNode() GraphNodeDestroy { - return n -} - -// GraphNodeDestroy impl. -func (n *graphNodeOrphanResource) CreateBeforeDestroy() bool { - return false -} - -func (n *graphNodeOrphanResource) CreateNode() dag.Vertex { - return n -} - -// Same as graphNodeOrphanResource, but for flattening -type graphNodeOrphanResourceFlat struct { - *graphNodeOrphanResource - - PathValue []string -} - -func (n *graphNodeOrphanResourceFlat) Name() string { - return fmt.Sprintf( - "%s.%s", modulePrefixStr(n.PathValue), n.graphNodeOrphanResource.Name()) -} - -func (n *graphNodeOrphanResourceFlat) Path() []string { - return n.PathValue -} - -// GraphNodeDestroyable impl. -func (n *graphNodeOrphanResourceFlat) DestroyNode() GraphNodeDestroy { - return n -} - -// GraphNodeDestroy impl. -func (n *graphNodeOrphanResourceFlat) CreateBeforeDestroy() bool { - return false -} - -func (n *graphNodeOrphanResourceFlat) CreateNode() dag.Vertex { - return n -} - -func (n *graphNodeOrphanResourceFlat) ProvidedBy() []string { - return modulePrefixList( - n.graphNodeOrphanResource.ProvidedBy(), - modulePrefixStr(n.PathValue)) -} diff --git a/terraform/transform_orphan_test.go b/terraform/transform_orphan_test.go deleted file mode 100644 index ef6b497ad..000000000 --- a/terraform/transform_orphan_test.go +++ /dev/null @@ -1,389 +0,0 @@ -package terraform - -import ( - "strings" - "testing" - - "github.com/hashicorp/terraform/dag" -) - -func TestOrphanTransformer(t *testing.T) { - mod := testModule(t, "transform-orphan-basic") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: RootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - - // The orphan - "aws_instance.db": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, - }, - } - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - transform := &OrphanTransformer{State: state, Module: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformOrphanBasicStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestOrphanTransformer_modules(t *testing.T) { - mod := testModule(t, "transform-orphan-modules") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: RootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, - - // Orphan module - &ModuleState{ - Path: []string{RootModuleName, "foo"}, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, - }, - } - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - transform := &OrphanTransformer{State: state, Module: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformOrphanModulesStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestOrphanTransformer_modulesDeps(t *testing.T) { - mod := testModule(t, "transform-orphan-modules") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: RootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, - - // Orphan module - &ModuleState{ - Path: []string{RootModuleName, "foo"}, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - Dependencies: []string{ - "aws_instance.foo", - }, - }, - }, - } - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - transform := &OrphanTransformer{State: state, Module: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformOrphanModulesDepsStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestOrphanTransformer_modulesDepsOrphan(t *testing.T) { - mod := testModule(t, "transform-orphan-modules") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: RootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, - - // Orphan module - &ModuleState{ - Path: []string{RootModuleName, "foo"}, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - Dependencies: []string{ - "aws_instance.web", - }, - }, - }, - } - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - transform := &OrphanTransformer{State: state, Module: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformOrphanModulesDepsOrphanStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestOrphanTransformer_modulesNoRoot(t *testing.T) { - mod := testModule(t, "transform-orphan-modules") - state := &State{ - Modules: []*ModuleState{ - // Orphan module - &ModuleState{ - Path: []string{RootModuleName, "foo"}, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, - }, - } - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - transform := &OrphanTransformer{State: state, Module: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformOrphanModulesNoRootStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestOrphanTransformer_resourceDepends(t *testing.T) { - mod := testModule(t, "transform-orphan-basic") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: RootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - - // The orphan - "aws_instance.db": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - Dependencies: []string{ - "aws_instance.web", - }, - }, - }, - }, - }, - } - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - transform := &OrphanTransformer{State: state, Module: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformOrphanResourceDependsStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestOrphanTransformer_nilState(t *testing.T) { - mod := testModule(t, "transform-orphan-basic") - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - transform := &OrphanTransformer{State: nil, Module: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformOrphanNilStateStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraphNodeOrphanModule_impl(t *testing.T) { - var _ dag.Vertex = new(graphNodeOrphanModule) - var _ dag.NamedVertex = new(graphNodeOrphanModule) - var _ GraphNodeExpandable = new(graphNodeOrphanModule) -} - -func TestGraphNodeOrphanResource_impl(t *testing.T) { - var _ dag.Vertex = new(graphNodeOrphanResource) - var _ dag.NamedVertex = new(graphNodeOrphanResource) - var _ GraphNodeProviderConsumer = new(graphNodeOrphanResource) - var _ GraphNodeAddressable = new(graphNodeOrphanResource) -} - -func TestGraphNodeOrphanResource_ProvidedBy(t *testing.T) { - n := &graphNodeOrphanResource{ResourceKey: &ResourceStateKey{Type: "aws_instance"}} - if v := n.ProvidedBy(); v[0] != "aws" { - t.Fatalf("bad: %#v", v) - } -} - -func TestGraphNodeOrphanResource_ProvidedBy_alias(t *testing.T) { - n := &graphNodeOrphanResource{ResourceKey: &ResourceStateKey{Type: "aws_instance"}, Provider: "aws.bar"} - if v := n.ProvidedBy(); v[0] != "aws.bar" { - t.Fatalf("bad: %#v", v) - } -} - -const testTransformOrphanBasicStr = ` -aws_instance.db (orphan) -aws_instance.web -` - -const testTransformOrphanModulesStr = ` -aws_instance.foo -module.foo (orphan) -` - -const testTransformOrphanModulesDepsStr = ` -aws_instance.foo -module.foo (orphan) - aws_instance.foo -` - -const testTransformOrphanModulesDepsOrphanStr = ` -aws_instance.foo -aws_instance.web (orphan) -module.foo (orphan) - aws_instance.web (orphan) -` - -const testTransformOrphanNilStateStr = ` -aws_instance.web -` - -const testTransformOrphanResourceDependsStr = ` -aws_instance.db (orphan) - aws_instance.web -aws_instance.web -` - -const testTransformOrphanModulesNoRootStr = ` -aws_instance.foo -module.foo (orphan) -`