diff --git a/terraform/graph_builder_plan.go b/terraform/graph_builder_plan.go index 9c7e4c1db..9d05d4a43 100644 --- a/terraform/graph_builder_plan.go +++ b/terraform/graph_builder_plan.go @@ -74,6 +74,9 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { Module: b.Module, }, + // Add the local values + &LocalTransformer{Module: b.Module}, + // Add the outputs &OutputTransformer{Module: b.Module}, diff --git a/terraform/graph_builder_plan_test.go b/terraform/graph_builder_plan_test.go index 25578ebaf..321ddbe3f 100644 --- a/terraform/graph_builder_plan_test.go +++ b/terraform/graph_builder_plan_test.go @@ -61,16 +61,22 @@ aws_load_balancer.weblb provider.aws aws_security_group.firewall provider.aws +local.instance_id + aws_instance.web meta.count-boundary (count boundary fixup) aws_instance.web aws_load_balancer.weblb aws_security_group.firewall + local.instance_id openstack_floating_ip.random + output.instance_id provider.aws provider.openstack var.foo openstack_floating_ip.random provider.openstack +output.instance_id + local.instance_id provider.aws openstack_floating_ip.random provider.aws (close) diff --git a/terraform/graph_builder_refresh.go b/terraform/graph_builder_refresh.go index 3d3e968fa..beb5a4a44 100644 --- a/terraform/graph_builder_refresh.go +++ b/terraform/graph_builder_refresh.go @@ -133,6 +133,9 @@ func (b *RefreshGraphBuilder) Steps() []GraphTransformer { &ParentProviderTransformer{}, &AttachProviderConfigTransformer{Module: b.Module}, + // Add the local values + &LocalTransformer{Module: b.Module}, + // Add the outputs &OutputTransformer{Module: b.Module}, diff --git a/terraform/node_local.go b/terraform/node_local.go new file mode 100644 index 000000000..e6ae140a6 --- /dev/null +++ b/terraform/node_local.go @@ -0,0 +1,80 @@ +package terraform + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform/config" +) + +// NodeLocal represents a named local value in a particular module. +// +// Local value nodes only have one operation, common to all walk types: +// evaluate the result and place it in state. +type NodeLocal struct { + PathValue []string + Config *config.Local +} + +func (n *NodeLocal) Name() string { + result := fmt.Sprintf("local.%s", n.Config.Name) + if len(n.PathValue) > 1 { + result = fmt.Sprintf("%s.%s", modulePrefixStr(n.PathValue), result) + } + + return result +} + +// GraphNodeSubPath +func (n *NodeLocal) Path() []string { + return n.PathValue +} + +// RemovableIfNotTargeted +func (n *NodeLocal) RemoveIfNotTargeted() bool { + return true +} + +// GraphNodeReferenceable +func (n *NodeLocal) ReferenceableName() []string { + name := fmt.Sprintf("local.%s", n.Config.Name) + return []string{name} +} + +// GraphNodeReferencer +func (n *NodeLocal) References() []string { + var result []string + result = append(result, ReferencesFromConfig(n.Config.RawConfig)...) + for _, v := range result { + split := strings.Split(v, "/") + for i, s := range split { + split[i] = s + ".destroy" + } + + result = append(result, strings.Join(split, "/")) + } + + return result +} + +// GraphNodeEvalable +func (n *NodeLocal) EvalTree() EvalNode { + return &EvalOpFilter{ + Ops: []walkOperation{ + walkInput, + walkValidate, + walkRefresh, + walkPlan, + walkApply, + walkDestroy, + }, + Node: &EvalSequence{ + Nodes: []EvalNode{ + /*&EvalWriteLocal{ + Name: n.Config.Name, + Value: n.Config.RawConfig, + },*/ + }, + }, + } +} diff --git a/terraform/test-fixtures/graph-builder-plan-basic/main.tf b/terraform/test-fixtures/graph-builder-plan-basic/main.tf index a40802cc9..47cf9590b 100644 --- a/terraform/test-fixtures/graph-builder-plan-basic/main.tf +++ b/terraform/test-fixtures/graph-builder-plan-basic/main.tf @@ -22,3 +22,11 @@ resource "aws_instance" "web" { resource "aws_load_balancer" "weblb" { members = "${aws_instance.web.id_list}" } + +locals { + instance_id = "${aws_instance.web.id}" +} + +output "instance_id" { + value = "${local.instance_id}" +} diff --git a/terraform/transform_local.go b/terraform/transform_local.go new file mode 100644 index 000000000..95ecfc0a4 --- /dev/null +++ b/terraform/transform_local.go @@ -0,0 +1,40 @@ +package terraform + +import ( + "github.com/hashicorp/terraform/config/module" +) + +// LocalTransformer is a GraphTransformer that adds all the local values +// from the configuration to the graph. +type LocalTransformer struct { + Module *module.Tree +} + +func (t *LocalTransformer) Transform(g *Graph) error { + return t.transformModule(g, t.Module) +} + +func (t *LocalTransformer) transformModule(g *Graph, m *module.Tree) error { + if m == nil { + // Can't have any locals if there's no config + return nil + } + + for _, local := range m.Config().Locals { + node := &NodeLocal{ + PathValue: normalizeModulePath(m.Path()), + Config: local, + } + + g.Add(node) + } + + // Also populate locals for child modules + for _, c := range m.Children() { + if err := t.transformModule(g, c); err != nil { + return err + } + } + + return nil +} diff --git a/terraform/transform_reference.go b/terraform/transform_reference.go index c5452354d..ce55e1d77 100644 --- a/terraform/transform_reference.go +++ b/terraform/transform_reference.go @@ -296,6 +296,8 @@ func ReferenceFromInterpolatedVar(v config.InterpolatedVariable) []string { return []string{fmt.Sprintf("%s.%d/%s.N", id, idx, id)} case *config.UserVariable: return []string{fmt.Sprintf("var.%s", v.Name)} + case *config.LocalVariable: + return []string{fmt.Sprintf("local.%s", v.Name)} default: return nil }