From af1778cd5e8f26392cd61d15cc245c2ad360058d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 13 Feb 2015 18:22:45 -0800 Subject: [PATCH] terraform: goodbye graph.go --- terraform/graph_dot.go | 376 ------- terraform/graph_old.go | 2038 ----------------------------------- terraform/graph_old_test.go | 1646 ---------------------------- 3 files changed, 4060 deletions(-) delete mode 100644 terraform/graph_dot.go delete mode 100644 terraform/graph_old.go delete mode 100644 terraform/graph_old_test.go diff --git a/terraform/graph_dot.go b/terraform/graph_dot.go deleted file mode 100644 index 5b66d2753..000000000 --- a/terraform/graph_dot.go +++ /dev/null @@ -1,376 +0,0 @@ -package terraform - -import ( - "bufio" - "bytes" - "fmt" - "strings" - - "github.com/hashicorp/terraform/depgraph" -) - -// GraphDotOpts are options for turning a graph into dot format. -type GraphDotOpts struct { - // ModuleDepth is the depth of modules to expand. Zero is no expansion, - // one expands the first set of modules, etc. If this is set to -1, then - // all modules are expanded. - ModuleDepth int - - // Depth is an internal track of what depth we're at within - // the graph, used to control indentation and other such things. - depth int -} - -// GraphDot returns the dot formatting of a visual representation of -// the given Terraform graph. -func GraphDot(g *depgraph.Graph, opts *GraphDotOpts) string { - buf := new(bytes.Buffer) - - if opts.depth == 0 { - buf.WriteString("digraph {\n") - buf.WriteString("\tcompound = true;\n") - } - - // Determine and add the title - // graphDotTitle(buf, g) - - // Add all the resource. - graphDotAddResources(buf, g, opts) - - // Add all the resource providers - graphDotAddResourceProviders(buf, g, opts) - - // Add all the modules - graphDotAddModules(buf, g, opts) - - if opts.depth == 0 { - buf.WriteString("}\n") - } - - return buf.String() -} - -func graphDotAddRoot(buf *bytes.Buffer, n *depgraph.Noun) { - buf.WriteString(fmt.Sprintf("\t\"%s\" [shape=circle];\n", "root")) - - for _, e := range n.Edges() { - target := e.Tail() - buf.WriteString(fmt.Sprintf( - "\t\"%s\" -> \"%s\";\n", - "root", - target)) - } -} - -func graphDotAddModules(buf *bytes.Buffer, g *depgraph.Graph, opts *GraphDotOpts) { - for _, n := range g.Nouns { - _, ok := n.Meta.(*GraphNodeModule) - if !ok { - continue - } - - if graphExpand(opts) { - // We're expanding - graphDotAddModuleExpand(buf, n, opts) - } else { - // We're not expanding, so just add the module on its own - graphDotAddModuleSingle(buf, n, opts) - } - - graphWriteEdges(buf, n, opts) - } -} - -func graphDotAddModuleExpand( - buf *bytes.Buffer, n *depgraph.Noun, opts *GraphDotOpts) { - m := n.Meta.(*GraphNodeModule) - tab := strings.Repeat("\t", opts.depth+1) - uniqueName := graphUniqueName(n, opts) - - // Wrap ourselves in a subgraph - buf.WriteString(fmt.Sprintf("%ssubgraph \"cluster_%s\" {\n", tab, uniqueName)) - defer buf.WriteString(fmt.Sprintf("%s}\n", tab)) - - // Add our label so that we have the proper name. - buf.WriteString(fmt.Sprintf("%s\tlabel = \"%s\";\n", tab, n)) - - // Add a hidden name for edges to point from/to - buf.WriteString(fmt.Sprintf("%s\t\"%s_hidden\" [fixedsize=true,width=0,height=0,label=\"\",style=invisible];\n", tab, uniqueName)) - - // Graph the subgraph just as we would any other graph - subOpts := *opts - subOpts.depth++ - subStr := GraphDot(m.Graph, &subOpts) - - // Tab all the lines of the subgraph - s := bufio.NewScanner(strings.NewReader(subStr)) - for s.Scan() { - buf.WriteString(fmt.Sprintf("%s%s\n", tab, s.Text())) - } -} - -func graphDotAddModuleSingle( - buf *bytes.Buffer, n *depgraph.Noun, opts *GraphDotOpts) { - tab := strings.Repeat("\t", opts.depth+1) - uniqueName := graphUniqueName(n, opts) - - // Create this node. - buf.WriteString(fmt.Sprintf("%s\"%s\" [\n", tab, uniqueName)) - buf.WriteString(fmt.Sprintf("%s\tlabel=\"%s\"\n", tab, n)) - buf.WriteString(fmt.Sprintf("%s\tshape=component\n", tab)) - buf.WriteString(fmt.Sprintf("%s];\n", tab)) -} - -func graphDotAddResources( - buf *bytes.Buffer, g *depgraph.Graph, opts *GraphDotOpts) { - // Determine if we have diffs. If we do, then we're graphing a - // plan, which alters our graph a bit. - hasDiff := false - for _, n := range g.Nouns { - rn, ok := n.Meta.(*GraphNodeResource) - if !ok { - continue - } - if rn.Resource.Diff != nil && !rn.Resource.Diff.Empty() { - hasDiff = true - break - } - } - - var edgeBuf bytes.Buffer - // Do all the non-destroy resources - buf.WriteString("\tsubgraph {\n") - for _, n := range g.Nouns { - rn, ok := n.Meta.(*GraphNodeResource) - if !ok { - continue - } - if rn.Resource.Diff != nil && rn.Resource.Diff.Destroy { - continue - } - - // If we have diffs then we're graphing a plan. If we don't have - // have a diff on this resource, don't graph anything, since the - // plan wouldn't do anything to this resource. - if hasDiff { - if rn.Resource.Diff == nil || rn.Resource.Diff.Empty() { - continue - } - } - - // Determine the colors. White = no change, yellow = change, - // green = create. Destroy is in the next section. - var color, fillColor string - if rn.Resource.Diff != nil && !rn.Resource.Diff.Empty() { - if rn.Resource.State != nil && rn.Resource.State.ID != "" { - color = "#FFFF00" - fillColor = "#FFFF94" - } else { - color = "#00FF00" - fillColor = "#9EFF9E" - } - } - - uniqueName := fmt.Sprintf("%d_%s", opts.depth, n) - - // Create this node. - buf.WriteString(fmt.Sprintf("\t\t\"%s\" [\n", uniqueName)) - buf.WriteString(fmt.Sprintf("\t\t\tlabel=\"%s\"\n", n)) - buf.WriteString("\t\t\tshape=box\n") - if color != "" { - buf.WriteString("\t\t\tstyle=filled\n") - buf.WriteString(fmt.Sprintf("\t\t\tcolor=\"%s\"\n", color)) - buf.WriteString(fmt.Sprintf("\t\t\tfillcolor=\"%s\"\n", fillColor)) - } - buf.WriteString("\t\t];\n") - - // Build up all the edges in a separate buffer so they're not in the - // subgraph. - graphWriteEdges(&edgeBuf, n, opts) - } - buf.WriteString("\t}\n\n") - if edgeBuf.Len() > 0 { - buf.WriteString(edgeBuf.String()) - buf.WriteString("\n") - } - - // Do all the destroy resources - edgeBuf.Reset() - buf.WriteString("\tsubgraph {\n") - for _, n := range g.Nouns { - rn, ok := n.Meta.(*GraphNodeResource) - if !ok { - continue - } - if rn.Resource.Diff == nil || !rn.Resource.Diff.Destroy { - continue - } - - uniqueName := fmt.Sprintf("%d_%s", opts.depth, n) - - buf.WriteString(fmt.Sprintf( - "\t\t\"%s\" [label=\"%s\",shape=box,style=filled,color=\"#FF0000\",fillcolor=\"#FF9494\"];\n", uniqueName, n)) - - graphWriteEdges(&edgeBuf, n, opts) - } - buf.WriteString("\t}\n\n") - if edgeBuf.Len() > 0 { - buf.WriteString(edgeBuf.String()) - buf.WriteString("\n") - } - - // Handle the meta resources - /* - edgeBuf.Reset() - for _, n := range g.Nouns { - _, ok := n.Meta.(*GraphNodeResourceMeta) - if !ok { - continue - } - - // Determine which edges to add - var edges []digraph.Edge - if hasDiff { - for _, e := range n.Edges() { - rn, ok := e.Tail().(*depgraph.Noun).Meta.(*GraphNodeResource) - if !ok { - continue - } - if rn.Resource.Diff == nil || rn.Resource.Diff.Empty() { - continue - } - edges = append(edges, e) - } - } else { - edges = n.Edges() - } - - // Do not draw if we have no edges - if len(edges) == 0 { - continue - } - - uniqueName := fmt.Sprintf("%d_%s", opts.depth, n) - for _, e := range edges { - target := e.Tail() - uniqueTarget := fmt.Sprintf("%d_%s", opts.depth, target) - edgeBuf.WriteString(fmt.Sprintf( - "\t\"%s\" -> \"%s\";\n", - uniqueName, - uniqueTarget)) - } - } - if edgeBuf.Len() > 0 { - buf.WriteString(edgeBuf.String()) - buf.WriteString("\n") - } - */ -} - -func graphDotAddResourceProviders( - buf *bytes.Buffer, g *depgraph.Graph, opts *GraphDotOpts) { - var edgeBuf bytes.Buffer - buf.WriteString("\tsubgraph {\n") - for _, n := range g.Nouns { - _, ok := n.Meta.(*GraphNodeResourceProvider) - if !ok { - continue - } - - uniqueName := fmt.Sprintf("%d_%s", opts.depth, n) - - // Create this node. - buf.WriteString(fmt.Sprintf("\t\t\"%s\" [\n", uniqueName)) - buf.WriteString(fmt.Sprintf("\t\t\tlabel=\"%s\"\n", n)) - buf.WriteString("\t\t\tshape=diamond\n") - buf.WriteString("\t\t];\n") - - // Build up all the edges in a separate buffer so they're not in the - // subgraph. - graphWriteEdges(&edgeBuf, n, opts) - } - buf.WriteString("\t}\n\n") - if edgeBuf.Len() > 0 { - buf.WriteString(edgeBuf.String()) - buf.WriteString("\n") - } -} - -func graphDotTitle(buf *bytes.Buffer, g *depgraph.Graph) { - // Determine if we have diffs. If we do, then we're graphing a - // plan, which alters our graph a bit. - hasDiff := false - for _, n := range g.Nouns { - rn, ok := n.Meta.(*GraphNodeResource) - if !ok { - continue - } - if rn.Resource.Diff != nil && !rn.Resource.Diff.Empty() { - hasDiff = true - break - } - } - - graphType := "Configuration" - if hasDiff { - graphType = "Plan" - } - title := fmt.Sprintf("Terraform %s Resource Graph", graphType) - - buf.WriteString(fmt.Sprintf("\tlabel=\"%s\\n\\n\\n\";\n", title)) - buf.WriteString("\tlabelloc=\"t\";\n\n") -} - -func graphExpand(opts *GraphDotOpts) bool { - return opts.ModuleDepth > opts.depth || opts.ModuleDepth == -1 -} - -func graphUniqueName(n *depgraph.Noun, opts *GraphDotOpts) string { - return fmt.Sprintf("%d_%s", opts.depth, n) -} - -func graphWriteEdges( - buf *bytes.Buffer, n *depgraph.Noun, opts *GraphDotOpts) { - tab := strings.Repeat("\t", opts.depth+1) - - uniqueName := graphUniqueName(n, opts) - var ltail string - if _, ok := n.Meta.(*GraphNodeModule); ok && graphExpand(opts) { - ltail = "cluster_" + uniqueName - uniqueName = uniqueName + "_hidden" - } - - for _, e := range n.Edges() { - target := e.Tail() - targetN := target.(*depgraph.Noun) - uniqueTarget := graphUniqueName(targetN, opts) - - var lhead string - if _, ok := targetN.Meta.(*GraphNodeModule); ok && graphExpand(opts) { - lhead = "cluster_" + uniqueTarget - uniqueTarget = uniqueTarget + "_hidden" - } - - var attrs string - if lhead != "" || ltail != "" { - var attrList []string - if lhead != "" { - attrList = append(attrList, fmt.Sprintf( - "lhead=\"%s\"", lhead)) - } - if ltail != "" { - attrList = append(attrList, fmt.Sprintf( - "ltail=\"%s\"", ltail)) - } - - attrs = fmt.Sprintf(" [%s]", strings.Join(attrList, ",")) - } - - buf.WriteString(fmt.Sprintf( - "%s\"%s\" -> \"%s\"%s;\n", - tab, - uniqueName, - uniqueTarget, - attrs)) - } -} diff --git a/terraform/graph_old.go b/terraform/graph_old.go deleted file mode 100644 index f174bbe41..000000000 --- a/terraform/graph_old.go +++ /dev/null @@ -1,2038 +0,0 @@ -package terraform - -import ( - "errors" - "fmt" - "log" - "sort" - "strings" - - "github.com/hashicorp/terraform/config" - "github.com/hashicorp/terraform/config/module" - "github.com/hashicorp/terraform/depgraph" - "github.com/hashicorp/terraform/helper/multierror" -) - -// GraphOpts are options used to create the resource graph that Terraform -// walks to make changes to infrastructure. -// -// Depending on what options are set, the resulting graph will come in -// varying degrees of completeness. -type GraphOpts struct { - // Config is the configuration from which to build the basic graph. - // This is the only required item. - //Config *config.Config - - // Module is the relative root of a module tree for this graph. This - // is the only required item. This should always be the absolute root - // of the tree. ModulePath below should be used to constrain the depth. - // - // ModulePath specifies the place in the tree where Module exists. - // This is used for State lookups. - Module *module.Tree - ModulePath []string - - // Diff of changes that will be applied to the given state. This will - // associate a ResourceDiff with applicable resources. Additionally, - // new resource nodes representing resource destruction may be inserted - // into the graph. - Diff *Diff - - // State, if present, will make the ResourceState available on each - // resource node. Additionally, any orphans will be added automatically - // to the graph. - // - // Note: the state will be modified so it is initialized with basic - // empty states for all modules/resources in this graph. If you call prune - // later, these will be removed, but the graph adds important metadata. - State *State - - // Providers is a mapping of prefixes to a resource provider. If given, - // resource providers will be found, initialized, and associated to the - // resources in the graph. - // - // This will also potentially insert new nodes into the graph for - // the configuration of resource providers. - Providers map[string]ResourceProviderFactory - - // Provisioners is a mapping of names to a resource provisioner. - // These must be provided to support resource provisioners. - Provisioners map[string]ResourceProvisionerFactory - - // parent specifies the parent graph if there is one. This should not be - // set manually. - parent *depgraph.Graph -} - -// GraphRootNode is the name of the root node in the Terraform resource -// graph. This node is just a placemarker and has no associated functionality. -const GraphRootNode = "root" - -// GraphMeta is the metadata attached to the graph itself. -type GraphMeta struct { - // ModulePath is the path of the module that this graph represents. - ModulePath []string -} - -// GraphNodeModule is a node type in the graph that represents a module -// that will be created/managed. -type GraphNodeModule struct { - Config *config.Module - Path []string - Graph *depgraph.Graph - State *ModuleState - Flags ResourceFlag -} - -// GraphNodeResource is a node type in the graph that represents a resource -// that will be created or managed. Unlike the GraphNodeResourceMeta node, -// this represents a _single_, _resource_ to be managed, not a set of resources -// or a component of a resource. -type GraphNodeResource struct { - Index int - Config *config.Resource - Resource *Resource - ResourceProviderNode string - - // All the fields below are related to expansion. These are set by - // the graph but aren't useful individually. - ExpandMode ResourceExpandMode - Diff *ModuleDiff - State *ModuleState -} - -// GraphNodeResourceProvider is a node type in the graph that represents -// the configuration for a resource provider. -type GraphNodeResourceProvider struct { - ID string - Provider *graphSharedProvider -} - -// graphSharedProvider is a structure that stores a configuration -// with initialized providers and might be shared across different -// graphs in order to have only one instance of a provider. -type graphSharedProvider struct { - Config *config.ProviderConfig - Providers map[string]ResourceProvider - ProviderKeys []string - Parent *graphSharedProvider - - overrideConfig map[string]map[string]interface{} - parentNoun *depgraph.Noun -} - -// ResourceExpandMode specifies the expand behavior of the GraphNodeResource -// node. -type ResourceExpandMode byte - -const ( - ResourceExpandNone ResourceExpandMode = iota - ResourceExpandApply - ResourceExpandDestroy -) - -// Graph builds a dependency graph of all the resources for infrastructure -// change. -// -// This dependency graph shows the correct order that any resources need -// to be operated on. -// -// The Meta field of a graph Noun can contain one of the follow types. A -// description is next to each type to explain what it is. -// -// *GraphNodeResource - A resource. See the documentation of this -// struct for more details. -// *GraphNodeResourceProvider - A resource provider that needs to be -// configured at this point. -// -func GraphOld(opts *GraphOpts) (*depgraph.Graph, error) { - if opts.Module == nil { - return nil, errors.New("Module is required for Graph") - } - if opts.ModulePath == nil { - opts.ModulePath = rootModulePath - } - if !opts.Module.Loaded() { - return nil, errors.New("Module must be loaded") - } - - // Get the correct module in the tree that we're looking for. - currentModule := opts.Module - for _, n := range opts.ModulePath[1:] { - children := currentModule.Children() - currentModule = children[n] - } - - var conf *config.Config - if currentModule != nil { - conf = currentModule.Config() - } else { - conf = new(config.Config) - } - - // Get the state and diff of the module that we're working with. - var modDiff *ModuleDiff - var modState *ModuleState - if opts.Diff != nil { - modDiff = opts.Diff.ModuleByPath(opts.ModulePath) - } - if opts.State != nil { - modState = opts.State.ModuleByPath(opts.ModulePath) - } - - log.Printf("[DEBUG] Creating graph for path: %v", opts.ModulePath) - - g := new(depgraph.Graph) - g.Meta = &GraphMeta{ - ModulePath: opts.ModulePath, - } - - // First, build the initial resource graph. This only has the resources - // and no dependencies. This only adds resources that are in the config - // and not "orphans" (that are in the state, but not in the config). - graphAddConfigResources(g, conf, modState) - - if modState != nil { - // Next, add the state orphans if we have any - graphAddOrphans(g, conf, modState) - - // Add tainted resources if we have any. - graphAddTainted(g, modState) - } - - // Create the resource provider nodes for explicitly configured - // providers within our graph. - graphAddConfigProviderConfigs(g, conf) - - if opts.parent != nil { - // Add/merge the provider configurations from the parent so that - // we properly "inherit" providers. - graphAddParentProviderConfigs(g, opts.parent) - } - - // First pass matching resources to providers. This will allow us to - // determine what providers are missing. - graphMapResourceProviderId(g) - - if opts.Providers != nil { - // Add missing providers from the mapping. - if err := graphAddMissingResourceProviders(g, opts.Providers); err != nil { - return nil, err - } - - // Initialize all the providers - if err := graphInitResourceProviders(g, opts.Providers); err != nil { - return nil, err - } - - // Map the providers to resources - if err := graphMapResourceProviders(g); err != nil { - return nil, err - } - } - - // Add the modules that are in the configuration. - if err := graphAddConfigModules(g, conf, opts); err != nil { - return nil, err - } - - if opts.State != nil { - // Add module orphans if we have any of those - if ms := opts.State.Children(opts.ModulePath); len(ms) > 0 { - if err := graphAddModuleOrphans(g, conf, ms, opts); err != nil { - return nil, err - } - } - } - - // Add the orphan dependencies - graphAddOrphanDeps(g, modState) - - // Add the orphan module dependencies - graphAddOrphanModuleDeps(g, modState) - - // Add the provider dependencies - graphAddResourceProviderDeps(g) - - // Now, prune the providers that we aren't using. - graphPruneResourceProviders(g) - - // Add explicit dependsOn dependencies to the graph - graphAddExplicitDeps(g) - - // Setup the provisioners. These may have variable dependencies, - // and must be done before dependency setup - if err := graphMapResourceProvisioners(g, opts.Provisioners); err != nil { - return nil, err - } - - // Add all the variable dependencies - graphAddVariableDeps(g) - - // Build the root so that we have a single valid root - graphAddRoot(g) - - // If we have a diff, then make sure to add that in - if modDiff != nil { - if err := graphAddDiff(g, opts.Diff, modDiff); err != nil { - return nil, err - } - } - - // Encode the dependencies - graphEncodeDependencies(g) - - // Validate - if err := g.Validate(); err != nil { - return nil, err - } - - log.Printf( - "[DEBUG] Graph %v created and valid. %d nouns.", - opts.ModulePath, - len(g.Nouns)) - - return g, nil -} - -// graphEncodeDependencies is used to initialize a State with a ResourceState -// for every resource. -// -// This method is very important to call because it will properly setup -// the ResourceState dependency information with data from the graph. This -// allows orphaned resources to be destroyed in the proper order. -func graphEncodeDependencies(g *depgraph.Graph) { - for _, n := range g.Nouns { - switch rn := n.Meta.(type) { - case *GraphNodeResource: - // If we are using create-before-destroy, there - // are some special depedencies injected on the - // deposed node that would cause a circular depedency - // chain if persisted. We must only handle the new node, - // node the deposed node. - r := rn.Resource - if r.Flags&FlagDeposed != 0 { - continue - } - - // Update the dependencies - var inject []string - for _, dep := range n.Deps { - switch target := dep.Target.Meta.(type) { - case *GraphNodeModule: - inject = append(inject, dep.Target.Name) - - case *GraphNodeResource: - if target.Resource.Id == r.Id { - continue - } - inject = append(inject, target.Resource.Id) - - case *GraphNodeResourceProvider: - // Do nothing - - default: - panic(fmt.Sprintf("Unknown graph node: %#v", dep.Target)) - } - } - - // Update the dependencies - r.Dependencies = inject - - case *GraphNodeModule: - // Update the dependencies - var inject []string - for _, dep := range n.Deps { - switch target := dep.Target.Meta.(type) { - case *GraphNodeModule: - if dep.Target.Name == n.Name { - continue - } - inject = append(inject, dep.Target.Name) - - case *GraphNodeResource: - inject = append(inject, target.Resource.Id) - - case *GraphNodeResourceProvider: - // Do nothing - - default: - panic(fmt.Sprintf("Unknown graph node: %#v", dep.Target)) - } - - } - - // Update the dependencies - if rn.State != nil { - rn.State.Dependencies = inject - } - } - } -} - -// graphAddConfigModules adds the modules from a configuration structure -// into the graph, expanding each to their own sub-graph. -func graphAddConfigModules( - g *depgraph.Graph, - c *config.Config, - opts *GraphOpts) error { - // Just short-circuit the whole thing if we don't have modules - if len(c.Modules) == 0 { - return nil - } - - // Build the list of nouns to add to the graph - nounsList := make([]*depgraph.Noun, 0, len(c.Modules)) - for _, m := range c.Modules { - if n, err := graphModuleNoun(m.Name, m, g, opts); err != nil { - return err - } else { - // Attach the module state if any - if opts.State != nil { - module := n.Meta.(*GraphNodeModule) - module.State = opts.State.ModuleByPath(module.Path) - if module.State == nil { - module.State = opts.State.AddModule(module.Path) - } - } - nounsList = append(nounsList, n) - } - } - - g.Nouns = append(g.Nouns, nounsList...) - return nil -} - -// configGraph turns a configuration structure into a dependency graph. -func graphAddConfigResources( - g *depgraph.Graph, c *config.Config, mod *ModuleState) { - meta := g.Meta.(*GraphMeta) - - // This tracks all the resource nouns - nounsList := make([]*depgraph.Noun, len(c.Resources)) - for i, r := range c.Resources { - name := r.Id() - - // Build the noun - nounsList[i] = &depgraph.Noun{ - Name: name, - Meta: &GraphNodeResource{ - Index: -1, - Config: r, - Resource: &Resource{ - Id: name, - Info: &InstanceInfo{ - Id: name, - ModulePath: meta.ModulePath, - Type: r.Type, - }, - }, - State: mod.View(name), - ExpandMode: ResourceExpandApply, - }, - } - - /* - TODO: probably did something important, bring it back somehow - resourceNouns := make([]*depgraph.Noun, r.Count) - for i := 0; i < r.Count; i++ { - name := r.Id() - index := -1 - - // If we have a count that is more than one, then make sure - // we suffix with the number of the resource that this is. - if r.Count > 1 { - name = fmt.Sprintf("%s.%d", name, i) - index = i - } - - var state *ResourceState - if mod != nil { - // Lookup the resource state - state = mod.Resources[name] - if state == nil { - if r.Count == 1 { - // If the count is one, check the state for ".0" - // appended, which might exist if we go from - // count > 1 to count == 1. - state = mod.Resources[r.Id()+".0"] - } else if i == 0 { - // If count is greater than one, check for state - // with just the ID, which might exist if we go - // from count == 1 to count > 1 - state = mod.Resources[r.Id()] - } - - // TODO(mitchellh): If one of the above works, delete - // the old style and just copy it to the new style. - } - } - - if state == nil { - state = &ResourceState{ - Type: r.Type, - } - } - - flags := FlagPrimary - if len(state.Tainted) > 0 { - flags |= FlagHasTainted - } - - resourceNouns[i] = &depgraph.Noun{ - Name: name, - Meta: &GraphNodeResource{ - Index: index, - Config: r, - Resource: &Resource{ - Id: name, - Info: &InstanceInfo{ - Id: name, - ModulePath: meta.ModulePath, - Type: r.Type, - }, - State: state.Primary, - Config: NewResourceConfig(r.RawConfig), - Flags: flags, - }, - }, - } - } - - // If we have more than one, then create a meta node to track - // the resources. - if r.Count > 1 { - metaNoun := &depgraph.Noun{ - Name: r.Id(), - Meta: &GraphNodeResourceMeta{ - ID: r.Id(), - Name: r.Name, - Type: r.Type, - Count: r.Count, - }, - } - - // Create the dependencies on this noun - for _, n := range resourceNouns { - metaNoun.Deps = append(metaNoun.Deps, &depgraph.Dependency{ - Name: n.Name, - Source: metaNoun, - Target: n, - }) - } - - // Assign it to the map so that we have it - nouns[metaNoun.Name] = metaNoun - } - - for _, n := range resourceNouns { - nouns[n.Name] = n - } - */ - } - - g.Name = "terraform" - g.Nouns = append(g.Nouns, nounsList...) -} - -// graphAddDiff takes an already-built graph of resources and adds the -// diffs to the resource nodes themselves. -// -// This may also introduces new graph elements. If there are diffs that -// require a destroy, new elements may be introduced since destroy order -// is different than create order. For example, destroying a VPC requires -// destroying the VPC's subnets first, whereas creating a VPC requires -// doing it before the subnets are created. This function handles inserting -// these nodes for you. -func graphAddDiff(g *depgraph.Graph, gDiff *Diff, d *ModuleDiff) error { - var nlist []*depgraph.Noun - var modules []*depgraph.Noun - injected := make(map[*depgraph.Dependency]struct{}) - for _, n := range g.Nouns { - // A module is being destroyed if all it's resources are being - // destroyed (via a destroy plan) or if it is orphaned. Only in - // those cases do we need to handle depedency inversion. - if mod, ok := n.Meta.(*GraphNodeModule); ok { - md := gDiff.ModuleByPath(mod.Path) - if mod.Flags&FlagOrphan != 0 || (md != nil && md.Destroy) { - modules = append(modules, n) - } - continue - } - - rn, ok := n.Meta.(*GraphNodeResource) - if !ok { - continue - } - if rn.Resource.Flags&FlagTainted != 0 { - continue - } - - change := false - destroy := false - diffs := d.Instances(rn.Resource.Id) - if len(diffs) == 0 { - continue - } - for _, d := range diffs { - if d.Destroy { - destroy = true - } - - if len(d.Attributes) > 0 { - change = true - } - } - - // If we're expanding, save the diff so we can add it on later - if rn.ExpandMode > ResourceExpandNone { - rn.Diff = d - } - - // If we are not expanding, then we assign the - // instance diff to the resource. - var rd *InstanceDiff - if rn.ExpandMode == ResourceExpandNone { - rd = diffs[0] - } - - if destroy { - // If we're destroying, we create a new destroy node with - // the proper dependencies. Perform a dirty copy operation. - newNode := new(GraphNodeResource) - *newNode = *rn - newNode.Resource = new(Resource) - *newNode.Resource = *rn.Resource - - // Make the diff _just_ the destroy. - newNode.Resource.Diff = &InstanceDiff{Destroy: true} - - // Make sure ExpandDestroy is set if Expand - if newNode.ExpandMode == ResourceExpandApply { - newNode.ExpandMode = ResourceExpandDestroy - } - - // Create the new node - newN := &depgraph.Noun{ - Name: fmt.Sprintf("%s (destroy)", newNode.Resource.Id), - Meta: newNode, - } - newN.Deps = make([]*depgraph.Dependency, len(n.Deps)) - - // Copy all the dependencies and do a fixup later - copy(newN.Deps, n.Deps) - - // Append it to the list so we handle it later - nlist = append(nlist, newN) - - if rd != nil { - // Mark the old diff to not destroy since we handle that in - // the dedicated node. - newDiff := new(InstanceDiff) - *newDiff = *rd - newDiff.Destroy = false - rd = newDiff - } - - // The dependency ordering depends on if the CreateBeforeDestroy - // flag is enabled. If so, we must create the replacement first, - // and then destroy the old instance. - if rn.Config != nil && rn.Config.Lifecycle.CreateBeforeDestroy && change { - dep := &depgraph.Dependency{ - Name: n.Name, - Source: newN, - Target: n, - } - - // Add the old noun to the new noun dependencies so that - // the create happens before the destroy. - newN.Deps = append(newN.Deps, dep) - - // Mark that this dependency has been injected so that - // we do not invert the direction below. - injected[dep] = struct{}{} - - // Add a depedency from the root, since the create node - // does not depend on us - if g.Root != nil { - g.Root.Deps = append(g.Root.Deps, &depgraph.Dependency{ - Name: newN.Name, - Source: g.Root, - Target: newN, - }) - } - - // Set the ReplacePrimary flag on the new instance so that - // it will become the new primary, and Deposed flag on the - // existing instance so that it will step down - rn.Resource.Flags |= FlagReplacePrimary - newNode.Resource.Flags |= FlagDeposed - - // This logic is not intuitive, but we need to make the - // destroy depend upon any resources that depend on the - // create. The reason is suppose you have a LB depend on - // a web server. You need the order to be create, update LB, - // destroy. Without this, the update LB and destroy can - // be executed in an arbitrary order (likely in parallel). - incoming := g.DependsOn(n) - for _, inc := range incoming { - // Ignore the root... - if inc == g.Root { - continue - } - dep := &depgraph.Dependency{ - Name: inc.Name, - Source: newN, - Target: inc, - } - injected[dep] = struct{}{} - newN.Deps = append(newN.Deps, dep) - } - - } else { - dep := &depgraph.Dependency{ - Name: newN.Name, - Source: n, - Target: newN, - } - - // Add the new noun to our dependencies so that - // the destroy happens before the apply. - n.Deps = append(n.Deps, dep) - } - } - - rn.Resource.Diff = rd - } - - // Go through each resource and module and make sure we - // calculate all the dependencies properly. - invertDeps := [][]*depgraph.Noun{nlist, modules} - for _, list := range invertDeps { - for _, n := range list { - deps := n.Deps - num := len(deps) - for i := 0; i < num; i++ { - dep := deps[i] - - // Check if this dependency was just injected, otherwise - // we will incorrectly flip the depedency twice. - if _, ok := injected[dep]; ok { - continue - } - - switch target := dep.Target.Meta.(type) { - case *GraphNodeResource: - // If the other node is also being deleted, - // we must be deleted first. E.g. if A -> B, - // then when we create, B is created first then A. - // On teardown, A is destroyed first, then B. - // Thus we must flip our depedency and instead inject - // it on B. - for _, n2 := range nlist { - rn2 := n2.Meta.(*GraphNodeResource) - if target.Resource.Id == rn2.Resource.Id { - newDep := &depgraph.Dependency{ - Name: n.Name, - Source: n2, - Target: n, - } - injected[newDep] = struct{}{} - n2.Deps = append(n2.Deps, newDep) - break - } - } - - // Drop the dependency. We may have created - // an inverse depedency if the dependent resource - // is also being deleted, but this dependence is - // no longer required. - deps[i], deps[num-1] = deps[num-1], nil - num-- - i-- - - case *GraphNodeModule: - // We invert any module dependencies so we're destroyed - // first, before any modules are applied. - newDep := &depgraph.Dependency{ - Name: n.Name, - Source: dep.Target, - Target: n, - } - dep.Target.Deps = append(dep.Target.Deps, newDep) - - // Drop the dependency. We may have created - // an inverse depedency if the dependent resource - // is also being deleted, but this dependence is - // no longer required. - deps[i], deps[num-1] = deps[num-1], nil - num-- - i-- - case *GraphNodeResourceProvider: - // Keep these around, but fix up the source to be ourselves - // rather than the old node. - newDep := *dep - newDep.Source = n - deps[i] = &newDep - default: - panic(fmt.Errorf("Unhandled depedency type: %#v", dep.Target.Meta)) - } - } - n.Deps = deps[:num] - } - } - - // Add the nouns to the graph - g.Nouns = append(g.Nouns, nlist...) - - return nil -} - -// graphAddExplicitDeps adds the dependencies to the graph for the explicit -// dependsOn configurations. -func graphAddExplicitDeps(g *depgraph.Graph) { - depends := false - - rs := make(map[string]*depgraph.Noun) - for _, n := range g.Nouns { - rn, ok := n.Meta.(*GraphNodeResource) - if !ok { - continue - } - if rn.Config == nil { - // Orphan. It can't be depended on or have depends (explicit) - // anyways. - continue - } - - rs[rn.Resource.Id] = n - if rn.Config != nil && len(rn.Config.DependsOn) > 0 { - depends = true - } - } - - // If we didn't have any dependsOn, just return - if !depends { - return - } - - for _, n1 := range rs { - rn1 := n1.Meta.(*GraphNodeResource) - for _, d := range rn1.Config.DependsOn { - for _, n2 := range rs { - rn2 := n2.Meta.(*GraphNodeResource) - if rn2.Config.Id() != d { - continue - } - - n1.Deps = append(n1.Deps, &depgraph.Dependency{ - Name: d, - Source: n1, - Target: n2, - }) - } - } - } -} - -// graphAddMissingResourceProviders adds GraphNodeResourceProvider nodes for -// the resources that do not have an explicit resource provider specified -// because no provider configuration was given. -func graphAddMissingResourceProviders( - g *depgraph.Graph, - ps map[string]ResourceProviderFactory) error { - var errs []error - - for _, n := range g.Nouns { - rn, ok := n.Meta.(*GraphNodeResource) - if !ok { - continue - } - if rn.ResourceProviderNode != "" { - continue - } - - prefixes := matchingPrefixes(rn.Resource.Info.Type, ps) - if len(prefixes) == 0 { - errs = append(errs, fmt.Errorf( - "No matching provider for type: %s", - rn.Resource.Info.Type)) - continue - } - - // The resource provider ID is simply the shortest matching - // prefix, since that'll give us the most resource providers - // to choose from. - id := prefixes[len(prefixes)-1] - rn.ResourceProviderNode = fmt.Sprintf("provider.%s", id) - - // If we don't have a matching noun for this yet, insert it. - if g.Noun(rn.ResourceProviderNode) == nil { - pn := &depgraph.Noun{ - Name: rn.ResourceProviderNode, - Meta: &GraphNodeResourceProvider{ - ID: id, - Provider: new(graphSharedProvider), - }, - } - g.Nouns = append(g.Nouns, pn) - } - } - - if len(errs) > 0 { - return &multierror.Error{Errors: errs} - } - - return nil -} - -func graphAddModuleOrphans( - g *depgraph.Graph, - config *config.Config, - ms []*ModuleState, - opts *GraphOpts) error { - // Build a lookup map for the modules we do have defined - childrenKeys := make(map[string]struct{}) - for _, m := range config.Modules { - childrenKeys[m.Name] = struct{}{} - } - - // Go through each of the child modules. If we don't have it in our - // config, it is an orphan. - var nounsList []*depgraph.Noun - for _, m := range ms { - k := m.Path[len(m.Path)-1] - if _, ok := childrenKeys[k]; ok { - // We have this module configured - continue - } - - if n, err := graphModuleNoun(k, nil, g, opts); err != nil { - return err - } else { - // Mark this module as being an orphan - module := n.Meta.(*GraphNodeModule) - module.Flags |= FlagOrphan - module.State = m - nounsList = append(nounsList, n) - } - } - - g.Nouns = append(g.Nouns, nounsList...) - return nil -} - -// graphAddOrphanDeps adds the dependencies to the orphans based on their -// explicit Dependencies state. -func graphAddOrphanDeps(g *depgraph.Graph, mod *ModuleState) { - for _, n := range g.Nouns { - rn, ok := n.Meta.(*GraphNodeResource) - if !ok { - continue - } - if rn.Resource.Flags&FlagOrphan == 0 { - continue - } - - // If we have no dependencies, then just continue - rs := mod.Resources[n.Name] - if len(rs.Dependencies) == 0 { - continue - } - - for _, n2 := range g.Nouns { - // Don't ever depend on ourselves - if n2.Meta == n.Meta { - continue - } - - var compareName string - switch rn2 := n2.Meta.(type) { - case *GraphNodeModule: - compareName = n2.Name - case *GraphNodeResource: - compareName = rn2.Resource.Id - } - if compareName == "" { - continue - } - - for _, depName := range rs.Dependencies { - if !strings.HasPrefix(depName, compareName) { - continue - } - dep := &depgraph.Dependency{ - Name: depName, - Source: n, - Target: n2, - } - n.Deps = append(n.Deps, dep) - break - } - } - } -} - -// graphAddOrphanModuleDeps adds the dependencies to the orphan -// modules based on their explicit Dependencies state. -func graphAddOrphanModuleDeps(g *depgraph.Graph, mod *ModuleState) { - for _, n := range g.Nouns { - module, ok := n.Meta.(*GraphNodeModule) - if !ok { - continue - } - if module.Flags&FlagOrphan == 0 { - continue - } - - // If we have no dependencies, then just continue - if len(module.State.Dependencies) == 0 { - continue - } - - for _, n2 := range g.Nouns { - // Don't ever depend on ourselves - if n2.Meta == n.Meta { - continue - } - - var compareName string - switch rn2 := n2.Meta.(type) { - case *GraphNodeModule: - compareName = n2.Name - case *GraphNodeResource: - compareName = rn2.Resource.Id - } - if compareName == "" { - continue - } - - for _, depName := range module.State.Dependencies { - if !strings.HasPrefix(depName, compareName) { - continue - } - dep := &depgraph.Dependency{ - Name: depName, - Source: n, - Target: n2, - } - n.Deps = append(n.Deps, dep) - break - } - } - } -} - -// graphAddOrphans adds the orphans to the graph. -func graphAddOrphans(g *depgraph.Graph, c *config.Config, mod *ModuleState) { - meta := g.Meta.(*GraphMeta) - - var nlist []*depgraph.Noun - for _, k := range mod.Orphans(c) { - rs := mod.Resources[k] - noun := &depgraph.Noun{ - Name: k, - Meta: &GraphNodeResource{ - Index: -1, - Resource: &Resource{ - Id: k, - Info: &InstanceInfo{ - Id: k, - ModulePath: meta.ModulePath, - Type: rs.Type, - }, - State: rs.Primary, - Config: NewResourceConfig(nil), - Flags: FlagOrphan, - }, - }, - } - - // Append it to the list so we handle it later - nlist = append(nlist, noun) - } - - // Add the nouns to the graph - g.Nouns = append(g.Nouns, nlist...) -} - -// graphAddParentProviderConfigs goes through and adds/merges provider -// configurations from the parent. -func graphAddParentProviderConfigs(g, parent *depgraph.Graph) { - var nounsList []*depgraph.Noun - for _, n := range parent.Nouns { - pn, ok := n.Meta.(*GraphNodeResourceProvider) - if !ok { - continue - } - - // If we have a provider configuration with the exact same - // name, then set specify the parent pointer to their shared - // config. - ourProviderRaw := g.Noun(n.Name) - - // If we don't have a matching configuration, then create one. - if ourProviderRaw == nil { - noun := &depgraph.Noun{ - Name: n.Name, - Meta: &GraphNodeResourceProvider{ - ID: pn.ID, - Provider: &graphSharedProvider{ - Parent: pn.Provider, - parentNoun: n, - }, - }, - } - - nounsList = append(nounsList, noun) - continue - } - - // If we have a matching configuration, then set the parent pointer - ourProvider := ourProviderRaw.Meta.(*GraphNodeResourceProvider) - ourProvider.Provider.Parent = pn.Provider - ourProvider.Provider.parentNoun = n - } - - g.Nouns = append(g.Nouns, nounsList...) -} - -// graphAddConfigProviderConfigs adds a GraphNodeResourceProvider for every -// `provider` configuration block. Note that a provider may exist that -// isn't used for any resources. These will be pruned later. -func graphAddConfigProviderConfigs(g *depgraph.Graph, c *config.Config) { - nounsList := make([]*depgraph.Noun, 0, len(c.ProviderConfigs)) - for _, pc := range c.ProviderConfigs { - noun := &depgraph.Noun{ - Name: fmt.Sprintf("provider.%s", pc.Name), - Meta: &GraphNodeResourceProvider{ - ID: pc.Name, - Provider: &graphSharedProvider{ - Config: pc, - }, - }, - } - - nounsList = append(nounsList, noun) - } - - // Add all the provider config nouns to the graph - g.Nouns = append(g.Nouns, nounsList...) -} - -// graphAddRoot adds a root element to the graph so that there is a single -// root to point to all the dependencies. -func graphAddRoot(g *depgraph.Graph) { - root := &depgraph.Noun{Name: GraphRootNode} - for _, n := range g.Nouns { - switch n.Meta.(type) { - case *GraphNodeResourceProvider: - // ResourceProviders don't need to be in the root deps because - // they're always pointed to by some resource. - continue - } - - root.Deps = append(root.Deps, &depgraph.Dependency{ - Name: n.Name, - Source: root, - Target: n, - }) - } - g.Nouns = append(g.Nouns, root) - g.Root = root -} - -// graphAddVariableDeps inspects all the nouns and adds any dependencies -// based on variable values. -func graphAddVariableDeps(g *depgraph.Graph) { - for _, n := range g.Nouns { - switch m := n.Meta.(type) { - case *GraphNodeModule: - if m.Config != nil { - vars := m.Config.RawConfig.Variables - nounAddVariableDeps(g, n, vars, false) - } - - case *GraphNodeResource: - if m.Config != nil { - // Handle the count variables - vars := m.Config.RawCount.Variables - nounAddVariableDeps(g, n, vars, false) - - // Handle the resource variables - vars = m.Config.RawConfig.Variables - nounAddVariableDeps(g, n, vars, false) - } - - // Handle the variables of the resource provisioners - for _, p := range m.Resource.Provisioners { - vars := p.RawConfig.Variables - nounAddVariableDeps(g, n, vars, true) - - vars = p.ConnInfo.Variables - nounAddVariableDeps(g, n, vars, true) - } - - case *GraphNodeResourceProvider: - if m.Provider != nil && m.Provider.Config != nil { - vars := m.Provider.Config.RawConfig.Variables - nounAddVariableDeps(g, n, vars, false) - } - - default: - // Other node types don't have dependencies or we don't support it - continue - } - } -} - -// graphAddTainted adds the tainted instances to the graph. -func graphAddTainted(g *depgraph.Graph, mod *ModuleState) { - meta := g.Meta.(*GraphMeta) - - var nlist []*depgraph.Noun - for k, rs := range mod.Resources { - // If we have no tainted resources, continue on - if len(rs.Tainted) == 0 { - continue - } - - // Find the untainted resource of this in the noun list. If our - // name is 3 parts, then we mus be a count instance, so our - // untainted node is just the noun without the count. - var untainted *depgraph.Noun - untaintedK := k - if ps := strings.Split(k, "."); len(ps) == 3 { - untaintedK = strings.Join(ps[0:2], ".") - } - for _, n := range g.Nouns { - if n.Name == untaintedK { - untainted = n - break - } - } - - for i, is := range rs.Tainted { - name := fmt.Sprintf("%s (tainted #%d)", k, i+1) - - // Add each of the tainted resources to the graph, and encode - // a dependency from the non-tainted resource to this so that - // tainted resources are always destroyed first. - noun := &depgraph.Noun{ - Name: name, - Meta: &GraphNodeResource{ - Index: -1, - Resource: &Resource{ - Id: k, - Info: &InstanceInfo{ - Id: k, - ModulePath: meta.ModulePath, - Type: rs.Type, - }, - State: is, - Config: NewResourceConfig(nil), - Diff: &InstanceDiff{Destroy: true}, - Flags: FlagTainted, - TaintedIndex: i, - }, - }, - } - - // Append it to the list so we handle it later - nlist = append(nlist, noun) - - // If we have an untainted version, then make sure to add - // the dependency. - if untainted != nil { - dep := &depgraph.Dependency{ - Name: name, - Source: untainted, - Target: noun, - } - - untainted.Deps = append(untainted.Deps, dep) - } - } - } - - // Add the nouns to the graph - g.Nouns = append(g.Nouns, nlist...) -} - -// graphModuleNoun creates a noun for a module. -func graphModuleNoun( - n string, m *config.Module, - g *depgraph.Graph, opts *GraphOpts) (*depgraph.Noun, error) { - name := fmt.Sprintf("module.%s", n) - path := make([]string, len(opts.ModulePath)+1) - copy(path, opts.ModulePath) - path[len(opts.ModulePath)] = n - - // Build the opts we'll use to make the next graph - subOpts := *opts - subOpts.ModulePath = path - subOpts.parent = g - subGraph, err := GraphOld(&subOpts) - if err != nil { - return nil, fmt.Errorf( - "Error building module graph '%s': %s", - n, err) - } - - return &depgraph.Noun{ - Name: name, - Meta: &GraphNodeModule{ - Config: m, - Path: path, - Graph: subGraph, - }, - }, nil -} - -// nounAddVariableDeps updates the dependencies of a noun given -// a set of associated variable values -func nounAddVariableDeps( - g *depgraph.Graph, - n *depgraph.Noun, - vars map[string]config.InterpolatedVariable, - removeSelf bool) { - for _, rawV := range vars { - var name string - var target *depgraph.Noun - - switch v := rawV.(type) { - case *config.ModuleVariable: - name = fmt.Sprintf("module.%s", v.Name) - target = g.Noun(name) - case *config.ResourceVariable: - // For resource variables, if we ourselves are a resource, then - // we have to check whether to expand or not to create the proper - // resource dependency. - rn, ok := n.Meta.(*GraphNodeResource) - if !ok || rn.ExpandMode > ResourceExpandNone { - name = v.ResourceId() - target = g.Noun(v.ResourceId()) - break - } - - // We're an expanded resource, so add the specific index - // as the dependency. - name = fmt.Sprintf("%s.%d", v.ResourceId(), v.Index) - target = g.Noun(name) - default: - } - - if target == nil { - continue - } - - // If we're ignoring self-references, then don't add that - // dependency. - if removeSelf && n == target { - continue - } - - // Build the dependency - dep := &depgraph.Dependency{ - Name: name, - Source: n, - Target: target, - } - - n.Deps = append(n.Deps, dep) - } -} - -// graphInitResourceProviders maps the resource providers onto the graph -// given a mapping of prefixes to resource providers. -// -// Unlike the graphAdd* functions, this one can return an error if resource -// providers can't be found or can't be instantiated. -func graphInitResourceProviders( - g *depgraph.Graph, - ps map[string]ResourceProviderFactory) error { - var errs []error - - // Keep track of providers we know we couldn't instantiate so - // that we don't get a ton of errors about the same provider. - failures := make(map[string]struct{}) - - for _, n := range g.Nouns { - // We only care about the resource providers first. There is guaranteed - // to be only one node per tuple (providerId, providerConfig), which - // means we don't need to verify we have instantiated it before. - rn, ok := n.Meta.(*GraphNodeResourceProvider) - if !ok { - continue - } - - prefixes := matchingPrefixes(rn.ID, ps) - if len(prefixes) > 0 { - if _, ok := failures[prefixes[0]]; ok { - // We already failed this provider, meaning this - // resource will never succeed, so just continue. - continue - } - } - - sharedProvider := rn.Provider - - // Go through each prefix and instantiate if necessary, then - // verify if this provider is of use to us or not. - sharedProvider.Providers = make(map[string]ResourceProvider) - sharedProvider.ProviderKeys = prefixes - for _, prefix := range prefixes { - p, err := ps[prefix]() - if err != nil { - errs = append(errs, fmt.Errorf( - "Error instantiating resource provider for "+ - "prefix %s: %s", prefix, err)) - - // Record the error so that we don't check it again - failures[prefix] = struct{}{} - - // Jump to the next prefix - continue - } - - sharedProvider.Providers[prefix] = p - } - - // If we never found a provider, then error and continue - if len(sharedProvider.Providers) == 0 { - errs = append(errs, fmt.Errorf( - "Provider for configuration '%s' not found.", - rn.ID)) - continue - } - } - - if len(errs) > 0 { - return &multierror.Error{Errors: errs} - } - - return nil -} - -// graphAddResourceProviderDeps goes through all the nodes in the graph -// and adds any dependencies to resource providers as needed. -func graphAddResourceProviderDeps(g *depgraph.Graph) { - for _, rawN := range g.Nouns { - switch n := rawN.Meta.(type) { - case *GraphNodeModule: - // Check if the module depends on any of our providers - // by seeing if there is a parent node back. - for _, moduleRaw := range n.Graph.Nouns { - pn, ok := moduleRaw.Meta.(*GraphNodeResourceProvider) - if !ok { - continue - } - if pn.Provider.parentNoun == nil { - continue - } - - // Create the dependency to the provider - dep := &depgraph.Dependency{ - Name: pn.Provider.parentNoun.Name, - Source: rawN, - Target: pn.Provider.parentNoun, - } - rawN.Deps = append(rawN.Deps, dep) - } - case *GraphNodeResource: - // Not sure how this would happen, but we might as well - // check for it. - if n.ResourceProviderNode == "" { - continue - } - - // Get the noun this depends on. - target := g.Noun(n.ResourceProviderNode) - - // Create the dependency to the provider - dep := &depgraph.Dependency{ - Name: target.Name, - Source: rawN, - Target: target, - } - rawN.Deps = append(rawN.Deps, dep) - } - } -} - -// graphPruneResourceProviders will remove the GraphNodeResourceProvider -// nodes that aren't used in any way. -func graphPruneResourceProviders(g *depgraph.Graph) { - // First, build a mapping of the providers we have. - ps := make(map[string]struct{}) - for _, n := range g.Nouns { - _, ok := n.Meta.(*GraphNodeResourceProvider) - if !ok { - continue - } - - ps[n.Name] = struct{}{} - } - - // Now go through all the dependencies throughout and find - // if any of these aren't reachable. - for _, n := range g.Nouns { - for _, dep := range n.Deps { - delete(ps, dep.Target.Name) - } - } - - if len(ps) == 0 { - // We used all of them! - return - } - - // Now go through and remove these nodes that aren't used - for i := 0; i < len(g.Nouns); i++ { - if _, ok := ps[g.Nouns[i].Name]; !ok { - continue - } - - // Delete this node - copy(g.Nouns[i:], g.Nouns[i+1:]) - g.Nouns[len(g.Nouns)-1] = nil - g.Nouns = g.Nouns[:len(g.Nouns)-1] - i-- - } -} - -// graphMapResourceProviderId goes through the graph and maps the -// ID of a resource provider node to each resource. This lets us know which -// configuration is for which resource. -// -// This is safe to call multiple times. -func graphMapResourceProviderId(g *depgraph.Graph) { - // Build the list of provider configs we have - ps := make(map[string]string) - for _, n := range g.Nouns { - pn, ok := n.Meta.(*GraphNodeResourceProvider) - if !ok { - continue - } - - ps[n.Name] = pn.ID - } - - // Go through every resource and find the shortest matching provider - for _, n := range g.Nouns { - rn, ok := n.Meta.(*GraphNodeResource) - if !ok { - continue - } - - var match, matchNode string - for n, p := range ps { - if !strings.HasPrefix(rn.Resource.Info.Type, p) { - continue - } - if len(p) > len(match) { - match = p - matchNode = n - } - } - if matchNode == "" { - continue - } - - rn.ResourceProviderNode = matchNode - } -} - -// graphMapResourceProviders takes a graph that already has initialized -// the resource providers (using graphInitResourceProviders) and maps the -// resource providers to the resources themselves. -func graphMapResourceProviders(g *depgraph.Graph) error { - var errs []error - - // First build a mapping of resource provider ID to the node that - // contains those resources. - mapping := make(map[string]*GraphNodeResourceProvider) - for _, n := range g.Nouns { - rn, ok := n.Meta.(*GraphNodeResourceProvider) - if !ok { - continue - } - mapping[rn.ID] = rn - } - - // Now go through each of the resources and find a matching provider. - for _, n := range g.Nouns { - rn, ok := n.Meta.(*GraphNodeResource) - if !ok { - continue - } - - rpnRaw := g.Noun(rn.ResourceProviderNode) - if rpnRaw == nil { - // This should never happen since when building the graph - // we ensure that everything matches up. - panic(fmt.Sprintf( - "Resource provider not found: %s (type: %s)", - rn.ResourceProviderNode, - rn.Resource.Info.Type)) - } - rpn := rpnRaw.Meta.(*GraphNodeResourceProvider) - - var provider ResourceProvider - for _, k := range rpn.Provider.ProviderKeys { - // Only try this provider if it has the right prefix - if !strings.HasPrefix(rn.Resource.Info.Type, k) { - continue - } - - rp := rpn.Provider.Providers[k] - if ProviderSatisfies(rp, rn.Resource.Info.Type) { - provider = rp - break - } - } - - if provider == nil { - errs = append(errs, fmt.Errorf( - "Resource provider not found for resource type '%s'", - rn.Resource.Info.Type)) - continue - } - - rn.Resource.Provider = provider - } - - if len(errs) > 0 { - return &multierror.Error{Errors: errs} - } - - return nil -} - -// graphMapResourceProvisioners takes a graph that already has -// the resources and maps the resource provisioners to the resources themselves. -func graphMapResourceProvisioners(g *depgraph.Graph, - provisioners map[string]ResourceProvisionerFactory) error { - var errs []error - - // Create a cache of resource provisioners, avoids duplicate - // initialization of the instances - cache := make(map[string]ResourceProvisioner) - - // Go through each of the resources and find a matching provisioners - for _, n := range g.Nouns { - rn, ok := n.Meta.(*GraphNodeResource) - if !ok { - continue - } - - // Ignore orphan nodes with no provisioners - if rn.Config == nil { - continue - } - - // Check each provisioner - for _, p := range rn.Config.Provisioners { - // Check for a cached provisioner - provisioner, ok := cache[p.Type] - if !ok { - // Lookup the factory method - factory, ok := provisioners[p.Type] - if !ok { - errs = append(errs, fmt.Errorf( - "Resource provisioner not found for provisioner type '%s'", - p.Type)) - continue - } - - // Initialize the provisioner - prov, err := factory() - if err != nil { - errs = append(errs, fmt.Errorf( - "Failed to instantiate provisioner type '%s': %v", - p.Type, err)) - continue - } - provisioner = prov - - // Cache this type of provisioner - cache[p.Type] = prov - } - - // Save the provisioner - rn.Resource.Provisioners = append(rn.Resource.Provisioners, &ResourceProvisionerConfig{ - Type: p.Type, - Provisioner: provisioner, - Config: NewResourceConfig(p.RawConfig), - RawConfig: p.RawConfig, - ConnInfo: p.ConnInfo, - }) - } - } - - if len(errs) > 0 { - return &multierror.Error{Errors: errs} - } - return nil -} - -// grpahRemoveInvalidDeps goes through the graph and removes dependencies -// that no longer exist. -func graphRemoveInvalidDeps(g *depgraph.Graph) { - check := make(map[*depgraph.Noun]struct{}) - for _, n := range g.Nouns { - check[n] = struct{}{} - } - for _, n := range g.Nouns { - deps := n.Deps - num := len(deps) - for i := 0; i < num; i++ { - if _, ok := check[deps[i].Target]; !ok { - deps[i], deps[num-1] = deps[num-1], nil - i-- - num-- - } - } - n.Deps = deps[:num] - } -} - -// MergeConfig merges all the configurations in the proper order -// to result in the final configuration to use to configure this -// provider. -func (p *graphSharedProvider) MergeConfig( - raw bool, override map[string]interface{}) *ResourceConfig { - var rawMap map[string]interface{} - if p.Config != nil { - rawMap = p.Config.RawConfig.Config() - } - if rawMap == nil { - rawMap = make(map[string]interface{}) - } - for k, v := range override { - rawMap[k] = v - } - - // Merge in all the parent configurations - if p.Parent != nil { - parent := p.Parent - for parent != nil { - if parent.Config != nil { - var merge map[string]interface{} - if raw { - merge = parent.Config.RawConfig.Raw - } else { - merge = parent.Config.RawConfig.Config() - } - - for k, v := range merge { - rawMap[k] = v - } - } - - parent = parent.Parent - } - } - - rc, err := config.NewRawConfig(rawMap) - if err != nil { - panic("error building config: " + err.Error()) - } - - return NewResourceConfig(rc) -} - -// Expand will expand this node into a subgraph if Expand is set. -func (n *GraphNodeResource) Expand() (*depgraph.Graph, error) { - // Expand the count out, which should be interpolated at this point. - count, err := n.Config.Count() - if err != nil { - return nil, err - } - log.Printf("[DEBUG] %s: expanding to count = %d", n.Resource.Id, count) - - // TODO: can we DRY this up? - g := new(depgraph.Graph) - g.Meta = &GraphMeta{ - ModulePath: n.Resource.Info.ModulePath, - } - - // Do the initial expansion of the nodes, attaching diffs if - // applicable - n.expand(g, count, n.Diff) - - // Add all the variable dependencies - graphAddVariableDeps(g) - - // Filter the nodes depending on the expansion type - switch n.ExpandMode { - case ResourceExpandApply: - n.filterResources(g, false) - case ResourceExpandDestroy: - n.filterResources(g, true) - default: - panic(fmt.Sprintf("Unhandled expansion mode %d", n.ExpandMode)) - } - - // Return the finalized graph - return g, n.finalizeGraph(g) -} - -// expand expands this resource and adds the resources to the graph. It -// adds both create and destroy resources. -func (n *GraphNodeResource) expand(g *depgraph.Graph, count int, diff *ModuleDiff) { - // Create the list of nouns - result := make([]*depgraph.Noun, 0, count) - - // Build the key set so we know what is removed - var keys map[string]struct{} - if n.State != nil { - keys = make(map[string]struct{}) - for k, _ := range n.State.Resources { - keys[k] = struct{}{} - } - } - - // First thing, expand the counts that we have defined for our - // current config into the full set of resources that are being - // created. - r := n.Config - for i := 0; i < count; i++ { - name := r.Id() - index := -1 - - // If we have a count that is more than one, then make sure - // we suffix with the number of the resource that this is. - if count > 1 { - name = fmt.Sprintf("%s.%d", name, i) - index = i - } - - var state *ResourceState - if n.State != nil { - // Lookup the resource state - if s, ok := n.State.Resources[name]; ok { - state = s - delete(keys, name) - } - - if count == 1 { - // If the count is one, check the state for ".0" - // appended, which might exist if we go from - // count > 1 to count == 1. - k := r.Id() + ".0" - if state == nil { - state = n.State.Resources[k] - } - delete(keys, k) - } else if i == 0 { - // If count is greater than one, check for state - // with just the ID, which might exist if we go - // from count == 1 to count > 1 - if state == nil { - state = n.State.Resources[r.Id()] - } - delete(keys, r.Id()) - } - } - - // Add in the diff if we have it - var inDiff *InstanceDiff - if diff != nil { - // Looup the instance diff - if d, ok := diff.Resources[name]; ok { - inDiff = d - } - - if inDiff == nil { - if count == 1 { - // If the count is one, check the state for ".0" - // appended, which might exist if we go from - // count > 1 to count == 1. - k := r.Id() + ".0" - inDiff = diff.Resources[k] - } else if i == 0 { - // If count is greater than one, check for state - // with just the ID, which might exist if we go - // from count == 1 to count > 1 - inDiff = diff.Resources[r.Id()] - } - } - } - - // Initialize a default state if not available - if state == nil { - state = &ResourceState{ - Type: r.Type, - } - } - - // Prepare the diff if it exists - if inDiff != nil { - switch n.ExpandMode { - case ResourceExpandApply: - // Disable Destroy if we aren't doing a destroy expansion. - // There is a seperate expansion for the destruction action. - d := new(InstanceDiff) - *d = *inDiff - inDiff = d - inDiff.Destroy = false - - // If we require a new resource, there is a seperate delete - // phase, so the create phase must not have access to the ID. - if inDiff.RequiresNew() { - s := new(ResourceState) - *s = *state - state = s - state.Primary = nil - } - - case ResourceExpandDestroy: - // If we are doing a destroy, make sure it is exclusively - // a destroy, since there is a seperate expansion for the apply - inDiff = new(InstanceDiff) - inDiff.Destroy = true - - default: - panic(fmt.Sprintf("Unhandled expansion mode %d", n.ExpandMode)) - } - } - - // Inherit the existing flags! - flags := n.Resource.Flags - if len(state.Tainted) > 0 { - flags |= FlagHasTainted - } - - // Copy the base resource so we can fill it in - resource := n.copyResource(name) - resource.CountIndex = i - resource.State = state.Primary - resource.Flags = flags - resource.Diff = inDiff - - // Add the result - result = append(result, &depgraph.Noun{ - Name: name, - Meta: &GraphNodeResource{ - Index: index, - Config: r, - Resource: resource, - }, - }) - } - - // Go over the leftover keys which are orphans (decreasing counts) - for k, _ := range keys { - rs := n.State.Resources[k] - - resource := n.copyResource(k) - resource.Config = NewResourceConfig(nil) - resource.State = rs.Primary - resource.Flags = FlagOrphan - resource.Diff = &InstanceDiff{Destroy: true} - - noun := &depgraph.Noun{ - Name: k, - Meta: &GraphNodeResource{ - Index: -1, - Resource: resource, - }, - } - - result = append(result, noun) - } - - g.Nouns = append(g.Nouns, result...) -} - -// copyResource copies the Resource structure to assign to a subgraph. -func (n *GraphNodeResource) copyResource(id string) *Resource { - info := *n.Resource.Info - info.Id = id - resource := *n.Resource - resource.Id = id - resource.Info = &info - resource.Config = NewResourceConfig(n.Config.RawConfig) - resource.Diff = nil - return &resource -} - -// filterResources is used to remove resources from the sub-graph based -// on the ExpandMode. This is because there is a Destroy sub-graph, and -// Apply sub-graph, and we cannot includes the same instances in both -// sub-graphs. -func (n *GraphNodeResource) filterResources(g *depgraph.Graph, destroy bool) { - result := make([]*depgraph.Noun, 0, len(g.Nouns)) - for _, n := range g.Nouns { - rn, ok := n.Meta.(*GraphNodeResource) - if !ok { - continue - } - - if destroy { - if rn.Resource.Diff != nil && rn.Resource.Diff.Destroy { - result = append(result, n) - } - continue - } - - if rn.Resource.Flags&FlagOrphan != 0 || - rn.Resource.Diff == nil || !rn.Resource.Diff.Destroy { - result = append(result, n) - } - } - g.Nouns = result -} - -// finalizeGraph is used to ensure the generated graph is valid -func (n *GraphNodeResource) finalizeGraph(g *depgraph.Graph) error { - // Remove the dependencies that don't exist - graphRemoveInvalidDeps(g) - - // Build the root so that we have a single valid root - graphAddRoot(g) - - // Validate - if err := g.Validate(); err != nil { - return err - } - return nil -} - -// matchingPrefixes takes a resource type and a set of resource -// providers we know about by prefix and returns a list of prefixes -// that might be valid for that resource. -// -// The list returned is in the order that they should be attempted. -func matchingPrefixes( - t string, - ps map[string]ResourceProviderFactory) []string { - result := make([]string, 0, 1) - for prefix, _ := range ps { - if strings.HasPrefix(t, prefix) { - result = append(result, prefix) - } - } - - // Sort by longest first - sort.Sort(stringLenSort(result)) - - return result -} - -// stringLenSort implements sort.Interface and sorts strings in increasing -// length order. i.e. "a", "aa", "aaa" -type stringLenSort []string - -func (s stringLenSort) Len() int { - return len(s) -} - -func (s stringLenSort) Less(i, j int) bool { - return len(s[i]) < len(s[j]) -} - -func (s stringLenSort) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} diff --git a/terraform/graph_old_test.go b/terraform/graph_old_test.go deleted file mode 100644 index 4867b2215..000000000 --- a/terraform/graph_old_test.go +++ /dev/null @@ -1,1646 +0,0 @@ -package terraform - -import ( - "reflect" - "sort" - "strings" - "testing" -) - -func TestGraph_basic(t *testing.T) { - m := testModule(t, "graph-basic") - - g, err := GraphOld(&GraphOpts{Module: m}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraph_configRequired(t *testing.T) { - if _, err := GraphOld(new(GraphOpts)); err == nil { - t.Fatal("should error") - } -} - -func TestGraph_count(t *testing.T) { - m := testModule(t, "graph-count") - - g, err := GraphOld(&GraphOpts{Module: m}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphCountStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraph_countTainted(t *testing.T) { - m := testModule(t, "graph-count") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: []string{"root"}, - Resources: map[string]*ResourceState{ - "aws_instance.web.0": &ResourceState{ - Type: "aws_instance", - Tainted: []*InstanceState{ - &InstanceState{ - ID: "foo", - }, - }, - }, - }, - }, - }, - } - - g, err := GraphOld(&GraphOpts{Module: m, State: state}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphCountTaintedStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraph_varResource(t *testing.T) { - m := testModule(t, "graph-count-var-resource") - - g, err := GraphOld(&GraphOpts{Module: m}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphCountVarResourceStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraph_cycle(t *testing.T) { - m := testModule(t, "graph-cycle") - - _, err := GraphOld(&GraphOpts{Module: m}) - if err == nil { - t.Fatal("should error") - } -} - -func TestGraph_dependsOn(t *testing.T) { - m := testModule(t, "graph-depends-on") - - g, err := GraphOld(&GraphOpts{Module: m}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphDependsStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraph_dependsOnCount(t *testing.T) { - m := testModule(t, "graph-depends-on-count") - - g, err := GraphOld(&GraphOpts{Module: m}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphDependsCountStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraph_dependsOnWithOrphan(t *testing.T) { - m := testModule(t, "graph-depends-on") - - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: []string{"root"}, - Resources: map[string]*ResourceState{ - "aws_instance.old": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, - }, - } - - g, err := GraphOld(&GraphOpts{Module: m, State: state}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphDependsOrphanStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraph_modules(t *testing.T) { - m := testModule(t, "graph-modules") - - g, err := GraphOld(&GraphOpts{Module: m}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphModulesStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } - - n := g.Noun("module.consul") - if n == nil { - t.Fatal("can't find noun") - } - mn := n.Meta.(*GraphNodeModule) - - if !reflect.DeepEqual(mn.Path, []string{"root", "consul"}) { - t.Fatalf("bad: %#v", mn.Path) - } - - actual = strings.TrimSpace(mn.Graph.String()) - expected = strings.TrimSpace(testTerraformGraphModulesConsulStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraph_moduleOrphan(t *testing.T) { - m := testModule(t, "graph-module-orphan") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: []string{"root", "consul"}, - - Resources: map[string]*ResourceState{ - "aws_instance.old": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, - }, - } - - g, err := GraphOld(&GraphOpts{Module: m, State: state}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphModuleOrphanStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } - - n := g.Noun("module.consul") - if n == nil { - t.Fatal("can't find noun") - } - mn := n.Meta.(*GraphNodeModule) - - if !reflect.DeepEqual(mn.Path, []string{"root", "consul"}) { - t.Fatalf("bad: %#v", mn.Path) - } - - actual = strings.TrimSpace(mn.Graph.String()) - expected = strings.TrimSpace(testTerraformGraphModuleOrphanConsulStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraph_providerPrune(t *testing.T) { - m := testModule(t, "graph-provider-prune") - - g, err := GraphOld(&GraphOpts{Module: m}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphProviderPruneStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraph_state(t *testing.T) { - m := testModule(t, "graph-basic") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - - Resources: map[string]*ResourceState{ - "aws_instance.old": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, - }, - } - - g, err := GraphOld(&GraphOpts{Module: m, State: state}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphStateStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraph_tainted(t *testing.T) { - m := testModule(t, "graph-tainted") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - Tainted: []*InstanceState{ - &InstanceState{ - ID: "bar", - }, - }, - }, - }, - }, - }, - } - - g, err := GraphOld(&GraphOpts{Module: m, State: state}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphTaintedStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraph_taintedMulti(t *testing.T) { - m := testModule(t, "graph-tainted") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - Tainted: []*InstanceState{ - &InstanceState{ - ID: "bar", - }, - &InstanceState{ - ID: "baz", - }, - }, - }, - }, - }, - }, - } - - g, err := GraphOld(&GraphOpts{Module: m, State: state}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphTaintedMultiStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraphFull(t *testing.T) { - rpAws := new(MockResourceProvider) - rpOS := new(MockResourceProvider) - - rpAws.ResourcesReturn = []ResourceType{ - ResourceType{Name: "aws_instance"}, - ResourceType{Name: "aws_load_balancer"}, - ResourceType{Name: "aws_security_group"}, - } - rpOS.ResourcesReturn = []ResourceType{ - ResourceType{Name: "openstack_floating_ip"}, - } - - ps := map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(rpAws), - "open": testProviderFuncFixed(rpOS), - } - - m := testModule(t, "graph-basic") - g, err := GraphOld(&GraphOpts{Module: m, Providers: ps}) - if err != nil { - t.Fatalf("err: %s", err) - } - - // A helper to help get us the provider for a resource. - graphProvider := func(n string) ResourceProvider { - return g.Noun(n).Meta.(*GraphNodeResource).Resource.Provider - } - - // Test a couple - if graphProvider("aws_instance.web") != rpAws { - t.Fatalf("bad: %#v", graphProvider("aws_instance.web")) - } - if graphProvider("openstack_floating_ip.random") != rpOS { - t.Fatalf("bad: %#v", graphProvider("openstack_floating_ip.random")) - } - - // Test that all providers have been set - for _, n := range g.Nouns { - switch m := n.Meta.(type) { - case *GraphNodeResource: - if m.Resource.Provider == nil { - t.Fatalf("bad: %#v", m) - } - case *GraphNodeResourceProvider: - if len(m.Provider.Providers) == 0 { - t.Fatalf("bad: %#v", m) - } - default: - continue - } - } -} - -func TestGraphProvisioners(t *testing.T) { - rpAws := new(MockResourceProvider) - provShell := new(MockResourceProvisioner) - provWinRM := new(MockResourceProvisioner) - - rpAws.ResourcesReturn = []ResourceType{ - ResourceType{Name: "aws_instance"}, - ResourceType{Name: "aws_load_balancer"}, - ResourceType{Name: "aws_security_group"}, - } - - ps := map[string]ResourceProvisionerFactory{ - "shell": testProvisionerFuncFixed(provShell), - "winrm": testProvisionerFuncFixed(provWinRM), - } - - pf := map[string]ResourceProviderFactory{ - "aws": testProviderFuncFixed(rpAws), - } - - m := testModule(t, "graph-provisioners") - g, err := GraphOld(&GraphOpts{Module: m, Providers: pf, Provisioners: ps}) - if err != nil { - t.Fatalf("err: %s", err) - } - - // A helper to help get us the provider for a resource. - graphProvisioner := func(n string, idx int) *ResourceProvisionerConfig { - return g.Noun(n).Meta.(*GraphNodeResource).Resource.Provisioners[idx] - } - - // A helper to verify depedencies - depends := func(a, b string) bool { - aNoun := g.Noun(a) - bNoun := g.Noun(b) - for _, dep := range aNoun.Deps { - if dep.Source == aNoun && dep.Target == bNoun { - return true - } - } - return false - } - - // Test a couple - prov := graphProvisioner("aws_instance.web", 0) - if prov.Provisioner != provWinRM { - t.Fatalf("bad: %#v", prov) - } - if prov.RawConfig.Config()["cmd"] != "echo foo" { - t.Fatalf("bad: %#v", prov) - } - - prov = graphProvisioner("aws_instance.web", 1) - if prov.Provisioner != provWinRM { - t.Fatalf("bad: %#v", prov) - } - if prov.RawConfig.Config()["cmd"] != "echo bar" { - t.Fatalf("bad: %#v", prov) - } - - prov = graphProvisioner("aws_load_balancer.weblb", 0) - if prov.Provisioner != provShell { - t.Fatalf("bad: %#v", prov) - } - if prov.RawConfig.Config()["cmd"] != "add ${aws_instance.web.id}" { - t.Fatalf("bad: %#v", prov) - } - if prov.ConnInfo == nil || len(prov.ConnInfo.Raw) != 2 { - t.Fatalf("bad: %#v", prov) - } - - // Check that the variable dependency is handled - if !depends("aws_load_balancer.weblb", "aws_instance.web") { - t.Fatalf("missing dependency from provisioner variable") - } - - // Check that the connection variable dependency is handled - if !depends("aws_load_balancer.weblb", "aws_security_group.firewall") { - t.Fatalf("missing dependency from provisioner connection") - } -} - -func TestGraphAddDiff(t *testing.T) { - m := testModule(t, "graph-diff") - diff := &Diff{ - Modules: []*ModuleDiff{ - &ModuleDiff{ - Path: rootModulePath, - Resources: map[string]*InstanceDiff{ - "aws_instance.foo": &InstanceDiff{ - Attributes: map[string]*ResourceAttrDiff{ - "foo": &ResourceAttrDiff{ - New: "bar", - }, - }, - }, - }, - }, - }, - } - - g, err := GraphOld(&GraphOpts{Module: m, Diff: diff}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphDiffStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } - - /* - TODO: test this somewhere - // Verify that the state has been added - n := g.Noun("aws_instance.foo") - rn := n.Meta.(*GraphNodeResource) - - expected2 := diff.RootModule().Resources["aws_instance.foo"] - actual2 := rn.Resource.Diff - if !reflect.DeepEqual(actual2, expected2) { - t.Fatalf("bad: %#v", actual2) - } - */ -} - -func TestGraphAddDiff_destroy(t *testing.T) { - m := testModule(t, "graph-diff-destroy") - diff := &Diff{ - Modules: []*ModuleDiff{ - &ModuleDiff{ - Path: rootModulePath, - Resources: map[string]*InstanceDiff{ - "aws_instance.foo": &InstanceDiff{ - Destroy: true, - }, - "aws_instance.bar": &InstanceDiff{ - Destroy: true, - }, - }, - }, - }, - } - 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{"foo"}, - Primary: &InstanceState{ - ID: "bar", - }, - }, - }, - }, - }, - } - - diffHash := checksumStruct(t, diff) - - g, err := GraphOld(&GraphOpts{ - Module: m, - Diff: diff, - State: state, - }) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphDiffDestroyStr) - if actual != expected { - t.Fatalf("bad:\n\n%s\n\nexpected:\n\n%s", actual, expected) - } - - // Verify that the state has been added - n := g.Noun("aws_instance.foo (destroy)") - rn := n.Meta.(*GraphNodeResource) - - expected2 := &InstanceDiff{Destroy: true} - actual2 := rn.Resource.Diff - if !reflect.DeepEqual(actual2, expected2) { - t.Fatalf("bad: %#v", actual2) - } - - // Verify that our original structure has not been modified - diffHash2 := checksumStruct(t, diff) - if diffHash != diffHash2 { - t.Fatal("diff has been modified") - } -} - -func TestGraphAddDiff_destroy_counts(t *testing.T) { - m := testModule(t, "graph-count") - diff := &Diff{ - Modules: []*ModuleDiff{ - &ModuleDiff{ - Path: rootModulePath, - Resources: map[string]*InstanceDiff{ - "aws_instance.web.0": &InstanceDiff{ - Destroy: true, - }, - "aws_instance.web.1": &InstanceDiff{ - Destroy: true, - }, - "aws_instance.web.2": &InstanceDiff{ - Destroy: true, - }, - "aws_load_balancer.weblb": &InstanceDiff{ - Destroy: true, - }, - }, - }, - }, - } - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.web.0": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - "aws_instance.web.1": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - "aws_instance.web.2": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - "aws_load_balancer.weblb": &ResourceState{ - Type: "aws_load_balancer", - Dependencies: []string{"aws_instance.web.0", "aws_instance.web.1", "aws_instance.web.2"}, - Primary: &InstanceState{ - ID: "bar", - }, - }, - }, - }, - }, - } - - diffHash := checksumStruct(t, diff) - - g, err := GraphOld(&GraphOpts{ - Module: m, - Diff: diff, - State: state, - }) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphDiffDestroyCountsStr) - if actual != expected { - t.Fatalf("bad:\n\n%s\n\nexpected:\n\n%s", actual, expected) - } - - // Verify that the state has been added - n := g.Noun("aws_instance.web (destroy)") - rn := n.Meta.(*GraphNodeResource) - - if rn.ExpandMode != ResourceExpandDestroy { - t.Fatalf("bad: %#v", rn) - } - - // Verify that our original structure has not been modified - diffHash2 := checksumStruct(t, diff) - if diffHash != diffHash2 { - t.Fatal("diff has been modified") - } -} - -func TestGraphAddDiff_module(t *testing.T) { - m := testModule(t, "graph-diff-module") - diff := &Diff{ - Modules: []*ModuleDiff{ - &ModuleDiff{ - Path: rootModulePath, - Resources: map[string]*InstanceDiff{ - "aws_instance.foo": &InstanceDiff{ - Destroy: true, - }, - }, - }, - }, - } - - g, err := GraphOld(&GraphOpts{Module: m, Diff: diff}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphDiffModuleStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraphAddDiff_module_depends(t *testing.T) { - m := testModule(t, "graph-diff-module-dep") - diff := &Diff{ - Modules: []*ModuleDiff{ - &ModuleDiff{ - Path: rootModulePath, - Resources: map[string]*InstanceDiff{ - "aws_instance.foo": &InstanceDiff{ - Destroy: true, - }, - }, - }, - &ModuleDiff{ - Path: []string{"root", "child"}, - Destroy: true, - Resources: map[string]*InstanceDiff{ - "aws_instance.foo": &InstanceDiff{ - Destroy: true, - }, - }, - }, - }, - } - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: []string{"root", "orphan"}, - Resources: map[string]*ResourceState{ - "aws_instance.dead": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "dead", - }, - }, - }, - Dependencies: []string{ - "aws_instance.foo", - "module.child", - }, - }, - }, - } - - g, err := GraphOld(&GraphOpts{Module: m, Diff: diff, State: state}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphDiffModuleDependsStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraphAddDiff_moduleDependsModule(t *testing.T) { - m := testModule(t, "graph-diff-module-dep-module") - diff := &Diff{ - Modules: []*ModuleDiff{ - &ModuleDiff{ - Path: []string{"root"}, - Destroy: true, - }, - &ModuleDiff{ - Path: []string{"root", "foo"}, - Destroy: true, - Resources: map[string]*InstanceDiff{ - "aws_instance.foo": &InstanceDiff{ - Destroy: true, - }, - }, - }, - &ModuleDiff{ - Path: []string{"root", "bar"}, - Destroy: true, - Resources: map[string]*InstanceDiff{ - "aws_instance.foo": &InstanceDiff{ - Destroy: true, - }, - }, - }, - }, - } - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: []string{"root", "foo"}, - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, - - &ModuleState{ - Path: []string{"root", "bar"}, - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - Dependencies: []string{ - "module.foo", - }, - }, - }, - } - - g, err := GraphOld(&GraphOpts{Module: m, Diff: diff, State: state}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphDiffModuleDependsModuleStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraphAddDiff_createBeforeDestroy(t *testing.T) { - m := testModule(t, "graph-diff-create-before") - diff := &Diff{ - Modules: []*ModuleDiff{ - &ModuleDiff{ - Path: rootModulePath, - Resources: map[string]*InstanceDiff{ - "aws_instance.bar": &InstanceDiff{ - Destroy: true, - Attributes: map[string]*ResourceAttrDiff{ - "ami": &ResourceAttrDiff{ - Old: "abc", - New: "xyz", - RequiresNew: true, - }, - }, - }, - }, - }, - }, - } - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.bar": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "ami": "abc", - }, - }, - }, - }, - }, - }, - } - - diffHash := checksumStruct(t, diff) - - g, err := GraphOld(&GraphOpts{ - Module: m, - Diff: diff, - State: state, - }) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphDiffCreateBeforeDestroyStr) - if actual != expected { - t.Fatalf("bad:\n\n%s\n\nexpected:\n\n%s", actual, expected) - } - - // Verify the flags are set - r := g.Noun("aws_instance.bar") - if r.Meta.(*GraphNodeResource).Resource.Flags&FlagReplacePrimary == 0 { - t.Fatalf("missing FlagReplacePrimary") - } - - r = g.Noun("aws_instance.bar (destroy)") - if r.Meta.(*GraphNodeResource).Resource.Flags&FlagDeposed == 0 { - t.Fatalf("missing FlagDeposed") - } - - // Verify that our original structure has not been modified - diffHash2 := checksumStruct(t, diff) - if diffHash != diffHash2 { - t.Fatal("diff has been modified") - } -} - -func TestGraphAddDiff_moduleDestroy(t *testing.T) { - m := testModule(t, "graph-diff-module") - diff := &Diff{ - Modules: []*ModuleDiff{ - &ModuleDiff{ - Path: rootModulePath, - Resources: map[string]*InstanceDiff{ - "aws_instance.foo": &InstanceDiff{ - Destroy: true, - }, - }, - }, - &ModuleDiff{ - Path: []string{"root", "child"}, - Resources: map[string]*InstanceDiff{ - "aws_instance.foo": &InstanceDiff{ - Destroy: true, - }, - }, - }, - }, - } - - g, err := GraphOld(&GraphOpts{Module: m, Diff: diff}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphDiffModuleStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestGraphEncodeDependencies(t *testing.T) { - m := testModule(t, "graph-basic") - - g, err := GraphOld(&GraphOpts{Module: m}) - if err != nil { - t.Fatalf("err: %s", err) - } - - // This should encode the dependency information into the state - graphEncodeDependencies(g) - - web := g.Noun("aws_instance.web").Meta.(*GraphNodeResource).Resource - if len(web.Dependencies) != 1 || web.Dependencies[0] != "aws_security_group.firewall" { - t.Fatalf("bad: %#v", web) - } - - weblb := g.Noun("aws_load_balancer.weblb").Meta.(*GraphNodeResource).Resource - if len(weblb.Dependencies) != 1 || weblb.Dependencies[0] != "aws_instance.web" { - t.Fatalf("bad: %#v", weblb) - } -} - -func TestGraphEncodeDependencies_count(t *testing.T) { - m := testModule(t, "graph-count") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: map[string]*ResourceState{ - "aws_instance.web.0": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - "aws_load_balancer.weblb": &ResourceState{ - Type: "aws_load_balancer", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, - }, - } - - g, err := GraphOld(&GraphOpts{Module: m, State: state}) - if err != nil { - t.Fatalf("err: %s", err) - } - - // This should encode the dependency information into the state - graphEncodeDependencies(g) - - web := g.Noun("aws_instance.web").Meta.(*GraphNodeResource).Resource - if len(web.Dependencies) != 0 { - t.Fatalf("bad: %#v", web) - } - - weblb := g.Noun("aws_load_balancer.weblb").Meta.(*GraphNodeResource).Resource - if len(weblb.Dependencies) != 1 { - t.Fatalf("bad: %#v", weblb) - } -} - -func TestGraphEncodeDependencies_module(t *testing.T) { - m := testModule(t, "graph-modules") - - g, err := GraphOld(&GraphOpts{Module: m, State: &State{}}) - if err != nil { - t.Fatalf("err: %s", err) - } - - // This should encode the dependency information into the state - graphEncodeDependencies(g) - - web := g.Noun("aws_instance.web").Meta.(*GraphNodeResource).Resource - sort.Strings(web.Dependencies) - if len(web.Dependencies) != 2 { - t.Fatalf("bad: %#v", web) - } - if web.Dependencies[0] != "aws_security_group.firewall" { - t.Fatalf("bad: %#v", web) - } - if web.Dependencies[1] != "module.consul" { - t.Fatalf("bad: %#v", web) - } - - mod := g.Noun("module.consul").Meta.(*GraphNodeModule) - deps := mod.State.Dependencies - if len(deps) != 1 { - t.Fatalf("Bad: %#v", deps) - } - if deps[0] != "aws_security_group.firewall" { - t.Fatalf("Bad: %#v", deps) - } -} - -func TestGraph_orphan_dependencies(t *testing.T) { - m := testModule(t, "graph-count") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - - Resources: map[string]*ResourceState{ - "aws_instance.web.0": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - "aws_instance.web.1": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - "aws_load_balancer.old": &ResourceState{ - Type: "aws_load_balancer", - Primary: &InstanceState{ - ID: "foo", - }, - Dependencies: []string{ - "aws_instance.web.0", - "aws_instance.web.1", - "aws_instance.web.2", - }, - }, - }, - }, - }, - } - - g, err := GraphOld(&GraphOpts{Module: m, State: state}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphCountOrphanStr) - if actual != expected { - t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\n%s", actual, expected) - } -} - -func TestGraph_orphanDependenciesModules(t *testing.T) { - m := testModule(t, "graph-modules") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - Dependencies: []string{ - "module.consul", - }, - }, - }, - }, - }, - } - - g, err := GraphOld(&GraphOpts{Module: m, State: state}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphOrphanModuleDepsStr) - if actual != expected { - t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\n%s", actual, expected) - } -} - -func TestGraph_orphanModules_Dependencies(t *testing.T) { - m := testModule(t, "graph-modules") - state := &State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - - Resources: map[string]*ResourceState{ - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - Dependencies: []string{ - "module.consul", - }, - }, - }, - }, - - // Add an orphan module - &ModuleState{ - Path: []string{"root", "orphan"}, - Resources: map[string]*ResourceState{ - "aws_instance.bar": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "bar", - }, - }, - }, - Dependencies: []string{ - "aws_instance.foo", - "aws_instance.web", - }, - }, - }, - } - - g, err := GraphOld(&GraphOpts{Module: m, State: state}) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphOrphanedModuleDepsStr) - if actual != expected { - t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\n%s", actual, expected) - } -} - -func TestGraphNodeResourceExpand(t *testing.T) { - m := testModule(t, "graph-resource-expand") - - g, err := GraphOld(&GraphOpts{Module: m}) - if err != nil { - t.Fatalf("err: %s", err) - } - - // Get the resource we care about expanding - n := g.Noun("aws_instance.web") - if n == nil { - t.Fatal("could not find") - } - rn := n.Meta.(*GraphNodeResource) - - g, err = rn.Expand() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphResourceExpandStr) - if actual != expected { - t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\n%s", actual, expected) - } -} - -func TestGraphNodeResourceExpand_provDeps(t *testing.T) { - m := testModule(t, "graph-resource-expand-prov-deps") - provs := map[string]ResourceProvisionerFactory{ - "remote-exec": func() (ResourceProvisioner, error) { - return new(MockResourceProvisioner), nil - }, - } - - g, err := GraphOld(&GraphOpts{Module: m, Provisioners: provs}) - if err != nil { - t.Fatalf("err: %s", err) - } - - // Get the resource we care about expanding - n := g.Noun("aws_instance.web") - if n == nil { - t.Fatal("could not find") - } - rn := n.Meta.(*GraphNodeResource) - - g, err = rn.Expand() - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTerraformGraphResourceExpandProvDepsStr) - if actual != expected { - t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\n%s", actual, expected) - } -} - -const testTerraformGraphStr = ` -root: root -aws_instance.web - aws_instance.web -> aws_security_group.firewall - aws_instance.web -> provider.aws -aws_load_balancer.weblb - aws_load_balancer.weblb -> aws_instance.web - aws_load_balancer.weblb -> provider.aws -aws_security_group.firewall - aws_security_group.firewall -> provider.aws -openstack_floating_ip.random -provider.aws - provider.aws -> openstack_floating_ip.random -root - root -> aws_instance.web - root -> aws_load_balancer.weblb - root -> aws_security_group.firewall - root -> openstack_floating_ip.random -` - -const testTerraformGraphCountStr = ` -root: root -aws_instance.web -aws_load_balancer.weblb - aws_load_balancer.weblb -> aws_instance.web -root - root -> aws_instance.web - root -> aws_load_balancer.weblb -` - -const testTerraformGraphCountTaintedStr = ` -root: root -aws_instance.web - aws_instance.web -> aws_instance.web.0 (tainted #1) -aws_instance.web.0 (tainted #1) -aws_load_balancer.weblb - aws_load_balancer.weblb -> aws_instance.web -root - root -> aws_instance.web - root -> aws_instance.web.0 (tainted #1) - root -> aws_load_balancer.weblb -` - -const testTerraformGraphCountVarResourceStr = ` -root: root -aws_instance.foo -aws_instance.web - aws_instance.web -> aws_instance.foo -aws_load_balancer.weblb - aws_load_balancer.weblb -> aws_instance.web -root - root -> aws_instance.foo - root -> aws_instance.web - root -> aws_load_balancer.weblb -` - -const testTerraformGraphDependsStr = ` -root: root -aws_instance.db - aws_instance.db -> aws_instance.web -aws_instance.web -root - root -> aws_instance.db - root -> aws_instance.web -` - -const testTerraformGraphDependsCountStr = ` -root: root -aws_instance.db - aws_instance.db -> aws_instance.web -aws_instance.web -root - root -> aws_instance.db - root -> aws_instance.web -` - -const testTerraformGraphDependsOrphanStr = ` -root: root -aws_instance.db - aws_instance.db -> aws_instance.web -aws_instance.old -aws_instance.web -root - root -> aws_instance.db - root -> aws_instance.old - root -> aws_instance.web -` - -const testTerraformGraphDiffStr = ` -root: root -aws_instance.foo -root - root -> aws_instance.foo -` - -const testTerraformGraphDiffDestroyStr = ` -root: root -aws_instance.bar - aws_instance.bar -> aws_instance.bar (destroy) - aws_instance.bar -> aws_instance.foo - aws_instance.bar -> provider.aws -aws_instance.bar (destroy) - aws_instance.bar (destroy) -> provider.aws -aws_instance.foo - aws_instance.foo -> aws_instance.foo (destroy) - aws_instance.foo -> provider.aws -aws_instance.foo (destroy) - aws_instance.foo (destroy) -> aws_instance.bar (destroy) - aws_instance.foo (destroy) -> provider.aws -provider.aws -root - root -> aws_instance.bar - root -> aws_instance.foo -` - -const testTerraformGraphDiffDestroyCountsStr = ` -root: root -aws_instance.web - aws_instance.web -> aws_instance.web (destroy) -aws_instance.web (destroy) - aws_instance.web (destroy) -> aws_load_balancer.weblb (destroy) -aws_load_balancer.weblb - aws_load_balancer.weblb -> aws_instance.web - aws_load_balancer.weblb -> aws_load_balancer.weblb (destroy) -aws_load_balancer.weblb (destroy) -root - root -> aws_instance.web - root -> aws_load_balancer.weblb -` - -const testTerraformGraphDiffModuleStr = ` -root: root -aws_instance.foo - aws_instance.foo -> aws_instance.foo (destroy) - aws_instance.foo -> module.child -aws_instance.foo (destroy) -module.child - module.child -> aws_instance.foo (destroy) -root - root -> aws_instance.foo - root -> module.child -` - -const testTerraformGraphDiffModuleDependsStr = ` -root: root -aws_instance.foo - aws_instance.foo -> aws_instance.foo (destroy) -aws_instance.foo (destroy) - aws_instance.foo (destroy) -> module.child - aws_instance.foo (destroy) -> module.orphan -module.child - module.child -> module.orphan -module.orphan -root - root -> aws_instance.foo - root -> module.child - root -> module.orphan -` - -const testTerraformGraphDiffModuleDependsModuleStr = ` -root: root -module.bar -module.foo - module.foo -> module.bar -root - root -> module.bar - root -> module.foo -` - -const testTerraformGraphModulesStr = ` -root: root -aws_instance.web - aws_instance.web -> aws_security_group.firewall - aws_instance.web -> module.consul - aws_instance.web -> provider.aws -aws_security_group.firewall - aws_security_group.firewall -> provider.aws -module.consul - module.consul -> aws_security_group.firewall - module.consul -> provider.aws -provider.aws -root - root -> aws_instance.web - root -> aws_security_group.firewall - root -> module.consul -` - -const testTerraformGraphModulesConsulStr = ` -root: root -aws_instance.server - aws_instance.server -> provider.aws -provider.aws -root - root -> aws_instance.server -` - -const testTerraformGraphModuleOrphanStr = ` -root: root -aws_instance.web - aws_instance.web -> aws_security_group.firewall - aws_instance.web -> provider.aws -aws_security_group.firewall - aws_security_group.firewall -> provider.aws -module.consul - module.consul -> provider.aws -provider.aws -root - root -> aws_instance.web - root -> aws_security_group.firewall - root -> module.consul -` - -const testTerraformGraphModuleOrphanConsulStr = ` -root: root -aws_instance.old - aws_instance.old -> provider.aws -provider.aws -root - root -> aws_instance.old -` - -const testTerraformGraphProviderPruneStr = ` -root: root -aws_load_balancer.weblb - aws_load_balancer.weblb -> provider.aws -provider.aws -root - root -> aws_load_balancer.weblb` - -const testTerraformGraphDiffCreateBeforeDestroyStr = ` -root: root -aws_instance.bar - aws_instance.bar -> provider.aws -aws_instance.bar (destroy) - aws_instance.bar (destroy) -> aws_instance.bar - aws_instance.bar (destroy) -> provider.aws -provider.aws -root - root -> aws_instance.bar - root -> aws_instance.bar (destroy)` - -const testTerraformGraphStateStr = ` -root: root -aws_instance.old - aws_instance.old -> provider.aws -aws_instance.web - aws_instance.web -> aws_security_group.firewall - aws_instance.web -> provider.aws -aws_load_balancer.weblb - aws_load_balancer.weblb -> aws_instance.web - aws_load_balancer.weblb -> provider.aws -aws_security_group.firewall - aws_security_group.firewall -> provider.aws -openstack_floating_ip.random -provider.aws - provider.aws -> openstack_floating_ip.random -root - root -> aws_instance.old - root -> aws_instance.web - root -> aws_load_balancer.weblb - root -> aws_security_group.firewall - root -> openstack_floating_ip.random -` - -const testTerraformGraphTaintedStr = ` -root: root -aws_instance.web - aws_instance.web -> aws_instance.web (tainted #1) - aws_instance.web -> aws_security_group.firewall - aws_instance.web -> provider.aws -aws_instance.web (tainted #1) - aws_instance.web (tainted #1) -> provider.aws -aws_security_group.firewall - aws_security_group.firewall -> provider.aws -provider.aws -root - root -> aws_instance.web - root -> aws_instance.web (tainted #1) - root -> aws_security_group.firewall -` - -const testTerraformGraphTaintedMultiStr = ` -root: root -aws_instance.web - aws_instance.web -> aws_instance.web (tainted #1) - aws_instance.web -> aws_instance.web (tainted #2) - aws_instance.web -> aws_security_group.firewall - aws_instance.web -> provider.aws -aws_instance.web (tainted #1) - aws_instance.web (tainted #1) -> provider.aws -aws_instance.web (tainted #2) - aws_instance.web (tainted #2) -> provider.aws -aws_security_group.firewall - aws_security_group.firewall -> provider.aws -provider.aws -root - root -> aws_instance.web - root -> aws_instance.web (tainted #1) - root -> aws_instance.web (tainted #2) - root -> aws_security_group.firewall -` - -const testTerraformGraphCountOrphanStr = ` -root: root -aws_instance.web -aws_load_balancer.old - aws_load_balancer.old -> aws_instance.web -aws_load_balancer.weblb - aws_load_balancer.weblb -> aws_instance.web -root - root -> aws_instance.web - root -> aws_load_balancer.old - root -> aws_load_balancer.weblb -` - -const testTerraformGraphOrphanModuleDepsStr = ` -root: root -aws_instance.foo - aws_instance.foo -> module.consul - aws_instance.foo -> provider.aws -aws_instance.web - aws_instance.web -> aws_security_group.firewall - aws_instance.web -> module.consul - aws_instance.web -> provider.aws -aws_security_group.firewall - aws_security_group.firewall -> provider.aws -module.consul - module.consul -> aws_security_group.firewall - module.consul -> provider.aws -provider.aws -root - root -> aws_instance.foo - root -> aws_instance.web - root -> aws_security_group.firewall - root -> module.consul -` - -const testTerraformGraphOrphanedModuleDepsStr = ` -root: root -aws_instance.foo - aws_instance.foo -> module.consul - aws_instance.foo -> provider.aws -aws_instance.web - aws_instance.web -> aws_security_group.firewall - aws_instance.web -> module.consul - aws_instance.web -> provider.aws -aws_security_group.firewall - aws_security_group.firewall -> provider.aws -module.consul - module.consul -> aws_security_group.firewall - module.consul -> provider.aws -module.orphan - module.orphan -> aws_instance.foo - module.orphan -> aws_instance.web - module.orphan -> provider.aws -provider.aws -root - root -> aws_instance.foo - root -> aws_instance.web - root -> aws_security_group.firewall - root -> module.consul - root -> module.orphan -` - -const testTerraformGraphResourceExpandStr = ` -root: root -aws_instance.web.0 -aws_instance.web.1 -aws_instance.web.2 -root - root -> aws_instance.web.0 - root -> aws_instance.web.1 - root -> aws_instance.web.2 -` - -const testTerraformGraphResourceExpandProvDepsStr = ` -root: root -aws_instance.web.0 -aws_instance.web.1 - aws_instance.web.1 -> aws_instance.web.0 -aws_instance.web.2 - aws_instance.web.2 -> aws_instance.web.0 -root - root -> aws_instance.web.0 - root -> aws_instance.web.1 - root -> aws_instance.web.2 -`