{"action":"created","comment":{"url":"https://api.github.com/repos/hashicorp/terraform/pulls/comments/29605711","id":29605711,"diff_hunk":"@@ -0,0 +1,230 @@\n+package terraform\n+\n+import (\n+\t\"fmt\"\n+\t\"strings\"\n+\n+\t\"github.com/hashicorp/terraform/config\"\n+\t\"github.com/hashicorp/terraform/config/module\"\n+\t\"github.com/hashicorp/terraform/dag\"\n+\t\"github.com/hashicorp/terraform/dot\"\n+)\n+\n+// GraphNodeConfigModule represents a module within the configuration graph.\n+type GraphNodeConfigModule struct {\n+\tPath   []string\n+\tModule *config.Module\n+\tTree   *module.Tree\n+}\n+\n+func (n *GraphNodeConfigModule) ConfigType() GraphNodeConfigType {\n+\treturn GraphNodeConfigTypeModule\n+}\n+\n+func (n *GraphNodeConfigModule) DependableName() []string {\n+\tconfig := n.Tree.Config()\n+\n+\tresult := make([]string, 1, len(config.Outputs)+1)\n+\tresult[0] = n.Name()\n+\tfor _, o := range config.Outputs {\n+\t\tresult = append(result, fmt.Sprintf(\"%s.output.%s\", n.Name(), o.Name))\n+\t}\n+\n+\treturn result\n+}\n+\n+func (n *GraphNodeConfigModule) DependentOn() []string {\n+\tvars := n.Module.RawConfig.Variables\n+\tresult := make([]string, 0, len(vars))\n+\tfor _, v := range vars {\n+\t\tif vn := varNameForVar(v); vn != \"\" {\n+\t\t\tresult = append(result, vn)\n+\t\t}\n+\t}\n+\n+\treturn result\n+}\n+\n+func (n *GraphNodeConfigModule) Name() string {\n+\treturn fmt.Sprintf(\"module.%s\", n.Module.Name)\n+}\n+\n+// GraphNodeExpandable\n+func (n *GraphNodeConfigModule) Expand(b GraphBuilder) (GraphNodeSubgraph, error) {\n+\t// Build the graph first\n+\tgraph, err := b.Build(n.Path)\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\n+\t// Add the parameters node to the module\n+\tt := &ModuleInputTransformer{Variables: make(map[string]string)}\n+\tif err := t.Transform(graph); err != nil {\n+\t\treturn nil, err\n+\t}\n+\n+\t{\n+\t\t// Add the destroy marker to the graph\n+\t\tt := &ModuleDestroyTransformer{}\n+\t\tif err := t.Transform(graph); err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\t}\n+\n+\t// Build the actual subgraph node\n+\treturn &graphNodeModuleExpanded{\n+\t\tOriginal:  n,\n+\t\tGraph:     graph,\n+\t\tVariables: t.Variables,\n+\t}, nil\n+}\n+\n+// GraphNodeExpandable\n+func (n *GraphNodeConfigModule) ProvidedBy() []string {\n+\t// Build up the list of providers by simply going over our configuration\n+\t// to find the providers that are configured there as well as the\n+\t// providers that the resources use.\n+\tconfig := n.Tree.Config()\n+\tproviders := make(map[string]struct{})\n+\tfor _, p := range config.ProviderConfigs {\n+\t\tproviders[p.Name] = struct{}{}\n+\t}\n+\tfor _, r := range config.Resources {\n+\t\tproviders[resourceProvider(r.Type, r.Provider)] = struct{}{}\n+\t}\n+\n+\t// Turn the map into a string. This makes sure that the list is\n+\t// de-dupped since we could be going over potentially many resources.\n+\tresult := make([]string, 0, len(providers))\n+\tfor p, _ := range providers {\n+\t\tresult = append(result, p)\n+\t}\n+\n+\treturn result\n+}\n+\n+// graphNodeModuleExpanded represents a module where the graph has\n+// been expanded. It stores the graph of the module as well as a reference\n+// to the map of variables.\n+type graphNodeModuleExpanded struct {\n+\tOriginal *GraphNodeConfigModule\n+\tGraph    *Graph\n+\n+\t// Variables is a map of the input variables. This reference should\n+\t// be shared with ModuleInputTransformer in order to create a connection\n+\t// where the variables are set properly.\n+\tVariables map[string]string\n+}\n+\n+func (n *graphNodeModuleExpanded) Name() string {\n+\treturn fmt.Sprintf(\"%s (expanded)\", dag.VertexName(n.Original))\n+}\n+\n+func (n *graphNodeModuleExpanded) ConfigType() GraphNodeConfigType {\n+\treturn GraphNodeConfigTypeModule\n+}\n+\n+// GraphNodeDependable\n+func (n *graphNodeModuleExpanded) DependableName() []string {\n+\treturn n.Original.DependableName()\n+}\n+\n+// GraphNodeDependent\n+func (n *graphNodeModuleExpanded) DependentOn() []string {\n+\treturn n.Original.DependentOn()\n+}\n+\n+// GraphNodeDotter impl.\n+func (n *graphNodeModuleExpanded) DotNode(name string, opts *GraphDotOpts) *dot.Node {\n+\treturn dot.NewNode(name, map[string]string{\n+\t\t\"label\": dag.VertexName(n.Original),\n+\t\t\"shape\": \"component\",\n+\t})\n+}\n+\n+// GraphNodeEvalable impl.\n+func (n *graphNodeModuleExpanded) EvalTree() EvalNode {\n+\tvar resourceConfig *ResourceConfig\n+\treturn &EvalSequence{\n+\t\tNodes: []EvalNode{\n+\t\t\t&EvalInterpolate{\n+\t\t\t\tConfig: n.Original.Module.RawConfig,\n+\t\t\t\tOutput: &resourceConfig,\n+\t\t\t},\n+\n+\t\t\t&EvalVariableBlock{\n+\t\t\t\tConfig:    &resourceConfig,\n+\t\t\t\tVariables: n.Variables,\n+\t\t\t},\n+\t\t},\n+\t}\n+}\n+\n+// GraphNodeFlattenable impl.\n+func (n *graphNodeModuleExpanded) FlattenGraph() *Graph {\n+\tgraph := n.Subgraph()\n+\tinput := n.Original.Module.RawConfig\n+\n+\t// Go over each vertex in the graph and wrap the configuration\n+\t// items so that the dependencies properly map to the modules.\n+\t// See the docs for graphNodeModuleWrappable for more info.","path":"terraform/graph_config_node_module.go","position":170,"original_position":170,"commit_id":"cc0c208364229b5adfbfe1c39b82eb7265a1e905","original_commit_id":"cc0c208364229b5adfbfe1c39b82eb7265a1e905","user":{"login":"mitchellh","id":1299,"avatar_url":"https://avatars.githubusercontent.com/u/1299?v=3","gravatar_id":"","url":"https://api.github.com/users/mitchellh","html_url":"https://github.com/mitchellh","followers_url":"https://api.github.com/users/mitchellh/followers","following_url":"https://api.github.com/users/mitchellh/following{/other_user}","gists_url":"https://api.github.com/users/mitchellh/gists{/gist_id}","starred_url":"https://api.github.com/users/mitchellh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mitchellh/subscriptions","organizations_url":"https://api.github.com/users/mitchellh/orgs","repos_url":"https://api.github.com/users/mitchellh/repos","events_url":"https://api.github.com/users/mitchellh/events{/privacy}","received_events_url":"https://api.github.com/users/mitchellh/received_events","type":"User","site_admin":false},"body":"Oh yeah, it is, I'll update the comment.","created_at":"2015-05-04T17:48:40Z","updated_at":"2015-05-04T17:48:40Z","html_url":"https://github.com/hashicorp/terraform/pull/1781#discussion_r29605711","pull_request_url":"https://api.github.com/repos/hashicorp/terraform/pulls/1781","_links":{"self":{"href":"https://api.github.com/repos/hashicorp/terraform/pulls/comments/29605711"},"html":{"href":"https://github.com/hashicorp/terraform/pull/1781#discussion_r29605711"},"pull_request":{"href":"https://api.github.com/repos/hashicorp/terraform/pulls/1781"}}},"pull_request":{"url":"https://api.github.com/repos/hashicorp/terraform/pulls/1781","id":34594850,"html_url":"https://github.com/hashicorp/terraform/pull/1781","diff_url":"https://github.com/hashicorp/terraform/pull/1781.diff","patch_url":"https://github.com/hashicorp/terraform/pull/1781.patch","issue_url":"https://api.github.com/repos/hashicorp/terraform/issues/1781","number":1781,"state":"open","locked":false,"title":"Flatten modules into main graph","user":{"login":"mitchellh","id":1299,"avatar_url":"https://avatars.githubusercontent.com/u/1299?v=3","gravatar_id":"","url":"https://api.github.com/users/mitchellh","html_url":"https://github.com/mitchellh","followers_url":"https://api.github.com/users/mitchellh/followers","following_url":"https://api.github.com/users/mitchellh/following{/other_user}","gists_url":"https://api.github.com/users/mitchellh/gists{/gist_id}","starred_url":"https://api.github.com/users/mitchellh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mitchellh/subscriptions","organizations_url":"https://api.github.com/users/mitchellh/orgs","repos_url":"https://api.github.com/users/mitchellh/repos","events_url":"https://api.github.com/users/mitchellh/events{/privacy}","received_events_url":"https://api.github.com/users/mitchellh/received_events","type":"User","site_admin":false},"body":"Fixes #1582 \r\n\r\nMight affect #1637 (hopefully fixes, unsure due to private repo). \r\n\r\nAll context tests pass but this is a pretty big change and I'll want to try at least on our core ops repo before merging (since we use modules heavily). \r\n\r\nThis changes the way the graph treats modules. Prior to this PR, modules were their own isolated subgraphs. When evaluating, the entire subgraph would get walked in one step. The problem with this is that it turns out that modules can't always be seen as a single executable unit. \r\n\r\nThis PR flattens the subgraphs of modules into the main root graph, then does the destroy transformations _after_ this flattening. This lets the destroy edge manipulations have a global view of the graph, and create the proper ordering of things.\r\n\r\nMost of this was surprisingly straightforward. The only part I'm really unhappy about is the special casing in graph builder to only do some steps for the root path. \r\n\r\nI think a future change/refactor can probably simplify this even further by just getting rid of static subgraphs altogether and just having the transform add all the nodes for all the modules at once. I was about to go down this route, but decided that if this works, we should get this in first, and refactor later, especially given that the changes here aren't hideous. \r\n\r\nI have a feeling this will fix weird module-boundary create-before-destroy issues as well but I don't have a test case yet to reproduce anything of that sort so I can't confirm.\r\n\r\nAdditionally, @armon raised good points about this will hurt performance of Terraform for very large graphs. This is because the O(N) lookups for graph vertices will get bad since N Is getting quite large (before it was N within each subgraph). Given that almost all O(N) lookups are for interface implementations, I think we can just do interface-specific lookaside tables in the future and speed everything up to O(1).\r\n\r\n@phinze: One thing for you is to ask if the graph functionality looks good? I don't want to affect the `terraform graph` output we did for 0.5 for this.","created_at":"2015-05-03T01:35:23Z","updated_at":"2015-05-04T17:48:40Z","closed_at":null,"merged_at":null,"merge_commit_sha":"dcc954c01c972ad5a24aaea065567d7dffe55d67","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/hashicorp/terraform/pulls/1781/commits","review_comments_url":"https://api.github.com/repos/hashicorp/terraform/pulls/1781/comments","review_comment_url":"https://api.github.com/repos/hashicorp/terraform/pulls/comments{/number}","comments_url":"https://api.github.com/repos/hashicorp/terraform/issues/1781/comments","statuses_url":"https://api.github.com/repos/hashicorp/terraform/statuses/cc0c208364229b5adfbfe1c39b82eb7265a1e905","head":{"label":"hashicorp:b-module-deps","ref":"b-module-deps","sha":"cc0c208364229b5adfbfe1c39b82eb7265a1e905","user":{"login":"hashicorp","id":761456,"avatar_url":"https://avatars.githubusercontent.com/u/761456?v=3","gravatar_id":"","url":"https://api.github.com/users/hashicorp","html_url":"https://github.com/hashicorp","followers_url":"https://api.github.com/users/hashicorp/followers","following_url":"https://api.github.com/users/hashicorp/following{/other_user}","gists_url":"https://api.github.com/users/hashicorp/gists{/gist_id}","starred_url":"https://api.github.com/users/hashicorp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hashicorp/subscriptions","organizations_url":"https://api.github.com/users/hashicorp/orgs","repos_url":"https://api.github.com/users/hashicorp/repos","events_url":"https://api.github.com/users/hashicorp/events{/privacy}","received_events_url":"https://api.github.com/users/hashicorp/received_events","type":"Organization","site_admin":false},"repo":{"id":17728164,"name":"terraform","full_name":"hashicorp/terraform","owner":{"login":"hashicorp","id":761456,"avatar_url":"https://avatars.githubusercontent.com/u/761456?v=3","gravatar_id":"","url":"https://api.github.com/users/hashicorp","html_url":"https://github.com/hashicorp","followers_url":"https://api.github.com/users/hashicorp/followers","following_url":"https://api.github.com/users/hashicorp/following{/other_user}","gists_url":"https://api.github.com/users/hashicorp/gists{/gist_id}","starred_url":"https://api.github.com/users/hashicorp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hashicorp/subscriptions","organizations_url":"https://api.github.com/users/hashicorp/orgs","repos_url":"https://api.github.com/users/hashicorp/repos","events_url":"https://api.github.com/users/hashicorp/events{/privacy}","received_events_url":"https://api.github.com/users/hashicorp/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/hashicorp/terraform","description":"Terraform is a tool for building, changing, and combining infrastructure safely and efficiently.","fork":false,"url":"https://api.github.com/repos/hashicorp/terraform","forks_url":"https://api.github.com/repos/hashicorp/terraform/forks","keys_url":"https://api.github.com/repos/hashicorp/terraform/keys{/key_id}","collaborators_url":"https://api.github.com/repos/hashicorp/terraform/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/hashicorp/terraform/teams","hooks_url":"https://api.github.com/repos/hashicorp/terraform/hooks","issue_events_url":"https://api.github.com/repos/hashicorp/terraform/issues/events{/number}","events_url":"https://api.github.com/repos/hashicorp/terraform/events","assignees_url":"https://api.github.com/repos/hashicorp/terraform/assignees{/user}","branches_url":"https://api.github.com/repos/hashicorp/terraform/branches{/branch}","tags_url":"https://api.github.com/repos/hashicorp/terraform/tags","blobs_url":"https://api.github.com/repos/hashicorp/terraform/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/hashicorp/terraform/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/hashicorp/terraform/git/refs{/sha}","trees_url":"https://api.github.com/repos/hashicorp/terraform/git/trees{/sha}","statuses_url":"https://api.github.com/repos/hashicorp/terraform/statuses/{sha}","languages_url":"https://api.github.com/repos/hashicorp/terraform/languages","stargazers_url":"https://api.github.com/repos/hashicorp/terraform/stargazers","contributors_url":"https://api.github.com/repos/hashicorp/terraform/contributors","subscribers_url":"https://api.github.com/repos/hashicorp/terraform/subscribers","subscription_url":"https://api.github.com/repos/hashicorp/terraform/subscription","commits_url":"https://api.github.com/repos/hashicorp/terraform/commits{/sha}","git_commits_url":"https://api.github.com/repos/hashicorp/terraform/git/commits{/sha}","comments_url":"https://api.github.com/repos/hashicorp/terraform/comments{/number}","issue_comment_url":"https://api.github.com/repos/hashicorp/terraform/issues/comments{/number}","contents_url":"https://api.github.com/repos/hashicorp/terraform/contents/{+path}","compare_url":"https://api.github.com/repos/hashicorp/terraform/compare/{base}...{head}","merges_url":"https://api.github.com/repos/hashicorp/terraform/merges","archive_url":"https://api.github.com/repos/hashicorp/terraform/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/hashicorp/terraform/downloads","issues_url":"https://api.github.com/repos/hashicorp/terraform/issues{/number}","pulls_url":"https://api.github.com/repos/hashicorp/terraform/pulls{/number}","milestones_url":"https://api.github.com/repos/hashicorp/terraform/milestones{/number}","notifications_url":"https://api.github.com/repos/hashicorp/terraform/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/hashicorp/terraform/labels{/name}","releases_url":"https://api.github.com/repos/hashicorp/terraform/releases{/id}","created_at":"2014-03-13T22:25:48Z","updated_at":"2015-05-04T17:05:55Z","pushed_at":"2015-05-04T17:39:02Z","git_url":"git://github.com/hashicorp/terraform.git","ssh_url":"git@github.com:hashicorp/terraform.git","clone_url":"https://github.com/hashicorp/terraform.git","svn_url":"https://github.com/hashicorp/terraform","homepage":"http://www.terraform.io","size":44805,"stargazers_count":2332,"watchers_count":2332,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":411,"mirror_url":null,"open_issues_count":270,"forks":411,"open_issues":270,"watchers":2332,"default_branch":"master"}},"base":{"label":"hashicorp:master","ref":"master","sha":"cc7f4edd27ae79f08c11aaafa0b9abb2838a0a1e","user":{"login":"hashicorp","id":761456,"avatar_url":"https://avatars.githubusercontent.com/u/761456?v=3","gravatar_id":"","url":"https://api.github.com/users/hashicorp","html_url":"https://github.com/hashicorp","followers_url":"https://api.github.com/users/hashicorp/followers","following_url":"https://api.github.com/users/hashicorp/following{/other_user}","gists_url":"https://api.github.com/users/hashicorp/gists{/gist_id}","starred_url":"https://api.github.com/users/hashicorp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hashicorp/subscriptions","organizations_url":"https://api.github.com/users/hashicorp/orgs","repos_url":"https://api.github.com/users/hashicorp/repos","events_url":"https://api.github.com/users/hashicorp/events{/privacy}","received_events_url":"https://api.github.com/users/hashicorp/received_events","type":"Organization","site_admin":false},"repo":{"id":17728164,"name":"terraform","full_name":"hashicorp/terraform","owner":{"login":"hashicorp","id":761456,"avatar_url":"https://avatars.githubusercontent.com/u/761456?v=3","gravatar_id":"","url":"https://api.github.com/users/hashicorp","html_url":"https://github.com/hashicorp","followers_url":"https://api.github.com/users/hashicorp/followers","following_url":"https://api.github.com/users/hashicorp/following{/other_user}","gists_url":"https://api.github.com/users/hashicorp/gists{/gist_id}","starred_url":"https://api.github.com/users/hashicorp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hashicorp/subscriptions","organizations_url":"https://api.github.com/users/hashicorp/orgs","repos_url":"https://api.github.com/users/hashicorp/repos","events_url":"https://api.github.com/users/hashicorp/events{/privacy}","received_events_url":"https://api.github.com/users/hashicorp/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/hashicorp/terraform","description":"Terraform is a tool for building, changing, and combining infrastructure safely and efficiently.","fork":false,"url":"https://api.github.com/repos/hashicorp/terraform","forks_url":"https://api.github.com/repos/hashicorp/terraform/forks","keys_url":"https://api.github.com/repos/hashicorp/terraform/keys{/key_id}","collaborators_url":"https://api.github.com/repos/hashicorp/terraform/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/hashicorp/terraform/teams","hooks_url":"https://api.github.com/repos/hashicorp/terraform/hooks","issue_events_url":"https://api.github.com/repos/hashicorp/terraform/issues/events{/number}","events_url":"https://api.github.com/repos/hashicorp/terraform/events","assignees_url":"https://api.github.com/repos/hashicorp/terraform/assignees{/user}","branches_url":"https://api.github.com/repos/hashicorp/terraform/branches{/branch}","tags_url":"https://api.github.com/repos/hashicorp/terraform/tags","blobs_url":"https://api.github.com/repos/hashicorp/terraform/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/hashicorp/terraform/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/hashicorp/terraform/git/refs{/sha}","trees_url":"https://api.github.com/repos/hashicorp/terraform/git/trees{/sha}","statuses_url":"https://api.github.com/repos/hashicorp/terraform/statuses/{sha}","languages_url":"https://api.github.com/repos/hashicorp/terraform/languages","stargazers_url":"https://api.github.com/repos/hashicorp/terraform/stargazers","contributors_url":"https://api.github.com/repos/hashicorp/terraform/contributors","subscribers_url":"https://api.github.com/repos/hashicorp/terraform/subscribers","subscription_url":"https://api.github.com/repos/hashicorp/terraform/subscription","commits_url":"https://api.github.com/repos/hashicorp/terraform/commits{/sha}","git_commits_url":"https://api.github.com/repos/hashicorp/terraform/git/commits{/sha}","comments_url":"https://api.github.com/repos/hashicorp/terraform/comments{/number}","issue_comment_url":"https://api.github.com/repos/hashicorp/terraform/issues/comments{/number}","contents_url":"https://api.github.com/repos/hashicorp/terraform/contents/{+path}","compare_url":"https://api.github.com/repos/hashicorp/terraform/compare/{base}...{head}","merges_url":"https://api.github.com/repos/hashicorp/terraform/merges","archive_url":"https://api.github.com/repos/hashicorp/terraform/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/hashicorp/terraform/downloads","issues_url":"https://api.github.com/repos/hashicorp/terraform/issues{/number}","pulls_url":"https://api.github.com/repos/hashicorp/terraform/pulls{/number}","milestones_url":"https://api.github.com/repos/hashicorp/terraform/milestones{/number}","notifications_url":"https://api.github.com/repos/hashicorp/terraform/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/hashicorp/terraform/labels{/name}","releases_url":"https://api.github.com/repos/hashicorp/terraform/releases{/id}","created_at":"2014-03-13T22:25:48Z","updated_at":"2015-05-04T17:05:55Z","pushed_at":"2015-05-04T17:39:02Z","git_url":"git://github.com/hashicorp/terraform.git","ssh_url":"git@github.com:hashicorp/terraform.git","clone_url":"https://github.com/hashicorp/terraform.git","svn_url":"https://github.com/hashicorp/terraform","homepage":"http://www.terraform.io","size":44805,"stargazers_count":2332,"watchers_count":2332,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":411,"mirror_url":null,"open_issues_count":270,"forks":411,"open_issues":270,"watchers":2332,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/hashicorp/terraform/pulls/1781"},"html":{"href":"https://github.com/hashicorp/terraform/pull/1781"},"issue":{"href":"https://api.github.com/repos/hashicorp/terraform/issues/1781"},"comments":{"href":"https://api.github.com/repos/hashicorp/terraform/issues/1781/comments"},"review_comments":{"href":"https://api.github.com/repos/hashicorp/terraform/pulls/1781/comments"},"review_comment":{"href":"https://api.github.com/repos/hashicorp/terraform/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/hashicorp/terraform/pulls/1781/commits"},"statuses":{"href":"https://api.github.com/repos/hashicorp/terraform/statuses/cc0c208364229b5adfbfe1c39b82eb7265a1e905"}}}}