github.com/shvar/terraform@v0.6.9-0.20151215234924-3365cd2231df/config/module/tree.go (about) 1 package module 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "path/filepath" 8 "strings" 9 "sync" 10 11 "github.com/hashicorp/go-getter" 12 "github.com/hashicorp/terraform/config" 13 ) 14 15 // RootName is the name of the root tree. 16 const RootName = "root" 17 18 // Tree represents the module import tree of configurations. 19 // 20 // This Tree structure can be used to get (download) new modules, load 21 // all the modules without getting, flatten the tree into something 22 // Terraform can use, etc. 23 type Tree struct { 24 name string 25 config *config.Config 26 children map[string]*Tree 27 path []string 28 lock sync.RWMutex 29 } 30 31 // NewTree returns a new Tree for the given config structure. 32 func NewTree(name string, c *config.Config) *Tree { 33 return &Tree{config: c, name: name} 34 } 35 36 // NewTreeModule is like NewTree except it parses the configuration in 37 // the directory and gives it a specific name. Use a blank name "" to specify 38 // the root module. 39 func NewTreeModule(name, dir string) (*Tree, error) { 40 c, err := config.LoadDir(dir) 41 if err != nil { 42 return nil, err 43 } 44 45 return NewTree(name, c), nil 46 } 47 48 // Config returns the configuration for this module. 49 func (t *Tree) Config() *config.Config { 50 return t.config 51 } 52 53 // Child returns the child with the given path (by name). 54 func (t *Tree) Child(path []string) *Tree { 55 if len(path) == 0 { 56 return t 57 } 58 59 c := t.Children()[path[0]] 60 if c == nil { 61 return nil 62 } 63 64 return c.Child(path[1:]) 65 } 66 67 // Children returns the children of this tree (the modules that are 68 // imported by this root). 69 // 70 // This will only return a non-nil value after Load is called. 71 func (t *Tree) Children() map[string]*Tree { 72 t.lock.RLock() 73 defer t.lock.RUnlock() 74 return t.children 75 } 76 77 // Loaded says whether or not this tree has been loaded or not yet. 78 func (t *Tree) Loaded() bool { 79 t.lock.RLock() 80 defer t.lock.RUnlock() 81 return t.children != nil 82 } 83 84 // Modules returns the list of modules that this tree imports. 85 // 86 // This is only the imports of _this_ level of the tree. To retrieve the 87 // full nested imports, you'll have to traverse the tree. 88 func (t *Tree) Modules() []*Module { 89 result := make([]*Module, len(t.config.Modules)) 90 for i, m := range t.config.Modules { 91 result[i] = &Module{ 92 Name: m.Name, 93 Source: m.Source, 94 } 95 } 96 97 return result 98 } 99 100 // Name returns the name of the tree. This will be "<root>" for the root 101 // tree and then the module name given for any children. 102 func (t *Tree) Name() string { 103 if t.name == "" { 104 return RootName 105 } 106 107 return t.name 108 } 109 110 // Load loads the configuration of the entire tree. 111 // 112 // The parameters are used to tell the tree where to find modules and 113 // whether it can download/update modules along the way. 114 // 115 // Calling this multiple times will reload the tree. 116 // 117 // Various semantic-like checks are made along the way of loading since 118 // module trees inherently require the configuration to be in a reasonably 119 // sane state: no circular dependencies, proper module sources, etc. A full 120 // suite of validations can be done by running Validate (after loading). 121 func (t *Tree) Load(s getter.Storage, mode GetMode) error { 122 t.lock.Lock() 123 defer t.lock.Unlock() 124 125 // Reset the children if we have any 126 t.children = nil 127 128 modules := t.Modules() 129 children := make(map[string]*Tree) 130 131 // Go through all the modules and get the directory for them. 132 for _, m := range modules { 133 if _, ok := children[m.Name]; ok { 134 return fmt.Errorf( 135 "module %s: duplicated. module names must be unique", m.Name) 136 } 137 138 // Determine the path to this child 139 path := make([]string, len(t.path), len(t.path)+1) 140 copy(path, t.path) 141 path = append(path, m.Name) 142 143 // Split out the subdir if we have one 144 source, subDir := getter.SourceDirSubdir(m.Source) 145 146 source, err := getter.Detect(source, t.config.Dir, getter.Detectors) 147 if err != nil { 148 return fmt.Errorf("module %s: %s", m.Name, err) 149 } 150 151 // Check if the detector introduced something new. 152 source, subDir2 := getter.SourceDirSubdir(source) 153 if subDir2 != "" { 154 subDir = filepath.Join(subDir2, subDir) 155 } 156 157 // Get the directory where this module is so we can load it 158 key := strings.Join(path, ".") 159 key = "root." + key 160 dir, ok, err := getStorage(s, key, source, mode) 161 if err != nil { 162 return err 163 } 164 if !ok { 165 return fmt.Errorf( 166 "module %s: not found, may need to be downloaded", m.Name) 167 } 168 169 // If we have a subdirectory, then merge that in 170 if subDir != "" { 171 dir = filepath.Join(dir, subDir) 172 } 173 174 // Load the configurations.Dir(source) 175 children[m.Name], err = NewTreeModule(m.Name, dir) 176 if err != nil { 177 return fmt.Errorf( 178 "module %s: %s", m.Name, err) 179 } 180 181 // Set the path of this child 182 children[m.Name].path = path 183 } 184 185 // Go through all the children and load them. 186 for _, c := range children { 187 if err := c.Load(s, mode); err != nil { 188 return err 189 } 190 } 191 192 // Set our tree up 193 t.children = children 194 195 return nil 196 } 197 198 // Path is the full path to this tree. 199 func (t *Tree) Path() []string { 200 return t.path 201 } 202 203 // String gives a nice output to describe the tree. 204 func (t *Tree) String() string { 205 var result bytes.Buffer 206 path := strings.Join(t.path, ", ") 207 if path != "" { 208 path = fmt.Sprintf(" (path: %s)", path) 209 } 210 result.WriteString(t.Name() + path + "\n") 211 212 cs := t.Children() 213 if cs == nil { 214 result.WriteString(" not loaded") 215 } else { 216 // Go through each child and get its string value, then indent it 217 // by two. 218 for _, c := range cs { 219 r := strings.NewReader(c.String()) 220 scanner := bufio.NewScanner(r) 221 for scanner.Scan() { 222 result.WriteString(" ") 223 result.WriteString(scanner.Text()) 224 result.WriteString("\n") 225 } 226 } 227 } 228 229 return result.String() 230 } 231 232 // Validate does semantic checks on the entire tree of configurations. 233 // 234 // This will call the respective config.Config.Validate() functions as well 235 // as verifying things such as parameters/outputs between the various modules. 236 // 237 // Load must be called prior to calling Validate or an error will be returned. 238 func (t *Tree) Validate() error { 239 if !t.Loaded() { 240 return fmt.Errorf("tree must be loaded before calling Validate") 241 } 242 243 // If something goes wrong, here is our error template 244 newErr := &TreeError{Name: []string{t.Name()}} 245 246 // Validate our configuration first. 247 if err := t.config.Validate(); err != nil { 248 newErr.Err = err 249 return newErr 250 } 251 252 // Get the child trees 253 children := t.Children() 254 255 // Validate all our children 256 for _, c := range children { 257 err := c.Validate() 258 if err == nil { 259 continue 260 } 261 262 verr, ok := err.(*TreeError) 263 if !ok { 264 // Unknown error, just return... 265 return err 266 } 267 268 // Append ourselves to the error and then return 269 verr.Name = append(verr.Name, t.Name()) 270 return verr 271 } 272 273 // Go over all the modules and verify that any parameters are valid 274 // variables into the module in question. 275 for _, m := range t.config.Modules { 276 tree, ok := children[m.Name] 277 if !ok { 278 // This should never happen because Load watches us 279 panic("module not found in children: " + m.Name) 280 } 281 282 // Build the variables that the module defines 283 requiredMap := make(map[string]struct{}) 284 varMap := make(map[string]struct{}) 285 for _, v := range tree.config.Variables { 286 varMap[v.Name] = struct{}{} 287 288 if v.Required() { 289 requiredMap[v.Name] = struct{}{} 290 } 291 } 292 293 // Compare to the keys in our raw config for the module 294 for k, _ := range m.RawConfig.Raw { 295 if _, ok := varMap[k]; !ok { 296 newErr.Err = fmt.Errorf( 297 "module %s: %s is not a valid parameter", 298 m.Name, k) 299 return newErr 300 } 301 302 // Remove the required 303 delete(requiredMap, k) 304 } 305 306 // If we have any required left over, they aren't set. 307 for k, _ := range requiredMap { 308 newErr.Err = fmt.Errorf( 309 "module %s: required variable %s not set", 310 m.Name, k) 311 return newErr 312 } 313 } 314 315 // Go over all the variables used and make sure that any module 316 // variables represent outputs properly. 317 for source, vs := range t.config.InterpolatedVariables() { 318 for _, v := range vs { 319 mv, ok := v.(*config.ModuleVariable) 320 if !ok { 321 continue 322 } 323 324 tree, ok := children[mv.Name] 325 if !ok { 326 // This should never happen because Load watches us 327 panic("module not found in children: " + mv.Name) 328 } 329 330 found := false 331 for _, o := range tree.config.Outputs { 332 if o.Name == mv.Field { 333 found = true 334 break 335 } 336 } 337 if !found { 338 newErr.Err = fmt.Errorf( 339 "%s: %s is not a valid output for module %s", 340 source, mv.Field, mv.Name) 341 return newErr 342 } 343 } 344 } 345 346 return nil 347 } 348 349 // TreeError is an error returned by Tree.Validate if an error occurs 350 // with validation. 351 type TreeError struct { 352 Name []string 353 Err error 354 } 355 356 func (e *TreeError) Error() string { 357 // Build up the name 358 var buf bytes.Buffer 359 for _, n := range e.Name { 360 buf.WriteString(n) 361 buf.WriteString(".") 362 } 363 buf.Truncate(buf.Len() - 1) 364 365 // Format the value 366 return fmt.Sprintf("module %s: %s", buf.String(), e.Err) 367 }