github.com/maheshbr/terraform@v0.3.1-0.20141020033300-deec7194a3ea/terraform/context.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 "os" 7 "sort" 8 "strconv" 9 "strings" 10 "sync" 11 "sync/atomic" 12 13 "github.com/hashicorp/terraform/config" 14 "github.com/hashicorp/terraform/config/module" 15 "github.com/hashicorp/terraform/depgraph" 16 "github.com/hashicorp/terraform/helper/multierror" 17 ) 18 19 // This is a function type used to implement a walker for the resource 20 // tree internally on the Terraform structure. 21 type genericWalkFunc func(*walkContext, *Resource) error 22 23 // Context represents all the context that Terraform needs in order to 24 // perform operations on infrastructure. This structure is built using 25 // ContextOpts and NewContext. See the documentation for those. 26 // 27 // Additionally, a context can be created from a Plan using Plan.Context. 28 type Context struct { 29 module *module.Tree 30 diff *Diff 31 hooks []Hook 32 state *State 33 providerConfig map[string]map[string]map[string]interface{} 34 providers map[string]ResourceProviderFactory 35 provisioners map[string]ResourceProvisionerFactory 36 variables map[string]string 37 uiInput UIInput 38 39 parallelSem Semaphore // Semaphore used to limit parallelism 40 l sync.Mutex // Lock acquired during any task 41 sl sync.RWMutex // Lock acquired to R/W internal data 42 runCh <-chan struct{} 43 sh *stopHook 44 } 45 46 // ContextOpts are the user-creatable configuration structure to create 47 // a context with NewContext. 48 type ContextOpts struct { 49 Diff *Diff 50 Hooks []Hook 51 Module *module.Tree 52 Parallelism int 53 State *State 54 Providers map[string]ResourceProviderFactory 55 Provisioners map[string]ResourceProvisionerFactory 56 Variables map[string]string 57 58 UIInput UIInput 59 } 60 61 // InputMode defines what sort of input will be asked for when Input 62 // is called on Context. 63 type InputMode byte 64 65 const ( 66 // InputModeVar asks for variables 67 InputModeVar InputMode = 1 << iota 68 69 // InputModeProvider asks for provider variables 70 InputModeProvider 71 72 // InputModeStd is the standard operating mode and asks for both variables 73 // and providers. 74 InputModeStd = InputModeVar | InputModeProvider 75 ) 76 77 // NewContext creates a new context. 78 // 79 // Once a context is created, the pointer values within ContextOpts should 80 // not be mutated in any way, since the pointers are copied, not the values 81 // themselves. 82 func NewContext(opts *ContextOpts) *Context { 83 sh := new(stopHook) 84 85 // Copy all the hooks and add our stop hook. We don't append directly 86 // to the Config so that we're not modifying that in-place. 87 hooks := make([]Hook, len(opts.Hooks)+1) 88 copy(hooks, opts.Hooks) 89 hooks[len(opts.Hooks)] = sh 90 91 // Make the parallelism channel 92 par := opts.Parallelism 93 if par == 0 { 94 par = 10 95 } 96 97 return &Context{ 98 diff: opts.Diff, 99 hooks: hooks, 100 module: opts.Module, 101 state: opts.State, 102 providerConfig: make(map[string]map[string]map[string]interface{}), 103 providers: opts.Providers, 104 provisioners: opts.Provisioners, 105 variables: opts.Variables, 106 uiInput: opts.UIInput, 107 108 parallelSem: NewSemaphore(par), 109 sh: sh, 110 } 111 } 112 113 // Apply applies the changes represented by this context and returns 114 // the resulting state. 115 // 116 // In addition to returning the resulting state, this context is updated 117 // with the latest state. 118 func (c *Context) Apply() (*State, error) { 119 v := c.acquireRun() 120 defer c.releaseRun(v) 121 122 // Set our state right away. No matter what, this IS our new state, 123 // even if there is an error below. 124 c.state = c.state.deepcopy() 125 if c.state == nil { 126 c.state = &State{} 127 } 128 c.state.init() 129 130 // Walk 131 log.Printf("[INFO] Apply walk starting") 132 err := c.walkContext(walkApply, rootModulePath).Walk() 133 log.Printf("[INFO] Apply walk complete") 134 135 // Prune the state so that we have as clean a state as possible 136 c.state.prune() 137 138 return c.state, err 139 } 140 141 // Graph returns the graph for this context. 142 func (c *Context) Graph() (*depgraph.Graph, error) { 143 return Graph(&GraphOpts{ 144 Diff: c.diff, 145 Module: c.module, 146 Providers: c.providers, 147 Provisioners: c.provisioners, 148 State: c.state, 149 }) 150 } 151 152 // Input asks for input to fill variables and provider configurations. 153 // This modifies the configuration in-place, so asking for Input twice 154 // may result in different UI output showing different current values. 155 func (c *Context) Input(mode InputMode) error { 156 v := c.acquireRun() 157 defer c.releaseRun(v) 158 159 if mode&InputModeVar != 0 { 160 // Walk the variables first for the root module. We walk them in 161 // alphabetical order for UX reasons. 162 rootConf := c.module.Config() 163 names := make([]string, len(rootConf.Variables)) 164 m := make(map[string]*config.Variable) 165 for i, v := range rootConf.Variables { 166 names[i] = v.Name 167 m[v.Name] = v 168 } 169 sort.Strings(names) 170 for _, n := range names { 171 v := m[n] 172 switch v.Type() { 173 case config.VariableTypeMap: 174 continue 175 case config.VariableTypeString: 176 // Good! 177 default: 178 panic(fmt.Sprintf("Unknown variable type: %s", v.Type())) 179 } 180 181 var defaultString string 182 if v.Default != nil { 183 defaultString = v.Default.(string) 184 } 185 186 // Ask the user for a value for this variable 187 var value string 188 for { 189 var err error 190 value, err = c.uiInput.Input(&InputOpts{ 191 Id: fmt.Sprintf("var.%s", n), 192 Query: fmt.Sprintf("var.%s", n), 193 Default: defaultString, 194 Description: v.Description, 195 }) 196 if err != nil { 197 return fmt.Errorf( 198 "Error asking for %s: %s", n, err) 199 } 200 201 if value == "" && v.Required() { 202 // Redo if it is required. 203 continue 204 } 205 206 if value == "" { 207 // No value, just exit the loop. With no value, we just 208 // use whatever is currently set in variables. 209 break 210 } 211 212 break 213 } 214 215 if value != "" { 216 c.variables[n] = value 217 } 218 } 219 } 220 221 if mode&InputModeProvider != 0 { 222 // Create the walk context and walk the inputs, which will gather the 223 // inputs for any resource providers. 224 wc := c.walkContext(walkInput, rootModulePath) 225 wc.Meta = new(walkInputMeta) 226 return wc.Walk() 227 } 228 229 return nil 230 } 231 232 // Plan generates an execution plan for the given context. 233 // 234 // The execution plan encapsulates the context and can be stored 235 // in order to reinstantiate a context later for Apply. 236 // 237 // Plan also updates the diff of this context to be the diff generated 238 // by the plan, so Apply can be called after. 239 func (c *Context) Plan(opts *PlanOpts) (*Plan, error) { 240 v := c.acquireRun() 241 defer c.releaseRun(v) 242 243 p := &Plan{ 244 Module: c.module, 245 Vars: c.variables, 246 State: c.state, 247 } 248 249 wc := c.walkContext(walkInvalid, rootModulePath) 250 wc.Meta = p 251 252 if opts != nil && opts.Destroy { 253 wc.Operation = walkPlanDestroy 254 } else { 255 // Set our state to be something temporary. We do this so that 256 // the plan can update a fake state so that variables work, then 257 // we replace it back with our old state. 258 old := c.state 259 if old == nil { 260 c.state = &State{} 261 c.state.init() 262 } else { 263 c.state = old.deepcopy() 264 } 265 defer func() { 266 c.state = old 267 }() 268 269 wc.Operation = walkPlan 270 } 271 272 // Walk and run the plan 273 err := wc.Walk() 274 275 // Update the diff so that our context is up-to-date 276 c.diff = p.Diff 277 278 return p, err 279 } 280 281 // Refresh goes through all the resources in the state and refreshes them 282 // to their latest state. This will update the state that this context 283 // works with, along with returning it. 284 // 285 // Even in the case an error is returned, the state will be returned and 286 // will potentially be partially updated. 287 func (c *Context) Refresh() (*State, error) { 288 v := c.acquireRun() 289 defer c.releaseRun(v) 290 291 // Update our state 292 c.state = c.state.deepcopy() 293 294 // Walk the graph 295 err := c.walkContext(walkRefresh, rootModulePath).Walk() 296 297 // Prune the state 298 c.state.prune() 299 return c.state, err 300 } 301 302 // Stop stops the running task. 303 // 304 // Stop will block until the task completes. 305 func (c *Context) Stop() { 306 c.l.Lock() 307 ch := c.runCh 308 309 // If we aren't running, then just return 310 if ch == nil { 311 c.l.Unlock() 312 return 313 } 314 315 // Tell the hook we want to stop 316 c.sh.Stop() 317 318 // Wait for us to stop 319 c.l.Unlock() 320 <-ch 321 } 322 323 // Validate validates the configuration and returns any warnings or errors. 324 func (c *Context) Validate() ([]string, []error) { 325 var rerr *multierror.Error 326 327 // Validate the configuration itself 328 if err := c.module.Validate(); err != nil { 329 rerr = multierror.ErrorAppend(rerr, err) 330 } 331 332 // This only needs to be done for the root module, since inter-module 333 // variables are validated in the module tree. 334 if config := c.module.Config(); config != nil { 335 // Validate the user variables 336 if errs := smcUserVariables(config, c.variables); len(errs) > 0 { 337 rerr = multierror.ErrorAppend(rerr, errs...) 338 } 339 } 340 341 // Validate the entire graph 342 walkMeta := new(walkValidateMeta) 343 wc := c.walkContext(walkValidate, rootModulePath) 344 wc.Meta = walkMeta 345 if err := wc.Walk(); err != nil { 346 rerr = multierror.ErrorAppend(rerr, err) 347 } 348 349 // Flatten the warns/errs so that we get all the module errors as well, 350 // then aggregate. 351 warns, errs := walkMeta.Flatten() 352 if len(errs) > 0 { 353 rerr = multierror.ErrorAppend(rerr, errs...) 354 } 355 356 errs = nil 357 if rerr != nil && len(rerr.Errors) > 0 { 358 errs = rerr.Errors 359 } 360 361 return warns, errs 362 } 363 364 func (c *Context) acquireRun() chan<- struct{} { 365 c.l.Lock() 366 defer c.l.Unlock() 367 368 // Wait for no channel to exist 369 for c.runCh != nil { 370 c.l.Unlock() 371 ch := c.runCh 372 <-ch 373 c.l.Lock() 374 } 375 376 ch := make(chan struct{}) 377 c.runCh = ch 378 return ch 379 } 380 381 func (c *Context) releaseRun(ch chan<- struct{}) { 382 c.l.Lock() 383 defer c.l.Unlock() 384 385 close(ch) 386 c.runCh = nil 387 c.sh.Reset() 388 } 389 390 func (c *Context) walkContext(op walkOperation, path []string) *walkContext { 391 // Get the config structure 392 m := c.module 393 for _, n := range path[1:] { 394 cs := m.Children() 395 m = cs[n] 396 } 397 var conf *config.Config 398 if m != nil { 399 conf = m.Config() 400 } 401 402 // Calculate the default variable values 403 defaultVars := make(map[string]string) 404 if conf != nil { 405 for _, v := range conf.Variables { 406 for k, val := range v.DefaultsMap() { 407 defaultVars[k] = val 408 } 409 } 410 } 411 412 return &walkContext{ 413 Context: c, 414 Operation: op, 415 Path: path, 416 Variables: c.variables, 417 418 defaultVariables: defaultVars, 419 } 420 } 421 422 // walkContext is the context in which a graph walk is done. It stores 423 // much the same as a Context but works on a specific module. 424 type walkContext struct { 425 Context *Context 426 Meta interface{} 427 Operation walkOperation 428 Path []string 429 Variables map[string]string 430 431 defaultVariables map[string]string 432 433 // This is only set manually by subsequent context creations 434 // in genericWalkFunc. 435 graph *depgraph.Graph 436 } 437 438 // walkOperation is an enum which tells the walkContext what to do. 439 type walkOperation byte 440 441 const ( 442 walkInvalid walkOperation = iota 443 walkInput 444 walkApply 445 walkPlan 446 walkPlanDestroy 447 walkRefresh 448 walkValidate 449 ) 450 451 func (c *walkContext) Walk() error { 452 g := c.graph 453 if g == nil { 454 gopts := &GraphOpts{ 455 Module: c.Context.module, 456 Providers: c.Context.providers, 457 Provisioners: c.Context.provisioners, 458 State: c.Context.state, 459 } 460 if c.Operation == walkApply { 461 gopts.Diff = c.Context.diff 462 } 463 464 var err error 465 g, err = Graph(gopts) 466 if err != nil { 467 return err 468 } 469 } 470 471 var walkFn depgraph.WalkFunc 472 switch c.Operation { 473 case walkInput: 474 walkFn = c.inputWalkFn() 475 case walkApply: 476 walkFn = c.applyWalkFn() 477 case walkPlan: 478 walkFn = c.planWalkFn() 479 case walkPlanDestroy: 480 walkFn = c.planDestroyWalkFn() 481 case walkRefresh: 482 walkFn = c.refreshWalkFn() 483 case walkValidate: 484 walkFn = c.validateWalkFn() 485 default: 486 panic(fmt.Sprintf("unknown operation: %s", c.Operation)) 487 } 488 489 if err := g.Walk(walkFn); err != nil { 490 return err 491 } 492 493 switch c.Operation { 494 case walkInput: 495 fallthrough 496 case walkValidate: 497 // Don't calculate outputs 498 return nil 499 } 500 501 // We did an apply, so we need to calculate the outputs. If we have no 502 // outputs, then we're done. 503 m := c.Context.module 504 for _, n := range c.Path[1:] { 505 cs := m.Children() 506 m = cs[n] 507 } 508 if m == nil { 509 return nil 510 } 511 conf := m.Config() 512 if len(conf.Outputs) == 0 { 513 return nil 514 } 515 516 // Likewise, if we have no resources in our state, we're done. This 517 // guards against the case that we destroyed. 518 mod := c.Context.state.ModuleByPath(c.Path) 519 if mod == nil { 520 return nil 521 } 522 if c.Operation == walkApply { 523 // On Apply, we prune so that we don't do outputs if we destroyed 524 mod.prune() 525 } 526 if len(mod.Resources) == 0 { 527 mod.Outputs = nil 528 return nil 529 } 530 531 outputs := make(map[string]string) 532 for _, o := range conf.Outputs { 533 if err := c.computeVars(o.RawConfig, nil); err != nil { 534 return err 535 } 536 vraw := o.RawConfig.Config()["value"] 537 if vraw == nil { 538 // This likely means that the result of the output is 539 // a computed variable. 540 if o.RawConfig.Raw["value"] != nil { 541 vraw = config.UnknownVariableValue 542 } 543 } 544 if vraw != nil { 545 outputs[o.Name] = vraw.(string) 546 } 547 } 548 549 // Assign the outputs to the root module 550 mod.Outputs = outputs 551 552 return nil 553 } 554 555 func (c *walkContext) inputWalkFn() depgraph.WalkFunc { 556 meta := c.Meta.(*walkInputMeta) 557 meta.Lock() 558 if meta.Done == nil { 559 meta.Done = make(map[string]struct{}) 560 } 561 meta.Unlock() 562 563 return func(n *depgraph.Noun) error { 564 // If it is the root node, ignore 565 if n.Name == GraphRootNode { 566 return nil 567 } 568 569 switch rn := n.Meta.(type) { 570 case *GraphNodeModule: 571 // Build another walkContext for this module and walk it. 572 wc := c.Context.walkContext(c.Operation, rn.Path) 573 574 // Set the graph to specifically walk this subgraph 575 wc.graph = rn.Graph 576 577 // Preserve the meta 578 wc.Meta = c.Meta 579 580 return wc.Walk() 581 case *GraphNodeResource: 582 // Resources don't matter for input. Continue. 583 return nil 584 case *GraphNodeResourceProvider: 585 // Acquire the lock the whole time so we only ask for input 586 // one at a time. 587 meta.Lock() 588 defer meta.Unlock() 589 590 // If we already did this provider, then we're done. 591 if _, ok := meta.Done[rn.ID]; ok { 592 return nil 593 } 594 595 // Get the raw configuration because this is what we 596 // pass into the API. 597 var raw *config.RawConfig 598 sharedProvider := rn.Provider 599 if sharedProvider.Config != nil { 600 raw = sharedProvider.Config.RawConfig 601 } 602 rc := NewResourceConfig(raw) 603 rc.Config = make(map[string]interface{}) 604 605 // Wrap the input into a namespace 606 input := &PrefixUIInput{ 607 IdPrefix: fmt.Sprintf("provider.%s", rn.ID), 608 QueryPrefix: fmt.Sprintf("provider.%s.", rn.ID), 609 UIInput: c.Context.uiInput, 610 } 611 612 // Go through each provider and capture the input necessary 613 // to satisfy it. 614 configs := make(map[string]map[string]interface{}) 615 for k, p := range sharedProvider.Providers { 616 newc, err := p.Input(input, rc) 617 if err != nil { 618 return fmt.Errorf( 619 "Error configuring %s: %s", k, err) 620 } 621 if newc != nil && len(newc.Config) > 0 { 622 configs[k] = newc.Config 623 } 624 } 625 626 // Mark this provider as done 627 meta.Done[rn.ID] = struct{}{} 628 629 // Set the configuration 630 c.Context.providerConfig[rn.ID] = configs 631 } 632 633 return nil 634 } 635 } 636 637 func (c *walkContext) applyWalkFn() depgraph.WalkFunc { 638 cb := func(c *walkContext, r *Resource) error { 639 var err error 640 641 diff := r.Diff 642 if diff.Empty() { 643 log.Printf("[DEBUG] %s: Diff is empty. Will not apply.", r.Id) 644 return nil 645 } 646 647 is := r.State 648 if is == nil { 649 is = new(InstanceState) 650 } 651 is.init() 652 653 if !diff.Destroy { 654 // Since we need the configuration, interpolate the variables 655 if err := r.Config.interpolate(c, r); err != nil { 656 return err 657 } 658 659 diff, err = r.Provider.Diff(r.Info, is, r.Config) 660 if err != nil { 661 return err 662 } 663 664 // This can happen if we aren't actually applying anything 665 // except an ID (the "null" provider). It is not really an issue 666 // since the Same check later down will catch any real problems. 667 if diff == nil { 668 diff = new(InstanceDiff) 669 diff.init() 670 } 671 672 // Delete id from the diff because it is dependent on 673 // our internal plan function. 674 delete(r.Diff.Attributes, "id") 675 delete(diff.Attributes, "id") 676 677 // Verify the diffs are the same 678 if !r.Diff.Same(diff) { 679 log.Printf( 680 "[ERROR] Diffs don't match.\n\nDiff 1: %#v"+ 681 "\n\nDiff 2: %#v", 682 r.Diff, diff) 683 return fmt.Errorf( 684 "%s: diffs didn't match during apply. This is a "+ 685 "bug with the resource provider, please report a bug.", 686 r.Id) 687 } 688 } 689 690 // Remove any output values from the diff 691 for k, ad := range diff.Attributes { 692 if ad.Type == DiffAttrOutput { 693 delete(diff.Attributes, k) 694 } 695 } 696 697 for _, h := range c.Context.hooks { 698 handleHook(h.PreApply(r.Info, is, diff)) 699 } 700 701 // We create a new instance if there was no ID 702 // previously or the diff requires re-creating the 703 // underlying instance 704 createNew := (is.ID == "" && !diff.Destroy) || diff.RequiresNew() 705 706 // With the completed diff, apply! 707 log.Printf("[DEBUG] %s: Executing Apply", r.Id) 708 is, applyerr := r.Provider.Apply(r.Info, is, diff) 709 710 var errs []error 711 if applyerr != nil { 712 errs = append(errs, applyerr) 713 } 714 715 // Make sure the result is instantiated 716 if is == nil { 717 is = new(InstanceState) 718 } 719 is.init() 720 721 // Force the "id" attribute to be our ID 722 if is.ID != "" { 723 is.Attributes["id"] = is.ID 724 } 725 726 for ak, av := range is.Attributes { 727 // If the value is the unknown variable value, then it is an error. 728 // In this case we record the error and remove it from the state 729 if av == config.UnknownVariableValue { 730 errs = append(errs, fmt.Errorf( 731 "Attribute with unknown value: %s", ak)) 732 delete(is.Attributes, ak) 733 } 734 } 735 736 // Set the result state 737 r.State = is 738 c.persistState(r) 739 740 // Invoke any provisioners we have defined. This is only done 741 // if the resource was created, as updates or deletes do not 742 // invoke provisioners. 743 // 744 // Additionally, we need to be careful to not run this if there 745 // was an error during the provider apply. 746 tainted := false 747 if createNew && len(r.Provisioners) > 0 { 748 if applyerr == nil { 749 // If the apply succeeded, we have to run the provisioners 750 for _, h := range c.Context.hooks { 751 handleHook(h.PreProvisionResource(r.Info, is)) 752 } 753 754 if err := c.applyProvisioners(r, is); err != nil { 755 errs = append(errs, err) 756 tainted = true 757 } 758 759 for _, h := range c.Context.hooks { 760 handleHook(h.PostProvisionResource(r.Info, is)) 761 } 762 } else { 763 // If we failed to create properly and we have provisioners, 764 // then we have to mark ourselves as tainted to try again. 765 tainted = true 766 } 767 } 768 769 // If we're tainted then we need to update some flags 770 if tainted && r.Flags&FlagTainted == 0 { 771 r.Flags &^= FlagPrimary 772 r.Flags &^= FlagHasTainted 773 r.Flags |= FlagTainted 774 r.TaintedIndex = -1 775 c.persistState(r) 776 } 777 778 for _, h := range c.Context.hooks { 779 handleHook(h.PostApply(r.Info, is, applyerr)) 780 } 781 782 // Determine the new state and update variables 783 err = nil 784 if len(errs) > 0 { 785 err = &multierror.Error{Errors: errs} 786 } 787 788 return err 789 } 790 791 return c.genericWalkFn(cb) 792 } 793 794 func (c *walkContext) planWalkFn() depgraph.WalkFunc { 795 var l sync.Mutex 796 797 // Initialize the result 798 result := c.Meta.(*Plan) 799 result.init() 800 801 cb := func(c *walkContext, r *Resource) error { 802 if r.Flags&FlagTainted != 0 { 803 // We don't diff tainted resources. 804 return nil 805 } 806 807 var diff *InstanceDiff 808 809 is := r.State 810 811 for _, h := range c.Context.hooks { 812 handleHook(h.PreDiff(r.Info, is)) 813 } 814 815 if r.Flags&FlagOrphan != 0 { 816 log.Printf("[DEBUG] %s: Orphan, marking for destroy", r.Id) 817 818 // This is an orphan (no config), so we mark it to be destroyed 819 diff = &InstanceDiff{Destroy: true} 820 } else { 821 // Make sure the configuration is interpolated 822 if err := r.Config.interpolate(c, r); err != nil { 823 return err 824 } 825 826 // Get a diff from the newest state 827 log.Printf("[DEBUG] %s: Executing diff", r.Id) 828 var err error 829 830 diffIs := is 831 if diffIs == nil || r.Flags&FlagHasTainted != 0 { 832 // If we're tainted, we pretend to create a new thing. 833 diffIs = new(InstanceState) 834 } 835 diffIs.init() 836 837 diff, err = r.Provider.Diff(r.Info, diffIs, r.Config) 838 if err != nil { 839 return err 840 } 841 } 842 843 if diff == nil { 844 diff = new(InstanceDiff) 845 } 846 847 if r.Flags&FlagHasTainted != 0 { 848 // This primary has a tainted resource, so just mark for 849 // destroy... 850 log.Printf("[DEBUG] %s: Tainted children, marking for destroy", r.Id) 851 diff.DestroyTainted = true 852 } 853 854 if diff.RequiresNew() && is != nil && is.ID != "" { 855 // This will also require a destroy 856 diff.Destroy = true 857 } 858 859 if diff.RequiresNew() || is == nil || is.ID == "" { 860 var oldID string 861 if is != nil { 862 oldID = is.Attributes["id"] 863 } 864 865 // Add diff to compute new ID 866 diff.init() 867 diff.Attributes["id"] = &ResourceAttrDiff{ 868 Old: oldID, 869 NewComputed: true, 870 RequiresNew: true, 871 Type: DiffAttrOutput, 872 } 873 } 874 875 if !diff.Empty() { 876 log.Printf("[DEBUG] %s: Diff: %#v", r.Id, diff) 877 878 l.Lock() 879 md := result.Diff.ModuleByPath(c.Path) 880 if md == nil { 881 md = result.Diff.AddModule(c.Path) 882 } 883 md.Resources[r.Id] = diff 884 l.Unlock() 885 } 886 887 for _, h := range c.Context.hooks { 888 handleHook(h.PostDiff(r.Info, diff)) 889 } 890 891 // Determine the new state and update variables 892 if !diff.Empty() { 893 is = is.MergeDiff(diff) 894 } 895 896 // Set it so that it can be updated 897 r.State = is 898 c.persistState(r) 899 900 return nil 901 } 902 903 return c.genericWalkFn(cb) 904 } 905 906 func (c *walkContext) planDestroyWalkFn() depgraph.WalkFunc { 907 var l sync.Mutex 908 909 // Initialize the result 910 result := c.Meta.(*Plan) 911 result.init() 912 913 var walkFn depgraph.WalkFunc 914 walkFn = func(n *depgraph.Noun) error { 915 switch m := n.Meta.(type) { 916 case *GraphNodeModule: 917 // Build another walkContext for this module and walk it. 918 wc := c.Context.walkContext(c.Operation, m.Path) 919 920 // Set the graph to specifically walk this subgraph 921 wc.graph = m.Graph 922 923 // Preserve the meta 924 wc.Meta = c.Meta 925 926 return wc.Walk() 927 case *GraphNodeResource: 928 // If we're expanding, then expand the nodes, and then rewalk the graph 929 if m.ExpandMode > ResourceExpandNone { 930 return c.genericWalkResource(m, walkFn) 931 } 932 933 r := m.Resource 934 935 if r.State != nil && r.State.ID != "" { 936 log.Printf("[DEBUG] %s: Making for destroy", r.Id) 937 938 l.Lock() 939 defer l.Unlock() 940 md := result.Diff.ModuleByPath(c.Path) 941 if md == nil { 942 md = result.Diff.AddModule(c.Path) 943 } 944 md.Resources[r.Id] = &InstanceDiff{Destroy: true} 945 } else { 946 log.Printf("[DEBUG] %s: Not marking for destroy, no ID", r.Id) 947 } 948 } 949 950 return nil 951 } 952 953 return walkFn 954 } 955 956 func (c *walkContext) refreshWalkFn() depgraph.WalkFunc { 957 cb := func(c *walkContext, r *Resource) error { 958 is := r.State 959 960 if is == nil || is.ID == "" { 961 log.Printf("[DEBUG] %s: Not refreshing, ID is empty", r.Id) 962 return nil 963 } 964 965 for _, h := range c.Context.hooks { 966 handleHook(h.PreRefresh(r.Info, is)) 967 } 968 969 is, err := r.Provider.Refresh(r.Info, is) 970 if err != nil { 971 return err 972 } 973 if is == nil { 974 is = new(InstanceState) 975 is.init() 976 } 977 978 // Set the updated state 979 r.State = is 980 c.persistState(r) 981 982 for _, h := range c.Context.hooks { 983 handleHook(h.PostRefresh(r.Info, is)) 984 } 985 986 return nil 987 } 988 989 return c.genericWalkFn(cb) 990 } 991 992 func (c *walkContext) validateWalkFn() depgraph.WalkFunc { 993 var l sync.Mutex 994 995 meta := c.Meta.(*walkValidateMeta) 996 if meta.Children == nil { 997 meta.Children = make(map[string]*walkValidateMeta) 998 } 999 1000 var walkFn depgraph.WalkFunc 1001 walkFn = func(n *depgraph.Noun) error { 1002 // If it is the root node, ignore 1003 if n.Name == GraphRootNode { 1004 return nil 1005 } 1006 1007 switch rn := n.Meta.(type) { 1008 case *GraphNodeModule: 1009 // Build another walkContext for this module and walk it. 1010 wc := c.Context.walkContext(walkValidate, rn.Path) 1011 1012 // Set the graph to specifically walk this subgraph 1013 wc.graph = rn.Graph 1014 1015 // Build the meta parameter. Do this by sharing the Children 1016 // reference but copying the rest into our own Children list. 1017 newMeta := new(walkValidateMeta) 1018 newMeta.Children = meta.Children 1019 wc.Meta = newMeta 1020 1021 if err := wc.Walk(); err != nil { 1022 return err 1023 } 1024 1025 newMeta.Children = nil 1026 meta.Children[strings.Join(rn.Path, ".")] = newMeta 1027 return nil 1028 case *GraphNodeResource: 1029 if rn.Resource == nil { 1030 panic("resource should never be nil") 1031 } 1032 1033 // If we're expanding, then expand the nodes, and then rewalk the graph 1034 if rn.ExpandMode > ResourceExpandNone { 1035 // Interpolate the count and verify it is non-negative 1036 rc := NewResourceConfig(rn.Config.RawCount) 1037 rc.interpolate(c, rn.Resource) 1038 if !rc.IsComputed(rn.Config.RawCount.Key) { 1039 count, err := rn.Config.Count() 1040 if err == nil { 1041 if count < 0 { 1042 err = fmt.Errorf( 1043 "%s error: count must be positive", rn.Resource.Id) 1044 } 1045 } 1046 if err != nil { 1047 l.Lock() 1048 defer l.Unlock() 1049 meta.Errs = append(meta.Errs, err) 1050 return nil 1051 } 1052 } 1053 1054 return c.genericWalkResource(rn, walkFn) 1055 } 1056 1057 // If it doesn't have a provider, that is a different problem 1058 if rn.Resource.Provider == nil { 1059 return nil 1060 } 1061 1062 // Don't validate orphans or tainted since they never have a config 1063 if rn.Resource.Flags&FlagOrphan != 0 { 1064 return nil 1065 } 1066 if rn.Resource.Flags&FlagTainted != 0 { 1067 return nil 1068 } 1069 1070 // If the resouce name doesn't match the name regular 1071 // expression, show a warning. 1072 if !config.NameRegexp.Match([]byte(rn.Config.Name)) { 1073 l.Lock() 1074 meta.Warns = append(meta.Warns, fmt.Sprintf( 1075 "%s: module name can only contain letters, numbers, "+ 1076 "dashes, and underscores.\n"+ 1077 "This will be an error in Terraform 0.4", 1078 rn.Resource.Id)) 1079 l.Unlock() 1080 } 1081 1082 // Compute the variables in this resource 1083 rn.Resource.Config.interpolate(c, rn.Resource) 1084 1085 log.Printf("[INFO] Validating resource: %s", rn.Resource.Id) 1086 ws, es := rn.Resource.Provider.ValidateResource( 1087 rn.Resource.Info.Type, rn.Resource.Config) 1088 for i, w := range ws { 1089 ws[i] = fmt.Sprintf("'%s' warning: %s", rn.Resource.Id, w) 1090 } 1091 for i, e := range es { 1092 es[i] = fmt.Errorf("'%s' error: %s", rn.Resource.Id, e) 1093 } 1094 1095 l.Lock() 1096 meta.Warns = append(meta.Warns, ws...) 1097 meta.Errs = append(meta.Errs, es...) 1098 l.Unlock() 1099 1100 for idx, p := range rn.Resource.Provisioners { 1101 ws, es := p.Provisioner.Validate(p.Config) 1102 for i, w := range ws { 1103 ws[i] = fmt.Sprintf("'%s.provisioner.%d' warning: %s", rn.Resource.Id, idx, w) 1104 } 1105 for i, e := range es { 1106 es[i] = fmt.Errorf("'%s.provisioner.%d' error: %s", rn.Resource.Id, idx, e) 1107 } 1108 1109 l.Lock() 1110 meta.Warns = append(meta.Warns, ws...) 1111 meta.Errs = append(meta.Errs, es...) 1112 l.Unlock() 1113 } 1114 1115 case *GraphNodeResourceProvider: 1116 sharedProvider := rn.Provider 1117 1118 // Check if we have an override 1119 cs, ok := c.Context.providerConfig[rn.ID] 1120 if !ok { 1121 cs = make(map[string]map[string]interface{}) 1122 } 1123 1124 for k, p := range sharedProvider.Providers { 1125 // Merge the configurations to get what we use to configure with 1126 rc := sharedProvider.MergeConfig(false, cs[k]) 1127 rc.interpolate(c, nil) 1128 1129 log.Printf("[INFO] Validating provider: %s", k) 1130 ws, es := p.Validate(rc) 1131 for i, w := range ws { 1132 ws[i] = fmt.Sprintf("Provider '%s' warning: %s", k, w) 1133 } 1134 for i, e := range es { 1135 es[i] = fmt.Errorf("Provider '%s' error: %s", k, e) 1136 } 1137 1138 l.Lock() 1139 meta.Warns = append(meta.Warns, ws...) 1140 meta.Errs = append(meta.Errs, es...) 1141 l.Unlock() 1142 } 1143 } 1144 1145 return nil 1146 } 1147 1148 return walkFn 1149 } 1150 1151 func (c *walkContext) genericWalkFn(cb genericWalkFunc) depgraph.WalkFunc { 1152 // This will keep track of whether we're stopped or not 1153 var stop uint32 = 0 1154 1155 var walkFn depgraph.WalkFunc 1156 walkFn = func(n *depgraph.Noun) error { 1157 // If it is the root node, ignore 1158 if n.Name == GraphRootNode { 1159 return nil 1160 } 1161 1162 // If we're stopped, return right away 1163 if atomic.LoadUint32(&stop) != 0 { 1164 return nil 1165 } 1166 1167 switch m := n.Meta.(type) { 1168 case *GraphNodeModule: 1169 // Build another walkContext for this module and walk it. 1170 wc := c.Context.walkContext(c.Operation, m.Path) 1171 1172 // Set the graph to specifically walk this subgraph 1173 wc.graph = m.Graph 1174 1175 // Preserve the meta 1176 wc.Meta = c.Meta 1177 1178 // Set the variables 1179 if m.Config != nil { 1180 wc.Variables = make(map[string]string) 1181 1182 rc := NewResourceConfig(m.Config.RawConfig) 1183 rc.interpolate(c, nil) 1184 for k, v := range rc.Config { 1185 wc.Variables[k] = v.(string) 1186 } 1187 for k, _ := range rc.Raw { 1188 if _, ok := wc.Variables[k]; !ok { 1189 wc.Variables[k] = config.UnknownVariableValue 1190 } 1191 } 1192 } 1193 1194 return wc.Walk() 1195 case *GraphNodeResource: 1196 // Continue, we care about this the most 1197 case *GraphNodeResourceProvider: 1198 sharedProvider := m.Provider 1199 1200 // Check if we have an override 1201 cs, ok := c.Context.providerConfig[m.ID] 1202 if !ok { 1203 cs = make(map[string]map[string]interface{}) 1204 } 1205 1206 for k, p := range sharedProvider.Providers { 1207 // Interpolate our own configuration before merging 1208 if sharedProvider.Config != nil { 1209 rc := NewResourceConfig(sharedProvider.Config.RawConfig) 1210 rc.interpolate(c, nil) 1211 } 1212 1213 // Merge the configurations to get what we use to configure 1214 // with. We don't need to interpolate this because the 1215 // lines above verify that all parents are interpolated 1216 // properly. 1217 rc := sharedProvider.MergeConfig(false, cs[k]) 1218 1219 log.Printf("[INFO] Configuring provider: %s", k) 1220 err := p.Configure(rc) 1221 if err != nil { 1222 return err 1223 } 1224 } 1225 1226 return nil 1227 default: 1228 panic(fmt.Sprintf("unknown graph node: %#v", n.Meta)) 1229 } 1230 1231 rn := n.Meta.(*GraphNodeResource) 1232 1233 // If we're expanding, then expand the nodes, and then rewalk the graph 1234 if rn.ExpandMode > ResourceExpandNone { 1235 return c.genericWalkResource(rn, walkFn) 1236 } 1237 1238 // Make sure that at least some resource configuration is set 1239 if rn.Config == nil { 1240 rn.Resource.Config = new(ResourceConfig) 1241 } else { 1242 rn.Resource.Config = NewResourceConfig(rn.Config.RawConfig) 1243 } 1244 1245 // Handle recovery of special panic scenarios 1246 defer func() { 1247 if v := recover(); v != nil { 1248 if v == HookActionHalt { 1249 atomic.StoreUint32(&stop, 1) 1250 } else { 1251 panic(v) 1252 } 1253 } 1254 }() 1255 1256 // Limit parallelism 1257 c.Context.parallelSem.Acquire() 1258 defer c.Context.parallelSem.Release() 1259 1260 // Call the callack 1261 log.Printf( 1262 "[INFO] Module %s walking: %s (Graph node: %s)", 1263 strings.Join(c.Path, "."), 1264 rn.Resource.Id, 1265 n.Name) 1266 if err := cb(c, rn.Resource); err != nil { 1267 log.Printf("[ERROR] Error walking '%s': %s", rn.Resource.Id, err) 1268 return err 1269 } 1270 1271 return nil 1272 } 1273 1274 return walkFn 1275 } 1276 1277 func (c *walkContext) genericWalkResource( 1278 rn *GraphNodeResource, fn depgraph.WalkFunc) error { 1279 // Interpolate the count 1280 rc := NewResourceConfig(rn.Config.RawCount) 1281 rc.interpolate(c, rn.Resource) 1282 1283 // If we're validating, then we set the count to 1 if it is computed 1284 if c.Operation == walkValidate { 1285 if key := rn.Config.RawCount.Key; rc.IsComputed(key) { 1286 // Preserve the old value so that we reset it properly 1287 old := rn.Config.RawCount.Raw[key] 1288 defer func() { 1289 rn.Config.RawCount.Raw[key] = old 1290 }() 1291 1292 // Set th count to 1 for validation purposes 1293 rn.Config.RawCount.Raw[key] = "1" 1294 } 1295 } 1296 1297 // Expand the node to the actual resources 1298 g, err := rn.Expand() 1299 if err != nil { 1300 return err 1301 } 1302 1303 // Walk the graph with our function 1304 if err := g.Walk(fn); err != nil { 1305 return err 1306 } 1307 1308 return nil 1309 } 1310 1311 // applyProvisioners is used to run any provisioners a resource has 1312 // defined after the resource creation has already completed. 1313 func (c *walkContext) applyProvisioners(r *Resource, is *InstanceState) error { 1314 // Store the original connection info, restore later 1315 origConnInfo := is.Ephemeral.ConnInfo 1316 defer func() { 1317 is.Ephemeral.ConnInfo = origConnInfo 1318 }() 1319 1320 for _, prov := range r.Provisioners { 1321 // Interpolate since we may have variables that depend on the 1322 // local resource. 1323 if err := prov.Config.interpolate(c, r); err != nil { 1324 return err 1325 } 1326 1327 // Interpolate the conn info, since it may contain variables 1328 connInfo := NewResourceConfig(prov.ConnInfo) 1329 if err := connInfo.interpolate(c, r); err != nil { 1330 return err 1331 } 1332 1333 // Merge the connection information 1334 overlay := make(map[string]string) 1335 if origConnInfo != nil { 1336 for k, v := range origConnInfo { 1337 overlay[k] = v 1338 } 1339 } 1340 for k, v := range connInfo.Config { 1341 switch vt := v.(type) { 1342 case string: 1343 overlay[k] = vt 1344 case int64: 1345 overlay[k] = strconv.FormatInt(vt, 10) 1346 case int32: 1347 overlay[k] = strconv.FormatInt(int64(vt), 10) 1348 case int: 1349 overlay[k] = strconv.FormatInt(int64(vt), 10) 1350 case float32: 1351 overlay[k] = strconv.FormatFloat(float64(vt), 'f', 3, 32) 1352 case float64: 1353 overlay[k] = strconv.FormatFloat(vt, 'f', 3, 64) 1354 case bool: 1355 overlay[k] = strconv.FormatBool(vt) 1356 default: 1357 overlay[k] = fmt.Sprintf("%v", vt) 1358 } 1359 } 1360 is.Ephemeral.ConnInfo = overlay 1361 1362 // Invoke the Provisioner 1363 for _, h := range c.Context.hooks { 1364 handleHook(h.PreProvision(r.Info, prov.Type)) 1365 } 1366 1367 output := ProvisionerUIOutput{ 1368 Info: r.Info, 1369 Type: prov.Type, 1370 Hooks: c.Context.hooks, 1371 } 1372 err := prov.Provisioner.Apply(&output, is, prov.Config) 1373 if err != nil { 1374 return err 1375 } 1376 1377 for _, h := range c.Context.hooks { 1378 handleHook(h.PostProvision(r.Info, prov.Type)) 1379 } 1380 } 1381 1382 return nil 1383 } 1384 1385 // persistState persists the state in a Resource to the actual final 1386 // state location. 1387 func (c *walkContext) persistState(r *Resource) { 1388 // Acquire a state lock around this whole thing since we're updating that 1389 c.Context.sl.Lock() 1390 defer c.Context.sl.Unlock() 1391 1392 // If we have no state, then we don't persist. 1393 if c.Context.state == nil { 1394 return 1395 } 1396 1397 // Get the state for this resource. The resource state should always 1398 // exist because we call graphInitState before anything that could 1399 // potentially call this. 1400 module := c.Context.state.ModuleByPath(c.Path) 1401 if module == nil { 1402 module = c.Context.state.AddModule(c.Path) 1403 } 1404 rs := module.Resources[r.Id] 1405 if rs == nil { 1406 rs = &ResourceState{Type: r.Info.Type} 1407 rs.init() 1408 module.Resources[r.Id] = rs 1409 } 1410 rs.Dependencies = r.Dependencies 1411 1412 // Assign the instance state to the proper location 1413 if r.Flags&FlagDeposed != 0 { 1414 // We were previously the primary and have been deposed, so 1415 // now we are the final tainted resource 1416 r.TaintedIndex = len(rs.Tainted) - 1 1417 rs.Tainted[r.TaintedIndex] = r.State 1418 1419 } else if r.Flags&FlagTainted != 0 { 1420 if r.TaintedIndex >= 0 { 1421 // Tainted with a pre-existing index, just update that spot 1422 rs.Tainted[r.TaintedIndex] = r.State 1423 1424 } else if r.Flags&FlagReplacePrimary != 0 { 1425 // We just replaced the primary, so restore the primary 1426 rs.Primary = rs.Tainted[len(rs.Tainted)-1] 1427 1428 // Set ourselves as tainted 1429 rs.Tainted[len(rs.Tainted)-1] = r.State 1430 1431 } else { 1432 // Newly tainted, so append it to the list, update the 1433 // index, and remove the primary. 1434 rs.Tainted = append(rs.Tainted, r.State) 1435 r.TaintedIndex = len(rs.Tainted) - 1 1436 rs.Primary = nil 1437 } 1438 1439 } else if r.Flags&FlagReplacePrimary != 0 { 1440 // If the ID is blank (there was an error), then we leave 1441 // the primary that exists, and do not store this as a tainted 1442 // instance 1443 if r.State.ID == "" { 1444 return 1445 } 1446 1447 // Push the old primary into the tainted state 1448 rs.Tainted = append(rs.Tainted, rs.Primary) 1449 1450 // Set this as the new primary 1451 rs.Primary = r.State 1452 1453 } else { 1454 // The primary instance, so just set it directly 1455 rs.Primary = r.State 1456 } 1457 1458 // Do a pruning so that empty resources are not saved 1459 rs.prune() 1460 } 1461 1462 // computeVars takes the State and given RawConfig and processes all 1463 // the variables. This dynamically discovers the attributes instead of 1464 // using a static map[string]string that the genericWalkFn uses. 1465 func (c *walkContext) computeVars( 1466 raw *config.RawConfig, r *Resource) error { 1467 // If there isn't a raw configuration, don't do anything 1468 if raw == nil { 1469 return nil 1470 } 1471 1472 // Copy the default variables 1473 vs := make(map[string]string) 1474 for k, v := range c.defaultVariables { 1475 vs[k] = v 1476 } 1477 1478 // Next, the actual computed variables 1479 for n, rawV := range raw.Variables { 1480 switch v := rawV.(type) { 1481 case *config.CountVariable: 1482 switch v.Type { 1483 case config.CountValueIndex: 1484 if r != nil { 1485 vs[n] = strconv.FormatInt(int64(r.CountIndex), 10) 1486 } 1487 } 1488 case *config.ModuleVariable: 1489 if c.Operation == walkValidate { 1490 vs[n] = config.UnknownVariableValue 1491 continue 1492 } 1493 1494 value, err := c.computeModuleVariable(v) 1495 if err != nil { 1496 return err 1497 } 1498 1499 vs[n] = value 1500 case *config.PathVariable: 1501 switch v.Type { 1502 case config.PathValueCwd: 1503 wd, err := os.Getwd() 1504 if err != nil { 1505 return fmt.Errorf( 1506 "Couldn't get cwd for var %s: %s", 1507 v.FullKey(), err) 1508 } 1509 1510 vs[n] = wd 1511 case config.PathValueModule: 1512 if t := c.Context.module.Child(c.Path[1:]); t != nil { 1513 vs[n] = t.Config().Dir 1514 } 1515 case config.PathValueRoot: 1516 vs[n] = c.Context.module.Config().Dir 1517 } 1518 case *config.ResourceVariable: 1519 if c.Operation == walkValidate { 1520 vs[n] = config.UnknownVariableValue 1521 continue 1522 } 1523 1524 var attr string 1525 var err error 1526 if v.Multi && v.Index == -1 { 1527 attr, err = c.computeResourceMultiVariable(v) 1528 } else { 1529 attr, err = c.computeResourceVariable(v) 1530 } 1531 if err != nil { 1532 return err 1533 } 1534 1535 vs[n] = attr 1536 case *config.UserVariable: 1537 val, ok := c.Variables[v.Name] 1538 if ok { 1539 vs[n] = val 1540 continue 1541 } 1542 1543 if _, ok := vs[n]; !ok && c.Operation == walkValidate { 1544 vs[n] = config.UnknownVariableValue 1545 continue 1546 } 1547 1548 // Look up if we have any variables with this prefix because 1549 // those are map overrides. Include those. 1550 for k, val := range c.Variables { 1551 if strings.HasPrefix(k, v.Name+".") { 1552 vs["var."+k] = val 1553 } 1554 } 1555 } 1556 } 1557 1558 // Interpolate the variables 1559 return raw.Interpolate(vs) 1560 } 1561 1562 func (c *walkContext) computeModuleVariable( 1563 v *config.ModuleVariable) (string, error) { 1564 // Build the path to our child 1565 path := make([]string, len(c.Path), len(c.Path)+1) 1566 copy(path, c.Path) 1567 path = append(path, v.Name) 1568 1569 // Grab some locks 1570 c.Context.sl.RLock() 1571 defer c.Context.sl.RUnlock() 1572 1573 // Get that module from our state 1574 mod := c.Context.state.ModuleByPath(path) 1575 if mod == nil { 1576 return "", fmt.Errorf( 1577 "Module '%s' not found for variable '%s'", 1578 strings.Join(path[1:], "."), 1579 v.FullKey()) 1580 } 1581 1582 value, ok := mod.Outputs[v.Field] 1583 if !ok { 1584 return "", fmt.Errorf( 1585 "Output field '%s' not found for variable '%s'", 1586 v.Field, 1587 v.FullKey()) 1588 } 1589 1590 return value, nil 1591 } 1592 1593 func (c *walkContext) computeResourceVariable( 1594 v *config.ResourceVariable) (string, error) { 1595 id := v.ResourceId() 1596 if v.Multi { 1597 id = fmt.Sprintf("%s.%d", id, v.Index) 1598 } 1599 1600 c.Context.sl.RLock() 1601 defer c.Context.sl.RUnlock() 1602 1603 // Get the relevant module 1604 module := c.Context.state.ModuleByPath(c.Path) 1605 1606 r, ok := module.Resources[id] 1607 if !ok { 1608 if v.Multi && v.Index == 0 { 1609 r, ok = module.Resources[v.ResourceId()] 1610 } 1611 if !ok { 1612 return "", fmt.Errorf( 1613 "Resource '%s' not found for variable '%s'", 1614 id, 1615 v.FullKey()) 1616 } 1617 } 1618 1619 if r.Primary == nil { 1620 goto MISSING 1621 } 1622 1623 if attr, ok := r.Primary.Attributes[v.Field]; ok { 1624 return attr, nil 1625 } 1626 1627 // We didn't find the exact field, so lets separate the dots 1628 // and see if anything along the way is a computed set. i.e. if 1629 // we have "foo.0.bar" as the field, check to see if "foo" is 1630 // a computed list. If so, then the whole thing is computed. 1631 if parts := strings.Split(v.Field, "."); len(parts) > 1 { 1632 for i := 1; i < len(parts); i++ { 1633 key := fmt.Sprintf("%s.#", strings.Join(parts[:i], ".")) 1634 if attr, ok := r.Primary.Attributes[key]; ok { 1635 return attr, nil 1636 } 1637 } 1638 } 1639 1640 MISSING: 1641 return "", fmt.Errorf( 1642 "Resource '%s' does not have attribute '%s' "+ 1643 "for variable '%s'", 1644 id, 1645 v.Field, 1646 v.FullKey()) 1647 } 1648 1649 func (c *walkContext) computeResourceMultiVariable( 1650 v *config.ResourceVariable) (string, error) { 1651 c.Context.sl.RLock() 1652 defer c.Context.sl.RUnlock() 1653 1654 // Get the resource from the configuration so we can know how 1655 // many of the resource there is. 1656 var cr *config.Resource 1657 for _, r := range c.Context.module.Config().Resources { 1658 if r.Id() == v.ResourceId() { 1659 cr = r 1660 break 1661 } 1662 } 1663 if cr == nil { 1664 return "", fmt.Errorf( 1665 "Resource '%s' not found for variable '%s'", 1666 v.ResourceId(), 1667 v.FullKey()) 1668 } 1669 1670 // Get the relevant module 1671 // TODO: Not use only root module 1672 module := c.Context.state.RootModule() 1673 1674 count, err := cr.Count() 1675 if err != nil { 1676 return "", fmt.Errorf( 1677 "Error reading %s count: %s", 1678 v.ResourceId(), 1679 err) 1680 } 1681 1682 // If we have no count, return empty 1683 if count == 0 { 1684 return "", nil 1685 } 1686 1687 var values []string 1688 for i := 0; i < count; i++ { 1689 id := fmt.Sprintf("%s.%d", v.ResourceId(), i) 1690 1691 // If we're dealing with only a single resource, then the 1692 // ID doesn't have a trailing index. 1693 if count == 1 { 1694 id = v.ResourceId() 1695 } 1696 1697 r, ok := module.Resources[id] 1698 if !ok { 1699 continue 1700 } 1701 1702 if r.Primary == nil { 1703 continue 1704 } 1705 1706 attr, ok := r.Primary.Attributes[v.Field] 1707 if !ok { 1708 continue 1709 } 1710 1711 values = append(values, attr) 1712 } 1713 1714 if len(values) == 0 { 1715 return "", fmt.Errorf( 1716 "Resource '%s' does not have attribute '%s' "+ 1717 "for variable '%s'", 1718 v.ResourceId(), 1719 v.Field, 1720 v.FullKey()) 1721 } 1722 1723 return strings.Join(values, config.InterpSplitDelim), nil 1724 } 1725 1726 type walkInputMeta struct { 1727 sync.Mutex 1728 1729 Done map[string]struct{} 1730 } 1731 1732 type walkValidateMeta struct { 1733 Errs []error 1734 Warns []string 1735 Children map[string]*walkValidateMeta 1736 } 1737 1738 func (m *walkValidateMeta) Flatten() ([]string, []error) { 1739 // Prune out the empty children 1740 for k, m2 := range m.Children { 1741 if len(m2.Errs) == 0 && len(m2.Warns) == 0 { 1742 delete(m.Children, k) 1743 } 1744 } 1745 1746 // If we have no children, then just return what we have 1747 if len(m.Children) == 0 { 1748 return m.Warns, m.Errs 1749 } 1750 1751 // Otherwise, copy the errors and warnings 1752 errs := make([]error, len(m.Errs)) 1753 warns := make([]string, len(m.Warns)) 1754 for i, err := range m.Errs { 1755 errs[i] = err 1756 } 1757 for i, warn := range m.Warns { 1758 warns[i] = warn 1759 } 1760 1761 // Now go through each child and copy it in... 1762 for k, c := range m.Children { 1763 for _, err := range c.Errs { 1764 errs = append(errs, fmt.Errorf( 1765 "Module %s: %s", k, err)) 1766 } 1767 for _, warn := range c.Warns { 1768 warns = append(warns, fmt.Sprintf( 1769 "Module %s: %s", k, warn)) 1770 } 1771 } 1772 1773 return warns, errs 1774 }