opentofu/terraform/graph_config_node_module.go
James Nugent e57a399d71 core: Use native HIL maps instead of flatmaps
This changes the representation of maps in the interpolator from the
dotted flatmap form of a string variable named "var.variablename.key"
per map element to use native HIL maps instead.

This involves porting some of the interpolation functions in order to
keep the tests green, and adding support for map outputs.

There is one backwards incompatibility: as a result of an implementation
detail of maps, one could access an indexed map variable using the
syntax "${var.variablename.key}".

This is no longer possible - instead HIL native syntax -
"${var.variablename["key"]}" must be used. This was previously
documented, (though not heavily used) so it must be noted as a backward
compatibility issue for Terraform 0.7.
2016-05-10 14:49:13 -04:00

214 lines
5.4 KiB
Go

package terraform
import (
"fmt"
"strings"
"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/config/module"
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/dot"
)
// GraphNodeConfigModule represents a module within the configuration graph.
type GraphNodeConfigModule struct {
Path []string
Module *config.Module
Tree *module.Tree
}
func (n *GraphNodeConfigModule) ConfigType() GraphNodeConfigType {
return GraphNodeConfigTypeModule
}
func (n *GraphNodeConfigModule) DependableName() []string {
config := n.Tree.Config()
result := make([]string, 1, len(config.Outputs)+1)
result[0] = n.Name()
for _, o := range config.Outputs {
result = append(result, fmt.Sprintf("%s.output.%s", n.Name(), o.Name))
}
return result
}
func (n *GraphNodeConfigModule) DependentOn() []string {
vars := n.Module.RawConfig.Variables
result := make([]string, 0, len(vars))
for _, v := range vars {
if vn := varNameForVar(v); vn != "" {
result = append(result, vn)
}
}
return result
}
func (n *GraphNodeConfigModule) Name() string {
return fmt.Sprintf("module.%s", n.Module.Name)
}
// GraphNodeExpandable
func (n *GraphNodeConfigModule) Expand(b GraphBuilder) (GraphNodeSubgraph, error) {
// Build the graph first
graph, err := b.Build(n.Path)
if err != nil {
return nil, err
}
{
// Add the destroy marker to the graph
t := &ModuleDestroyTransformer{}
if err := t.Transform(graph); err != nil {
return nil, err
}
}
// Build the actual subgraph node
return &graphNodeModuleExpanded{
Original: n,
Graph: graph,
Variables: make(map[string]interface{}),
}, nil
}
// GraphNodeExpandable
func (n *GraphNodeConfigModule) ProvidedBy() []string {
// Build up the list of providers by simply going over our configuration
// to find the providers that are configured there as well as the
// providers that the resources use.
config := n.Tree.Config()
providers := make(map[string]struct{})
for _, p := range config.ProviderConfigs {
providers[p.Name] = struct{}{}
}
for _, r := range config.Resources {
providers[resourceProvider(r.Type, r.Provider)] = struct{}{}
}
// Turn the map into a string. This makes sure that the list is
// de-dupped since we could be going over potentially many resources.
result := make([]string, 0, len(providers))
for p, _ := range providers {
result = append(result, p)
}
return result
}
// graphNodeModuleExpanded represents a module where the graph has
// been expanded. It stores the graph of the module as well as a reference
// to the map of variables.
type graphNodeModuleExpanded struct {
Original *GraphNodeConfigModule
Graph *Graph
// Variables is a map of the input variables. This reference should
// be shared with ModuleInputTransformer in order to create a connection
// where the variables are set properly.
Variables map[string]interface{}
}
func (n *graphNodeModuleExpanded) Name() string {
return fmt.Sprintf("%s (expanded)", dag.VertexName(n.Original))
}
func (n *graphNodeModuleExpanded) ConfigType() GraphNodeConfigType {
return GraphNodeConfigTypeModule
}
// GraphNodeDependable
func (n *graphNodeModuleExpanded) DependableName() []string {
return n.Original.DependableName()
}
// GraphNodeDependent
func (n *graphNodeModuleExpanded) DependentOn() []string {
return n.Original.DependentOn()
}
// GraphNodeDotter impl.
func (n *graphNodeModuleExpanded) DotNode(name string, opts *GraphDotOpts) *dot.Node {
return dot.NewNode(name, map[string]string{
"label": dag.VertexName(n.Original),
"shape": "component",
})
}
// GraphNodeEvalable impl.
func (n *graphNodeModuleExpanded) EvalTree() EvalNode {
var resourceConfig *ResourceConfig
return &EvalSequence{
Nodes: []EvalNode{
&EvalInterpolate{
Config: n.Original.Module.RawConfig,
Output: &resourceConfig,
},
&EvalVariableBlock{
Config: &resourceConfig,
VariableValues: n.Variables,
},
},
}
}
// GraphNodeFlattenable impl.
func (n *graphNodeModuleExpanded) FlattenGraph() *Graph {
graph := n.Subgraph()
input := n.Original.Module.RawConfig
// Go over each vertex and do some modifications to the graph for
// flattening. We have to skip some nodes (graphNodeModuleSkippable)
// as well as setup the variable values.
for _, v := range graph.Vertices() {
// If this is a variable, then look it up in the raw configuration.
// If it exists in the raw configuration, set the value of it.
if vn, ok := v.(*GraphNodeConfigVariable); ok && input != nil {
key := vn.VariableName()
if v, ok := input.Raw[key]; ok {
config, err := config.NewRawConfig(map[string]interface{}{
key: v,
})
if err != nil {
// This shouldn't happen because it is already in
// a RawConfig above meaning it worked once before.
panic(err)
}
// Set the variable value so it is interpolated properly.
// Also set the module so we set the value on it properly.
vn.Module = graph.Path[len(graph.Path)-1]
vn.Value = config
}
}
}
return graph
}
// GraphNodeSubgraph impl.
func (n *graphNodeModuleExpanded) Subgraph() *Graph {
return n.Graph
}
func modulePrefixStr(p []string) string {
parts := make([]string, 0, len(p)*2)
for _, p := range p[1:] {
parts = append(parts, "module", p)
}
return strings.Join(parts, ".")
}
func modulePrefixList(result []string, prefix string) []string {
if prefix != "" {
for i, v := range result {
result[i] = fmt.Sprintf("%s.%s", prefix, v)
}
}
return result
}