github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/transform_config.go (about) 1 package terraform 2 3 import ( 4 "log" 5 "sync" 6 7 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 8 "github.com/hashicorp/terraform-plugin-sdk/internal/configs" 9 "github.com/hashicorp/terraform-plugin-sdk/internal/dag" 10 ) 11 12 // ConfigTransformer is a GraphTransformer that adds all the resources 13 // from the configuration to the graph. 14 // 15 // The module used to configure this transformer must be the root module. 16 // 17 // Only resources are added to the graph. Variables, outputs, and 18 // providers must be added via other transforms. 19 // 20 // Unlike ConfigTransformerOld, this transformer creates a graph with 21 // all resources including module resources, rather than creating module 22 // nodes that are then "flattened". 23 type ConfigTransformer struct { 24 Concrete ConcreteResourceNodeFunc 25 26 // Module is the module to add resources from. 27 Config *configs.Config 28 29 // Unique will only add resources that aren't already present in the graph. 30 Unique bool 31 32 // Mode will only add resources that match the given mode 33 ModeFilter bool 34 Mode addrs.ResourceMode 35 36 l sync.Mutex 37 uniqueMap map[string]struct{} 38 } 39 40 func (t *ConfigTransformer) Transform(g *Graph) error { 41 // Lock since we use some internal state 42 t.l.Lock() 43 defer t.l.Unlock() 44 45 // If no configuration is available, we don't do anything 46 if t.Config == nil { 47 return nil 48 } 49 50 // Reset the uniqueness map. If we're tracking uniques, then populate 51 // it with addresses. 52 t.uniqueMap = make(map[string]struct{}) 53 defer func() { t.uniqueMap = nil }() 54 if t.Unique { 55 for _, v := range g.Vertices() { 56 if rn, ok := v.(GraphNodeResource); ok { 57 t.uniqueMap[rn.ResourceAddr().String()] = struct{}{} 58 } 59 } 60 } 61 62 // Start the transformation process 63 return t.transform(g, t.Config) 64 } 65 66 func (t *ConfigTransformer) transform(g *Graph, config *configs.Config) error { 67 // If no config, do nothing 68 if config == nil { 69 return nil 70 } 71 72 // Add our resources 73 if err := t.transformSingle(g, config); err != nil { 74 return err 75 } 76 77 // Transform all the children. 78 for _, c := range config.Children { 79 if err := t.transform(g, c); err != nil { 80 return err 81 } 82 } 83 84 return nil 85 } 86 87 func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config) error { 88 path := config.Path 89 module := config.Module 90 log.Printf("[TRACE] ConfigTransformer: Starting for path: %v", path) 91 92 // For now we assume that each module call produces only one module 93 // instance with no key, since we don't yet support "count" and "for_each" 94 // on modules. 95 // FIXME: As part of supporting "count" and "for_each" on modules, rework 96 // this so that we'll "expand" the module call first and then create graph 97 // nodes for each module instance separately. 98 instPath := path.UnkeyedInstanceShim() 99 100 allResources := make([]*configs.Resource, 0, len(module.ManagedResources)+len(module.DataResources)) 101 for _, r := range module.ManagedResources { 102 allResources = append(allResources, r) 103 } 104 for _, r := range module.DataResources { 105 allResources = append(allResources, r) 106 } 107 108 for _, r := range allResources { 109 relAddr := r.Addr() 110 111 if t.ModeFilter && relAddr.Mode != t.Mode { 112 // Skip non-matching modes 113 continue 114 } 115 116 addr := relAddr.Absolute(instPath) 117 if _, ok := t.uniqueMap[addr.String()]; ok { 118 // We've already seen a resource with this address. This should 119 // never happen, because we enforce uniqueness in the config loader. 120 continue 121 } 122 123 abstract := &NodeAbstractResource{Addr: addr} 124 var node dag.Vertex = abstract 125 if f := t.Concrete; f != nil { 126 node = f(abstract) 127 } 128 129 g.Add(node) 130 } 131 132 return nil 133 }