diff --git a/command/graph.go b/command/graph.go index aa5f14d1ae..45299787af 100644 --- a/command/graph.go +++ b/command/graph.go @@ -158,7 +158,8 @@ Options: -no-color If specified, output won't contain any color. -type=plan Type of graph to output. Can be: plan, plan-destroy, apply, - legacy. + validate, input, refresh. + ` return strings.TrimSpace(helpText) diff --git a/helper/experiment/experiment.go b/helper/experiment/experiment.go index 9910110181..d3094291ad 100644 --- a/helper/experiment/experiment.go +++ b/helper/experiment/experiment.go @@ -50,9 +50,6 @@ import ( // of definition and use. This allows the compiler to enforce references // so it becomes easy to remove the features. var ( - // Reuse the old graphs from TF 0.7.x. These will be removed at some point. - X_legacyGraph = newBasicID("legacy-graph", "LEGACY_GRAPH", false) - // Shadow graph. This is already on by default. Disabling it will be // allowed for awhile in order for it to not block operations. X_shadow = newBasicID("shadow", "SHADOW", true) @@ -75,7 +72,6 @@ var ( func init() { // The list of all experiments, update this when an experiment is added. All = []ID{ - X_legacyGraph, X_shadow, x_force, } diff --git a/terraform/context.go b/terraform/context.go index 066ffa3793..616d4766fc 100644 --- a/terraform/context.go +++ b/terraform/context.go @@ -262,30 +262,11 @@ func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, error) { Targets: c.targets, Validate: opts.Validate, }).Build(RootModulePath) - - case GraphTypeLegacy: - return c.graphBuilder(opts).Build(RootModulePath) } return nil, fmt.Errorf("unknown graph type: %s", typ) } -// GraphBuilder returns the GraphBuilder that will be used to create -// the graphs for this context. -func (c *Context) graphBuilder(g *ContextGraphOpts) GraphBuilder { - return &BuiltinGraphBuilder{ - Root: c.module, - Diff: c.diff, - Providers: c.components.ResourceProviders(), - Provisioners: c.components.ResourceProvisioners(), - State: c.state, - Targets: c.targets, - Destroy: c.destroy, - Validate: g.Validate, - Verbose: g.Verbose, - } -} - // ShadowError returns any errors caught during a shadow operation. // // A shadow operation is an operation run in parallel to a real operation @@ -465,15 +446,8 @@ func (c *Context) Apply() (*State, error) { // Copy our own state c.state = c.state.DeepCopy() - // Enable the new graph by default - X_legacyGraph := experiment.Enabled(experiment.X_legacyGraph) - // Build the graph. - graphType := GraphTypeLegacy - if !X_legacyGraph { - graphType = GraphTypeApply - } - graph, err := c.Graph(graphType, nil) + graph, err := c.Graph(GraphTypeApply, nil) if err != nil { return nil, err } @@ -541,17 +515,10 @@ func (c *Context) Plan() (*Plan, error) { c.diff.init() c.diffLock.Unlock() - // Used throughout below - X_legacyGraph := experiment.Enabled(experiment.X_legacyGraph) - // Build the graph. - graphType := GraphTypeLegacy - if !X_legacyGraph { - if c.destroy { - graphType = GraphTypePlanDestroy - } else { - graphType = GraphTypePlan - } + graphType := GraphTypePlan + if c.destroy { + graphType = GraphTypePlanDestroy } graph, err := c.Graph(graphType, nil) if err != nil { @@ -576,15 +543,17 @@ func (c *Context) Plan() (*Plan, error) { p.Diff.DeepCopy() } - // We don't do the reverification during the new destroy plan because - // it will use a different apply process. - if X_legacyGraph { - // Now that we have a diff, we can build the exact graph that Apply will use - // and catch any possible cycles during the Plan phase. - if _, err := c.Graph(GraphTypeLegacy, nil); err != nil { - return nil, err + /* + // We don't do the reverification during the new destroy plan because + // it will use a different apply process. + if X_legacyGraph { + // Now that we have a diff, we can build the exact graph that Apply will use + // and catch any possible cycles during the Plan phase. + if _, err := c.Graph(GraphTypeLegacy, nil); err != nil { + return nil, err + } } - } + */ var errs error if len(walker.ValidationErrors) > 0 { @@ -606,15 +575,8 @@ func (c *Context) Refresh() (*State, error) { // Copy our own state c.state = c.state.DeepCopy() - // Used throughout below - X_legacyGraph := experiment.Enabled(experiment.X_legacyGraph) - // Build the graph. - graphType := GraphTypeLegacy - if !X_legacyGraph { - graphType = GraphTypeRefresh - } - graph, err := c.Graph(graphType, nil) + graph, err := c.Graph(GraphTypeRefresh, nil) if err != nil { return nil, err } diff --git a/terraform/context_apply_test.go b/terraform/context_apply_test.go index a96ac1a98c..0e543975a1 100644 --- a/terraform/context_apply_test.go +++ b/terraform/context_apply_test.go @@ -7229,7 +7229,7 @@ template_file.child: type = template_file Dependencies: - template_file.parent + template_file.parent.* template_file.parent: ID = foo template = Hi diff --git a/terraform/graph.go b/terraform/graph.go index 60258490dd..b6a54f2dd5 100644 --- a/terraform/graph.go +++ b/terraform/graph.go @@ -29,11 +29,6 @@ type Graph struct { // RootModuleName Path []string - // annotations are the annotations that are added to vertices. Annotations - // are arbitrary metadata taht is used for various logic. Annotations - // should have unique keys that are referenced via constants. - annotations map[dag.Vertex]map[string]interface{} - // dependableMap is a lookaside table for fast lookups for connecting // dependencies by their GraphNodeDependable value to avoid O(n^3)-like // situations and turn them into O(1) with respect to the number of new @@ -52,29 +47,6 @@ func (g *Graph) DirectedGraph() dag.Grapher { return &g.AcyclicGraph } -// Annotations returns the annotations that are configured for the -// given vertex. The map is guaranteed to be non-nil but may be empty. -// -// The returned map may be modified to modify the annotations of the -// vertex. -func (g *Graph) Annotations(v dag.Vertex) map[string]interface{} { - g.once.Do(g.init) - - // If this vertex isn't in the graph, then just return an empty map - if !g.HasVertex(v) { - return map[string]interface{}{} - } - - // Get the map, if it doesn't exist yet then initialize it - m, ok := g.annotations[v] - if !ok { - m = make(map[string]interface{}) - g.annotations[v] = m - } - - return m -} - // Add is the same as dag.Graph.Add. func (g *Graph) Add(v dag.Vertex) dag.Vertex { g.once.Do(g.init) @@ -89,14 +61,6 @@ func (g *Graph) Add(v dag.Vertex) dag.Vertex { } } - // If this initializes annotations, then do that - if av, ok := v.(GraphNodeAnnotationInit); ok { - as := g.Annotations(v) - for k, v := range av.AnnotationInit() { - as[k] = v - } - } - return v } @@ -111,9 +75,6 @@ func (g *Graph) Remove(v dag.Vertex) dag.Vertex { } } - // Remove the annotations - delete(g.annotations, v) - // Call upwards to remove it from the actual graph return g.Graph.Remove(v) } @@ -133,12 +94,6 @@ func (g *Graph) Replace(o, n dag.Vertex) bool { } } - // Move the annotation if it exists - if m, ok := g.annotations[o]; ok { - g.annotations[n] = m - delete(g.annotations, o) - } - return g.Graph.Replace(o, n) } @@ -195,13 +150,6 @@ func (g *Graph) ConnectTo(v dag.Vertex, targets []string) []string { return missing } -// Dependable finds the vertices in the graph that have the given dependable -// names and returns them. -func (g *Graph) Dependable(n string) dag.Vertex { - // TODO: do we need this? - return nil -} - // Walk walks the graph with the given walker for callbacks. The graph // will be walked with full parallelism, so the walker should expect // to be called in concurrently. @@ -210,10 +158,6 @@ func (g *Graph) Walk(walker GraphWalker) error { } func (g *Graph) init() { - if g.annotations == nil { - g.annotations = make(map[dag.Vertex]map[string]interface{}) - } - if g.dependableMap == nil { g.dependableMap = make(map[string]dag.Vertex) } @@ -346,16 +290,6 @@ func (g *Graph) walk(walker GraphWalker) error { return g.AcyclicGraph.Walk(walkFn) } -// GraphNodeAnnotationInit is an interface that allows a node to -// initialize it's annotations. -// -// AnnotationInit will be called _once_ when the node is added to a -// graph for the first time and is expected to return it's initial -// annotations. -type GraphNodeAnnotationInit interface { - AnnotationInit() map[string]interface{} -} - // GraphNodeDependable is an interface which says that a node can be // depended on (an edge can be placed between this node and another) according // to the well-known name returned by DependableName. diff --git a/terraform/graph_builder.go b/terraform/graph_builder.go index fb850df18b..6374bb9045 100644 --- a/terraform/graph_builder.go +++ b/terraform/graph_builder.go @@ -4,8 +4,6 @@ import ( "fmt" "log" "strings" - - "github.com/hashicorp/terraform/config/module" ) // GraphBuilder is an interface that can be implemented and used with @@ -77,160 +75,3 @@ func (b *BasicGraphBuilder) Build(path []string) (*Graph, error) { return g, nil } - -// BuiltinGraphBuilder is responsible for building the complete graph that -// Terraform uses for execution. It is an opinionated builder that defines -// the step order required to build a complete graph as is used and expected -// by Terraform. -// -// If you require a custom graph, you'll have to build it up manually -// on your own by building a new GraphBuilder implementation. -type BuiltinGraphBuilder struct { - // Root is the root module of the graph to build. - Root *module.Tree - - // Diff is the diff. The proper module diffs will be looked up. - Diff *Diff - - // State is the global state. The proper module states will be looked - // up by graph path. - State *State - - // Providers is the list of providers supported. - Providers []string - - // Provisioners is the list of provisioners supported. - Provisioners []string - - // Targets is the user-specified list of resources to target. - Targets []string - - // Destroy is set to true when we're in a `terraform destroy` or a - // `terraform plan -destroy` - Destroy bool - - // Determines whether the GraphBuilder should perform graph validation before - // returning the Graph. Generally you want this to be done, except when you'd - // like to inspect a problematic graph. - Validate bool - - // Verbose is set to true when the graph should be built "worst case", - // skipping any prune steps. This is used for early cycle detection during - // Validate and for manual inspection via `terraform graph -verbose`. - Verbose bool -} - -// Build builds the graph according to the steps returned by Steps. -func (b *BuiltinGraphBuilder) Build(path []string) (*Graph, error) { - basic := &BasicGraphBuilder{ - Steps: b.Steps(path), - Validate: b.Validate, - Name: "BuiltinGraphBuilder", - } - - return basic.Build(path) -} - -// Steps returns the ordered list of GraphTransformers that must be executed -// to build a complete graph. -func (b *BuiltinGraphBuilder) Steps(path []string) []GraphTransformer { - steps := []GraphTransformer{ - // Create all our resources from the configuration and state - &ConfigTransformerOld{Module: b.Root}, - &OrphanTransformer{ - State: b.State, - Module: b.Root, - }, - - // Output-related transformations - &AddOutputOrphanTransformer{State: b.State}, - - // Provider-related transformations - &MissingProviderTransformer{Providers: b.Providers}, - &ProviderTransformer{}, - &DisableProviderTransformerOld{}, - - // Provisioner-related transformations - &MissingProvisionerTransformer{Provisioners: b.Provisioners}, - &ProvisionerTransformer{}, - - // Run our vertex-level transforms - &VertexTransformer{ - Transforms: []GraphVertexTransformer{ - // Expand any statically expanded nodes, such as module graphs - &ExpandTransform{ - Builder: b, - }, - }, - }, - - // Flatten stuff - &FlattenTransformer{}, - - // Make sure all the connections that are proxies are connected through - &ProxyTransformer{}, - } - - // If we're on the root path, then we do a bunch of other stuff. - // We don't do the following for modules. - if len(path) <= 1 { - steps = append(steps, - // Optionally reduces the graph to a user-specified list of targets and - // their dependencies. - &TargetsTransformer{Targets: b.Targets, Destroy: b.Destroy}, - - // Create orphan output nodes - &OrphanOutputTransformer{Module: b.Root, State: b.State}, - - // Prune the providers. This must happen only once because flattened - // modules might depend on empty providers. - &PruneProviderTransformer{}, - - // Create the destruction nodes - &DestroyTransformer{FullDestroy: b.Destroy}, - b.conditional(&conditionalOpts{ - If: func() bool { return !b.Destroy }, - Then: &CreateBeforeDestroyTransformer{}, - }), - b.conditional(&conditionalOpts{ - If: func() bool { return !b.Verbose }, - Then: &PruneDestroyTransformer{Diff: b.Diff, State: b.State}, - }), - - // Remove the noop nodes - &PruneNoopTransformer{Diff: b.Diff, State: b.State}, - - // Insert nodes to close opened plugin connections - &CloseProviderTransformer{}, - &CloseProvisionerTransformer{}, - - // Perform the transitive reduction to make our graph a bit - // more sane if possible (it usually is possible). - &TransitiveReductionTransformer{}, - ) - } - - // Make sure we have a single root - steps = append(steps, &RootTransformer{}) - - // Remove nils - for i, s := range steps { - if s == nil { - steps = append(steps[:i], steps[i+1:]...) - } - } - - return steps -} - -type conditionalOpts struct { - If func() bool - Then GraphTransformer -} - -func (b *BuiltinGraphBuilder) conditional(o *conditionalOpts) GraphTransformer { - if o.If != nil && o.Then != nil && o.If() { - return o.Then - } - return nil -} diff --git a/terraform/graph_builder_test.go b/terraform/graph_builder_test.go index f73308b2ce..6d7f4b61b8 100644 --- a/terraform/graph_builder_test.go +++ b/terraform/graph_builder_test.go @@ -65,212 +65,6 @@ func TestBasicGraphBuilder_validateOff(t *testing.T) { } } -func TestBuiltinGraphBuilder_impl(t *testing.T) { - var _ GraphBuilder = new(BuiltinGraphBuilder) -} - -// This test is not meant to test all the transforms but rather just -// to verify we get some basic sane graph out. Special tests to ensure -// specific ordering of steps should be added in other tests. -func TestBuiltinGraphBuilder(t *testing.T) { - b := &BuiltinGraphBuilder{ - Root: testModule(t, "graph-builder-basic"), - Validate: true, - } - - g, err := b.Build(RootModulePath) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testBuiltinGraphBuilderBasicStr) - if actual != expected { - t.Fatalf("bad: %s", actual) - } -} - -func TestBuiltinGraphBuilder_Verbose(t *testing.T) { - b := &BuiltinGraphBuilder{ - Root: testModule(t, "graph-builder-basic"), - Validate: true, - Verbose: true, - } - - g, err := b.Build(RootModulePath) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testBuiltinGraphBuilderVerboseStr) - if actual != expected { - t.Fatalf("bad: %s", actual) - } -} - -// This tests that the CreateBeforeDestoryTransformer is not present when -// we perform a "terraform destroy" operation. We don't actually do anything -// else. -func TestBuiltinGraphBuilder_CreateBeforeDestroy_Destroy_Bypass(t *testing.T) { - b := &BuiltinGraphBuilder{ - Root: testModule(t, "graph-builder-basic"), - Validate: true, - Destroy: true, - } - - steps := b.Steps([]string{}) - - actual := false - expected := false - for _, v := range steps { - switch v.(type) { - case *CreateBeforeDestroyTransformer: - actual = true - } - } - - if actual != expected { - t.Fatalf("bad: CreateBeforeDestroyTransformer still in root path") - } -} - -// This tests that the CreateBeforeDestoryTransformer *is* present -// during a non-destroy operation (ie: Destroy not set). -func TestBuiltinGraphBuilder_CreateBeforeDestroy_NonDestroy_Present(t *testing.T) { - b := &BuiltinGraphBuilder{ - Root: testModule(t, "graph-builder-basic"), - Validate: true, - } - - steps := b.Steps([]string{}) - - actual := false - expected := true - for _, v := range steps { - switch v.(type) { - case *CreateBeforeDestroyTransformer: - actual = true - } - } - - if actual != expected { - t.Fatalf("bad: CreateBeforeDestroyTransformer not in root path") - } -} - -// This tests a cycle we got when a CBD resource depends on a non-CBD -// resource. This cycle shouldn't happen in the general case anymore. -func TestBuiltinGraphBuilder_cbdDepNonCbd(t *testing.T) { - b := &BuiltinGraphBuilder{ - Root: testModule(t, "graph-builder-cbd-non-cbd"), - Validate: true, - } - - _, err := b.Build(RootModulePath) - if err != nil { - t.Fatalf("err: %s", err) - } -} - -// This now returns no errors due to a general fix while building the graph -func TestBuiltinGraphBuilder_cbdDepNonCbd_errorsWhenVerbose(t *testing.T) { - b := &BuiltinGraphBuilder{ - Root: testModule(t, "graph-builder-cbd-non-cbd"), - Validate: true, - Verbose: true, - } - - _, err := b.Build(RootModulePath) - if err != nil { - t.Fatalf("err: %s", err) - } -} - -func TestBuiltinGraphBuilder_multiLevelModule(t *testing.T) { - b := &BuiltinGraphBuilder{ - Root: testModule(t, "graph-builder-multi-level-module"), - Validate: true, - } - - g, err := b.Build(RootModulePath) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testBuiltinGraphBuilderMultiLevelStr) - if actual != expected { - t.Fatalf("bad: %s", actual) - } -} - -func TestBuiltinGraphBuilder_orphanDeps(t *testing.T) { - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - - "aws_instance.bar": &ResourceState{ - Type: "aws_instance", - Dependencies: []string{"aws_instance.foo"}, - Primary: &InstanceState{ - ID: "bar", - }, - }, - }, - }, - }, - } - - b := &BuiltinGraphBuilder{ - Root: testModule(t, "graph-builder-orphan-deps"), - State: state, - Validate: true, - } - - g, err := b.Build(RootModulePath) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testBuiltinGraphBuilderOrphanDepsStr) - if actual != expected { - t.Fatalf("bad: %s", actual) - } -} - -/* -TODO: This exposes a really bad bug we need to fix after we merge -the f-ast-branch. This bug still exists in master. - -// This test tests that the graph builder properly expands modules. -func TestBuiltinGraphBuilder_modules(t *testing.T) { - b := &BuiltinGraphBuilder{ - Root: testModule(t, "graph-builder-modules"), - } - - g, err := b.Build(RootModulePath) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testBuiltinGraphBuilderModuleStr) - if actual != expected { - t.Fatalf("bad: %s", actual) - } -} -*/ - type testBasicGraphBuilderTransform struct { V dag.Vertex } @@ -283,76 +77,3 @@ func (t *testBasicGraphBuilderTransform) Transform(g *Graph) error { const testBasicGraphBuilderStr = ` 1 ` - -const testBuiltinGraphBuilderBasicStr = ` -aws_instance.db - provider.aws -aws_instance.web - aws_instance.db -provider.aws -provider.aws (close) - aws_instance.web -` - -const testBuiltinGraphBuilderVerboseStr = ` -aws_instance.db - aws_instance.db (destroy) -aws_instance.db (destroy) - aws_instance.web (destroy) -aws_instance.web - aws_instance.db -aws_instance.web (destroy) - provider.aws -provider.aws -provider.aws (close) - aws_instance.web -` - -const testBuiltinGraphBuilderMultiLevelStr = ` -module.foo.module.bar.output.value - module.foo.module.bar.var.bar - module.foo.var.foo -module.foo.module.bar.plan-destroy -module.foo.module.bar.var.bar - module.foo.var.foo -module.foo.plan-destroy -module.foo.var.foo -root - module.foo.module.bar.output.value - module.foo.module.bar.plan-destroy - module.foo.module.bar.var.bar - module.foo.plan-destroy - module.foo.var.foo -` - -const testBuiltinGraphBuilderOrphanDepsStr = ` -aws_instance.bar (orphan) - provider.aws -aws_instance.foo (orphan) - aws_instance.bar (orphan) -provider.aws -provider.aws (close) - aws_instance.foo (orphan) -` - -/* -TODO: Commented out this const as it's likely this needs to -be updated when the TestBuiltinGraphBuilder_modules test is -enabled again. -const testBuiltinGraphBuilderModuleStr = ` -aws_instance.web - aws_instance.web (destroy) -aws_instance.web (destroy) - aws_security_group.firewall - module.consul (expanded) - provider.aws -aws_security_group.firewall - aws_security_group.firewall (destroy) -aws_security_group.firewall (destroy) - provider.aws -module.consul (expanded) - aws_security_group.firewall - provider.aws -provider.aws -` -*/ diff --git a/terraform/graph_config_node.go b/terraform/graph_config_node.go deleted file mode 100644 index 57d565ca27..0000000000 --- a/terraform/graph_config_node.go +++ /dev/null @@ -1,37 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform/dag" -) - -// graphNodeConfig is an interface that all graph nodes for the -// configuration graph need to implement in order to build the variable -// dependencies properly. -type graphNodeConfig interface { - dag.NamedVertex - - // All graph nodes should be dependent on other things, and able to - // be depended on. - GraphNodeDependable - GraphNodeDependent - - // ConfigType returns the type of thing in the configuration that - // this node represents, such as a resource, module, etc. - ConfigType() GraphNodeConfigType -} - -// GraphNodeAddressable is an interface that all graph nodes for the -// configuration graph need to implement in order to be be addressed / targeted -// properly. -type GraphNodeAddressable interface { - ResourceAddress() *ResourceAddress -} - -// GraphNodeTargetable is an interface for graph nodes to implement when they -// need to be told about incoming targets. This is useful for nodes that need -// to respect targets as they dynamically expand. Note that the list of targets -// provided will contain every target provided, and each implementing graph -// node must filter this list to targets considered relevant. -type GraphNodeTargetable interface { - SetTargets([]ResourceAddress) -} diff --git a/terraform/graph_config_node_module.go b/terraform/graph_config_node_module.go deleted file mode 100644 index 8a6b522019..0000000000 --- a/terraform/graph_config_node_module.go +++ /dev/null @@ -1,215 +0,0 @@ -package terraform - -import ( - "fmt" - "strings" - - "github.com/hashicorp/terraform/config" - "github.com/hashicorp/terraform/config/module" - "github.com/hashicorp/terraform/dag" -) - -// GraphNodeConfigModule represents a module within the configuration graph. -type GraphNodeConfigModule struct { - Path []string - Module *config.Module - Tree *module.Tree -} - -func (n *GraphNodeConfigModule) ConfigType() GraphNodeConfigType { - return GraphNodeConfigTypeModule -} - -func (n *GraphNodeConfigModule) DependableName() []string { - config := n.Tree.Config() - - result := make([]string, 1, len(config.Outputs)+1) - result[0] = n.Name() - for _, o := range config.Outputs { - result = append(result, fmt.Sprintf("%s.output.%s", n.Name(), o.Name)) - } - - return result -} - -func (n *GraphNodeConfigModule) DependentOn() []string { - vars := n.Module.RawConfig.Variables - result := make([]string, 0, len(vars)) - for _, v := range vars { - if vn := varNameForVar(v); vn != "" { - result = append(result, vn) - } - } - - return result -} - -func (n *GraphNodeConfigModule) Name() string { - return fmt.Sprintf("module.%s", n.Module.Name) -} - -// GraphNodeExpandable -func (n *GraphNodeConfigModule) Expand(b GraphBuilder) (GraphNodeSubgraph, error) { - // Build the graph first - graph, err := b.Build(n.Path) - if err != nil { - return nil, err - } - - { - // Add the destroy marker to the graph - t := &ModuleDestroyTransformerOld{} - if err := t.Transform(graph); err != nil { - return nil, err - } - } - - // Build the actual subgraph node - return &graphNodeModuleExpanded{ - Original: n, - Graph: graph, - Variables: make(map[string]interface{}), - }, nil -} - -// GraphNodeExpandable -func (n *GraphNodeConfigModule) ProvidedBy() []string { - // Build up the list of providers by simply going over our configuration - // to find the providers that are configured there as well as the - // providers that the resources use. - config := n.Tree.Config() - providers := make(map[string]struct{}) - for _, p := range config.ProviderConfigs { - providers[p.Name] = struct{}{} - } - for _, r := range config.Resources { - providers[resourceProvider(r.Type, r.Provider)] = struct{}{} - } - - // Turn the map into a string. This makes sure that the list is - // de-dupped since we could be going over potentially many resources. - result := make([]string, 0, len(providers)) - for p, _ := range providers { - result = append(result, p) - } - - return result -} - -// graphNodeModuleExpanded represents a module where the graph has -// been expanded. It stores the graph of the module as well as a reference -// to the map of variables. -type graphNodeModuleExpanded struct { - Original *GraphNodeConfigModule - Graph *Graph - - // Variables is a map of the input variables. This reference should - // be shared with ModuleInputTransformer in order to create a connection - // where the variables are set properly. - Variables map[string]interface{} -} - -func (n *graphNodeModuleExpanded) Name() string { - return fmt.Sprintf("%s (expanded)", dag.VertexName(n.Original)) -} - -func (n *graphNodeModuleExpanded) ConfigType() GraphNodeConfigType { - return GraphNodeConfigTypeModule -} - -// GraphNodeDependable -func (n *graphNodeModuleExpanded) DependableName() []string { - return n.Original.DependableName() -} - -// GraphNodeDependent -func (n *graphNodeModuleExpanded) DependentOn() []string { - return n.Original.DependentOn() -} - -// GraphNodeDotter impl. -func (n *graphNodeModuleExpanded) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { - return &dag.DotNode{ - Name: name, - Attrs: map[string]string{ - "label": dag.VertexName(n.Original), - "shape": "component", - }, - } -} - -// GraphNodeEvalable impl. -func (n *graphNodeModuleExpanded) EvalTree() EvalNode { - var resourceConfig *ResourceConfig - return &EvalSequence{ - Nodes: []EvalNode{ - &EvalInterpolate{ - Config: n.Original.Module.RawConfig, - Output: &resourceConfig, - }, - - &EvalVariableBlock{ - Config: &resourceConfig, - VariableValues: n.Variables, - }, - }, - } -} - -// GraphNodeFlattenable impl. -func (n *graphNodeModuleExpanded) FlattenGraph() *Graph { - graph := n.Subgraph().(*Graph) - input := n.Original.Module.RawConfig - - // Go over each vertex and do some modifications to the graph for - // flattening. We have to skip some nodes (graphNodeModuleSkippable) - // as well as setup the variable values. - for _, v := range graph.Vertices() { - // If this is a variable, then look it up in the raw configuration. - // If it exists in the raw configuration, set the value of it. - if vn, ok := v.(*GraphNodeConfigVariable); ok && input != nil { - key := vn.VariableName() - if v, ok := input.Raw[key]; ok { - config, err := config.NewRawConfig(map[string]interface{}{ - key: v, - }) - if err != nil { - // This shouldn't happen because it is already in - // a RawConfig above meaning it worked once before. - panic(err) - } - - // Set the variable value so it is interpolated properly. - // Also set the module so we set the value on it properly. - vn.Module = graph.Path[len(graph.Path)-1] - vn.Value = config - } - } - } - - return graph -} - -// GraphNodeSubgraph impl. -func (n *graphNodeModuleExpanded) Subgraph() dag.Grapher { - return n.Graph -} - -func modulePrefixStr(p []string) string { - parts := make([]string, 0, len(p)*2) - for _, p := range p[1:] { - parts = append(parts, "module", p) - } - - return strings.Join(parts, ".") -} - -func modulePrefixList(result []string, prefix string) []string { - if prefix != "" { - for i, v := range result { - result[i] = fmt.Sprintf("%s.%s", prefix, v) - } - } - - return result -} diff --git a/terraform/graph_config_node_module_test.go b/terraform/graph_config_node_module_test.go deleted file mode 100644 index 6aeacf7882..0000000000 --- a/terraform/graph_config_node_module_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package terraform - -import ( - "strings" - "testing" - - "github.com/hashicorp/terraform/config" - "github.com/hashicorp/terraform/dag" -) - -func TestGraphNodeConfigModule_impl(t *testing.T) { - var _ dag.Vertex = new(GraphNodeConfigModule) - var _ dag.NamedVertex = new(GraphNodeConfigModule) - var _ graphNodeConfig = new(GraphNodeConfigModule) - var _ GraphNodeExpandable = new(GraphNodeConfigModule) -} - -func TestGraphNodeConfigModuleExpand(t *testing.T) { - mod := testModule(t, "graph-node-module-expand") - - node := &GraphNodeConfigModule{ - Path: []string{RootModuleName, "child"}, - Module: &config.Module{}, - Tree: nil, - } - - g, err := node.Expand(&BasicGraphBuilder{ - Steps: []GraphTransformer{ - &ConfigTransformerOld{Module: mod}, - }, - }) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.Subgraph().(*Graph).String()) - expected := strings.TrimSpace(testGraphNodeModuleExpandStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraphNodeConfigModuleExpandFlatten(t *testing.T) { - mod := testModule(t, "graph-node-module-flatten") - - node := &GraphNodeConfigModule{ - Path: []string{RootModuleName, "child"}, - Module: &config.Module{}, - Tree: nil, - } - - g, err := node.Expand(&BasicGraphBuilder{ - Steps: []GraphTransformer{ - &ConfigTransformerOld{Module: mod}, - }, - }) - if err != nil { - t.Fatalf("err: %s", err) - } - - fg := g.(GraphNodeFlatGraph) - - actual := strings.TrimSpace(fg.FlattenGraph().String()) - expected := strings.TrimSpace(testGraphNodeModuleExpandFlattenStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -const testGraphNodeModuleExpandStr = ` -aws_instance.bar - aws_instance.foo -aws_instance.foo -plan-destroy -` - -const testGraphNodeModuleExpandFlattenStr = ` -aws_instance.foo -plan-destroy -` diff --git a/terraform/graph_config_node_output.go b/terraform/graph_config_node_output.go deleted file mode 100644 index 0704a0cb75..0000000000 --- a/terraform/graph_config_node_output.go +++ /dev/null @@ -1,106 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform/config" - "github.com/hashicorp/terraform/dag" -) - -// GraphNodeConfigOutput represents an output configured within the -// configuration. -type GraphNodeConfigOutput struct { - Output *config.Output -} - -func (n *GraphNodeConfigOutput) Name() string { - return fmt.Sprintf("output.%s", n.Output.Name) -} - -func (n *GraphNodeConfigOutput) ConfigType() GraphNodeConfigType { - return GraphNodeConfigTypeOutput -} - -func (n *GraphNodeConfigOutput) OutputName() string { - return n.Output.Name -} - -func (n *GraphNodeConfigOutput) DependableName() []string { - return []string{n.Name()} -} - -func (n *GraphNodeConfigOutput) DependentOn() []string { - vars := n.Output.RawConfig.Variables - result := make([]string, 0, len(vars)) - for _, v := range vars { - if vn := varNameForVar(v); vn != "" { - result = append(result, vn) - } - } - - return result -} - -// GraphNodeEvalable impl. -func (n *GraphNodeConfigOutput) EvalTree() EvalNode { - return &EvalOpFilter{ - Ops: []walkOperation{walkRefresh, walkPlan, walkApply, - walkDestroy, walkInput, walkValidate}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - &EvalWriteOutput{ - Name: n.Output.Name, - Sensitive: n.Output.Sensitive, - Value: n.Output.RawConfig, - }, - }, - }, - } -} - -// GraphNodeProxy impl. -func (n *GraphNodeConfigOutput) Proxy() bool { - return true -} - -// GraphNodeDestroyEdgeInclude impl. -func (n *GraphNodeConfigOutput) DestroyEdgeInclude(dag.Vertex) bool { - return false -} - -// GraphNodeFlattenable impl. -func (n *GraphNodeConfigOutput) Flatten(p []string) (dag.Vertex, error) { - return &GraphNodeConfigOutputFlat{ - GraphNodeConfigOutput: n, - PathValue: p, - }, nil -} - -// Same as GraphNodeConfigOutput, but for flattening -type GraphNodeConfigOutputFlat struct { - *GraphNodeConfigOutput - - PathValue []string -} - -func (n *GraphNodeConfigOutputFlat) Name() string { - return fmt.Sprintf( - "%s.%s", modulePrefixStr(n.PathValue), n.GraphNodeConfigOutput.Name()) -} - -func (n *GraphNodeConfigOutputFlat) Path() []string { - return n.PathValue -} - -func (n *GraphNodeConfigOutputFlat) DependableName() []string { - return modulePrefixList( - n.GraphNodeConfigOutput.DependableName(), - modulePrefixStr(n.PathValue)) -} - -func (n *GraphNodeConfigOutputFlat) DependentOn() []string { - prefix := modulePrefixStr(n.PathValue) - return modulePrefixList( - n.GraphNodeConfigOutput.DependentOn(), - prefix) -} diff --git a/terraform/graph_config_node_provider.go b/terraform/graph_config_node_provider.go deleted file mode 100644 index 59fbfcb0b9..0000000000 --- a/terraform/graph_config_node_provider.go +++ /dev/null @@ -1,133 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform/config" - "github.com/hashicorp/terraform/dag" -) - -// GraphNodeConfigProvider represents a configured provider within the -// configuration graph. These are only immediately in the graph when an -// explicit `provider` configuration block is in the configuration. -type GraphNodeConfigProvider struct { - Provider *config.ProviderConfig -} - -func (n *GraphNodeConfigProvider) Name() string { - return fmt.Sprintf("provider.%s", n.ProviderName()) -} - -func (n *GraphNodeConfigProvider) ConfigType() GraphNodeConfigType { - return GraphNodeConfigTypeProvider -} - -func (n *GraphNodeConfigProvider) DependableName() []string { - return []string{n.Name()} -} - -func (n *GraphNodeConfigProvider) DependentOn() []string { - vars := n.Provider.RawConfig.Variables - result := make([]string, 0, len(vars)) - for _, v := range vars { - if vn := varNameForVar(v); vn != "" { - result = append(result, vn) - } - } - - return result -} - -// GraphNodeEvalable impl. -func (n *GraphNodeConfigProvider) EvalTree() EvalNode { - return ProviderEvalTree(n.ProviderName(), n.Provider.RawConfig) -} - -// GraphNodeProvider implementation -func (n *GraphNodeConfigProvider) ProviderName() string { - if n.Provider.Alias == "" { - return n.Provider.Name - } else { - return fmt.Sprintf("%s.%s", n.Provider.Name, n.Provider.Alias) - } -} - -// GraphNodeProvider implementation -func (n *GraphNodeConfigProvider) ProviderConfig() *config.RawConfig { - return n.Provider.RawConfig -} - -// GraphNodeDotter impl. -func (n *GraphNodeConfigProvider) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { - return &dag.DotNode{ - Name: name, - Attrs: map[string]string{ - "label": n.Name(), - "shape": "diamond", - }, - } -} - -// GraphNodeDotterOrigin impl. -func (n *GraphNodeConfigProvider) DotOrigin() bool { - return true -} - -// GraphNodeFlattenable impl. -func (n *GraphNodeConfigProvider) Flatten(p []string) (dag.Vertex, error) { - return &GraphNodeConfigProviderFlat{ - GraphNodeConfigProvider: n, - PathValue: p, - }, nil -} - -// Same as GraphNodeConfigProvider, but for flattening -type GraphNodeConfigProviderFlat struct { - *GraphNodeConfigProvider - - PathValue []string -} - -func (n *GraphNodeConfigProviderFlat) Name() string { - return fmt.Sprintf( - "%s.%s", modulePrefixStr(n.PathValue), n.GraphNodeConfigProvider.Name()) -} - -func (n *GraphNodeConfigProviderFlat) Path() []string { - return n.PathValue -} - -func (n *GraphNodeConfigProviderFlat) DependableName() []string { - return modulePrefixList( - n.GraphNodeConfigProvider.DependableName(), - modulePrefixStr(n.PathValue)) -} - -func (n *GraphNodeConfigProviderFlat) DependentOn() []string { - prefixed := modulePrefixList( - n.GraphNodeConfigProvider.DependentOn(), - modulePrefixStr(n.PathValue)) - - result := make([]string, len(prefixed), len(prefixed)+1) - copy(result, prefixed) - - // If we're in a module, then depend on our parent's provider - if len(n.PathValue) > 1 { - prefix := modulePrefixStr(n.PathValue[:len(n.PathValue)-1]) - if prefix != "" { - prefix += "." - } - - result = append(result, fmt.Sprintf( - "%s%s", - prefix, n.GraphNodeConfigProvider.Name())) - } - - return result -} - -func (n *GraphNodeConfigProviderFlat) ProviderName() string { - return fmt.Sprintf( - "%s.%s", modulePrefixStr(n.PathValue), - n.GraphNodeConfigProvider.ProviderName()) -} diff --git a/terraform/graph_config_node_resource.go b/terraform/graph_config_node_resource.go deleted file mode 100644 index cecedb6ec6..0000000000 --- a/terraform/graph_config_node_resource.go +++ /dev/null @@ -1,539 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - "strings" - - "github.com/hashicorp/terraform/config" - "github.com/hashicorp/terraform/dag" -) - -// GraphNodeCountDependent is implemented by resources for giving only -// the dependencies they have from the "count" field. -type GraphNodeCountDependent interface { - CountDependentOn() []string -} - -// GraphNodeConfigResource represents a resource within the config graph. -type GraphNodeConfigResource struct { - Resource *config.Resource - - // If set to true, this resource represents a resource - // that will be destroyed in some way. - Destroy bool - - // Used during DynamicExpand to target indexes - Targets []ResourceAddress - - Path []string -} - -func (n *GraphNodeConfigResource) Copy() *GraphNodeConfigResource { - ncr := &GraphNodeConfigResource{ - Resource: n.Resource.Copy(), - Destroy: n.Destroy, - Targets: make([]ResourceAddress, 0, len(n.Targets)), - Path: make([]string, 0, len(n.Path)), - } - for _, t := range n.Targets { - ncr.Targets = append(ncr.Targets, *t.Copy()) - } - for _, p := range n.Path { - ncr.Path = append(ncr.Path, p) - } - return ncr -} - -func (n *GraphNodeConfigResource) ConfigType() GraphNodeConfigType { - return GraphNodeConfigTypeResource -} - -func (n *GraphNodeConfigResource) DependableName() []string { - return []string{n.Resource.Id()} -} - -// GraphNodeCountDependent impl. -func (n *GraphNodeConfigResource) CountDependentOn() []string { - result := make([]string, 0, len(n.Resource.RawCount.Variables)) - for _, v := range n.Resource.RawCount.Variables { - if vn := varNameForVar(v); vn != "" { - result = append(result, vn) - } - } - - return result -} - -// GraphNodeDependent impl. -func (n *GraphNodeConfigResource) DependentOn() []string { - result := make([]string, len(n.Resource.DependsOn), - (len(n.Resource.RawCount.Variables)+ - len(n.Resource.RawConfig.Variables)+ - len(n.Resource.DependsOn))*2) - copy(result, n.Resource.DependsOn) - - for _, v := range n.Resource.RawCount.Variables { - if vn := varNameForVar(v); vn != "" { - result = append(result, vn) - } - } - for _, v := range n.Resource.RawConfig.Variables { - if vn := varNameForVar(v); vn != "" { - result = append(result, vn) - } - } - for _, p := range n.Resource.Provisioners { - for _, v := range p.ConnInfo.Variables { - if vn := varNameForVar(v); vn != "" && vn != n.Resource.Id() { - result = append(result, vn) - } - } - for _, v := range p.RawConfig.Variables { - if vn := varNameForVar(v); vn != "" && vn != n.Resource.Id() { - result = append(result, vn) - } - } - } - - return result -} - -// VarWalk calls a callback for all the variables that this resource -// depends on. -func (n *GraphNodeConfigResource) VarWalk(fn func(config.InterpolatedVariable)) { - for _, v := range n.Resource.RawCount.Variables { - fn(v) - } - for _, v := range n.Resource.RawConfig.Variables { - fn(v) - } - for _, p := range n.Resource.Provisioners { - for _, v := range p.ConnInfo.Variables { - fn(v) - } - for _, v := range p.RawConfig.Variables { - fn(v) - } - } -} - -func (n *GraphNodeConfigResource) Name() string { - result := n.Resource.Id() - if n.Destroy { - result += " (destroy)" - } - return result -} - -// GraphNodeDotter impl. -func (n *GraphNodeConfigResource) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { - if n.Destroy && !opts.Verbose { - return nil - } - return &dag.DotNode{ - Name: name, - Attrs: map[string]string{ - "label": n.Name(), - "shape": "box", - }, - } -} - -// GraphNodeFlattenable impl. -func (n *GraphNodeConfigResource) Flatten(p []string) (dag.Vertex, error) { - return &GraphNodeConfigResourceFlat{ - GraphNodeConfigResource: n, - PathValue: p, - }, nil -} - -// GraphNodeDynamicExpandable impl. -func (n *GraphNodeConfigResource) DynamicExpand(ctx EvalContext) (*Graph, error) { - state, lock := ctx.State() - lock.RLock() - defer lock.RUnlock() - - // Start creating the steps - steps := make([]GraphTransformer, 0, 5) - - // Expand counts. - steps = append(steps, &ResourceCountTransformerOld{ - Resource: n.Resource, - Destroy: n.Destroy, - Targets: n.Targets, - }) - - // 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(), - }) - } - - // We always want to apply targeting - steps = append(steps, &TargetsTransformer{ - ParsedTargets: n.Targets, - Destroy: n.Destroy, - }) - - // Always end with the root being added - steps = append(steps, &RootTransformer{}) - - // Build the graph - b := &BasicGraphBuilder{ - Steps: steps, - Validate: true, - Name: "GraphNodeConfigResource", - } - return b.Build(ctx.Path()) -} - -// GraphNodeAddressable impl. -func (n *GraphNodeConfigResource) ResourceAddress() *ResourceAddress { - return &ResourceAddress{ - Path: n.Path[1:], - Index: -1, - InstanceType: TypePrimary, - Name: n.Resource.Name, - Type: n.Resource.Type, - Mode: n.Resource.Mode, - } -} - -// GraphNodeTargetable impl. -func (n *GraphNodeConfigResource) SetTargets(targets []ResourceAddress) { - n.Targets = targets -} - -// GraphNodeEvalable impl. -func (n *GraphNodeConfigResource) EvalTree() EvalNode { - return &EvalSequence{ - Nodes: []EvalNode{ - &EvalInterpolate{Config: n.Resource.RawCount}, - &EvalCountCheckComputed{Resource: n.Resource}, - &EvalOpFilter{ - Ops: []walkOperation{walkValidate}, - Node: &EvalValidateCount{Resource: n.Resource}, - }, - &EvalCountFixZeroOneBoundary{Resource: n.Resource}, - }, - } -} - -// GraphNodeProviderConsumer -func (n *GraphNodeConfigResource) ProvidedBy() []string { - return []string{resourceProvider(n.Resource.Type, n.Resource.Provider)} -} - -// GraphNodeProvisionerConsumer -func (n *GraphNodeConfigResource) ProvisionedBy() []string { - result := make([]string, len(n.Resource.Provisioners)) - for i, p := range n.Resource.Provisioners { - result[i] = p.Type - } - - return result -} - -// GraphNodeDestroyable -func (n *GraphNodeConfigResource) DestroyNode() GraphNodeDestroy { - // If we're already a destroy node, then don't do anything - if n.Destroy { - return nil - } - - result := &graphNodeResourceDestroy{ - GraphNodeConfigResource: *n.Copy(), - Original: n, - } - result.Destroy = true - - return result -} - -// GraphNodeNoopPrunable -func (n *GraphNodeConfigResource) Noop(opts *NoopOpts) bool { - log.Printf("[DEBUG] Checking resource noop: %s", n.Name()) - // We don't have any noop optimizations for destroy nodes yet - if n.Destroy { - log.Printf("[DEBUG] Destroy node, not a noop") - return false - } - - // If there is no diff, then we aren't a noop since something needs to - // be done (such as a plan). We only check if we're a noop in a diff. - if opts.Diff == nil || opts.Diff.Empty() { - log.Printf("[DEBUG] No diff, not a noop") - return false - } - - // If the count has any interpolations, we can't prune this node since - // we need to be sure to evaluate the count so that splat variables work - // later (which need to know the full count). - if len(n.Resource.RawCount.Interpolations) > 0 { - log.Printf("[DEBUG] Count has interpolations, not a noop") - return false - } - - // If we have no module diff, we're certainly a noop. This is because - // it means there is a diff, and that the module we're in just isn't - // in it, meaning we're not doing anything. - if opts.ModDiff == nil || opts.ModDiff.Empty() { - log.Printf("[DEBUG] No mod diff, treating resource as a noop") - return true - } - - // Grab the ID which is the prefix (in the case count > 0 at some point) - prefix := n.Resource.Id() - - // Go through the diff and if there are any with our name on it, keep us - found := false - for k, _ := range opts.ModDiff.Resources { - if strings.HasPrefix(k, prefix) { - log.Printf("[DEBUG] Diff has %s, resource is not a noop", k) - found = true - break - } - } - - log.Printf("[DEBUG] Final noop value: %t", !found) - return !found -} - -// Same as GraphNodeConfigResource, but for flattening -type GraphNodeConfigResourceFlat struct { - *GraphNodeConfigResource - - PathValue []string -} - -func (n *GraphNodeConfigResourceFlat) Name() string { - return fmt.Sprintf( - "%s.%s", modulePrefixStr(n.PathValue), n.GraphNodeConfigResource.Name()) -} - -func (n *GraphNodeConfigResourceFlat) Path() []string { - return n.PathValue -} - -func (n *GraphNodeConfigResourceFlat) DependableName() []string { - return modulePrefixList( - n.GraphNodeConfigResource.DependableName(), - modulePrefixStr(n.PathValue)) -} - -func (n *GraphNodeConfigResourceFlat) DependentOn() []string { - prefix := modulePrefixStr(n.PathValue) - return modulePrefixList( - n.GraphNodeConfigResource.DependentOn(), - prefix) -} - -func (n *GraphNodeConfigResourceFlat) ProvidedBy() []string { - prefix := modulePrefixStr(n.PathValue) - return modulePrefixList( - n.GraphNodeConfigResource.ProvidedBy(), - prefix) -} - -func (n *GraphNodeConfigResourceFlat) ProvisionedBy() []string { - prefix := modulePrefixStr(n.PathValue) - return modulePrefixList( - n.GraphNodeConfigResource.ProvisionedBy(), - prefix) -} - -// GraphNodeDestroyable impl. -func (n *GraphNodeConfigResourceFlat) DestroyNode() GraphNodeDestroy { - // Get our parent destroy node. If we don't have any, just return - raw := n.GraphNodeConfigResource.DestroyNode() - if raw == nil { - return nil - } - - node, ok := raw.(*graphNodeResourceDestroy) - if !ok { - panic(fmt.Sprintf("unknown destroy node: %s %T", dag.VertexName(raw), raw)) - } - - // Otherwise, wrap it so that it gets the proper module treatment. - return &graphNodeResourceDestroyFlat{ - graphNodeResourceDestroy: node, - PathValue: n.PathValue, - FlatCreateNode: n, - } -} - -type graphNodeResourceDestroyFlat struct { - *graphNodeResourceDestroy - - PathValue []string - - // Needs to be able to properly yield back a flattened create node to prevent - FlatCreateNode *GraphNodeConfigResourceFlat -} - -func (n *graphNodeResourceDestroyFlat) Name() string { - return fmt.Sprintf( - "%s.%s", modulePrefixStr(n.PathValue), n.graphNodeResourceDestroy.Name()) -} - -func (n *graphNodeResourceDestroyFlat) Path() []string { - return n.PathValue -} - -func (n *graphNodeResourceDestroyFlat) CreateNode() dag.Vertex { - return n.FlatCreateNode -} - -func (n *graphNodeResourceDestroyFlat) ProvidedBy() []string { - prefix := modulePrefixStr(n.PathValue) - return modulePrefixList( - n.GraphNodeConfigResource.ProvidedBy(), - prefix) -} - -// graphNodeResourceDestroy represents the logical destruction of a -// resource. This node doesn't mean it will be destroyed for sure, but -// instead that if a destroy were to happen, it must happen at this point. -type graphNodeResourceDestroy struct { - GraphNodeConfigResource - Original *GraphNodeConfigResource -} - -func (n *graphNodeResourceDestroy) CreateBeforeDestroy() bool { - // CBD is enabled if the resource enables it - return n.Original.Resource.Lifecycle.CreateBeforeDestroy && n.Destroy -} - -func (n *graphNodeResourceDestroy) CreateNode() dag.Vertex { - return n.Original -} - -func (n *graphNodeResourceDestroy) DestroyInclude(d *ModuleDiff, s *ModuleState) bool { - if n.Destroy { - return n.destroyInclude(d, s) - } - - return true -} - -func (n *graphNodeResourceDestroy) destroyInclude( - d *ModuleDiff, s *ModuleState) bool { - // Get the count, and specifically the raw value of the count - // (with interpolations and all). If the count is NOT a static "1", - // then we keep the destroy node no matter what. - // - // The reasoning for this is complicated and not intuitively obvious, - // but I attempt to explain it below. - // - // The destroy transform works by generating the worst case graph, - // with worst case being the case that every resource already exists - // and needs to be destroy/created (force-new). There is a single important - // edge case where this actually results in a real-life cycle: if a - // create-before-destroy (CBD) resource depends on a non-CBD resource. - // Imagine a EC2 instance "foo" with CBD depending on a security - // group "bar" without CBD, and conceptualize the worst case destroy - // order: - // - // 1.) SG must be destroyed (non-CBD) - // 2.) SG must be created/updated - // 3.) EC2 instance must be created (CBD, requires the SG be made) - // 4.) EC2 instance must be destroyed (requires SG be destroyed) - // - // Except, #1 depends on #4, since the SG can't be destroyed while - // an EC2 instance is using it (AWS API requirements). As you can see, - // this is a real life cycle that can't be automatically reconciled - // except under two conditions: - // - // 1.) SG is also CBD. This doesn't work 100% of the time though - // since the non-CBD resource might not support CBD. To make matters - // worse, the entire transitive closure of dependencies must be - // CBD (if the SG depends on a VPC, you have the same problem). - // 2.) EC2 must not CBD. This can't happen automatically because CBD - // is used as a way to ensure zero (or minimal) downtime Terraform - // applies, and it isn't acceptable for TF to ignore this request, - // since it can result in unexpected downtime. - // - // Therefore, we compromise with this edge case here: if there is - // a static count of "1", we prune the diff to remove cycles during a - // graph optimization path if we don't see the resource in the diff. - // If the count is set to ANYTHING other than a static "1" (variable, - // computed attribute, static number greater than 1), then we keep the - // destroy, since it is required for dynamic graph expansion to find - // orphan count objects. - // - // This isn't ideal logic, but its strictly better without introducing - // new impossibilities. It breaks the cycle in practical cases, and the - // cycle comes back in no cases we've found to be practical, but just - // as the cycle would already exist without this anyways. - count := n.Original.Resource.RawCount - if raw := count.Raw[count.Key]; raw != "1" { - return true - } - - // Okay, we're dealing with a static count. There are a few ways - // to include this resource. - prefix := n.Original.Resource.Id() - - // If we're present in the diff proper, then keep it. We're looking - // only for resources in the diff that match our resource or a count-index - // of our resource that are marked for destroy. - if d != nil { - for k, v := range d.Resources { - match := k == prefix || strings.HasPrefix(k, prefix+".") - if match && v.GetDestroy() { - return true - } - } - } - - // If we're in the state as a primary in any form, then keep it. - // This does a prefix check so it will also catch orphans on count - // decreases to "1". - if s != nil { - for k, v := range s.Resources { - // Ignore exact matches - if k == prefix { - continue - } - - // Ignore anything that doesn't have a "." afterwards so that - // we only get our own resource and any counts on it. - if !strings.HasPrefix(k, prefix+".") { - continue - } - - // Ignore exact matches and the 0'th index. We only care - // about if there is a decrease in count. - if k == prefix+".0" { - continue - } - - if v.Primary != nil { - return true - } - } - - // If we're in the state as _both_ "foo" and "foo.0", then - // keep it, since we treat the latter as an orphan. - _, okOne := s.Resources[prefix] - _, okTwo := s.Resources[prefix+".0"] - if okOne && okTwo { - return true - } - } - - return false -} diff --git a/terraform/graph_config_node_test.go b/terraform/graph_config_node_test.go deleted file mode 100644 index 670f07b39d..0000000000 --- a/terraform/graph_config_node_test.go +++ /dev/null @@ -1,110 +0,0 @@ -package terraform - -import ( - "reflect" - "testing" - - "github.com/hashicorp/terraform/config" - "github.com/hashicorp/terraform/dag" -) - -func TestGraphNodeConfigOutput_impl(t *testing.T) { - var _ dag.Vertex = new(GraphNodeConfigOutput) - var _ dag.NamedVertex = new(GraphNodeConfigOutput) - var _ graphNodeConfig = new(GraphNodeConfigOutput) - var _ GraphNodeOutput = new(GraphNodeConfigOutput) - var _ GraphNodeProxy = new(GraphNodeConfigOutput) -} - -func TestGraphNodeConfigProvider_impl(t *testing.T) { - var _ dag.Vertex = new(GraphNodeConfigProvider) - var _ dag.NamedVertex = new(GraphNodeConfigProvider) - var _ graphNodeConfig = new(GraphNodeConfigProvider) - var _ GraphNodeProvider = new(GraphNodeConfigProvider) -} - -func TestGraphNodeConfigProvider_ProviderName(t *testing.T) { - n := &GraphNodeConfigProvider{ - Provider: &config.ProviderConfig{Name: "foo"}, - } - - if v := n.ProviderName(); v != "foo" { - t.Fatalf("bad: %#v", v) - } -} - -func TestGraphNodeConfigProvider_ProviderName_alias(t *testing.T) { - n := &GraphNodeConfigProvider{ - Provider: &config.ProviderConfig{Name: "foo", Alias: "bar"}, - } - - if v := n.ProviderName(); v != "foo.bar" { - t.Fatalf("bad: %#v", v) - } -} - -func TestGraphNodeConfigProvider_Name(t *testing.T) { - n := &GraphNodeConfigProvider{ - Provider: &config.ProviderConfig{Name: "foo"}, - } - - if v := n.Name(); v != "provider.foo" { - t.Fatalf("bad: %#v", v) - } -} - -func TestGraphNodeConfigProvider_Name_alias(t *testing.T) { - n := &GraphNodeConfigProvider{ - Provider: &config.ProviderConfig{Name: "foo", Alias: "bar"}, - } - - if v := n.Name(); v != "provider.foo.bar" { - t.Fatalf("bad: %#v", v) - } -} - -func TestGraphNodeConfigResource_impl(t *testing.T) { - var _ dag.Vertex = new(GraphNodeConfigResource) - var _ dag.NamedVertex = new(GraphNodeConfigResource) - var _ graphNodeConfig = new(GraphNodeConfigResource) - var _ GraphNodeProviderConsumer = new(GraphNodeConfigResource) - var _ GraphNodeProvisionerConsumer = new(GraphNodeConfigResource) -} - -func TestGraphNodeConfigResource_ProvidedBy(t *testing.T) { - n := &GraphNodeConfigResource{ - Resource: &config.Resource{Type: "aws_instance"}, - } - - if v := n.ProvidedBy(); v[0] != "aws" { - t.Fatalf("bad: %#v", v) - } -} - -func TestGraphNodeConfigResource_ProvidedBy_alias(t *testing.T) { - n := &GraphNodeConfigResource{ - Resource: &config.Resource{Type: "aws_instance", Provider: "aws.bar"}, - } - - if v := n.ProvidedBy(); v[0] != "aws.bar" { - t.Fatalf("bad: %#v", v) - } -} - -func TestGraphNodeConfigResource_ProvisionedBy(t *testing.T) { - n := &GraphNodeConfigResource{ - Resource: &config.Resource{ - Type: "aws_instance", - Provisioners: []*config.Provisioner{ - &config.Provisioner{Type: "foo"}, - &config.Provisioner{Type: "bar"}, - }, - }, - } - - expected := []string{"foo", "bar"} - actual := n.ProvisionedBy() - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: %#v", actual) - } -} diff --git a/terraform/graph_config_node_type.go b/terraform/graph_config_node_type.go deleted file mode 100644 index 42dd8dc9fc..0000000000 --- a/terraform/graph_config_node_type.go +++ /dev/null @@ -1,16 +0,0 @@ -package terraform - -//go:generate stringer -type=GraphNodeConfigType graph_config_node_type.go - -// GraphNodeConfigType is an enum for the type of thing that a graph -// node represents from the configuration. -type GraphNodeConfigType int - -const ( - GraphNodeConfigTypeInvalid GraphNodeConfigType = 0 - GraphNodeConfigTypeResource GraphNodeConfigType = iota - GraphNodeConfigTypeProvider - GraphNodeConfigTypeModule - GraphNodeConfigTypeOutput - GraphNodeConfigTypeVariable -) diff --git a/terraform/graph_config_node_variable.go b/terraform/graph_config_node_variable.go deleted file mode 100644 index ba62eb0567..0000000000 --- a/terraform/graph_config_node_variable.go +++ /dev/null @@ -1,274 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/terraform/config" - "github.com/hashicorp/terraform/config/module" - "github.com/hashicorp/terraform/dag" -) - -// GraphNodeConfigVariable represents a Variable in the config. -type GraphNodeConfigVariable struct { - Variable *config.Variable - - // Value, if non-nil, will be used to set the value of the variable - // during evaluation. If this is nil, evaluation will do nothing. - // - // Module is the name of the module to set the variables on. - Module string - Value *config.RawConfig - - ModuleTree *module.Tree - ModulePath []string -} - -func (n *GraphNodeConfigVariable) Name() string { - return fmt.Sprintf("var.%s", n.Variable.Name) -} - -func (n *GraphNodeConfigVariable) ConfigType() GraphNodeConfigType { - return GraphNodeConfigTypeVariable -} - -func (n *GraphNodeConfigVariable) DependableName() []string { - return []string{n.Name()} -} - -// RemoveIfNotTargeted implements RemovableIfNotTargeted. -// When targeting is active, variables that are not targeted should be removed -// from the graph, because otherwise module variables trying to interpolate -// their references can fail when they're missing the referent resource node. -func (n *GraphNodeConfigVariable) RemoveIfNotTargeted() bool { - return true -} - -func (n *GraphNodeConfigVariable) DependentOn() []string { - // If we don't have any value set, we don't depend on anything - if n.Value == nil { - return nil - } - - // Get what we depend on based on our value - vars := n.Value.Variables - result := make([]string, 0, len(vars)) - for _, v := range vars { - if vn := varNameForVar(v); vn != "" { - result = append(result, vn) - } - } - - return result -} - -func (n *GraphNodeConfigVariable) VariableName() string { - return n.Variable.Name -} - -// GraphNodeDestroyEdgeInclude impl. -func (n *GraphNodeConfigVariable) DestroyEdgeInclude(v dag.Vertex) bool { - // Only include this variable in a destroy edge if the source vertex - // "v" has a count dependency on this variable. - log.Printf("[DEBUG] DestroyEdgeInclude: Checking: %s", dag.VertexName(v)) - cv, ok := v.(GraphNodeCountDependent) - if !ok { - log.Printf("[DEBUG] DestroyEdgeInclude: Not GraphNodeCountDependent: %s", dag.VertexName(v)) - return false - } - - for _, d := range cv.CountDependentOn() { - for _, d2 := range n.DependableName() { - log.Printf("[DEBUG] DestroyEdgeInclude: d = %s : d2 = %s", d, d2) - if d == d2 { - return true - } - } - } - - return false -} - -// GraphNodeNoopPrunable -func (n *GraphNodeConfigVariable) Noop(opts *NoopOpts) bool { - log.Printf("[DEBUG] Checking variable noop: %s", n.Name()) - // If we have no diff, always keep this in the graph. We have to do - // this primarily for validation: we want to validate that variable - // interpolations are valid even if there are no resources that - // depend on them. - if opts.Diff == nil || opts.Diff.Empty() { - log.Printf("[DEBUG] No diff, not a noop") - return false - } - - // We have to find our our module diff since we do funky things with - // the flat node's implementation of Path() below. - modDiff := opts.Diff.ModuleByPath(n.ModulePath) - - // If we're destroying, we have no need of variables unless they are depended - // on by the count of a resource. - if modDiff != nil && modDiff.Destroy { - if n.hasDestroyEdgeInPath(opts, nil) { - log.Printf("[DEBUG] Variable has destroy edge from %s, not a noop", - dag.VertexName(opts.Vertex)) - return false - } - log.Printf("[DEBUG] Variable has no included destroy edges: noop!") - return true - } - - for _, v := range opts.Graph.UpEdges(opts.Vertex).List() { - // This is terrible, but I can't think of a better way to do this. - if dag.VertexName(v) == rootNodeName { - continue - } - - log.Printf("[DEBUG] Found up edge to %s, var is not noop", dag.VertexName(v)) - return false - } - - log.Printf("[DEBUG] No up edges, treating variable as a noop") - return true -} - -// hasDestroyEdgeInPath recursively walks for a destroy edge, ensuring that -// a variable both has no immediate destroy edges or any in its full module -// path, ensuring that links do not get severed in the middle. -func (n *GraphNodeConfigVariable) hasDestroyEdgeInPath(opts *NoopOpts, vertex dag.Vertex) bool { - if vertex == nil { - vertex = opts.Vertex - } - - log.Printf("[DEBUG] hasDestroyEdgeInPath: Looking for destroy edge: %s - %T", dag.VertexName(vertex), vertex) - for _, v := range opts.Graph.UpEdges(vertex).List() { - if len(opts.Graph.UpEdges(v).List()) > 1 { - if n.hasDestroyEdgeInPath(opts, v) == true { - return true - } - } - - // Here we borrow the implementation of DestroyEdgeInclude, whose logic - // and semantics are exactly what we want here. We add a check for the - // the root node, since we have to always depend on its existance. - if cv, ok := vertex.(*GraphNodeConfigVariableFlat); ok { - if dag.VertexName(v) == rootNodeName || cv.DestroyEdgeInclude(v) { - return true - } - } - } - return false -} - -// GraphNodeProxy impl. -func (n *GraphNodeConfigVariable) Proxy() bool { - return true -} - -// GraphNodeEvalable impl. -func (n *GraphNodeConfigVariable) EvalTree() EvalNode { - // If we have no value, do nothing - if n.Value == nil { - return &EvalNoop{} - } - - // Otherwise, interpolate the value of this variable and set it - // within the variables mapping. - var config *ResourceConfig - variables := make(map[string]interface{}) - return &EvalSequence{ - Nodes: []EvalNode{ - &EvalInterpolate{ - Config: n.Value, - Output: &config, - }, - - &EvalVariableBlock{ - Config: &config, - VariableValues: variables, - }, - - &EvalCoerceMapVariable{ - Variables: variables, - ModulePath: n.ModulePath, - ModuleTree: n.ModuleTree, - }, - - &EvalTypeCheckVariable{ - Variables: variables, - ModulePath: n.ModulePath, - ModuleTree: n.ModuleTree, - }, - - &EvalSetVariables{ - Module: &n.Module, - Variables: variables, - }, - }, - } -} - -// GraphNodeFlattenable impl. -func (n *GraphNodeConfigVariable) Flatten(p []string) (dag.Vertex, error) { - return &GraphNodeConfigVariableFlat{ - GraphNodeConfigVariable: n, - PathValue: p, - }, nil -} - -type GraphNodeConfigVariableFlat struct { - *GraphNodeConfigVariable - - PathValue []string -} - -func (n *GraphNodeConfigVariableFlat) Name() string { - return fmt.Sprintf( - "%s.%s", modulePrefixStr(n.PathValue), n.GraphNodeConfigVariable.Name()) -} - -func (n *GraphNodeConfigVariableFlat) DependableName() []string { - return []string{n.Name()} -} - -func (n *GraphNodeConfigVariableFlat) DependentOn() []string { - // We only wrap the dependencies and such if we have a path that is - // longer than 2 elements (root, child, more). This is because when - // flattened, variables can point outside the graph. - prefix := "" - if len(n.PathValue) > 2 { - prefix = modulePrefixStr(n.PathValue[:len(n.PathValue)-1]) - } - - return modulePrefixList( - n.GraphNodeConfigVariable.DependentOn(), - prefix) -} - -func (n *GraphNodeConfigVariableFlat) Path() []string { - if len(n.PathValue) > 2 { - return n.PathValue[:len(n.PathValue)-1] - } - - return nil -} - -func (n *GraphNodeConfigVariableFlat) Noop(opts *NoopOpts) bool { - // First look for provider nodes that depend on this variable downstream - modDiff := opts.Diff.ModuleByPath(n.ModulePath) - if modDiff != nil && modDiff.Destroy { - ds, err := opts.Graph.Descendents(n) - if err != nil { - log.Printf("[ERROR] Error looking up descendents of %s: %s", n.Name(), err) - } else { - for _, d := range ds.List() { - if _, ok := d.(GraphNodeProvider); ok { - log.Printf("[DEBUG] This variable is depended on by a provider, can't be a noop.") - return false - } - } - } - } - - // Then fall back to existing impl - return n.GraphNodeConfigVariable.Noop(opts) -} diff --git a/terraform/graph_config_node_variable_test.go b/terraform/graph_config_node_variable_test.go deleted file mode 100644 index 9f0251b42b..0000000000 --- a/terraform/graph_config_node_variable_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package terraform - -import ( - "testing" - - "github.com/hashicorp/terraform/dag" -) - -func TestGraphNodeConfigVariable_impl(t *testing.T) { - var _ dag.Vertex = new(GraphNodeConfigVariable) - var _ dag.NamedVertex = new(GraphNodeConfigVariable) - var _ graphNodeConfig = new(GraphNodeConfigVariable) - var _ GraphNodeProxy = new(GraphNodeConfigVariable) -} - -func TestGraphNodeConfigVariableFlat_impl(t *testing.T) { - var _ dag.Vertex = new(GraphNodeConfigVariableFlat) - var _ dag.NamedVertex = new(GraphNodeConfigVariableFlat) - var _ graphNodeConfig = new(GraphNodeConfigVariableFlat) - var _ GraphNodeProxy = new(GraphNodeConfigVariableFlat) -} diff --git a/terraform/graphnodeconfigtype_string.go b/terraform/graphnodeconfigtype_string.go deleted file mode 100644 index 9ea0acbebe..0000000000 --- a/terraform/graphnodeconfigtype_string.go +++ /dev/null @@ -1,16 +0,0 @@ -// Code generated by "stringer -type=GraphNodeConfigType graph_config_node_type.go"; DO NOT EDIT - -package terraform - -import "fmt" - -const _GraphNodeConfigType_name = "GraphNodeConfigTypeInvalidGraphNodeConfigTypeResourceGraphNodeConfigTypeProviderGraphNodeConfigTypeModuleGraphNodeConfigTypeOutputGraphNodeConfigTypeVariable" - -var _GraphNodeConfigType_index = [...]uint8{0, 26, 53, 80, 105, 130, 157} - -func (i GraphNodeConfigType) String() string { - if i < 0 || i >= GraphNodeConfigType(len(_GraphNodeConfigType_index)-1) { - return fmt.Sprintf("GraphNodeConfigType(%d)", i) - } - return _GraphNodeConfigType_name[_GraphNodeConfigType_index[i]:_GraphNodeConfigType_index[i+1]] -} diff --git a/terraform/node_data_refresh.go b/terraform/node_data_refresh.go index ae64d844ab..d504c892c4 100644 --- a/terraform/node_data_refresh.go +++ b/terraform/node_data_refresh.go @@ -92,21 +92,10 @@ func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode { // If the config isn't empty we update the state if n.Config != nil { - // Determine the dependencies for the state. We use some older - // code for this that we've used for a long time. - var stateDeps []string - { - oldN := &graphNodeExpandedResource{ - Resource: n.Config, - Index: addr.Index, - } - stateDeps = oldN.StateDependencies() - } - rs = &ResourceState{ Type: n.Config.Type, Provider: n.Config.Provider, - Dependencies: stateDeps, + Dependencies: n.StateReferences(), } } diff --git a/terraform/node_provisioner.go b/terraform/node_provisioner.go new file mode 100644 index 0000000000..bb117c1d6f --- /dev/null +++ b/terraform/node_provisioner.go @@ -0,0 +1,44 @@ +package terraform + +import ( + "fmt" + + "github.com/hashicorp/terraform/config" +) + +// NodeProvisioner represents a provider that has no associated operations. +// It registers all the common interfaces across operations for providers. +type NodeProvisioner struct { + NameValue string + PathValue []string + + // The fields below will be automatically set using the Attach + // interfaces if you're running those transforms, but also be explicitly + // set if you already have that information. + + Config *config.ProviderConfig +} + +func (n *NodeProvisioner) Name() string { + result := fmt.Sprintf("provisioner.%s", n.NameValue) + if len(n.PathValue) > 1 { + result = fmt.Sprintf("%s.%s", modulePrefixStr(n.PathValue), result) + } + + return result +} + +// GraphNodeSubPath +func (n *NodeProvisioner) Path() []string { + return n.PathValue +} + +// GraphNodeProvisioner +func (n *NodeProvisioner) ProvisionerName() string { + return n.NameValue +} + +// GraphNodeEvalable impl. +func (n *NodeProvisioner) EvalTree() EvalNode { + return &EvalInitProvisioner{Name: n.NameValue} +} diff --git a/terraform/node_resource_abstract.go b/terraform/node_resource_abstract.go index 22218e224f..9946e358e9 100644 --- a/terraform/node_resource_abstract.go +++ b/terraform/node_resource_abstract.go @@ -2,6 +2,7 @@ package terraform import ( "fmt" + "strings" "github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/dag" @@ -109,6 +110,63 @@ func (n *NodeAbstractResource) References() []string { return nil } +// StateReferences returns the dependencies to put into the state for +// this resource. +func (n *NodeAbstractResource) StateReferences() []string { + self := n.ReferenceableName() + + // Determine what our "prefix" is for checking for references to + // ourself. + addrCopy := n.Addr.Copy() + addrCopy.Index = -1 + selfPrefix := addrCopy.String() + "." + + depsRaw := n.References() + deps := make([]string, 0, len(depsRaw)) + for _, d := range depsRaw { + // Ignore any variable dependencies + if strings.HasPrefix(d, "var.") { + continue + } + + // If this has a backup ref, ignore those for now. The old state + // file never contained those and I'd rather store the rich types we + // add in the future. + if idx := strings.IndexRune(d, '/'); idx != -1 { + d = d[:idx] + } + + // If we're referencing ourself, then ignore it + found := false + for _, s := range self { + if d == s { + found = true + } + } + if found { + continue + } + + // If this is a reference to ourself and a specific index, we keep + // it. For example, if this resource is "foo.bar" and the reference + // is "foo.bar.0" then we keep it exact. Otherwise, we strip it. + if strings.HasSuffix(d, ".0") && !strings.HasPrefix(d, selfPrefix) { + d = d[:len(d)-2] + } + + // This is sad. The dependencies are currently in the format of + // "module.foo.bar" (the full field). This strips the field off. + if strings.HasPrefix(d, "module.") { + parts := strings.SplitN(d, ".", 3) + d = strings.Join(parts[0:2], ".") + } + + deps = append(deps, d) + } + + return deps +} + // GraphNodeProviderConsumer func (n *NodeAbstractResource) ProvidedBy() []string { // If we have a config we prefer that above all else diff --git a/terraform/node_resource_apply.go b/terraform/node_resource_apply.go index b5ce47f6c6..3599782b9d 100644 --- a/terraform/node_resource_apply.go +++ b/terraform/node_resource_apply.go @@ -70,16 +70,8 @@ func (n *NodeApplyableResource) EvalTree() EvalNode { resource.CountIndex = 0 } - // Determine the dependencies for the state. We use some older - // code for this that we've used for a long time. - var stateDeps []string - { - oldN := &graphNodeExpandedResource{ - Resource: n.Config, - Index: addr.Index, - } - stateDeps = oldN.StateDependencies() - } + // Determine the dependencies for the state. + stateDeps := n.StateReferences() // Eval info is different depending on what kind of resource this is switch n.Config.Mode { diff --git a/terraform/node_resource_plan_instance.go b/terraform/node_resource_plan_instance.go index 418d0f657e..b52956908b 100644 --- a/terraform/node_resource_plan_instance.go +++ b/terraform/node_resource_plan_instance.go @@ -37,13 +37,8 @@ func (n *NodePlannableResourceInstance) EvalTree() EvalNode { resource.CountIndex = 0 } - // Determine the dependencies for the state. We use some older - // code for this that we've used for a long time. - var stateDeps []string - { - oldN := &graphNodeExpandedResource{Resource: n.Config} - stateDeps = oldN.StateDependencies() - } + // Determine the dependencies for the state. + stateDeps := n.StateReferences() // Eval info is different depending on what kind of resource this is switch n.Config.Mode { diff --git a/terraform/semantics.go b/terraform/semantics.go index 6d99875cd0..20f1d8a274 100644 --- a/terraform/semantics.go +++ b/terraform/semantics.go @@ -49,25 +49,6 @@ type SemanticChecker interface { Check(*dag.Graph, dag.Vertex) error } -// SemanticCheckModulesExist is an implementation of SemanticChecker that -// verifies that all the modules that are referenced in the graph exist. -type SemanticCheckModulesExist struct{} - -// TODO: test -func (*SemanticCheckModulesExist) Check(g *dag.Graph, v dag.Vertex) error { - mn, ok := v.(*GraphNodeConfigModule) - if !ok { - return nil - } - - if mn.Tree == nil { - return fmt.Errorf( - "module '%s' not found", mn.Module.Name) - } - - return nil -} - // smcUserVariables does all the semantic checks to verify that the // variables given satisfy the configuration itself. func smcUserVariables(c *config.Config, vs map[string]interface{}) []error { diff --git a/terraform/transform_config_old.go b/terraform/transform_config_old.go index 5f9851681a..ec4125822e 100644 --- a/terraform/transform_config_old.go +++ b/terraform/transform_config_old.go @@ -1,111 +1,11 @@ package terraform import ( - "errors" "fmt" - "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform/config" - "github.com/hashicorp/terraform/config/module" ) -// ConfigTransformerOld is a GraphTransformer that adds the configuration -// to the graph. The module used to configure this transformer must be -// the root module. We'll look up the child module by the Path in the -// Graph. -type ConfigTransformerOld struct { - Module *module.Tree -} - -func (t *ConfigTransformerOld) Transform(g *Graph) error { - // A module is required and also must be completely loaded. - if t.Module == nil { - return errors.New("module must not be nil") - } - if !t.Module.Loaded() { - return errors.New("module must be loaded") - } - - // Get the module we care about - module := t.Module.Child(g.Path[1:]) - if module == nil { - return nil - } - - // Get the configuration for this module - config := module.Config() - - // Create the node list we'll use for the graph - nodes := make([]graphNodeConfig, 0, - (len(config.Variables)+ - len(config.ProviderConfigs)+ - len(config.Modules)+ - len(config.Resources)+ - len(config.Outputs))*2) - - // Write all the variables out - for _, v := range config.Variables { - nodes = append(nodes, &GraphNodeConfigVariable{ - Variable: v, - ModuleTree: t.Module, - ModulePath: g.Path, - }) - } - - // Write all the provider configs out - for _, pc := range config.ProviderConfigs { - nodes = append(nodes, &GraphNodeConfigProvider{Provider: pc}) - } - - // Write all the resources out - for _, r := range config.Resources { - nodes = append(nodes, &GraphNodeConfigResource{ - Resource: r, - Path: g.Path, - }) - } - - // Write all the modules out - children := module.Children() - for _, m := range config.Modules { - path := make([]string, len(g.Path), len(g.Path)+1) - copy(path, g.Path) - path = append(path, m.Name) - - nodes = append(nodes, &GraphNodeConfigModule{ - Path: path, - Module: m, - Tree: children[m.Name], - }) - } - - // Write all the outputs out - for _, o := range config.Outputs { - nodes = append(nodes, &GraphNodeConfigOutput{Output: o}) - } - - // Err is where the final error value will go if there is one - var err error - - // Build the graph vertices - for _, n := range nodes { - g.Add(n) - } - - // Build up the dependencies. We have to do this outside of the above - // loop since the nodes need to be in place for us to build the deps. - for _, n := range nodes { - if missing := g.ConnectDependent(n); len(missing) > 0 { - for _, m := range missing { - err = multierror.Append(err, fmt.Errorf( - "%s: missing dependency: %s", n.Name(), m)) - } - } - } - - return err -} - // varNameForVar returns the VarName value for an interpolated variable. // This value is compared to the VarName() value for the nodes within the // graph to build the graph edges. diff --git a/terraform/transform_config_old_test.go b/terraform/transform_config_old_test.go deleted file mode 100644 index a5b0bbfbc5..0000000000 --- a/terraform/transform_config_old_test.go +++ /dev/null @@ -1,150 +0,0 @@ -package terraform - -import ( - "path/filepath" - "strings" - "testing" - - "github.com/hashicorp/terraform/config/module" -) - -func TestConfigTransformerOld_nilModule(t *testing.T) { - g := Graph{Path: RootModulePath} - tf := &ConfigTransformerOld{} - if err := tf.Transform(&g); err == nil { - t.Fatal("should error") - } -} - -func TestConfigTransformerOld_unloadedModule(t *testing.T) { - mod, err := module.NewTreeModule( - "", filepath.Join(fixtureDir, "graph-basic")) - if err != nil { - t.Fatalf("err: %s", err) - } - - g := Graph{Path: RootModulePath} - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err == nil { - t.Fatal("should error") - } -} - -func TestConfigTransformerOld(t *testing.T) { - g := Graph{Path: RootModulePath} - tf := &ConfigTransformerOld{Module: testModule(t, "graph-basic")} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testGraphBasicStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestConfigTransformerOld_dependsOn(t *testing.T) { - g := Graph{Path: RootModulePath} - tf := &ConfigTransformerOld{Module: testModule(t, "graph-depends-on")} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testGraphDependsOnStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestConfigTransformerOld_modules(t *testing.T) { - g := Graph{Path: RootModulePath} - tf := &ConfigTransformerOld{Module: testModule(t, "graph-modules")} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testGraphModulesStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestConfigTransformerOld_outputs(t *testing.T) { - g := Graph{Path: RootModulePath} - tf := &ConfigTransformerOld{Module: testModule(t, "graph-outputs")} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testGraphOutputsStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestConfigTransformerOld_providerAlias(t *testing.T) { - g := Graph{Path: RootModulePath} - tf := &ConfigTransformerOld{Module: testModule(t, "graph-provider-alias")} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testGraphProviderAliasStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestConfigTransformerOld_errMissingDeps(t *testing.T) { - g := Graph{Path: RootModulePath} - tf := &ConfigTransformerOld{Module: testModule(t, "graph-missing-deps")} - if err := tf.Transform(&g); err == nil { - t.Fatalf("err: %s", err) - } -} - -const testGraphBasicStr = ` -aws_instance.web - aws_security_group.firewall - var.foo -aws_load_balancer.weblb - aws_instance.web -aws_security_group.firewall -openstack_floating_ip.random -provider.aws - openstack_floating_ip.random -var.foo -` - -const testGraphDependsOnStr = ` -aws_instance.db - aws_instance.web -aws_instance.web -` - -const testGraphModulesStr = ` -aws_instance.web - aws_security_group.firewall - module.consul -aws_security_group.firewall -module.consul - aws_security_group.firewall -provider.aws -` - -const testGraphOutputsStr = ` -aws_instance.foo -output.foo - aws_instance.foo -` - -const testGraphProviderAliasStr = ` -provider.aws -provider.aws.bar -provider.aws.foo -` diff --git a/terraform/transform_destroy.go b/terraform/transform_destroy.go deleted file mode 100644 index 64e295805d..0000000000 --- a/terraform/transform_destroy.go +++ /dev/null @@ -1,284 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform/config" - "github.com/hashicorp/terraform/dag" -) - -// GraphNodeDestroyable is the interface that nodes that can be destroyed -// must implement. This is used to automatically handle the creation of -// destroy nodes in the graph and the dependency ordering of those destroys. -type GraphNodeDestroyable interface { - // DestroyNode returns the node used for the destroy with the given - // mode. If this returns nil, then a destroy node for that mode - // will not be added. - DestroyNode() GraphNodeDestroy -} - -// GraphNodeDestroy is the interface that must implemented by -// nodes that destroy. -type GraphNodeDestroy interface { - dag.Vertex - - // CreateBeforeDestroy is called to check whether this node - // should be created before it is destroyed. The CreateBeforeDestroy - // transformer uses this information to setup the graph. - CreateBeforeDestroy() bool - - // CreateNode returns the node used for the create side of this - // destroy. This must already exist within the graph. - CreateNode() dag.Vertex -} - -// GraphNodeDestroyPrunable is the interface that can be implemented to -// signal that this node can be pruned depending on state. -type GraphNodeDestroyPrunable interface { - // DestroyInclude is called to check if this node should be included - // with the given state. The state and diff must NOT be modified. - DestroyInclude(*ModuleDiff, *ModuleState) bool -} - -// GraphNodeEdgeInclude can be implemented to not include something -// as an edge within the destroy graph. This is usually done because it -// might cause unnecessary cycles. -type GraphNodeDestroyEdgeInclude interface { - DestroyEdgeInclude(dag.Vertex) bool -} - -// DestroyTransformer is a GraphTransformer that creates the destruction -// nodes for things that _might_ be destroyed. -type DestroyTransformer struct { - FullDestroy bool -} - -func (t *DestroyTransformer) Transform(g *Graph) error { - var connect, remove []dag.Edge - nodeToCn := make(map[dag.Vertex]dag.Vertex, len(g.Vertices())) - nodeToDn := make(map[dag.Vertex]dag.Vertex, len(g.Vertices())) - for _, v := range g.Vertices() { - // If it is not a destroyable, we don't care - cn, ok := v.(GraphNodeDestroyable) - if !ok { - continue - } - - // Grab the destroy side of the node and connect it through - n := cn.DestroyNode() - if n == nil { - continue - } - - // Store it - nodeToCn[n] = cn - nodeToDn[cn] = n - - // If the creation node is equal to the destroy node, then - // don't do any of the edge jump rope below. - if n.(interface{}) == cn.(interface{}) { - continue - } - - // Add it to the graph - g.Add(n) - - // Inherit all the edges from the old node - downEdges := g.DownEdges(v).List() - for _, edgeRaw := range downEdges { - // If this thing specifically requests to not be depended on - // by destroy nodes, then don't. - if i, ok := edgeRaw.(GraphNodeDestroyEdgeInclude); ok && - !i.DestroyEdgeInclude(v) { - continue - } - - g.Connect(dag.BasicEdge(n, edgeRaw.(dag.Vertex))) - } - - // Add a new edge to connect the node to be created to - // the destroy node. - connect = append(connect, dag.BasicEdge(v, n)) - } - - // Go through the nodes we added and determine if they depend - // on any nodes with a destroy node. If so, depend on that instead. - for n, _ := range nodeToCn { - for _, downRaw := range g.DownEdges(n).List() { - target := downRaw.(dag.Vertex) - cn2, ok := target.(GraphNodeDestroyable) - if !ok { - continue - } - - newTarget := nodeToDn[cn2] - if newTarget == nil { - continue - } - - // Make the new edge and transpose - connect = append(connect, dag.BasicEdge(newTarget, n)) - - // Remove the old edge - remove = append(remove, dag.BasicEdge(n, target)) - } - } - - // Atomatically add/remove the edges - for _, e := range connect { - g.Connect(e) - } - for _, e := range remove { - g.RemoveEdge(e) - } - - return nil -} - -// CreateBeforeDestroyTransformer is a GraphTransformer that modifies -// the destroys of some nodes so that the creation happens before the -// destroy. -type CreateBeforeDestroyTransformer struct{} - -func (t *CreateBeforeDestroyTransformer) Transform(g *Graph) error { - // We "stage" the edge connections/destroys in these slices so that - // while we're doing the edge transformations (transpositions) in - // the graph, we're not affecting future edge transpositions. These - // slices let us stage ALL the changes that WILL happen so that all - // of the transformations happen atomically. - var connect, destroy []dag.Edge - - for _, v := range g.Vertices() { - // We only care to use the destroy nodes - dn, ok := v.(GraphNodeDestroy) - if !ok { - continue - } - - // If the node doesn't need to create before destroy, then continue - if !dn.CreateBeforeDestroy() { - if noCreateBeforeDestroyAncestors(g, dn) { - continue - } - - // PURPOSELY HACKY FIX SINCE THIS TRANSFORM IS DEPRECATED. - // This is a hacky way to fix GH-10439. For a detailed description - // of the fix, see CBDEdgeTransformer, which is the equivalent - // transform used by the new graphs. - // - // This transform is deprecated because it is only used by the - // old graphs which are going to be removed. - var update *config.Resource - if dn, ok := v.(*graphNodeResourceDestroy); ok { - update = dn.Original.Resource - } - if dn, ok := v.(*graphNodeResourceDestroyFlat); ok { - update = dn.Original.Resource - } - if update != nil { - update.Lifecycle.CreateBeforeDestroy = true - } - } - - // Get the creation side of this node - cn := dn.CreateNode() - - // Take all the things which depend on the creation node and - // make them dependencies on the destruction. Clarifying this - // with an example: if you have a web server and a load balancer - // and the load balancer depends on the web server, then when we - // do a create before destroy, we want to make sure the steps are: - // - // 1.) Create new web server - // 2.) Update load balancer - // 3.) Delete old web server - // - // This ensures that. - for _, sourceRaw := range g.UpEdges(cn).List() { - source := sourceRaw.(dag.Vertex) - - // If the graph has a "root" node (one added by a RootTransformer and not - // just a resource that happens to have no ancestors), we don't want to - // add any edges to it, because then it ceases to be a root. - if _, ok := source.(graphNodeRoot); ok { - continue - } - - connect = append(connect, dag.BasicEdge(dn, source)) - } - - // Swap the edge so that the destroy depends on the creation - // happening... - connect = append(connect, dag.BasicEdge(dn, cn)) - destroy = append(destroy, dag.BasicEdge(cn, dn)) - } - - for _, edge := range connect { - g.Connect(edge) - } - for _, edge := range destroy { - g.RemoveEdge(edge) - } - - return nil -} - -// noCreateBeforeDestroyAncestors verifies that a vertex has no ancestors that -// are CreateBeforeDestroy. -// If this vertex has an ancestor with CreateBeforeDestroy, we will need to -// inherit that behavior and re-order the edges even if this node type doesn't -// directly implement CreateBeforeDestroy. -func noCreateBeforeDestroyAncestors(g *Graph, v dag.Vertex) bool { - s, _ := g.Ancestors(v) - if s == nil { - return true - } - for _, v := range s.List() { - dn, ok := v.(GraphNodeDestroy) - if !ok { - continue - } - - if dn.CreateBeforeDestroy() { - // some ancestor is CreateBeforeDestroy, so we need to follow suit - return false - } - } - return true -} - -// PruneDestroyTransformer is a GraphTransformer that removes the destroy -// nodes that aren't in the diff. -type PruneDestroyTransformer struct { - Diff *Diff - State *State -} - -func (t *PruneDestroyTransformer) Transform(g *Graph) error { - for _, v := range g.Vertices() { - // If it is not a destroyer, we don't care - dn, ok := v.(GraphNodeDestroyPrunable) - if !ok { - continue - } - - path := g.Path - if pn, ok := v.(GraphNodeSubPath); ok { - path = pn.Path() - } - - var modDiff *ModuleDiff - var modState *ModuleState - if t.Diff != nil { - modDiff = t.Diff.ModuleByPath(path) - } - if t.State != nil { - modState = t.State.ModuleByPath(path) - } - - // Remove it if we should - if !dn.DestroyInclude(modDiff, modState) { - g.Remove(v) - } - } - - return nil -} diff --git a/terraform/transform_destroy_test.go b/terraform/transform_destroy_test.go deleted file mode 100644 index f03064e564..0000000000 --- a/terraform/transform_destroy_test.go +++ /dev/null @@ -1,506 +0,0 @@ -package terraform - -import ( - "strings" - "testing" -) - -func TestDestroyTransformer(t *testing.T) { - mod := testModule(t, "transform-destroy-basic") - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &DestroyTransformer{} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformDestroyBasicStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestDestroyTransformer_dependsOn(t *testing.T) { - mod := testModule(t, "transform-destroy-depends-on") - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &DestroyTransformer{} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformDestroyBasicStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestCreateBeforeDestroyTransformer(t *testing.T) { - mod := testModule(t, "transform-create-before-destroy-basic") - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &DestroyTransformer{} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &CreateBeforeDestroyTransformer{} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformCreateBeforeDestroyBasicStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestCreateBeforeDestroyTransformer_twice(t *testing.T) { - mod := testModule(t, "transform-create-before-destroy-twice") - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &DestroyTransformer{} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &CreateBeforeDestroyTransformer{} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformCreateBeforeDestroyTwiceStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestPruneDestroyTransformer(t *testing.T) { - var diff *Diff - mod := testModule(t, "transform-destroy-basic") - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &DestroyTransformer{} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &PruneDestroyTransformer{Diff: diff} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformPruneDestroyBasicStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestPruneDestroyTransformer_diff(t *testing.T) { - mod := testModule(t, "transform-destroy-basic") - - diff := &Diff{ - Modules: []*ModuleDiff{ - &ModuleDiff{ - Path: RootModulePath, - Resources: map[string]*InstanceDiff{ - "aws_instance.bar": &InstanceDiff{}, - }, - }, - }, - } - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &DestroyTransformer{} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &PruneDestroyTransformer{Diff: diff} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformPruneDestroyBasicDiffStr) - if actual != expected { - t.Fatalf("expected:\n\n%s\n\nbad:\n\n%s", expected, actual) - } -} - -func TestPruneDestroyTransformer_count(t *testing.T) { - mod := testModule(t, "transform-destroy-prune-count") - - diff := &Diff{} - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &DestroyTransformer{} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &PruneDestroyTransformer{Diff: diff} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformPruneDestroyCountStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestPruneDestroyTransformer_countDec(t *testing.T) { - mod := testModule(t, "transform-destroy-basic") - - diff := &Diff{} - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: RootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.bar.1": &ResourceState{ - Primary: &InstanceState{}, - }, - "aws_instance.bar.2": &ResourceState{ - Primary: &InstanceState{}, - }, - }, - }, - }, - } - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &DestroyTransformer{} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &PruneDestroyTransformer{Diff: diff, State: state} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformPruneDestroyCountDecStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestPruneDestroyTransformer_countState(t *testing.T) { - mod := testModule(t, "transform-destroy-basic") - - diff := &Diff{} - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: RootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.bar": &ResourceState{ - Primary: &InstanceState{}, - }, - }, - }, - }, - } - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &DestroyTransformer{} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &PruneDestroyTransformer{Diff: diff, State: state} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformPruneDestroyCountStateStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestPruneDestroyTransformer_prefixMatch(t *testing.T) { - mod := testModule(t, "transform-destroy-prefix") - - diff := &Diff{} - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: RootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.foo-bar.0": &ResourceState{ - Primary: &InstanceState{ID: "foo"}, - }, - - "aws_instance.foo-bar.1": &ResourceState{ - Primary: &InstanceState{ID: "foo"}, - }, - }, - }, - }, - } - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &DestroyTransformer{} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &PruneDestroyTransformer{Diff: diff, State: state} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformPruneDestroyPrefixStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestPruneDestroyTransformer_tainted(t *testing.T) { - mod := testModule(t, "transform-destroy-basic") - - diff := &Diff{} - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: RootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.bar": &ResourceState{ - Primary: &InstanceState{ - ID: "foo", - Tainted: true, - }, - }, - }, - }, - }, - } - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &DestroyTransformer{} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - tf := &PruneDestroyTransformer{Diff: diff, State: state} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformPruneDestroyTaintedStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -const testTransformDestroyBasicStr = ` -aws_instance.bar - aws_instance.bar (destroy) - aws_instance.foo -aws_instance.bar (destroy) -aws_instance.foo - aws_instance.foo (destroy) -aws_instance.foo (destroy) - aws_instance.bar (destroy) -` - -const testTransformPruneDestroyBasicStr = ` -aws_instance.bar - aws_instance.foo -aws_instance.foo -` - -const testTransformPruneDestroyBasicDiffStr = ` -aws_instance.bar - aws_instance.foo -aws_instance.foo -` - -const testTransformPruneDestroyCountStr = ` -aws_instance.bar - aws_instance.bar (destroy) - aws_instance.foo -aws_instance.bar (destroy) -aws_instance.foo -` - -const testTransformPruneDestroyCountDecStr = ` -aws_instance.bar - aws_instance.bar (destroy) - aws_instance.foo -aws_instance.bar (destroy) -aws_instance.foo -` - -const testTransformPruneDestroyCountStateStr = ` -aws_instance.bar - aws_instance.foo -aws_instance.foo -` - -const testTransformPruneDestroyPrefixStr = ` -aws_instance.foo -aws_instance.foo-bar - aws_instance.foo-bar (destroy) -aws_instance.foo-bar (destroy) -` - -const testTransformPruneDestroyTaintedStr = ` -aws_instance.bar - aws_instance.foo -aws_instance.foo -` - -const testTransformCreateBeforeDestroyBasicStr = ` -aws_instance.web -aws_instance.web (destroy) - aws_instance.web - aws_load_balancer.lb - aws_load_balancer.lb (destroy) -aws_load_balancer.lb - aws_instance.web - aws_load_balancer.lb (destroy) -aws_load_balancer.lb (destroy) -` - -const testTransformCreateBeforeDestroyTwiceStr = ` -aws_autoscale.bar - aws_lc.foo -aws_autoscale.bar (destroy) - aws_autoscale.bar -aws_lc.foo -aws_lc.foo (destroy) - aws_autoscale.bar - aws_autoscale.bar (destroy) - aws_lc.foo -` diff --git a/terraform/transform_expand.go b/terraform/transform_expand.go index b58c6bf17a..982c098b81 100644 --- a/terraform/transform_expand.go +++ b/terraform/transform_expand.go @@ -46,20 +46,3 @@ func (t *ExpandTransform) Transform(v dag.Vertex) (dag.Vertex, error) { log.Printf("[DEBUG] vertex %q: static expanding", dag.VertexName(ev)) return ev.Expand(t.Builder) } - -type GraphNodeBasicSubgraph struct { - NameValue string - Graph *Graph -} - -func (n *GraphNodeBasicSubgraph) Name() string { - return n.NameValue -} - -func (n *GraphNodeBasicSubgraph) Subgraph() dag.Grapher { - return n.Graph -} - -func (n *GraphNodeBasicSubgraph) FlattenGraph() *Graph { - return n.Graph -} diff --git a/terraform/transform_flatten.go b/terraform/transform_flatten.go deleted file mode 100644 index 206bf97bb4..0000000000 --- a/terraform/transform_flatten.go +++ /dev/null @@ -1,107 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform/dag" -) - -// GraphNodeFlatGraph must be implemented by nodes that have subgraphs -// that they want flattened into the graph. -type GraphNodeFlatGraph interface { - FlattenGraph() *Graph -} - -// GraphNodeFlattenable must be implemented by all nodes that can be -// flattened. If a FlattenGraph returns any nodes that can't be flattened, -// it will be an error. -// -// If Flatten returns nil for the Vertex along with a nil error, it will -// removed from the graph. -type GraphNodeFlattenable interface { - Flatten(path []string) (dag.Vertex, error) -} - -// FlattenTransformer is a transformer that goes through the graph, finds -// subgraphs that can be flattened, and flattens them into this graph, -// removing the prior subgraph node. -type FlattenTransformer struct{} - -func (t *FlattenTransformer) Transform(g *Graph) error { - for _, v := range g.Vertices() { - fn, ok := v.(GraphNodeFlatGraph) - if !ok { - continue - } - - // If we don't want to be flattened, don't do it - subgraph := fn.FlattenGraph() - if subgraph == nil { - continue - } - - // Get all the things that depend on this node. We'll re-connect - // dependents later. We have to copy these here since the UpEdges - // value will be deleted after the Remove below. - dependents := make([]dag.Vertex, 0, 5) - for _, v := range g.UpEdges(v).List() { - dependents = append(dependents, v) - } - - // Remove the old node - g.Remove(v) - - // Go through the subgraph and flatten all the nodes - for _, sv := range subgraph.Vertices() { - // If the vertex already has a subpath then we assume it has - // already been flattened. Ignore it. - if _, ok := sv.(GraphNodeSubPath); ok { - continue - } - - fn, ok := sv.(GraphNodeFlattenable) - if !ok { - return fmt.Errorf( - "unflattenable node: %s %T", - dag.VertexName(sv), sv) - } - - v, err := fn.Flatten(subgraph.Path) - if err != nil { - return fmt.Errorf( - "error flattening %s (%T): %s", - dag.VertexName(sv), sv, err) - } - - if v == nil { - subgraph.Remove(v) - } else { - subgraph.Replace(sv, v) - } - } - - // Now that we've handled any changes to the graph that are - // needed, we can add them all to our graph along with their edges. - for _, sv := range subgraph.Vertices() { - g.Add(sv) - } - for _, se := range subgraph.Edges() { - g.Connect(se) - } - - // Connect the dependencies for all the new nodes that we added. - // This will properly connect variables to their sources, for example. - for _, sv := range subgraph.Vertices() { - g.ConnectDependent(sv) - } - - // Re-connect all the things that dependent on the graph - // we just flattened. This should connect them back into the - // correct nodes if their DependentOn() is setup correctly. - for _, v := range dependents { - g.ConnectDependent(v) - } - } - - return nil -} diff --git a/terraform/transform_flatten_test.go b/terraform/transform_flatten_test.go deleted file mode 100644 index 47bc5d2c5c..0000000000 --- a/terraform/transform_flatten_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package terraform - -import ( - "strings" - "testing" -) - -func TestFlattenTransformer(t *testing.T) { - mod := testModule(t, "transform-flatten") - - var b BasicGraphBuilder - b = BasicGraphBuilder{ - Steps: []GraphTransformer{ - &ConfigTransformerOld{Module: mod}, - &VertexTransformer{ - Transforms: []GraphVertexTransformer{ - &ExpandTransform{ - Builder: &b, - }, - }, - }, - &FlattenTransformer{}, - }, - } - - g, err := b.Build(rootModulePath) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformFlattenStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestFlattenTransformer_withProxy(t *testing.T) { - mod := testModule(t, "transform-flatten") - - var b BasicGraphBuilder - b = BasicGraphBuilder{ - Steps: []GraphTransformer{ - &ConfigTransformerOld{Module: mod}, - &VertexTransformer{ - Transforms: []GraphVertexTransformer{ - &ExpandTransform{ - Builder: &b, - }, - }, - }, - &FlattenTransformer{}, - &ProxyTransformer{}, - }, - } - - g, err := b.Build(rootModulePath) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformFlattenProxyStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -const testTransformFlattenStr = ` -aws_instance.parent -aws_instance.parent-output - module.child.output.output -module.child.aws_instance.child - module.child.var.var -module.child.output.output - module.child.aws_instance.child -module.child.plan-destroy -module.child.var.var - aws_instance.parent -` - -const testTransformFlattenProxyStr = ` -aws_instance.parent -aws_instance.parent-output - module.child.aws_instance.child - module.child.output.output -module.child.aws_instance.child - aws_instance.parent - module.child.var.var -module.child.output.output - module.child.aws_instance.child -module.child.plan-destroy -module.child.var.var - aws_instance.parent -` diff --git a/terraform/transform_module_destroy_old.go b/terraform/transform_module_destroy_old.go deleted file mode 100644 index e971838f1c..0000000000 --- a/terraform/transform_module_destroy_old.go +++ /dev/null @@ -1,62 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform/dag" -) - -// ModuleDestroyTransformer is a GraphTransformer that adds a node -// to the graph that will just mark the full module for destroy in -// the destroy scenario. -type ModuleDestroyTransformerOld struct{} - -func (t *ModuleDestroyTransformerOld) Transform(g *Graph) error { - // Create the node - n := &graphNodeModuleDestroy{Path: g.Path} - - // Add it to the graph. We don't need any edges because - // it can happen whenever. - g.Add(n) - - return nil -} - -type graphNodeModuleDestroy struct { - Path []string -} - -func (n *graphNodeModuleDestroy) Name() string { - return "plan-destroy" -} - -// GraphNodeEvalable impl. -func (n *graphNodeModuleDestroy) EvalTree() EvalNode { - return &EvalOpFilter{ - Ops: []walkOperation{walkPlanDestroy}, - Node: &EvalDiffDestroyModule{Path: n.Path}, - } -} - -// GraphNodeFlattenable impl. -func (n *graphNodeModuleDestroy) Flatten(p []string) (dag.Vertex, error) { - return &graphNodeModuleDestroyFlat{ - graphNodeModuleDestroy: n, - PathValue: p, - }, nil -} - -type graphNodeModuleDestroyFlat struct { - *graphNodeModuleDestroy - - PathValue []string -} - -func (n *graphNodeModuleDestroyFlat) Name() string { - return fmt.Sprintf( - "%s.%s", modulePrefixStr(n.PathValue), n.graphNodeModuleDestroy.Name()) -} - -func (n *graphNodeModuleDestroyFlat) Path() []string { - return n.PathValue -} diff --git a/terraform/transform_noop.go b/terraform/transform_noop.go deleted file mode 100644 index e36b619377..0000000000 --- a/terraform/transform_noop.go +++ /dev/null @@ -1,104 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform/dag" -) - -// GraphNodeNoopPrunable can be implemented by nodes that can be -// pruned if they are noops. -type GraphNodeNoopPrunable interface { - Noop(*NoopOpts) bool -} - -// NoopOpts are the options available to determine if your node is a noop. -type NoopOpts struct { - Graph *Graph - Vertex dag.Vertex - Diff *Diff - State *State - ModDiff *ModuleDiff - ModState *ModuleState -} - -// PruneNoopTransformer is a graph transform that prunes nodes that -// consider themselves no-ops. This is done to both simplify the graph -// as well as to remove graph nodes that might otherwise cause problems -// during the graph run. Therefore, this transformer isn't completely -// an optimization step, and can instead be considered critical to -// Terraform operations. -// -// Example of the above case: variables for modules interpolate their values. -// Interpolation will fail on destruction (since attributes are being deleted), -// but variables shouldn't even eval if there is nothing that will consume -// the variable. Therefore, variables can note that they can be omitted -// safely in this case. -// -// The PruneNoopTransformer will prune nodes depth first, and will automatically -// create connect through the dependencies of pruned nodes. For example, -// if we have a graph A => B => C (A depends on B, etc.), and B decides to -// be removed, we'll still be left with A => C; the edge will be properly -// connected. -type PruneNoopTransformer struct { - Diff *Diff - State *State -} - -func (t *PruneNoopTransformer) Transform(g *Graph) error { - // Find the leaves. - leaves := make([]dag.Vertex, 0, 10) - for _, v := range g.Vertices() { - if g.DownEdges(v).Len() == 0 { - leaves = append(leaves, v) - } - } - - // Do a depth first walk from the leaves and remove things. - return g.ReverseDepthFirstWalk(leaves, func(v dag.Vertex, depth int) error { - // We need a prunable - pn, ok := v.(GraphNodeNoopPrunable) - if !ok { - return nil - } - - // Start building the noop opts - path := g.Path - if pn, ok := v.(GraphNodeSubPath); ok { - path = pn.Path() - } - - var modDiff *ModuleDiff - var modState *ModuleState - if t.Diff != nil { - modDiff = t.Diff.ModuleByPath(path) - } - if t.State != nil { - modState = t.State.ModuleByPath(path) - } - - // Determine if its a noop. If it isn't, just return - noop := pn.Noop(&NoopOpts{ - Graph: g, - Vertex: v, - Diff: t.Diff, - State: t.State, - ModDiff: modDiff, - ModState: modState, - }) - if !noop { - return nil - } - - // It is a noop! We first preserve edges. - up := g.UpEdges(v).List() - for _, downV := range g.DownEdges(v).List() { - for _, upV := range up { - g.Connect(dag.BasicEdge(upV, downV)) - } - } - - // Then remove it - g.Remove(v) - - return nil - }) -} diff --git a/terraform/transform_noop_test.go b/terraform/transform_noop_test.go deleted file mode 100644 index 65db95fda1..0000000000 --- a/terraform/transform_noop_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package terraform - -import ( - "strings" - "testing" - - "github.com/hashicorp/terraform/dag" -) - -func TestPruneNoopTransformer(t *testing.T) { - g := Graph{Path: RootModulePath} - - a := &testGraphNodeNoop{NameValue: "A"} - b := &testGraphNodeNoop{NameValue: "B", Value: true} - c := &testGraphNodeNoop{NameValue: "C"} - - g.Add(a) - g.Add(b) - g.Add(c) - g.Connect(dag.BasicEdge(a, b)) - g.Connect(dag.BasicEdge(b, c)) - - { - tf := &PruneNoopTransformer{} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformPruneNoopStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -const testTransformPruneNoopStr = ` -A - C -C -` - -type testGraphNodeNoop struct { - NameValue string - Value bool -} - -func (v *testGraphNodeNoop) Name() string { - return v.NameValue -} - -func (v *testGraphNodeNoop) Noop(*NoopOpts) bool { - return v.Value -} diff --git a/terraform/transform_orphan.go b/terraform/transform_orphan.go deleted file mode 100644 index 1b288b39db..0000000000 --- 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 ef6b497ade..0000000000 --- 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) -` diff --git a/terraform/transform_output_orphan.go b/terraform/transform_output_orphan.go deleted file mode 100644 index ffaa0b7e26..0000000000 --- a/terraform/transform_output_orphan.go +++ /dev/null @@ -1,101 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform/dag" -) - -// GraphNodeOutput is an interface that nodes that are outputs must -// implement. The OutputName returned is the name of the output key -// that they manage. -type GraphNodeOutput interface { - OutputName() string -} - -// AddOutputOrphanTransformer is a transformer that adds output orphans -// to the graph. Output orphans are outputs that are no longer in the -// configuration and therefore need to be removed from the state. -// -// NOTE: This is the _old_ way to add output orphans that is used with -// legacy graph builders. The new way is OrphanOutputTransformer. -type AddOutputOrphanTransformer struct { - State *State -} - -func (t *AddOutputOrphanTransformer) Transform(g *Graph) error { - // Get the state for this module. If we have no state, we have no orphans - state := t.State.ModuleByPath(g.Path) - if state == nil { - return nil - } - - // Create the set of outputs we do have in the graph - found := make(map[string]struct{}) - for _, v := range g.Vertices() { - on, ok := v.(GraphNodeOutput) - if !ok { - continue - } - - found[on.OutputName()] = struct{}{} - } - - // Go over all the outputs. If we don't have a graph node for it, - // create it. It doesn't need to depend on anything, since its just - // setting it empty. - for k, _ := range state.Outputs { - if _, ok := found[k]; ok { - continue - } - - g.Add(&graphNodeOrphanOutput{OutputName: k}) - } - - return nil -} - -type graphNodeOrphanOutput struct { - OutputName string -} - -func (n *graphNodeOrphanOutput) Name() string { - return fmt.Sprintf("output.%s (orphan)", n.OutputName) -} - -func (n *graphNodeOrphanOutput) EvalTree() EvalNode { - return &EvalOpFilter{ - Ops: []walkOperation{walkApply, walkDestroy, walkRefresh}, - Node: &EvalDeleteOutput{ - Name: n.OutputName, - }, - } -} - -// GraphNodeFlattenable impl. -func (n *graphNodeOrphanOutput) Flatten(p []string) (dag.Vertex, error) { - return &graphNodeOrphanOutputFlat{ - graphNodeOrphanOutput: n, - PathValue: p, - }, nil -} - -type graphNodeOrphanOutputFlat struct { - *graphNodeOrphanOutput - - PathValue []string -} - -func (n *graphNodeOrphanOutputFlat) Name() string { - return fmt.Sprintf( - "%s.%s", modulePrefixStr(n.PathValue), n.graphNodeOrphanOutput.Name()) -} - -func (n *graphNodeOrphanOutputFlat) EvalTree() EvalNode { - return &EvalOpFilter{ - Ops: []walkOperation{walkApply, walkDestroy, walkRefresh}, - Node: &EvalDeleteOutput{ - Name: n.OutputName, - }, - } -} diff --git a/terraform/transform_output_orphan_test.go b/terraform/transform_output_orphan_test.go deleted file mode 100644 index 1c930ffa3c..0000000000 --- a/terraform/transform_output_orphan_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package terraform - -import ( - "strings" - "testing" -) - -func TestAddOutputOrphanTransformer(t *testing.T) { - mod := testModule(t, "transform-orphan-output-basic") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: RootModulePath, - Outputs: map[string]*OutputState{ - "foo": &OutputState{ - Value: "bar", - Type: "string", - }, - "bar": &OutputState{ - Value: "baz", - Type: "string", - }, - }, - }, - }, - } - - g := Graph{Path: RootModulePath} - { - tf := &ConfigTransformerOld{Module: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - transform := &AddOutputOrphanTransformer{State: state} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformOrphanOutputBasicStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -const testTransformOrphanOutputBasicStr = ` -output.bar (orphan) -output.foo -` diff --git a/terraform/transform_provider.go b/terraform/transform_provider.go index ac13bb9a5c..aa49922279 100644 --- a/terraform/transform_provider.go +++ b/terraform/transform_provider.go @@ -6,7 +6,6 @@ import ( "strings" "github.com/hashicorp/go-multierror" - "github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/dag" ) @@ -15,7 +14,6 @@ import ( // they satisfy. type GraphNodeProvider interface { ProviderName() string - ProviderConfig() *config.RawConfig } // GraphNodeCloseProvider is an interface that nodes that can be a close @@ -126,7 +124,7 @@ func (t *MissingProviderTransformer) Transform(g *Graph) error { // Initialize factory if t.Concrete == nil { t.Concrete = func(a *NodeAbstractProvider) dag.Vertex { - return &graphNodeProvider{ProviderNameValue: a.NameValue} + return a } } @@ -188,14 +186,6 @@ func (t *MissingProviderTransformer) Transform(g *Graph) error { PathValue: path, }).(dag.Vertex) if len(path) > 0 { - if fn, ok := v.(GraphNodeFlattenable); ok { - var err error - v, err = fn.Flatten(path) - if err != nil { - return err - } - } - // We'll need the parent provider as well, so let's // add a dummy node to check to make sure that we add // that parent provider. @@ -230,9 +220,6 @@ func (t *ParentProviderTransformer) Transform(g *Graph) error { // We eventually want to get rid of the flat version entirely so // this is a stop-gap while it still exists. var v dag.Vertex = raw - if f, ok := v.(*graphNodeProviderFlat); ok { - v = f.graphNodeProvider - } // Only care about providers pn, ok := v.(GraphNodeProvider) @@ -313,15 +300,7 @@ func providerVertexMap(g *Graph) map[string]dag.Vertex { m := make(map[string]dag.Vertex) for _, v := range g.Vertices() { if pv, ok := v.(GraphNodeProvider); ok { - key := pv.ProviderName() - - // This special case is because the new world view of providers - // is that they should return only their pure name (not the full - // module path with ProviderName). Working towards this future. - if _, ok := v.(*NodeApplyableProvider); ok { - key = providerMapKey(pv.ProviderName(), v) - } - + key := providerMapKey(pv.ProviderName(), v) m[key] = v } } @@ -376,97 +355,6 @@ func (n *graphNodeCloseProvider) DotNode(name string, opts *dag.DotOpts) *dag.Do } } -type graphNodeProvider struct { - ProviderNameValue string -} - -func (n *graphNodeProvider) Name() string { - return fmt.Sprintf("provider.%s", n.ProviderNameValue) -} - -// GraphNodeEvalable impl. -func (n *graphNodeProvider) EvalTree() EvalNode { - return ProviderEvalTree(n.ProviderNameValue, nil) -} - -// GraphNodeDependable impl. -func (n *graphNodeProvider) DependableName() []string { - return []string{n.Name()} -} - -// GraphNodeProvider -func (n *graphNodeProvider) ProviderName() string { - return n.ProviderNameValue -} - -func (n *graphNodeProvider) ProviderConfig() *config.RawConfig { - return nil -} - -// GraphNodeDotter impl. -func (n *graphNodeProvider) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { - return &dag.DotNode{ - Name: name, - Attrs: map[string]string{ - "label": n.Name(), - "shape": "diamond", - }, - } -} - -// GraphNodeDotterOrigin impl. -func (n *graphNodeProvider) DotOrigin() bool { - return true -} - -// GraphNodeFlattenable impl. -func (n *graphNodeProvider) Flatten(p []string) (dag.Vertex, error) { - return &graphNodeProviderFlat{ - graphNodeProvider: n, - PathValue: p, - }, nil -} - -// Same as graphNodeMissingProvider, but for flattening -type graphNodeProviderFlat struct { - *graphNodeProvider - - PathValue []string -} - -func (n *graphNodeProviderFlat) Name() string { - return fmt.Sprintf( - "%s.%s", modulePrefixStr(n.PathValue), n.graphNodeProvider.Name()) -} - -func (n *graphNodeProviderFlat) Path() []string { - return n.PathValue -} - -func (n *graphNodeProviderFlat) ProviderName() string { - return fmt.Sprintf( - "%s.%s", modulePrefixStr(n.PathValue), - n.graphNodeProvider.ProviderName()) -} - -// GraphNodeDependable impl. -func (n *graphNodeProviderFlat) DependableName() []string { - return []string{n.Name()} -} - -func (n *graphNodeProviderFlat) DependentOn() []string { - var result []string - - // If we're in a module, then depend on all parent providers. Some of - // these may not exist, hence we depend on all of them. - for i := len(n.PathValue); i > 1; i-- { - prefix := modulePrefixStr(n.PathValue[:i-1]) - result = modulePrefixList(n.graphNodeProvider.DependableName(), prefix) - } - - return result -} - // graphNodeProviderConsumerDummy is a struct that never enters the real // graph (though it could to no ill effect). It implements // GraphNodeProviderConsumer and GraphNodeSubpath as a way to force diff --git a/terraform/transform_provider_old.go b/terraform/transform_provider_old.go deleted file mode 100644 index 50b4522591..0000000000 --- a/terraform/transform_provider_old.go +++ /dev/null @@ -1,174 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform/config" - "github.com/hashicorp/terraform/dag" -) - -// DisableProviderTransformer "disables" any providers that are only -// depended on by modules. -// -// NOTE: "old" = used by old graph builders, will be removed one day -type DisableProviderTransformerOld struct{} - -func (t *DisableProviderTransformerOld) Transform(g *Graph) error { - // Since we're comparing against edges, we need to make sure we connect - g.ConnectDependents() - - for _, v := range g.Vertices() { - // We only care about providers - pn, ok := v.(GraphNodeProvider) - if !ok || pn.ProviderName() == "" { - continue - } - - // Go through all the up-edges (things that depend on this - // provider) and if any is not a module, then ignore this node. - nonModule := false - for _, sourceRaw := range g.UpEdges(v).List() { - source := sourceRaw.(dag.Vertex) - cn, ok := source.(graphNodeConfig) - if !ok { - nonModule = true - break - } - - if cn.ConfigType() != GraphNodeConfigTypeModule { - nonModule = true - break - } - } - if nonModule { - // We found something that depends on this provider that - // isn't a module, so skip it. - continue - } - - // Disable the provider by replacing it with a "disabled" provider - disabled := &graphNodeDisabledProvider{GraphNodeProvider: pn} - if !g.Replace(v, disabled) { - panic(fmt.Sprintf( - "vertex disappeared from under us: %s", - dag.VertexName(v))) - } - } - - return nil -} - -type graphNodeDisabledProvider struct { - GraphNodeProvider -} - -// GraphNodeEvalable impl. -func (n *graphNodeDisabledProvider) EvalTree() EvalNode { - var resourceConfig *ResourceConfig - - return &EvalOpFilter{ - Ops: []walkOperation{walkInput, walkValidate, walkRefresh, walkPlan, walkApply, walkDestroy}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - &EvalInterpolate{ - Config: n.ProviderConfig(), - Output: &resourceConfig, - }, - &EvalBuildProviderConfig{ - Provider: n.ProviderName(), - Config: &resourceConfig, - Output: &resourceConfig, - }, - &EvalSetProviderConfig{ - Provider: n.ProviderName(), - Config: &resourceConfig, - }, - }, - }, - } -} - -// GraphNodeFlattenable impl. -func (n *graphNodeDisabledProvider) Flatten(p []string) (dag.Vertex, error) { - return &graphNodeDisabledProviderFlat{ - graphNodeDisabledProvider: n, - PathValue: p, - }, nil -} - -func (n *graphNodeDisabledProvider) Name() string { - return fmt.Sprintf("%s (disabled)", dag.VertexName(n.GraphNodeProvider)) -} - -// GraphNodeDotter impl. -func (n *graphNodeDisabledProvider) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { - return &dag.DotNode{ - Name: name, - Attrs: map[string]string{ - "label": n.Name(), - "shape": "diamond", - }, - } -} - -// GraphNodeDotterOrigin impl. -func (n *graphNodeDisabledProvider) DotOrigin() bool { - return true -} - -// GraphNodeDependable impl. -func (n *graphNodeDisabledProvider) DependableName() []string { - return []string{"provider." + n.ProviderName()} -} - -// GraphNodeProvider impl. -func (n *graphNodeDisabledProvider) ProviderName() string { - return n.GraphNodeProvider.ProviderName() -} - -// GraphNodeProvider impl. -func (n *graphNodeDisabledProvider) ProviderConfig() *config.RawConfig { - return n.GraphNodeProvider.ProviderConfig() -} - -// Same as graphNodeDisabledProvider, but for flattening -type graphNodeDisabledProviderFlat struct { - *graphNodeDisabledProvider - - PathValue []string -} - -func (n *graphNodeDisabledProviderFlat) Name() string { - return fmt.Sprintf( - "%s.%s", modulePrefixStr(n.PathValue), n.graphNodeDisabledProvider.Name()) -} - -func (n *graphNodeDisabledProviderFlat) Path() []string { - return n.PathValue -} - -func (n *graphNodeDisabledProviderFlat) ProviderName() string { - return fmt.Sprintf( - "%s.%s", modulePrefixStr(n.PathValue), - n.graphNodeDisabledProvider.ProviderName()) -} - -// GraphNodeDependable impl. -func (n *graphNodeDisabledProviderFlat) DependableName() []string { - return modulePrefixList( - n.graphNodeDisabledProvider.DependableName(), - modulePrefixStr(n.PathValue)) -} - -func (n *graphNodeDisabledProviderFlat) DependentOn() []string { - var result []string - - // If we're in a module, then depend on our parent's provider - if len(n.PathValue) > 1 { - prefix := modulePrefixStr(n.PathValue[:len(n.PathValue)-1]) - result = modulePrefixList( - n.graphNodeDisabledProvider.DependableName(), prefix) - } - - return result -} diff --git a/terraform/transform_provider_test.go b/terraform/transform_provider_test.go index 1ea7be76d3..a845623793 100644 --- a/terraform/transform_provider_test.go +++ b/terraform/transform_provider_test.go @@ -3,8 +3,6 @@ package terraform import ( "strings" "testing" - - "github.com/hashicorp/terraform/dag" ) func TestProviderTransformer(t *testing.T) { @@ -12,12 +10,26 @@ func TestProviderTransformer(t *testing.T) { g := Graph{Path: RootModulePath} { - tf := &ConfigTransformerOld{Module: mod} + tf := &ConfigTransformer{Module: mod} if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) } } + { + transform := &AttachResourceConfigTransformer{Module: mod} + if err := transform.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + + { + transform := &MissingProviderTransformer{Providers: []string{"aws"}} + if err := transform.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + transform := &ProviderTransformer{} if err := transform.Transform(&g); err != nil { t.Fatalf("err: %s", err) @@ -73,12 +85,26 @@ func TestCloseProviderTransformer(t *testing.T) { g := Graph{Path: RootModulePath} { - tf := &ConfigTransformerOld{Module: mod} + tf := &ConfigTransformer{Module: mod} if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) } } + { + transform := &AttachResourceConfigTransformer{Module: mod} + if err := transform.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + + { + transform := &MissingProviderTransformer{Providers: []string{"aws"}} + if err := transform.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + { transform := &ProviderTransformer{} if err := transform.Transform(&g); err != nil { @@ -105,7 +131,8 @@ func TestCloseProviderTransformer_withTargets(t *testing.T) { g := Graph{Path: RootModulePath} transforms := []GraphTransformer{ - &ConfigTransformerOld{Module: mod}, + &ConfigTransformer{Module: mod}, + &MissingProviderTransformer{Providers: []string{"aws"}}, &ProviderTransformer{}, &CloseProviderTransformer{}, &TargetsTransformer{ @@ -135,14 +162,21 @@ func TestMissingProviderTransformer(t *testing.T) { g := Graph{Path: RootModulePath} { - tf := &ConfigTransformerOld{Module: mod} + tf := &ConfigTransformer{Module: mod} if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) } } { - transform := &MissingProviderTransformer{Providers: []string{"foo", "bar"}} + transform := &AttachResourceConfigTransformer{Module: mod} + if err := transform.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + + { + transform := &MissingProviderTransformer{Providers: []string{"aws", "foo", "bar"}} if err := transform.Transform(&g); err != nil { t.Fatalf("err: %s", err) } @@ -318,12 +352,19 @@ func TestPruneProviderTransformer(t *testing.T) { g := Graph{Path: RootModulePath} { - tf := &ConfigTransformerOld{Module: mod} + tf := &ConfigTransformer{Module: mod} if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) } } + { + transform := &AttachResourceConfigTransformer{Module: mod} + if err := transform.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + { transform := &MissingProviderTransformer{Providers: []string{"foo"}} if err := transform.Transform(&g); err != nil { @@ -359,71 +400,6 @@ func TestPruneProviderTransformer(t *testing.T) { } } -func TestDisableProviderTransformer(t *testing.T) { - mod := testModule(t, "transform-provider-disable") - - g := Graph{Path: RootModulePath} - transforms := []GraphTransformer{ - &ConfigTransformerOld{Module: mod}, - &MissingProviderTransformer{Providers: []string{"aws"}}, - &ProviderTransformer{}, - &DisableProviderTransformerOld{}, - &CloseProviderTransformer{}, - &PruneProviderTransformer{}, - } - - for _, tr := range transforms { - if err := tr.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformDisableProviderBasicStr) - if actual != expected { - t.Fatalf("expected:\n%s\n\ngot:\n%s\n", expected, actual) - } -} - -func TestDisableProviderTransformer_keep(t *testing.T) { - mod := testModule(t, "transform-provider-disable-keep") - - g := Graph{Path: RootModulePath} - transforms := []GraphTransformer{ - &ConfigTransformerOld{Module: mod}, - &MissingProviderTransformer{Providers: []string{"aws"}}, - &ProviderTransformer{}, - &DisableProviderTransformerOld{}, - &CloseProviderTransformer{}, - &PruneProviderTransformer{}, - } - - for _, tr := range transforms { - if err := tr.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformDisableProviderKeepStr) - if actual != expected { - t.Fatalf("expected:\n%s\n\ngot:\n%s\n", expected, actual) - } -} - -func TestGraphNodeProvider_impl(t *testing.T) { - var _ dag.Vertex = new(graphNodeProvider) - var _ dag.NamedVertex = new(graphNodeProvider) - var _ GraphNodeProvider = new(graphNodeProvider) -} - -func TestGraphNodeProvider_ProviderName(t *testing.T) { - n := &graphNodeProvider{ProviderNameValue: "foo"} - if v := n.ProviderName(); v != "foo" { - t.Fatalf("bad: %#v", v) - } -} - const testTransformProviderBasicStr = ` aws_instance.web provider.aws diff --git a/terraform/transform_provisioner.go b/terraform/transform_provisioner.go index 5bd3f65a17..f49d824107 100644 --- a/terraform/transform_provisioner.go +++ b/terraform/transform_provisioner.go @@ -107,18 +107,9 @@ func (t *MissingProvisionerTransformer) Transform(g *Graph) error { } // Build the vertex - var newV dag.Vertex = &graphNodeProvisioner{ProvisionerNameValue: p} - if len(path) > 0 { - // If we have a path, we do the flattening immediately. This - // is to support new-style graph nodes that are already - // flattened. - if fn, ok := newV.(GraphNodeFlattenable); ok { - var err error - newV, err = fn.Flatten(path) - if err != nil { - return err - } - } + var newV dag.Vertex = &NodeProvisioner{ + NameValue: p, + PathValue: path, } // Add the missing provisioner node to the graph @@ -178,7 +169,8 @@ func provisionerVertexMap(g *Graph) map[string]dag.Vertex { m := make(map[string]dag.Vertex) for _, v := range g.Vertices() { if pv, ok := v.(GraphNodeProvisioner); ok { - m[pv.ProvisionerName()] = v + key := provisionerMapKey(pv.ProvisionerName(), v) + m[key] = v } } @@ -212,50 +204,3 @@ func (n *graphNodeCloseProvisioner) EvalTree() EvalNode { func (n *graphNodeCloseProvisioner) CloseProvisionerName() string { return n.ProvisionerNameValue } - -type graphNodeProvisioner struct { - ProvisionerNameValue string -} - -func (n *graphNodeProvisioner) Name() string { - return fmt.Sprintf("provisioner.%s", n.ProvisionerNameValue) -} - -// GraphNodeEvalable impl. -func (n *graphNodeProvisioner) EvalTree() EvalNode { - return &EvalInitProvisioner{Name: n.ProvisionerNameValue} -} - -func (n *graphNodeProvisioner) ProvisionerName() string { - return n.ProvisionerNameValue -} - -// GraphNodeFlattenable impl. -func (n *graphNodeProvisioner) Flatten(p []string) (dag.Vertex, error) { - return &graphNodeProvisionerFlat{ - graphNodeProvisioner: n, - PathValue: p, - }, nil -} - -// Same as graphNodeMissingProvisioner, but for flattening -type graphNodeProvisionerFlat struct { - *graphNodeProvisioner - - PathValue []string -} - -func (n *graphNodeProvisionerFlat) Name() string { - return fmt.Sprintf( - "%s.%s", modulePrefixStr(n.PathValue), n.graphNodeProvisioner.Name()) -} - -func (n *graphNodeProvisionerFlat) Path() []string { - return n.PathValue -} - -func (n *graphNodeProvisionerFlat) ProvisionerName() string { - return fmt.Sprintf( - "%s.%s", modulePrefixStr(n.PathValue), - n.graphNodeProvisioner.ProvisionerName()) -} diff --git a/terraform/transform_provisioner_test.go b/terraform/transform_provisioner_test.go index 270910e78a..b3d7d4b660 100644 --- a/terraform/transform_provisioner_test.go +++ b/terraform/transform_provisioner_test.go @@ -12,12 +12,19 @@ func TestMissingProvisionerTransformer(t *testing.T) { g := Graph{Path: RootModulePath} { - tf := &ConfigTransformerOld{Module: mod} + tf := &ConfigTransformer{Module: mod} if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) } } + { + transform := &AttachResourceConfigTransformer{Module: mod} + if err := transform.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + { transform := &MissingProvisionerTransformer{Provisioners: []string{"shell"}} if err := transform.Transform(&g); err != nil { @@ -112,12 +119,19 @@ func TestCloseProvisionerTransformer(t *testing.T) { g := Graph{Path: RootModulePath} { - tf := &ConfigTransformerOld{Module: mod} + tf := &ConfigTransformer{Module: mod} if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) } } + { + transform := &AttachResourceConfigTransformer{Module: mod} + if err := transform.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + { transform := &MissingProvisionerTransformer{Provisioners: []string{"shell"}} if err := transform.Transform(&g); err != nil { @@ -145,18 +159,6 @@ func TestCloseProvisionerTransformer(t *testing.T) { t.Fatalf("bad:\n\n%s", actual) } } -func TestGraphNodeProvisioner_impl(t *testing.T) { - var _ dag.Vertex = new(graphNodeProvisioner) - var _ dag.NamedVertex = new(graphNodeProvisioner) - var _ GraphNodeProvisioner = new(graphNodeProvisioner) -} - -func TestGraphNodeProvisioner_ProvisionerName(t *testing.T) { - n := &graphNodeProvisioner{ProvisionerNameValue: "foo"} - if v := n.ProvisionerName(); v != "foo" { - t.Fatalf("bad: %#v", v) - } -} const testTransformMissingProvisionerBasicStr = ` aws_instance.web diff --git a/terraform/transform_proxy.go b/terraform/transform_proxy.go deleted file mode 100644 index db7b34ed8a..0000000000 --- a/terraform/transform_proxy.go +++ /dev/null @@ -1,62 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform/dag" -) - -// GraphNodeProxy must be implemented by nodes that are proxies. -// -// A node that is a proxy says that anything that depends on this -// node (the proxy), should also copy all the things that the proxy -// itself depends on. Example: -// -// A => proxy => C -// -// Should transform into (two edges): -// -// A => proxy => C -// A => C -// -// The purpose for this is because some transforms only look at direct -// edge connections and the proxy generally isn't meaningful in those -// situations, so we should complete all the edges. -type GraphNodeProxy interface { - Proxy() bool -} - -// ProxyTransformer is a transformer that goes through the graph, finds -// vertices that are marked as proxies, and connects through their -// dependents. See above for what a proxy is. -type ProxyTransformer struct{} - -func (t *ProxyTransformer) Transform(g *Graph) error { - for _, v := range g.Vertices() { - pn, ok := v.(GraphNodeProxy) - if !ok { - continue - } - - // If we don't want to be proxies, don't do it - if !pn.Proxy() { - continue - } - - // Connect all the things that depend on this to things that - // we depend on as the proxy. See docs for GraphNodeProxy for - // a visual explanation. - for _, s := range g.UpEdges(v).List() { - for _, t := range g.DownEdges(v).List() { - g.Connect(GraphProxyEdge{ - Edge: dag.BasicEdge(s, t), - }) - } - } - } - - return nil -} - -// GraphProxyEdge is the edge that is used for proxied edges. -type GraphProxyEdge struct { - dag.Edge -} diff --git a/terraform/transform_proxy_test.go b/terraform/transform_proxy_test.go deleted file mode 100644 index dc1b2cfbbf..0000000000 --- a/terraform/transform_proxy_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package terraform - -import ( - "strings" - "testing" - - "github.com/hashicorp/terraform/dag" -) - -func TestProxyTransformer(t *testing.T) { - var g Graph - proxy := &testNodeProxy{NameValue: "proxy"} - g.Add("A") - g.Add("C") - g.Add(proxy) - g.Connect(dag.BasicEdge("A", proxy)) - g.Connect(dag.BasicEdge(proxy, "C")) - - { - tf := &ProxyTransformer{} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testProxyTransformStr) - if actual != expected { - t.Fatalf("bad: %s", actual) - } -} - -type testNodeProxy struct { - NameValue string -} - -func (n *testNodeProxy) Name() string { - return n.NameValue -} - -func (n *testNodeProxy) Proxy() bool { - return true -} - -const testProxyTransformStr = ` -A - C - proxy -C -proxy - C -` diff --git a/terraform/transform_reference.go b/terraform/transform_reference.go index 7d8a5445db..c5452354d4 100644 --- a/terraform/transform_reference.go +++ b/terraform/transform_reference.go @@ -300,3 +300,22 @@ func ReferenceFromInterpolatedVar(v config.InterpolatedVariable) []string { return nil } } + +func modulePrefixStr(p []string) string { + parts := make([]string, 0, len(p)*2) + for _, p := range p[1:] { + parts = append(parts, "module", p) + } + + return strings.Join(parts, ".") +} + +func modulePrefixList(result []string, prefix string) []string { + if prefix != "" { + for i, v := range result { + result[i] = fmt.Sprintf("%s.%s", prefix, v) + } + } + + return result +} diff --git a/terraform/transform_resource.go b/terraform/transform_resource.go deleted file mode 100644 index 7430323775..0000000000 --- a/terraform/transform_resource.go +++ /dev/null @@ -1,967 +0,0 @@ -package terraform - -import ( - "fmt" - "strings" - - "github.com/hashicorp/terraform/config" - "github.com/hashicorp/terraform/dag" -) - -// ResourceCountTransformerOld is a GraphTransformer that expands the count -// out for a specific resource. -type ResourceCountTransformerOld struct { - Resource *config.Resource - Destroy bool - Targets []ResourceAddress -} - -func (t *ResourceCountTransformerOld) Transform(g *Graph) error { - // Expand the resource count - count, err := t.Resource.Count() - if err != nil { - return err - } - - // Don't allow the count to be negative - if count < 0 { - return fmt.Errorf("negative count: %d", count) - } - - // For each count, build and add the node - nodes := make([]dag.Vertex, 0, count) - for i := 0; i < count; i++ { - // Set the index. If our count is 1 we special case it so that - // we handle the "resource.0" and "resource" boundary properly. - index := i - if count == 1 { - index = -1 - } - - // Save the node for later so we can do connections. Make the - // proper node depending on if we're just a destroy node or if - // were a regular node. - var node dag.Vertex = &graphNodeExpandedResource{ - Index: index, - Resource: t.Resource, - Path: g.Path, - } - if t.Destroy { - node = &graphNodeExpandedResourceDestroy{ - graphNodeExpandedResource: node.(*graphNodeExpandedResource), - } - } - - // Skip nodes if targeting excludes them - if !t.nodeIsTargeted(node) { - continue - } - - // Add the node now - nodes = append(nodes, node) - g.Add(node) - } - - // Make the dependency connections - for _, n := range nodes { - // Connect the dependents. We ignore the return value for missing - // dependents since that should've been caught at a higher level. - g.ConnectDependent(n) - } - - return nil -} - -func (t *ResourceCountTransformerOld) nodeIsTargeted(node dag.Vertex) bool { - // no targets specified, everything stays in the graph - if len(t.Targets) == 0 { - return true - } - addressable, ok := node.(GraphNodeAddressable) - if !ok { - return false - } - - addr := addressable.ResourceAddress() - for _, targetAddr := range t.Targets { - if targetAddr.Equals(addr) { - return true - } - } - return false -} - -type graphNodeExpandedResource struct { - Index int - Resource *config.Resource - Path []string -} - -func (n *graphNodeExpandedResource) Name() string { - if n.Index == -1 { - return n.Resource.Id() - } - - return fmt.Sprintf("%s #%d", n.Resource.Id(), n.Index) -} - -// GraphNodeAddressable impl. -func (n *graphNodeExpandedResource) ResourceAddress() *ResourceAddress { - // We want this to report the logical index properly, so we must undo the - // special case from the expand - index := n.Index - if index == -1 { - index = 0 - } - return &ResourceAddress{ - Path: n.Path[1:], - Index: index, - InstanceType: TypePrimary, - Name: n.Resource.Name, - Type: n.Resource.Type, - Mode: n.Resource.Mode, - } -} - -// graphNodeConfig impl. -func (n *graphNodeExpandedResource) ConfigType() GraphNodeConfigType { - return GraphNodeConfigTypeResource -} - -// GraphNodeDependable impl. -func (n *graphNodeExpandedResource) DependableName() []string { - return []string{ - n.Resource.Id(), - n.stateId(), - } -} - -// GraphNodeDependent impl. -func (n *graphNodeExpandedResource) DependentOn() []string { - configNode := &GraphNodeConfigResource{Resource: n.Resource} - result := configNode.DependentOn() - - // Walk the variables to find any count-specific variables we depend on. - configNode.VarWalk(func(v config.InterpolatedVariable) { - rv, ok := v.(*config.ResourceVariable) - if !ok { - return - } - - // We only want ourselves - if rv.ResourceId() != n.Resource.Id() { - return - } - - // If this isn't a multi-access (which shouldn't be allowed but - // is verified elsewhere), then we depend on the specific count - // of this resource, ignoring ourself (which again should be - // validated elsewhere). - if rv.Index > -1 { - id := fmt.Sprintf("%s.%d", rv.ResourceId(), rv.Index) - if id != n.stateId() && id != n.stateId()+".0" { - result = append(result, id) - } - } - }) - - return result -} - -// GraphNodeProviderConsumer -func (n *graphNodeExpandedResource) ProvidedBy() []string { - return []string{resourceProvider(n.Resource.Type, n.Resource.Provider)} -} - -func (n *graphNodeExpandedResource) StateDependencies() []string { - depsRaw := n.DependentOn() - deps := make([]string, 0, len(depsRaw)) - for _, d := range depsRaw { - // Ignore any variable dependencies - if strings.HasPrefix(d, "var.") { - continue - } - - // This is sad. The dependencies are currently in the format of - // "module.foo.bar" (the full field). This strips the field off. - if strings.HasPrefix(d, "module.") { - parts := strings.SplitN(d, ".", 3) - d = strings.Join(parts[0:2], ".") - } - deps = append(deps, d) - } - - return deps -} - -// GraphNodeEvalable impl. -func (n *graphNodeExpandedResource) EvalTree() EvalNode { - var provider ResourceProvider - var resourceConfig *ResourceConfig - - // Build the resource. If we aren't part of a multi-resource, then - // we still consider ourselves as count index zero. - index := n.Index - if index < 0 { - index = 0 - } - resource := &Resource{ - Name: n.Resource.Name, - Type: n.Resource.Type, - CountIndex: index, - } - - seq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)} - - // Validate the resource - vseq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)} - vseq.Nodes = append(vseq.Nodes, &EvalGetProvider{ - Name: n.ProvidedBy()[0], - Output: &provider, - }) - vseq.Nodes = append(vseq.Nodes, &EvalInterpolate{ - Config: n.Resource.RawConfig.Copy(), - Resource: resource, - Output: &resourceConfig, - }) - vseq.Nodes = append(vseq.Nodes, &EvalValidateResource{ - Provider: &provider, - Config: &resourceConfig, - ResourceName: n.Resource.Name, - ResourceType: n.Resource.Type, - ResourceMode: n.Resource.Mode, - }) - - // Validate all the provisioners - for _, p := range n.Resource.Provisioners { - var provisioner ResourceProvisioner - vseq.Nodes = append(vseq.Nodes, &EvalGetProvisioner{ - Name: p.Type, - Output: &provisioner, - }, &EvalInterpolate{ - Config: p.RawConfig.Copy(), - Resource: resource, - Output: &resourceConfig, - }, &EvalValidateProvisioner{ - Provisioner: &provisioner, - Config: &resourceConfig, - }) - } - - // Add the validation operations - seq.Nodes = append(seq.Nodes, &EvalOpFilter{ - Ops: []walkOperation{walkValidate}, - Node: vseq, - }) - - // Build instance info - info := n.instanceInfo() - seq.Nodes = append(seq.Nodes, &EvalInstanceInfo{Info: info}) - - // Each resource mode has its own lifecycle - switch n.Resource.Mode { - case config.ManagedResourceMode: - seq.Nodes = append( - seq.Nodes, - n.managedResourceEvalNodes(resource, info, resourceConfig)..., - ) - case config.DataResourceMode: - seq.Nodes = append( - seq.Nodes, - n.dataResourceEvalNodes(resource, info, resourceConfig)..., - ) - default: - panic(fmt.Errorf("unsupported resource mode %s", n.Resource.Mode)) - } - - return seq -} - -func (n *graphNodeExpandedResource) managedResourceEvalNodes(resource *Resource, info *InstanceInfo, resourceConfig *ResourceConfig) []EvalNode { - var diff *InstanceDiff - var provider ResourceProvider - var state *InstanceState - - nodes := make([]EvalNode, 0, 5) - - // Refresh the resource - nodes = append(nodes, &EvalOpFilter{ - Ops: []walkOperation{walkRefresh}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Name: n.ProvidedBy()[0], - Output: &provider, - }, - &EvalReadState{ - Name: n.stateId(), - Output: &state, - }, - &EvalRefresh{ - Info: info, - Provider: &provider, - State: &state, - Output: &state, - }, - &EvalWriteState{ - Name: n.stateId(), - ResourceType: n.Resource.Type, - Provider: n.Resource.Provider, - Dependencies: n.StateDependencies(), - State: &state, - }, - }, - }, - }) - - // Diff the resource - nodes = append(nodes, &EvalOpFilter{ - Ops: []walkOperation{walkPlan}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - &EvalInterpolate{ - Config: n.Resource.RawConfig.Copy(), - Resource: resource, - Output: &resourceConfig, - }, - &EvalGetProvider{ - Name: n.ProvidedBy()[0], - Output: &provider, - }, - // Re-run validation to catch any errors we missed, e.g. type - // mismatches on computed values. - &EvalValidateResource{ - Provider: &provider, - Config: &resourceConfig, - ResourceName: n.Resource.Name, - ResourceType: n.Resource.Type, - ResourceMode: n.Resource.Mode, - IgnoreWarnings: true, - }, - &EvalReadState{ - Name: n.stateId(), - Output: &state, - }, - &EvalDiff{ - Info: info, - Config: &resourceConfig, - Resource: n.Resource, - Provider: &provider, - State: &state, - OutputDiff: &diff, - OutputState: &state, - }, - &EvalCheckPreventDestroy{ - Resource: n.Resource, - Diff: &diff, - }, - &EvalWriteState{ - Name: n.stateId(), - ResourceType: n.Resource.Type, - Provider: n.Resource.Provider, - Dependencies: n.StateDependencies(), - State: &state, - }, - &EvalWriteDiff{ - Name: n.stateId(), - Diff: &diff, - }, - }, - }, - }) - - // Diff the resource for destruction - nodes = append(nodes, &EvalOpFilter{ - Ops: []walkOperation{walkPlanDestroy}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - &EvalReadState{ - Name: n.stateId(), - Output: &state, - }, - &EvalDiffDestroy{ - Info: info, - State: &state, - Output: &diff, - }, - &EvalCheckPreventDestroy{ - Resource: n.Resource, - Diff: &diff, - }, - &EvalWriteDiff{ - Name: n.stateId(), - Diff: &diff, - }, - }, - }, - }) - - // Apply - var diffApply *InstanceDiff - var err error - var createNew bool - var createBeforeDestroyEnabled bool - nodes = append(nodes, &EvalOpFilter{ - Ops: []walkOperation{walkApply, walkDestroy}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - // Get the saved diff for apply - &EvalReadDiff{ - Name: n.stateId(), - Diff: &diffApply, - }, - - // We don't want to do any destroys - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - if diffApply == nil { - return true, EvalEarlyExitError{} - } - - if diffApply.GetDestroy() && diffApply.GetAttributesLen() == 0 { - return true, EvalEarlyExitError{} - } - - diffApply.SetDestroy(false) - return true, nil - }, - Then: EvalNoop{}, - }, - - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - destroy := false - if diffApply != nil { - destroy = diffApply.GetDestroy() || diffApply.RequiresNew() - } - - createBeforeDestroyEnabled = - n.Resource.Lifecycle.CreateBeforeDestroy && - destroy - - return createBeforeDestroyEnabled, nil - }, - Then: &EvalDeposeState{ - Name: n.stateId(), - }, - }, - - &EvalInterpolate{ - Config: n.Resource.RawConfig.Copy(), - Resource: resource, - Output: &resourceConfig, - }, - &EvalGetProvider{ - Name: n.ProvidedBy()[0], - Output: &provider, - }, - &EvalReadState{ - Name: n.stateId(), - Output: &state, - }, - // Re-run validation to catch any errors we missed, e.g. type - // mismatches on computed values. - &EvalValidateResource{ - Provider: &provider, - Config: &resourceConfig, - ResourceName: n.Resource.Name, - ResourceType: n.Resource.Type, - ResourceMode: n.Resource.Mode, - IgnoreWarnings: true, - }, - &EvalDiff{ - Info: info, - Config: &resourceConfig, - Resource: n.Resource, - Provider: &provider, - Diff: &diffApply, - State: &state, - OutputDiff: &diffApply, - }, - - // Get the saved diff - &EvalReadDiff{ - Name: n.stateId(), - Diff: &diff, - }, - - // Compare the diffs - &EvalCompareDiff{ - Info: info, - One: &diff, - Two: &diffApply, - }, - - &EvalGetProvider{ - Name: n.ProvidedBy()[0], - Output: &provider, - }, - &EvalReadState{ - Name: n.stateId(), - Output: &state, - }, - &EvalApplyPre{ - Info: info, - State: &state, - Diff: &diffApply, - }, - &EvalApply{ - Info: info, - State: &state, - Diff: &diffApply, - Provider: &provider, - Output: &state, - Error: &err, - CreateNew: &createNew, - }, - &EvalWriteState{ - Name: n.stateId(), - ResourceType: n.Resource.Type, - Provider: n.Resource.Provider, - Dependencies: n.StateDependencies(), - State: &state, - }, - &EvalApplyProvisioners{ - Info: info, - State: &state, - Resource: n.Resource, - InterpResource: resource, - CreateNew: &createNew, - Error: &err, - }, - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - return createBeforeDestroyEnabled && err != nil, nil - }, - Then: &EvalUndeposeState{ - Name: n.stateId(), - State: &state, - }, - Else: &EvalWriteState{ - Name: n.stateId(), - ResourceType: n.Resource.Type, - Provider: n.Resource.Provider, - Dependencies: n.StateDependencies(), - State: &state, - }, - }, - - // We clear the diff out here so that future nodes - // don't see a diff that is already complete. There - // is no longer a diff! - &EvalWriteDiff{ - Name: n.stateId(), - Diff: nil, - }, - - &EvalApplyPost{ - Info: info, - State: &state, - Error: &err, - }, - &EvalUpdateStateHook{}, - }, - }, - }) - - return nodes -} - -func (n *graphNodeExpandedResource) dataResourceEvalNodes(resource *Resource, info *InstanceInfo, resourceConfig *ResourceConfig) []EvalNode { - //var diff *InstanceDiff - var provider ResourceProvider - var config *ResourceConfig - var diff *InstanceDiff - var state *InstanceState - - nodes := make([]EvalNode, 0, 5) - - // Refresh the resource - nodes = append(nodes, &EvalOpFilter{ - Ops: []walkOperation{walkRefresh}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - - // Always destroy the existing state first, since we must - // make sure that values from a previous read will not - // get interpolated if we end up needing to defer our - // loading until apply time. - &EvalWriteState{ - Name: n.stateId(), - ResourceType: n.Resource.Type, - Provider: n.Resource.Provider, - Dependencies: n.StateDependencies(), - State: &state, // state is nil here - }, - - &EvalInterpolate{ - Config: n.Resource.RawConfig.Copy(), - Resource: resource, - Output: &config, - }, - - // The rest of this pass can proceed only if there are no - // computed values in our config. - // (If there are, we'll deal with this during the plan and - // apply phases.) - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - - if config.ComputedKeys != nil && len(config.ComputedKeys) > 0 { - return true, EvalEarlyExitError{} - } - - // If the config explicitly has a depends_on for this - // data source, assume the intention is to prevent - // refreshing ahead of that dependency. - if len(n.Resource.DependsOn) > 0 { - return true, EvalEarlyExitError{} - } - - return true, nil - }, - Then: EvalNoop{}, - }, - - // The remainder of this pass is the same as running - // a "plan" pass immediately followed by an "apply" pass, - // populating the state early so it'll be available to - // provider configurations that need this data during - // refresh/plan. - - &EvalGetProvider{ - Name: n.ProvidedBy()[0], - Output: &provider, - }, - - &EvalReadDataDiff{ - Info: info, - Config: &config, - Provider: &provider, - Output: &diff, - OutputState: &state, - }, - - &EvalReadDataApply{ - Info: info, - Diff: &diff, - Provider: &provider, - Output: &state, - }, - - &EvalWriteState{ - Name: n.stateId(), - ResourceType: n.Resource.Type, - Provider: n.Resource.Provider, - Dependencies: n.StateDependencies(), - State: &state, - }, - - &EvalUpdateStateHook{}, - }, - }, - }) - - // Diff the resource - nodes = append(nodes, &EvalOpFilter{ - Ops: []walkOperation{walkPlan}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - - &EvalReadState{ - Name: n.stateId(), - Output: &state, - }, - - // We need to re-interpolate the config here because some - // of the attributes may have become computed during - // earlier planning, due to other resources having - // "requires new resource" diffs. - &EvalInterpolate{ - Config: n.Resource.RawConfig.Copy(), - Resource: resource, - Output: &config, - }, - - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - computed := config.ComputedKeys != nil && len(config.ComputedKeys) > 0 - - // If the configuration is complete and we - // already have a state then we don't need to - // do any further work during apply, because we - // already populated the state during refresh. - if !computed && state != nil { - return true, EvalEarlyExitError{} - } - - return true, nil - }, - Then: EvalNoop{}, - }, - - &EvalGetProvider{ - Name: n.ProvidedBy()[0], - Output: &provider, - }, - - &EvalReadDataDiff{ - Info: info, - Config: &config, - Provider: &provider, - Output: &diff, - OutputState: &state, - }, - - &EvalWriteState{ - Name: n.stateId(), - ResourceType: n.Resource.Type, - Provider: n.Resource.Provider, - Dependencies: n.StateDependencies(), - State: &state, - }, - - &EvalWriteDiff{ - Name: n.stateId(), - Diff: &diff, - }, - }, - }, - }) - - // Diff the resource for destruction - nodes = append(nodes, &EvalOpFilter{ - Ops: []walkOperation{walkPlanDestroy}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - - &EvalReadState{ - Name: n.stateId(), - Output: &state, - }, - - // Since EvalDiffDestroy doesn't interact with the - // provider at all, we can safely share the same - // implementation for data vs. managed resources. - &EvalDiffDestroy{ - Info: info, - State: &state, - Output: &diff, - }, - - &EvalWriteDiff{ - Name: n.stateId(), - Diff: &diff, - }, - }, - }, - }) - - // Apply - nodes = append(nodes, &EvalOpFilter{ - Ops: []walkOperation{walkApply, walkDestroy}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - // Get the saved diff for apply - &EvalReadDiff{ - Name: n.stateId(), - Diff: &diff, - }, - - // Stop here if we don't actually have a diff - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - if diff == nil { - return true, EvalEarlyExitError{} - } - - if diff.GetAttributesLen() == 0 { - return true, EvalEarlyExitError{} - } - - return true, nil - }, - Then: EvalNoop{}, - }, - - // We need to re-interpolate the config here, rather than - // just using the diff's values directly, because we've - // potentially learned more variable values during the - // apply pass that weren't known when the diff was produced. - &EvalInterpolate{ - Config: n.Resource.RawConfig.Copy(), - Resource: resource, - Output: &config, - }, - - &EvalGetProvider{ - Name: n.ProvidedBy()[0], - Output: &provider, - }, - - // Make a new diff with our newly-interpolated config. - &EvalReadDataDiff{ - Info: info, - Config: &config, - Previous: &diff, - Provider: &provider, - Output: &diff, - }, - - &EvalReadDataApply{ - Info: info, - Diff: &diff, - Provider: &provider, - Output: &state, - }, - - &EvalWriteState{ - Name: n.stateId(), - ResourceType: n.Resource.Type, - Provider: n.Resource.Provider, - Dependencies: n.StateDependencies(), - State: &state, - }, - - // Clear the diff now that we've applied it, so - // later nodes won't see a diff that's now a no-op. - &EvalWriteDiff{ - Name: n.stateId(), - Diff: nil, - }, - - &EvalUpdateStateHook{}, - }, - }, - }) - - return nodes -} - -// instanceInfo is used for EvalTree. -func (n *graphNodeExpandedResource) instanceInfo() *InstanceInfo { - return &InstanceInfo{Id: n.stateId(), Type: n.Resource.Type} -} - -// stateId is the name used for the state key -func (n *graphNodeExpandedResource) stateId() string { - if n.Index == -1 { - return n.Resource.Id() - } - - return fmt.Sprintf("%s.%d", n.Resource.Id(), n.Index) -} - -// GraphNodeStateRepresentative impl. -func (n *graphNodeExpandedResource) StateId() []string { - return []string{n.stateId()} -} - -// graphNodeExpandedResourceDestroy represents an expanded resource that -// is to be destroyed. -type graphNodeExpandedResourceDestroy struct { - *graphNodeExpandedResource -} - -func (n *graphNodeExpandedResourceDestroy) Name() string { - return fmt.Sprintf("%s (destroy)", n.graphNodeExpandedResource.Name()) -} - -// graphNodeConfig impl. -func (n *graphNodeExpandedResourceDestroy) ConfigType() GraphNodeConfigType { - return GraphNodeConfigTypeResource -} - -// GraphNodeEvalable impl. -func (n *graphNodeExpandedResourceDestroy) EvalTree() EvalNode { - info := n.instanceInfo() - info.uniqueExtra = "destroy" - - var diffApply *InstanceDiff - var provider ResourceProvider - var state *InstanceState - var err error - return &EvalOpFilter{ - Ops: []walkOperation{walkApply, walkDestroy}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - // Get the saved diff for apply - &EvalReadDiff{ - Name: n.stateId(), - Diff: &diffApply, - }, - - // Filter the diff so we only get the destroy - &EvalFilterDiff{ - Diff: &diffApply, - Output: &diffApply, - Destroy: true, - }, - - // If we're not destroying, then compare diffs - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - if diffApply != nil && diffApply.GetDestroy() { - return true, nil - } - - return true, EvalEarlyExitError{} - }, - Then: EvalNoop{}, - }, - - // Load the instance info so we have the module path set - &EvalInstanceInfo{Info: info}, - - &EvalGetProvider{ - Name: n.ProvidedBy()[0], - Output: &provider, - }, - &EvalReadState{ - Name: n.stateId(), - Output: &state, - }, - &EvalRequireState{ - State: &state, - }, - // Make sure we handle data sources properly. - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - if n.Resource.Mode == config.DataResourceMode { - return true, nil - } - - return false, nil - }, - - Then: &EvalReadDataApply{ - Info: info, - Diff: &diffApply, - Provider: &provider, - Output: &state, - }, - Else: &EvalApply{ - Info: info, - State: &state, - Diff: &diffApply, - Provider: &provider, - Output: &state, - Error: &err, - }, - }, - &EvalWriteState{ - Name: n.stateId(), - ResourceType: n.Resource.Type, - Provider: n.Resource.Provider, - Dependencies: n.StateDependencies(), - State: &state, - }, - &EvalApplyPost{ - Info: info, - State: &state, - Error: &err, - }, - }, - }, - } -} diff --git a/terraform/transform_resource_test.go b/terraform/transform_resource_test.go deleted file mode 100644 index 017e7f1c24..0000000000 --- a/terraform/transform_resource_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package terraform - -import ( - "strings" - "testing" -) - -func TestResourceCountTransformerOld(t *testing.T) { - cfg := testModule(t, "transform-resource-count-basic").Config() - resource := cfg.Resources[0] - - g := Graph{Path: RootModulePath} - { - tf := &ResourceCountTransformerOld{Resource: resource} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testResourceCountTransformOldStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestResourceCountTransformerOld_countNegative(t *testing.T) { - cfg := testModule(t, "transform-resource-count-negative").Config() - resource := cfg.Resources[0] - - g := Graph{Path: RootModulePath} - { - tf := &ResourceCountTransformerOld{Resource: resource} - if err := tf.Transform(&g); err == nil { - t.Fatal("should error") - } - } -} - -func TestResourceCountTransformerOld_deps(t *testing.T) { - cfg := testModule(t, "transform-resource-count-deps").Config() - resource := cfg.Resources[0] - - g := Graph{Path: RootModulePath} - { - tf := &ResourceCountTransformerOld{Resource: resource} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testResourceCountTransformOldDepsStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -const testResourceCountTransformOldStr = ` -aws_instance.foo #0 -aws_instance.foo #1 -aws_instance.foo #2 -` - -const testResourceCountTransformOldDepsStr = ` -aws_instance.foo #0 -aws_instance.foo #1 - aws_instance.foo #0 -` diff --git a/terraform/transform_root.go b/terraform/transform_root.go index 7a422b8264..aee053d175 100644 --- a/terraform/transform_root.go +++ b/terraform/transform_root.go @@ -36,7 +36,3 @@ type graphNodeRoot struct{} func (n graphNodeRoot) Name() string { return rootNodeName } - -func (n graphNodeRoot) Flatten(p []string) (dag.Vertex, error) { - return n, nil -} diff --git a/terraform/transform_root_test.go b/terraform/transform_root_test.go index 1cfec3995f..9f231906ba 100644 --- a/terraform/transform_root_test.go +++ b/terraform/transform_root_test.go @@ -10,12 +10,21 @@ func TestRootTransformer(t *testing.T) { g := Graph{Path: RootModulePath} { - tf := &ConfigTransformerOld{Module: mod} + tf := &ConfigTransformer{Module: mod} if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) } } + { + transform := &MissingProviderTransformer{ + Providers: []string{"aws", "do"}, + } + if err := transform.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + { transform := &ProviderTransformer{} if err := transform.Transform(&g); err != nil { diff --git a/terraform/transform_targets.go b/terraform/transform_targets.go index 0ba98ee153..225ac4b4ae 100644 --- a/terraform/transform_targets.go +++ b/terraform/transform_targets.go @@ -6,6 +6,15 @@ import ( "github.com/hashicorp/terraform/dag" ) +// GraphNodeTargetable is an interface for graph nodes to implement when they +// need to be told about incoming targets. This is useful for nodes that need +// to respect targets as they dynamically expand. Note that the list of targets +// provided will contain every target provided, and each implementing graph +// node must filter this list to targets considered relevant. +type GraphNodeTargetable interface { + SetTargets([]ResourceAddress) +} + // TargetsTransformer is a GraphTransformer that, when the user specifies a // list of resources to target, limits the graph to only those resources and // their dependencies. @@ -40,7 +49,7 @@ func (t *TargetsTransformer) Transform(g *Graph) error { for _, v := range g.Vertices() { removable := false - if _, ok := v.(GraphNodeAddressable); ok { + if _, ok := v.(GraphNodeResource); ok { removable = true } if vr, ok := v.(RemovableIfNotTargeted); ok { @@ -90,15 +99,6 @@ func (t *TargetsTransformer) selectTargetedNodes( var err error if t.Destroy { deps, err = g.Descendents(v) - - // Select any variables that we depend on in case we need them later for - // interpolating in the count - ancestors, _ := g.Ancestors(v) - for _, a := range ancestors.List() { - if _, ok := a.(*GraphNodeConfigVariableFlat); ok { - deps.Add(a) - } - } } else { deps, err = g.Ancestors(v) } @@ -117,12 +117,12 @@ func (t *TargetsTransformer) selectTargetedNodes( func (t *TargetsTransformer) nodeIsTarget( v dag.Vertex, addrs []ResourceAddress) bool { - r, ok := v.(GraphNodeAddressable) + r, ok := v.(GraphNodeResource) if !ok { return false } - addr := r.ResourceAddress() + addr := r.ResourceAddr() for _, targetAddr := range addrs { if targetAddr.Equals(addr) { return true diff --git a/terraform/transform_targets_test.go b/terraform/transform_targets_test.go index 142fbb52f4..9418860e2a 100644 --- a/terraform/transform_targets_test.go +++ b/terraform/transform_targets_test.go @@ -10,12 +10,26 @@ func TestTargetsTransformer(t *testing.T) { g := Graph{Path: RootModulePath} { - tf := &ConfigTransformerOld{Module: mod} + tf := &ConfigTransformer{Module: mod} if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) } } + { + transform := &AttachResourceConfigTransformer{Module: mod} + if err := transform.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + + { + transform := &ReferenceTransformer{} + if err := transform.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + { transform := &TargetsTransformer{Targets: []string{"aws_instance.me"}} if err := transform.Transform(&g); err != nil { @@ -41,12 +55,26 @@ func TestTargetsTransformer_destroy(t *testing.T) { g := Graph{Path: RootModulePath} { - tf := &ConfigTransformerOld{Module: mod} + tf := &ConfigTransformer{Module: mod} if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) } } + { + transform := &AttachResourceConfigTransformer{Module: mod} + if err := transform.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + + { + transform := &ReferenceTransformer{} + if err := transform.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + { transform := &TargetsTransformer{ Targets: []string{"aws_instance.me"}, diff --git a/terraform/transform_transitive_reduction_test.go b/terraform/transform_transitive_reduction_test.go index 4c864e236f..63a89b11a9 100644 --- a/terraform/transform_transitive_reduction_test.go +++ b/terraform/transform_transitive_reduction_test.go @@ -10,12 +10,26 @@ func TestTransitiveReductionTransformer(t *testing.T) { g := Graph{Path: RootModulePath} { - tf := &ConfigTransformerOld{Module: mod} + tf := &ConfigTransformer{Module: mod} if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) } } + { + transform := &AttachResourceConfigTransformer{Module: mod} + if err := transform.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + + { + transform := &ReferenceTransformer{} + if err := transform.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + { transform := &TransitiveReductionTransformer{} if err := transform.Transform(&g); err != nil {