github.com/loicalbertin/terraform@v0.6.15-0.20170626182346-8e2583055467/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 opts.State = s.State() 53 54 // Build the context 55 var tfCtx *terraform.Context 56 if op.Plan != nil { 57 tfCtx, err = op.Plan.Context(&opts) 58 } else { 59 tfCtx, err = terraform.NewContext(&opts) 60 } 61 62 // any errors resolving plugins returns this 63 if rpe, ok := err.(*terraform.ResourceProviderError); ok { 64 b.pluginInitRequired(rpe) 65 // we wrote the full UI error here, so return a generic error for flow 66 // control in the command. 67 return nil, nil, errors.New("error satisfying plugin requirements") 68 } 69 70 if err != nil { 71 return nil, nil, err 72 } 73 74 // If we have an operation, then we automatically do the input/validate 75 // here since every option requires this. 76 if op.Type != backend.OperationTypeInvalid { 77 // If input asking is enabled, then do that 78 if op.Plan == nil && b.OpInput { 79 mode := terraform.InputModeProvider 80 mode |= terraform.InputModeVar 81 mode |= terraform.InputModeVarUnset 82 83 if err := tfCtx.Input(mode); err != nil { 84 return nil, nil, errwrap.Wrapf("Error asking for user input: {{err}}", err) 85 } 86 } 87 88 // If validation is enabled, validate 89 if b.OpValidation { 90 // We ignore warnings here on purpose. We expect users to be listening 91 // to the terraform.Hook called after a validation. 92 ws, es := tfCtx.Validate() 93 if len(ws) > 0 { 94 // Log just in case the CLI isn't enabled 95 log.Printf("[WARN] backend/local: %d warnings: %v", len(ws), ws) 96 97 // If we have a CLI, output the warnings 98 if b.CLI != nil { 99 b.CLI.Warn(strings.TrimSpace(validateWarnHeader) + "\n") 100 for _, w := range ws { 101 b.CLI.Warn(fmt.Sprintf(" * %s", w)) 102 } 103 104 // Make a newline before continuing 105 b.CLI.Output("") 106 } 107 } 108 109 if len(es) > 0 { 110 return nil, nil, multierror.Append(nil, es...) 111 } 112 } 113 } 114 115 return tfCtx, s, nil 116 } 117 118 const validateWarnHeader = ` 119 There are warnings related to your configuration. If no errors occurred, 120 Terraform will continue despite these warnings. It is a good idea to resolve 121 these warnings in the near future. 122 123 Warnings: 124 `