github.com/svenhamers/terraform@v0.11.12-beta1/backend/remote/backend_context.go (about) 1 package remote 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "log" 8 "strings" 9 10 "github.com/hashicorp/errwrap" 11 "github.com/hashicorp/terraform/backend" 12 "github.com/hashicorp/terraform/command/clistate" 13 "github.com/hashicorp/terraform/state" 14 "github.com/hashicorp/terraform/terraform" 15 ) 16 17 // Context implements backend.Enhanced. 18 func (b *Remote) Context(op *backend.Operation) (*terraform.Context, state.State, error) { 19 if op.LockState { 20 op.StateLocker = clistate.NewLocker(context.Background(), op.StateLockTimeout, b.CLI, b.cliColorize()) 21 } else { 22 op.StateLocker = clistate.NewNoopLocker() 23 } 24 25 // Get the latest state. 26 log.Printf("[TRACE] backend/remote: requesting state manager for workspace %q", op.Workspace) 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 log.Printf("[TRACE] backend/remote: requesting state lock for workspace %q", op.Workspace) 33 if err := op.StateLocker.Lock(s, op.Type.String()); err != nil { 34 return nil, nil, errwrap.Wrapf("Error locking state: {{err}}", err) 35 } 36 37 log.Printf("[TRACE] backend/remote: reading remote state for workspace %q", op.Workspace) 38 if err := s.RefreshState(); err != nil { 39 return nil, nil, errwrap.Wrapf("Error loading state: {{err}}", err) 40 } 41 42 // Initialize our context options 43 var opts terraform.ContextOpts 44 if v := b.ContextOpts; v != nil { 45 opts = *v 46 } 47 48 // Copy set options from the operation 49 opts.Destroy = op.Destroy 50 opts.Module = op.Module 51 opts.Targets = op.Targets 52 opts.UIInput = op.UIIn 53 54 // Load the latest state. If we enter contextFromPlanFile below then the 55 // state snapshot in the plan file must match this, or else it'll return 56 // error diagnostics. 57 log.Printf("[TRACE] backend/remote: retrieving remote state snapshot for workspace %q", op.Workspace) 58 opts.State = s.State() 59 60 tfCtx, err := terraform.NewContext(&opts) 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 log.Printf("[TRACE] backend/remote: finished building terraform.Context") 75 76 return tfCtx, s, nil 77 } 78 79 func (b *Remote) pluginInitRequired(providerErr *terraform.ResourceProviderError) { 80 b.CLI.Output(b.Colorize().Color(fmt.Sprintf( 81 strings.TrimSpace(errPluginInit)+"\n", 82 providerErr))) 83 } 84 85 // this relies on multierror to format the plugin errors below the copy 86 const errPluginInit = ` 87 [reset][bold][yellow]Plugin reinitialization required. Please run "terraform init".[reset] 88 [yellow]Reason: Could not satisfy plugin requirements. 89 90 Plugins are external binaries that Terraform uses to access and manipulate 91 resources. The configuration provided requires plugins which can't be located, 92 don't satisfy the version constraints, or are otherwise incompatible. 93 94 [reset][red]%s 95 96 [reset][yellow]Terraform automatically discovers provider requirements from your 97 configuration, including providers used in child modules. To see the 98 requirements and constraints from each module, run "terraform providers". 99 `