github.com/lorbuschris/terraform@v0.11.12-beta1/backend/local/backend_local.go (about)

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