github.com/emc-cmd/terraform@v0.7.8-0.20161101145618-f16309630e7c/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 // Loaded says whether or not this tree has been loaded or not yet. 96 func (t *Tree) Loaded() bool { 97 t.lock.RLock() 98 defer t.lock.RUnlock() 99 return t.children != nil 100 } 101 102 // Modules returns the list of modules that this tree imports. 103 // 104 // This is only the imports of _this_ level of the tree. To retrieve the 105 // full nested imports, you'll have to traverse the tree. 106 func (t *Tree) Modules() []*Module { 107 result := make([]*Module, len(t.config.Modules)) 108 for i, m := range t.config.Modules { 109 result[i] = &Module{ 110 Name: m.Name, 111 Source: m.Source, 112 } 113 } 114 115 return result 116 } 117 118 // Name returns the name of the tree. This will be "<root>" for the root 119 // tree and then the module name given for any children. 120 func (t *Tree) Name() string { 121 if t.name == "" { 122 return RootName 123 } 124 125 return t.name 126 } 127 128 // Load loads the configuration of the entire tree. 129 // 130 // The parameters are used to tell the tree where to find modules and 131 // whether it can download/update modules along the way. 132 // 133 // Calling this multiple times will reload the tree. 134 // 135 // Various semantic-like checks are made along the way of loading since 136 // module trees inherently require the configuration to be in a reasonably 137 // sane state: no circular dependencies, proper module sources, etc. A full 138 // suite of validations can be done by running Validate (after loading). 139 func (t *Tree) Load(s getter.Storage, mode GetMode) error { 140 t.lock.Lock() 141 defer t.lock.Unlock() 142 143 // Reset the children if we have any 144 t.children = nil 145 146 modules := t.Modules() 147 children := make(map[string]*Tree) 148 149 // Go through all the modules and get the directory for them. 150 for _, m := range modules { 151 if _, ok := children[m.Name]; ok { 152 return fmt.Errorf( 153 "module %s: duplicated. module names must be unique", m.Name) 154 } 155 156 // Determine the path to this child 157 path := make([]string, len(t.path), len(t.path)+1) 158 copy(path, t.path) 159 path = append(path, m.Name) 160 161 // Split out the subdir if we have one 162 source, subDir := getter.SourceDirSubdir(m.Source) 163 164 source, err := getter.Detect(source, t.config.Dir, getter.Detectors) 165 if err != nil { 166 return fmt.Errorf("module %s: %s", m.Name, err) 167 } 168 169 // Check if the detector introduced something new. 170 source, subDir2 := getter.SourceDirSubdir(source) 171 if subDir2 != "" { 172 subDir = filepath.Join(subDir2, subDir) 173 } 174 175 // Get the directory where this module is so we can load it 176 key := strings.Join(path, ".") 177 key = fmt.Sprintf("root.%s-%s", key, m.Source) 178 dir, ok, err := getStorage(s, key, source, mode) 179 if err != nil { 180 return err 181 } 182 if !ok { 183 return fmt.Errorf( 184 "module %s: not found, may need to be downloaded using 'terraform get'", m.Name) 185 } 186 187 // If we have a subdirectory, then merge that in 188 if subDir != "" { 189 dir = filepath.Join(dir, subDir) 190 } 191 192 // Load the configurations.Dir(source) 193 children[m.Name], err = NewTreeModule(m.Name, dir) 194 if err != nil { 195 return fmt.Errorf( 196 "module %s: %s", m.Name, err) 197 } 198 199 // Set the path of this child 200 children[m.Name].path = path 201 } 202 203 // Go through all the children and load them. 204 for _, c := range children { 205 if err := c.Load(s, mode); err != nil { 206 return err 207 } 208 } 209 210 // Set our tree up 211 t.children = children 212 213 return nil 214 } 215 216 // Path is the full path to this tree. 217 func (t *Tree) Path() []string { 218 return t.path 219 } 220 221 // String gives a nice output to describe the tree. 222 func (t *Tree) String() string { 223 var result bytes.Buffer 224 path := strings.Join(t.path, ", ") 225 if path != "" { 226 path = fmt.Sprintf(" (path: %s)", path) 227 } 228 result.WriteString(t.Name() + path + "\n") 229 230 cs := t.Children() 231 if cs == nil { 232 result.WriteString(" not loaded") 233 } else { 234 // Go through each child and get its string value, then indent it 235 // by two. 236 for _, c := range cs { 237 r := strings.NewReader(c.String()) 238 scanner := bufio.NewScanner(r) 239 for scanner.Scan() { 240 result.WriteString(" ") 241 result.WriteString(scanner.Text()) 242 result.WriteString("\n") 243 } 244 } 245 } 246 247 return result.String() 248 } 249 250 // Validate does semantic checks on the entire tree of configurations. 251 // 252 // This will call the respective config.Config.Validate() functions as well 253 // as verifying things such as parameters/outputs between the various modules. 254 // 255 // Load must be called prior to calling Validate or an error will be returned. 256 func (t *Tree) Validate() error { 257 if !t.Loaded() { 258 return fmt.Errorf("tree must be loaded before calling Validate") 259 } 260 261 // If something goes wrong, here is our error template 262 newErr := &TreeError{Name: []string{t.Name()}} 263 264 // Validate our configuration first. 265 if err := t.config.Validate(); err != nil { 266 newErr.Err = err 267 return newErr 268 } 269 270 // Get the child trees 271 children := t.Children() 272 273 // Validate all our children 274 for _, c := range children { 275 err := c.Validate() 276 if err == nil { 277 continue 278 } 279 280 verr, ok := err.(*TreeError) 281 if !ok { 282 // Unknown error, just return... 283 return err 284 } 285 286 // Append ourselves to the error and then return 287 verr.Name = append(verr.Name, t.Name()) 288 return verr 289 } 290 291 // Go over all the modules and verify that any parameters are valid 292 // variables into the module in question. 293 for _, m := range t.config.Modules { 294 tree, ok := children[m.Name] 295 if !ok { 296 // This should never happen because Load watches us 297 panic("module not found in children: " + m.Name) 298 } 299 300 // Build the variables that the module defines 301 requiredMap := make(map[string]struct{}) 302 varMap := make(map[string]struct{}) 303 for _, v := range tree.config.Variables { 304 varMap[v.Name] = struct{}{} 305 306 if v.Required() { 307 requiredMap[v.Name] = struct{}{} 308 } 309 } 310 311 // Compare to the keys in our raw config for the module 312 for k, _ := range m.RawConfig.Raw { 313 if _, ok := varMap[k]; !ok { 314 newErr.Err = fmt.Errorf( 315 "module %s: %s is not a valid parameter", 316 m.Name, k) 317 return newErr 318 } 319 320 // Remove the required 321 delete(requiredMap, k) 322 } 323 324 // If we have any required left over, they aren't set. 325 for k, _ := range requiredMap { 326 newErr.Err = fmt.Errorf( 327 "module %s: required variable %s not set", 328 m.Name, k) 329 return newErr 330 } 331 } 332 333 // Go over all the variables used and make sure that any module 334 // variables represent outputs properly. 335 for source, vs := range t.config.InterpolatedVariables() { 336 for _, v := range vs { 337 mv, ok := v.(*config.ModuleVariable) 338 if !ok { 339 continue 340 } 341 342 tree, ok := children[mv.Name] 343 if !ok { 344 // This should never happen because Load watches us 345 panic("module not found in children: " + mv.Name) 346 } 347 348 found := false 349 for _, o := range tree.config.Outputs { 350 if o.Name == mv.Field { 351 found = true 352 break 353 } 354 } 355 if !found { 356 newErr.Err = fmt.Errorf( 357 "%s: %s is not a valid output for module %s", 358 source, mv.Field, mv.Name) 359 return newErr 360 } 361 } 362 } 363 364 return nil 365 } 366 367 // TreeError is an error returned by Tree.Validate if an error occurs 368 // with validation. 369 type TreeError struct { 370 Name []string 371 Err error 372 } 373 374 func (e *TreeError) Error() string { 375 // Build up the name 376 var buf bytes.Buffer 377 for _, n := range e.Name { 378 buf.WriteString(n) 379 buf.WriteString(".") 380 } 381 buf.Truncate(buf.Len() - 1) 382 383 // Format the value 384 return fmt.Sprintf("module %s: %s", buf.String(), e.Err) 385 }