github.com/r3vit/terraform@v0.11.9-beta1.0.20181016131357-87d05607d3c5/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  	if op.Variables != nil {
    59  		opts.Variables = op.Variables
    60  	}
    61  
    62  	// Load our state
    63  	// By the time we get here, the backend creation code in "command" took
    64  	// care of making s.State() return a state compatible with our plan,
    65  	// if any, so we can safely pass this value in both the plan context
    66  	// and new context cases below.
    67  	opts.State = s.State()
    68  
    69  	// Build the context
    70  	var tfCtx *terraform.Context
    71  	if op.Plan != nil {
    72  		tfCtx, err = op.Plan.Context(&opts)
    73  	} else {
    74  		tfCtx, err = terraform.NewContext(&opts)
    75  	}
    76  
    77  	// any errors resolving plugins returns this
    78  	if rpe, ok := err.(*terraform.ResourceProviderError); ok {
    79  		b.pluginInitRequired(rpe)
    80  		// we wrote the full UI error here, so return a generic error for flow
    81  		// control in the command.
    82  		return nil, nil, errors.New("error satisfying plugin requirements")
    83  	}
    84  
    85  	if err != nil {
    86  		return nil, nil, err
    87  	}
    88  
    89  	// If we have an operation, then we automatically do the input/validate
    90  	// here since every option requires this.
    91  	if op.Type != backend.OperationTypeInvalid {
    92  		// If input asking is enabled, then do that
    93  		if op.Plan == nil && b.OpInput {
    94  			mode := terraform.InputModeProvider
    95  			mode |= terraform.InputModeVar
    96  			mode |= terraform.InputModeVarUnset
    97  
    98  			if err := tfCtx.Input(mode); err != nil {
    99  				return nil, nil, errwrap.Wrapf("Error asking for user input: {{err}}", err)
   100  			}
   101  		}
   102  
   103  		// If validation is enabled, validate
   104  		if b.OpValidation {
   105  			diags := tfCtx.Validate()
   106  			if len(diags) > 0 {
   107  				if diags.HasErrors() {
   108  					// If there are warnings _and_ errors then we'll take this
   109  					// path and return them all together in this error.
   110  					return nil, nil, diags.Err()
   111  				}
   112  
   113  				// For now we can't propagate warnings any further without
   114  				// printing them directly to the UI, so we'll need to
   115  				// format them here ourselves.
   116  				for _, diag := range diags {
   117  					if diag.Severity() != tfdiags.Warning {
   118  						continue
   119  					}
   120  					if b.CLI != nil {
   121  						b.CLI.Warn(format.Diagnostic(diag, b.Colorize(), 72))
   122  					} else {
   123  						desc := diag.Description()
   124  						log.Printf("[WARN] backend/local: %s", desc.Summary)
   125  					}
   126  				}
   127  
   128  				// Make a newline before continuing
   129  				b.CLI.Output("")
   130  			}
   131  		}
   132  	}
   133  
   134  	return tfCtx, s, nil
   135  }
   136  
   137  const validateWarnHeader = `
   138  There are warnings related to your configuration. If no errors occurred,
   139  Terraform will continue despite these warnings. It is a good idea to resolve
   140  these warnings in the near future.
   141  
   142  Warnings:
   143  `