github.com/kcburge/terraform@v0.11.12-beta1/configs/config_build.go (about) 1 package configs 2 3 import ( 4 "sort" 5 6 version "github.com/hashicorp/go-version" 7 "github.com/hashicorp/hcl2/hcl" 8 ) 9 10 // BuildConfig constructs a Config from a root module by loading all of its 11 // descendent modules via the given ModuleWalker. 12 // 13 // The result is a module tree that has so far only had basic module- and 14 // file-level invariants validated. If the returned diagnostics contains errors, 15 // the returned module tree may be incomplete but can still be used carefully 16 // for static analysis. 17 func BuildConfig(root *Module, walker ModuleWalker) (*Config, hcl.Diagnostics) { 18 var diags hcl.Diagnostics 19 cfg := &Config{ 20 Module: root, 21 } 22 cfg.Root = cfg // Root module is self-referential. 23 cfg.Children, diags = buildChildModules(cfg, walker) 24 return cfg, diags 25 } 26 27 func buildChildModules(parent *Config, walker ModuleWalker) (map[string]*Config, hcl.Diagnostics) { 28 var diags hcl.Diagnostics 29 ret := map[string]*Config{} 30 31 calls := parent.Module.ModuleCalls 32 33 // We'll sort the calls by their local names so that they'll appear in a 34 // predictable order in any logging that's produced during the walk. 35 callNames := make([]string, 0, len(calls)) 36 for k := range calls { 37 callNames = append(callNames, k) 38 } 39 sort.Strings(callNames) 40 41 for _, callName := range callNames { 42 call := calls[callName] 43 path := make([]string, len(parent.Path)+1) 44 copy(path, parent.Path) 45 path[len(path)-1] = call.Name 46 47 req := ModuleRequest{ 48 Name: call.Name, 49 Path: path, 50 SourceAddr: call.SourceAddr, 51 SourceAddrRange: call.SourceAddrRange, 52 VersionConstraint: call.Version, 53 Parent: parent, 54 CallRange: call.DeclRange, 55 } 56 57 mod, ver, modDiags := walker.LoadModule(&req) 58 diags = append(diags, modDiags...) 59 if mod == nil { 60 // nil can be returned if the source address was invalid and so 61 // nothing could be loaded whatsoever. LoadModule should've 62 // returned at least one error diagnostic in that case. 63 continue 64 } 65 66 child := &Config{ 67 Parent: parent, 68 Root: parent.Root, 69 Path: path, 70 Module: mod, 71 CallRange: call.DeclRange, 72 SourceAddr: call.SourceAddr, 73 SourceAddrRange: call.SourceAddrRange, 74 Version: ver, 75 } 76 77 child.Children, modDiags = buildChildModules(child, walker) 78 79 ret[call.Name] = child 80 } 81 82 return ret, diags 83 } 84 85 // A ModuleWalker knows how to find and load a child module given details about 86 // the module to be loaded and a reference to its partially-loaded parent 87 // Config. 88 type ModuleWalker interface { 89 // LoadModule finds and loads a requested child module. 90 // 91 // If errors are detected during loading, implementations should return them 92 // in the diagnostics object. If the diagnostics object contains any errors 93 // then the caller will tolerate the returned module being nil or incomplete. 94 // If no errors are returned, it should be non-nil and complete. 95 // 96 // Full validation need not have been performed but an implementation should 97 // ensure that the basic file- and module-validations performed by the 98 // LoadConfigDir function (valid syntax, no namespace collisions, etc) have 99 // been performed before returning a module. 100 LoadModule(req *ModuleRequest) (*Module, *version.Version, hcl.Diagnostics) 101 } 102 103 // ModuleWalkerFunc is an implementation of ModuleWalker that directly wraps 104 // a callback function, for more convenient use of that interface. 105 type ModuleWalkerFunc func(req *ModuleRequest) (*Module, *version.Version, hcl.Diagnostics) 106 107 // LoadModule implements ModuleWalker. 108 func (f ModuleWalkerFunc) LoadModule(req *ModuleRequest) (*Module, *version.Version, hcl.Diagnostics) { 109 return f(req) 110 } 111 112 // ModuleRequest is used with the ModuleWalker interface to describe a child 113 // module that must be loaded. 114 type ModuleRequest struct { 115 // Name is the "logical name" of the module call within configuration. 116 // This is provided in case the name is used as part of a storage key 117 // for the module, but implementations must otherwise treat it as an 118 // opaque string. It is guaranteed to have already been validated as an 119 // HCL identifier and UTF-8 encoded. 120 Name string 121 122 // Path is a list of logical names that traverse from the root module to 123 // this module. This can be used, for example, to form a lookup key for 124 // each distinct module call in a configuration, allowing for multiple 125 // calls with the same name at different points in the tree. 126 Path []string 127 128 // SourceAddr is the source address string provided by the user in 129 // configuration. 130 SourceAddr string 131 132 // SourceAddrRange is the source range for the SourceAddr value as it 133 // was provided in configuration. This can and should be used to generate 134 // diagnostics about the source address having invalid syntax, referring 135 // to a non-existent object, etc. 136 SourceAddrRange hcl.Range 137 138 // VersionConstraint is the version constraint applied to the module in 139 // configuration. This data structure includes the source range for 140 // the constraint, which can and should be used to generate diagnostics 141 // about constraint-related issues, such as constraints that eliminate all 142 // available versions of a module whose source is otherwise valid. 143 VersionConstraint VersionConstraint 144 145 // Parent is the partially-constructed module tree node that the loaded 146 // module will be added to. Callers may refer to any field of this 147 // structure except Children, which is still under construction when 148 // ModuleRequest objects are created and thus has undefined content. 149 // The main reason this is provided is so that full module paths can 150 // be constructed for uniqueness. 151 Parent *Config 152 153 // CallRange is the source range for the header of the "module" block 154 // in configuration that prompted this request. This can be used as the 155 // subject of an error diagnostic that relates to the module call itself, 156 // rather than to either its source address or its version number. 157 CallRange hcl.Range 158 }