github.com/jzbruno/terraform@v0.10.3-0.20180104230435-18975d727047/backend/local/backend_local.go (about) 1 package local 2 3 import ( 4 "errors" 5 "log" 6 7 "github.com/hashicorp/terraform/command/format" 8 9 "github.com/hashicorp/terraform/tfdiags" 10 11 "github.com/hashicorp/errwrap" 12 "github.com/hashicorp/terraform/backend" 13 "github.com/hashicorp/terraform/state" 14 "github.com/hashicorp/terraform/terraform" 15 ) 16 17 // backend.Local implementation. 18 func (b *Local) Context(op *backend.Operation) (*terraform.Context, state.State, error) { 19 // Make sure the type is invalid. We use this as a way to know not 20 // to ask for input/validate. 21 op.Type = backend.OperationTypeInvalid 22 23 return b.context(op) 24 } 25 26 func (b *Local) context(op *backend.Operation) (*terraform.Context, state.State, error) { 27 // Get the state. 28 s, err := b.State(op.Workspace) 29 if err != nil { 30 return nil, nil, errwrap.Wrapf("Error loading state: {{err}}", err) 31 } 32 33 if err := s.RefreshState(); err != nil { 34 return nil, nil, errwrap.Wrapf("Error loading state: {{err}}", err) 35 } 36 37 // Initialize our context options 38 var opts terraform.ContextOpts 39 if v := b.ContextOpts; v != nil { 40 opts = *v 41 } 42 43 // Copy set options from the operation 44 opts.Destroy = op.Destroy 45 opts.Module = op.Module 46 opts.Targets = op.Targets 47 opts.UIInput = op.UIIn 48 if op.Variables != nil { 49 opts.Variables = op.Variables 50 } 51 52 // Load our state 53 // By the time we get here, the backend creation code in "command" took 54 // care of making s.State() return a state compatible with our plan, 55 // if any, so we can safely pass this value in both the plan context 56 // and new context cases below. 57 opts.State = s.State() 58 59 // Build the context 60 var tfCtx *terraform.Context 61 if op.Plan != nil { 62 tfCtx, err = op.Plan.Context(&opts) 63 } else { 64 tfCtx, err = terraform.NewContext(&opts) 65 } 66 67 // any errors resolving plugins returns this 68 if rpe, ok := err.(*terraform.ResourceProviderError); ok { 69 b.pluginInitRequired(rpe) 70 // we wrote the full UI error here, so return a generic error for flow 71 // control in the command. 72 return nil, nil, errors.New("error satisfying plugin requirements") 73 } 74 75 if err != nil { 76 return nil, nil, err 77 } 78 79 // If we have an operation, then we automatically do the input/validate 80 // here since every option requires this. 81 if op.Type != backend.OperationTypeInvalid { 82 // If input asking is enabled, then do that 83 if op.Plan == nil && b.OpInput { 84 mode := terraform.InputModeProvider 85 mode |= terraform.InputModeVar 86 mode |= terraform.InputModeVarUnset 87 88 if err := tfCtx.Input(mode); err != nil { 89 return nil, nil, errwrap.Wrapf("Error asking for user input: {{err}}", err) 90 } 91 } 92 93 // If validation is enabled, validate 94 if b.OpValidation { 95 diags := tfCtx.Validate() 96 if len(diags) > 0 { 97 if diags.HasErrors() { 98 // If there are warnings _and_ errors then we'll take this 99 // path and return them all together in this error. 100 return nil, nil, diags.Err() 101 } 102 103 // For now we can't propagate warnings any further without 104 // printing them directly to the UI, so we'll need to 105 // format them here ourselves. 106 for _, diag := range diags { 107 if diag.Severity() != tfdiags.Warning { 108 continue 109 } 110 if b.CLI != nil { 111 b.CLI.Warn(format.Diagnostic(diag, b.Colorize(), 72)) 112 } else { 113 desc := diag.Description() 114 log.Printf("[WARN] backend/local: %s", desc.Summary) 115 } 116 } 117 118 // Make a newline before continuing 119 b.CLI.Output("") 120 } 121 } 122 } 123 124 return tfCtx, s, nil 125 } 126 127 const validateWarnHeader = ` 128 There are warnings related to your configuration. If no errors occurred, 129 Terraform will continue despite these warnings. It is a good idea to resolve 130 these warnings in the near future. 131 132 Warnings: 133 `