github.com/paybyphone/terraform@v0.9.5-0.20170613192930-9706042ddd51/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 // NewEmptyTree returns a new tree that is empty (contains no configuration). 37 func NewEmptyTree() *Tree { 38 t := &Tree{config: &config.Config{}} 39 40 // We do this dummy load so that the tree is marked as "loaded". It 41 // should never fail because this is just about a no-op. If it does fail 42 // we panic so we can know its a bug. 43 if err := t.Load(nil, GetModeGet); err != nil { 44 panic(err) 45 } 46 47 return t 48 } 49 50 // NewTreeModule is like NewTree except it parses the configuration in 51 // the directory and gives it a specific name. Use a blank name "" to specify 52 // the root module. 53 func NewTreeModule(name, dir string) (*Tree, error) { 54 c, err := config.LoadDir(dir) 55 if err != nil { 56 return nil, err 57 } 58 59 return NewTree(name, c), nil 60 } 61 62 // Config returns the configuration for this module. 63 func (t *Tree) Config() *config.Config { 64 return t.config 65 } 66 67 // Child returns the child with the given path (by name). 68 func (t *Tree) Child(path []string) *Tree { 69 if t == nil { 70 return nil 71 } 72 73 if len(path) == 0 { 74 return t 75 } 76 77 c := t.Children()[path[0]] 78 if c == nil { 79 return nil 80 } 81 82 return c.Child(path[1:]) 83 } 84 85 // Children returns the children of this tree (the modules that are 86 // imported by this root). 87 // 88 // This will only return a non-nil value after Load is called. 89 func (t *Tree) Children() map[string]*Tree { 90 t.lock.RLock() 91 defer t.lock.RUnlock() 92 return t.children 93 } 94 95 // DeepEach calls the provided callback for the receiver and then all of 96 // its descendents in the tree, allowing an operation to be performed on 97 // all modules in the tree. 98 // 99 // Parents will be visited before their children but otherwise the order is 100 // not defined. 101 func (t *Tree) DeepEach(cb func(*Tree)) { 102 t.lock.RLock() 103 defer t.lock.RUnlock() 104 t.deepEach(cb) 105 } 106 107 func (t *Tree) deepEach(cb func(*Tree)) { 108 cb(t) 109 for _, c := range t.children { 110 c.deepEach(cb) 111 } 112 } 113 114 // Loaded says whether or not this tree has been loaded or not yet. 115 func (t *Tree) Loaded() bool { 116 t.lock.RLock() 117 defer t.lock.RUnlock() 118 return t.children != nil 119 } 120 121 // Modules returns the list of modules that this tree imports. 122 // 123 // This is only the imports of _this_ level of the tree. To retrieve the 124 // full nested imports, you'll have to traverse the tree. 125 func (t *Tree) Modules() []*Module { 126 result := make([]*Module, len(t.config.Modules)) 127 for i, m := range t.config.Modules { 128 result[i] = &Module{ 129 Name: m.Name, 130 Source: m.Source, 131 } 132 } 133 134 return result 135 } 136 137 // Name returns the name of the tree. This will be "<root>" for the root 138 // tree and then the module name given for any children. 139 func (t *Tree) Name() string { 140 if t.name == "" { 141 return RootName 142 } 143 144 return t.name 145 } 146 147 // Load loads the configuration of the entire tree. 148 // 149 // The parameters are used to tell the tree where to find modules and 150 // whether it can download/update modules along the way. 151 // 152 // Calling this multiple times will reload the tree. 153 // 154 // Various semantic-like checks are made along the way of loading since 155 // module trees inherently require the configuration to be in a reasonably 156 // sane state: no circular dependencies, proper module sources, etc. A full 157 // suite of validations can be done by running Validate (after loading). 158 func (t *Tree) Load(s getter.Storage, mode GetMode) error { 159 t.lock.Lock() 160 defer t.lock.Unlock() 161 162 // Reset the children if we have any 163 t.children = nil 164 165 modules := t.Modules() 166 children := make(map[string]*Tree) 167 168 // Go through all the modules and get the directory for them. 169 for _, m := range modules { 170 if _, ok := children[m.Name]; ok { 171 return fmt.Errorf( 172 "module %s: duplicated. module names must be unique", m.Name) 173 } 174 175 // Determine the path to this child 176 path := make([]string, len(t.path), len(t.path)+1) 177 copy(path, t.path) 178 path = append(path, m.Name) 179 180 // Split out the subdir if we have one 181 source, subDir := getter.SourceDirSubdir(m.Source) 182 183 source, err := getter.Detect(source, t.config.Dir, getter.Detectors) 184 if err != nil { 185 return fmt.Errorf("module %s: %s", m.Name, err) 186 } 187 188 // Check if the detector introduced something new. 189 source, subDir2 := getter.SourceDirSubdir(source) 190 if subDir2 != "" { 191 subDir = filepath.Join(subDir2, subDir) 192 } 193 194 // Get the directory where this module is so we can load it 195 key := strings.Join(path, ".") 196 key = fmt.Sprintf("root.%s-%s", key, m.Source) 197 dir, ok, err := getStorage(s, key, source, mode) 198 if err != nil { 199 return err 200 } 201 if !ok { 202 return fmt.Errorf( 203 "module %s: not found, may need to be downloaded using 'terraform get'", m.Name) 204 } 205 206 // If we have a subdirectory, then merge that in 207 if subDir != "" { 208 dir = filepath.Join(dir, subDir) 209 } 210 211 // Load the configurations.Dir(source) 212 children[m.Name], err = NewTreeModule(m.Name, dir) 213 if err != nil { 214 return fmt.Errorf( 215 "module %s: %s", m.Name, err) 216 } 217 218 // Set the path of this child 219 children[m.Name].path = path 220 } 221 222 // Go through all the children and load them. 223 for _, c := range children { 224 if err := c.Load(s, mode); err != nil { 225 return err 226 } 227 } 228 229 // Set our tree up 230 t.children = children 231 232 return nil 233 } 234 235 // Path is the full path to this tree. 236 func (t *Tree) Path() []string { 237 return t.path 238 } 239 240 // String gives a nice output to describe the tree. 241 func (t *Tree) String() string { 242 var result bytes.Buffer 243 path := strings.Join(t.path, ", ") 244 if path != "" { 245 path = fmt.Sprintf(" (path: %s)", path) 246 } 247 result.WriteString(t.Name() + path + "\n") 248 249 cs := t.Children() 250 if cs == nil { 251 result.WriteString(" not loaded") 252 } else { 253 // Go through each child and get its string value, then indent it 254 // by two. 255 for _, c := range cs { 256 r := strings.NewReader(c.String()) 257 scanner := bufio.NewScanner(r) 258 for scanner.Scan() { 259 result.WriteString(" ") 260 result.WriteString(scanner.Text()) 261 result.WriteString("\n") 262 } 263 } 264 } 265 266 return result.String() 267 } 268 269 // Validate does semantic checks on the entire tree of configurations. 270 // 271 // This will call the respective config.Config.Validate() functions as well 272 // as verifying things such as parameters/outputs between the various modules. 273 // 274 // Load must be called prior to calling Validate or an error will be returned. 275 func (t *Tree) Validate() error { 276 if !t.Loaded() { 277 return fmt.Errorf("tree must be loaded before calling Validate") 278 } 279 280 // If something goes wrong, here is our error template 281 newErr := &treeError{Name: []string{t.Name()}} 282 283 // Terraform core does not handle root module children named "root". 284 // We plan to fix this in the future but this bug was brought up in 285 // the middle of a release and we don't want to introduce wide-sweeping 286 // changes at that time. 287 if len(t.path) == 1 && t.name == "root" { 288 return fmt.Errorf("root module cannot contain module named 'root'") 289 } 290 291 // Validate our configuration first. 292 if err := t.config.Validate(); err != nil { 293 newErr.Add(err) 294 } 295 296 // If we're the root, we do extra validation. This validation usually 297 // requires the entire tree (since children don't have parent pointers). 298 if len(t.path) == 0 { 299 if err := t.validateProviderAlias(); err != nil { 300 newErr.Add(err) 301 } 302 } 303 304 // Get the child trees 305 children := t.Children() 306 307 // Validate all our children 308 for _, c := range children { 309 err := c.Validate() 310 if err == nil { 311 continue 312 } 313 314 verr, ok := err.(*treeError) 315 if !ok { 316 // Unknown error, just return... 317 return err 318 } 319 320 // Append ourselves to the error and then return 321 verr.Name = append(verr.Name, t.Name()) 322 newErr.AddChild(verr) 323 } 324 325 // Go over all the modules and verify that any parameters are valid 326 // variables into the module in question. 327 for _, m := range t.config.Modules { 328 tree, ok := children[m.Name] 329 if !ok { 330 // This should never happen because Load watches us 331 panic("module not found in children: " + m.Name) 332 } 333 334 // Build the variables that the module defines 335 requiredMap := make(map[string]struct{}) 336 varMap := make(map[string]struct{}) 337 for _, v := range tree.config.Variables { 338 varMap[v.Name] = struct{}{} 339 340 if v.Required() { 341 requiredMap[v.Name] = struct{}{} 342 } 343 } 344 345 // Compare to the keys in our raw config for the module 346 for k, _ := range m.RawConfig.Raw { 347 if _, ok := varMap[k]; !ok { 348 newErr.Add(fmt.Errorf( 349 "module %s: %s is not a valid parameter", 350 m.Name, k)) 351 } 352 353 // Remove the required 354 delete(requiredMap, k) 355 } 356 357 // If we have any required left over, they aren't set. 358 for k, _ := range requiredMap { 359 newErr.Add(fmt.Errorf( 360 "module %s: required variable %q not set", 361 m.Name, k)) 362 } 363 } 364 365 // Go over all the variables used and make sure that any module 366 // variables represent outputs properly. 367 for source, vs := range t.config.InterpolatedVariables() { 368 for _, v := range vs { 369 mv, ok := v.(*config.ModuleVariable) 370 if !ok { 371 continue 372 } 373 374 tree, ok := children[mv.Name] 375 if !ok { 376 newErr.Add(fmt.Errorf( 377 "%s: undefined module referenced %s", 378 source, mv.Name)) 379 continue 380 } 381 382 found := false 383 for _, o := range tree.config.Outputs { 384 if o.Name == mv.Field { 385 found = true 386 break 387 } 388 } 389 if !found { 390 newErr.Add(fmt.Errorf( 391 "%s: %s is not a valid output for module %s", 392 source, mv.Field, mv.Name)) 393 } 394 } 395 } 396 397 return newErr.ErrOrNil() 398 } 399 400 // treeError is an error use by Tree.Validate to accumulates all 401 // validation errors. 402 type treeError struct { 403 Name []string 404 Errs []error 405 Children []*treeError 406 } 407 408 func (e *treeError) Add(err error) { 409 e.Errs = append(e.Errs, err) 410 } 411 412 func (e *treeError) AddChild(err *treeError) { 413 e.Children = append(e.Children, err) 414 } 415 416 func (e *treeError) ErrOrNil() error { 417 if len(e.Errs) > 0 || len(e.Children) > 0 { 418 return e 419 } 420 return nil 421 } 422 423 func (e *treeError) Error() string { 424 name := strings.Join(e.Name, ".") 425 var out bytes.Buffer 426 fmt.Fprintf(&out, "module %s: ", name) 427 428 if len(e.Errs) == 1 { 429 // single like error 430 out.WriteString(e.Errs[0].Error()) 431 } else { 432 // multi-line error 433 for _, err := range e.Errs { 434 fmt.Fprintf(&out, "\n %s", err) 435 } 436 } 437 438 if len(e.Children) > 0 { 439 // start the next error on a new line 440 out.WriteString("\n ") 441 } 442 for _, child := range e.Children { 443 out.WriteString(child.Error()) 444 } 445 446 return out.String() 447 }