github.com/hartzell/terraform@v0.8.6-0.20180503104400-0cc9e050ecd4/backend/local/backend_local.go (about)

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