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