github.com/ibm-cloud/terraform@v0.6.4-0.20170726051544-8872b87621df/backend/local/backend_local.go (about)

     1  package local
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  
     8  	"github.com/hashicorp/errwrap"
     9  	"github.com/hashicorp/go-multierror"
    10  	"github.com/hashicorp/terraform/backend"
    11  	"github.com/hashicorp/terraform/state"
    12  	"github.com/hashicorp/terraform/terraform"
    13  )
    14  
    15  // backend.Local implementation.
    16  func (b *Local) Context(op *backend.Operation) (*terraform.Context, state.State, error) {
    17  	// Make sure the type is invalid. We use this as a way to know not
    18  	// to ask for input/validate.
    19  	op.Type = backend.OperationTypeInvalid
    20  
    21  	return b.context(op)
    22  }
    23  
    24  func (b *Local) context(op *backend.Operation) (*terraform.Context, state.State, error) {
    25  	// Get the state.
    26  	s, err := b.State(op.Environment)
    27  	if err != nil {
    28  		return nil, nil, errwrap.Wrapf("Error loading state: {{err}}", err)
    29  	}
    30  
    31  	if err := s.RefreshState(); err != nil {
    32  		return nil, nil, errwrap.Wrapf("Error loading state: {{err}}", err)
    33  	}
    34  
    35  	// Initialize our context options
    36  	var opts terraform.ContextOpts
    37  	if v := b.ContextOpts; v != nil {
    38  		opts = *v
    39  	}
    40  
    41  	// Copy set options from the operation
    42  	opts.Destroy = op.Destroy
    43  	opts.Module = op.Module
    44  	opts.Targets = op.Targets
    45  	opts.UIInput = op.UIIn
    46  	if op.Variables != nil {
    47  		opts.Variables = op.Variables
    48  	}
    49  
    50  	// Load our state
    51  	opts.State = s.State()
    52  
    53  	// Build the context
    54  	var tfCtx *terraform.Context
    55  	if op.Plan != nil {
    56  		tfCtx, err = op.Plan.Context(&opts)
    57  	} else {
    58  		tfCtx, err = terraform.NewContext(&opts)
    59  	}
    60  	if err != nil {
    61  		return nil, nil, err
    62  	}
    63  
    64  	// If we have an operation, then we automatically do the input/validate
    65  	// here since every option requires this.
    66  	if op.Type != backend.OperationTypeInvalid {
    67  		// If input asking is enabled, then do that
    68  		if op.Plan == nil && b.OpInput {
    69  			mode := terraform.InputModeProvider
    70  			mode |= terraform.InputModeVar
    71  			mode |= terraform.InputModeVarUnset
    72  
    73  			if err := tfCtx.Input(mode); err != nil {
    74  				return nil, nil, errwrap.Wrapf("Error asking for user input: {{err}}", err)
    75  			}
    76  		}
    77  
    78  		// If validation is enabled, validate
    79  		if b.OpValidation {
    80  			// We ignore warnings here on purpose. We expect users to be listening
    81  			// to the terraform.Hook called after a validation.
    82  			ws, es := tfCtx.Validate()
    83  			if len(ws) > 0 {
    84  				// Log just in case the CLI isn't enabled
    85  				log.Printf("[WARN] backend/local: %d warnings: %v", len(ws), ws)
    86  
    87  				// If we have a CLI, output the warnings
    88  				if b.CLI != nil {
    89  					b.CLI.Warn(strings.TrimSpace(validateWarnHeader) + "\n")
    90  					for _, w := range ws {
    91  						b.CLI.Warn(fmt.Sprintf("  * %s", w))
    92  					}
    93  
    94  					// Make a newline before continuing
    95  					b.CLI.Output("")
    96  				}
    97  			}
    98  
    99  			if len(es) > 0 {
   100  				return nil, nil, multierror.Append(nil, es...)
   101  			}
   102  		}
   103  	}
   104  
   105  	return tfCtx, s, nil
   106  }
   107  
   108  const validateWarnHeader = `
   109  There are warnings related to your configuration. If no errors occurred,
   110  Terraform will continue despite these warnings. It is a good idea to resolve
   111  these warnings in the near future.
   112  
   113  Warnings:
   114  `