github.com/ezbercih/terraform@v0.1.1-0.20140729011846-3c33865e0839/command/meta.go (about)

     1  package command
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  
     8  	"github.com/hashicorp/terraform/config"
     9  	"github.com/hashicorp/terraform/terraform"
    10  	"github.com/mitchellh/cli"
    11  	"github.com/mitchellh/colorstring"
    12  )
    13  
    14  // Meta are the meta-options that are available on all or most commands.
    15  type Meta struct {
    16  	Color       bool
    17  	ContextOpts *terraform.ContextOpts
    18  	Ui          cli.Ui
    19  
    20  	// State read when calling `Context`. This is available after calling
    21  	// `Context`.
    22  	state *terraform.State
    23  
    24  	// This can be set by the command itself to provide extra hooks.
    25  	extraHooks []terraform.Hook
    26  
    27  	// Variables for the context (private)
    28  	variables map[string]string
    29  
    30  	color bool
    31  	oldUi cli.Ui
    32  }
    33  
    34  // Colorize returns the colorization structure for a command.
    35  func (m *Meta) Colorize() *colorstring.Colorize {
    36  	return &colorstring.Colorize{
    37  		Colors:  colorstring.DefaultColors,
    38  		Disable: !m.color,
    39  		Reset:   true,
    40  	}
    41  }
    42  
    43  // Context returns a Terraform Context taking into account the context
    44  // options used to initialize this meta configuration.
    45  func (m *Meta) Context(path, statePath string) (*terraform.Context, bool, error) {
    46  	opts := m.contextOpts()
    47  
    48  	// First try to just read the plan directly from the path given.
    49  	f, err := os.Open(path)
    50  	if err == nil {
    51  		plan, err := terraform.ReadPlan(f)
    52  		f.Close()
    53  		if err == nil {
    54  			if len(m.variables) > 0 {
    55  				return nil, false, fmt.Errorf(
    56  					"You can't set variables with the '-var' or '-var-file' flag\n" +
    57  						"when you're applying a plan file. The variables used when\n" +
    58  						"the plan was created will be used. If you wish to use different\n" +
    59  						"variable values, create a new plan file.")
    60  			}
    61  
    62  			return plan.Context(opts), true, nil
    63  		}
    64  	}
    65  
    66  	// Load up the state
    67  	var state *terraform.State
    68  	if statePath != "" {
    69  		f, err := os.Open(statePath)
    70  		if err != nil && os.IsNotExist(err) {
    71  			// If the state file doesn't exist, it is okay, since it
    72  			// is probably a new infrastructure.
    73  			err = nil
    74  		} else if err == nil {
    75  			state, err = terraform.ReadState(f)
    76  			f.Close()
    77  		}
    78  
    79  		if err != nil {
    80  			return nil, false, fmt.Errorf("Error loading state: %s", err)
    81  		}
    82  	}
    83  
    84  	// Store the loaded state
    85  	m.state = state
    86  
    87  	config, err := config.LoadDir(path)
    88  	if err != nil {
    89  		return nil, false, fmt.Errorf("Error loading config: %s", err)
    90  	}
    91  	if err := config.Validate(); err != nil {
    92  		return nil, false, fmt.Errorf("Error validating config: %s", err)
    93  	}
    94  
    95  	opts.Config = config
    96  	opts.State = state
    97  	ctx := terraform.NewContext(opts)
    98  	return ctx, false, nil
    99  }
   100  
   101  // contextOpts returns the options to use to initialize a Terraform
   102  // context with the settings from this Meta.
   103  func (m *Meta) contextOpts() *terraform.ContextOpts {
   104  	var opts terraform.ContextOpts = *m.ContextOpts
   105  	opts.Hooks = make(
   106  		[]terraform.Hook,
   107  		len(m.ContextOpts.Hooks)+len(m.extraHooks)+1)
   108  	opts.Hooks[0] = m.uiHook()
   109  	copy(opts.Hooks[1:], m.ContextOpts.Hooks)
   110  	copy(opts.Hooks[len(m.ContextOpts.Hooks)+1:], m.extraHooks)
   111  
   112  	if len(m.variables) > 0 {
   113  		vs := make(map[string]string)
   114  		for k, v := range opts.Variables {
   115  			vs[k] = v
   116  		}
   117  		for k, v := range m.variables {
   118  			vs[k] = v
   119  		}
   120  		opts.Variables = vs
   121  	}
   122  
   123  	return &opts
   124  }
   125  
   126  // flags adds the meta flags to the given FlagSet.
   127  func (m *Meta) flagSet(n string) *flag.FlagSet {
   128  	f := flag.NewFlagSet(n, flag.ContinueOnError)
   129  	f.Var((*FlagVar)(&m.variables), "var", "variables")
   130  	f.Var((*FlagVarFile)(&m.variables), "var-file", "variable file")
   131  	return f
   132  }
   133  
   134  // process will process the meta-parameters out of the arguments. This
   135  // will potentially modify the args in-place. It will return the resulting
   136  // slice.
   137  func (m *Meta) process(args []string) []string {
   138  	// We do this so that we retain the ability to technically call
   139  	// process multiple times, even if we have no plans to do so
   140  	if m.oldUi != nil {
   141  		m.Ui = m.oldUi
   142  	}
   143  
   144  	// Set colorization
   145  	m.color = m.Color
   146  	for i, v := range args {
   147  		if v == "-no-color" {
   148  			m.Color = false
   149  			args = append(args[:i], args[i+1:]...)
   150  			break
   151  		}
   152  	}
   153  
   154  	// Set the UI
   155  	m.oldUi = m.Ui
   156  	m.Ui = &ColorizeUi{
   157  		Colorize:   m.Colorize(),
   158  		ErrorColor: "[red]",
   159  		Ui:         m.oldUi,
   160  	}
   161  
   162  	return args
   163  }
   164  
   165  // uiHook returns the UiHook to use with the context.
   166  func (m *Meta) uiHook() *UiHook {
   167  	return &UiHook{
   168  		Colorize: m.Colorize(),
   169  		Ui:       m.Ui,
   170  	}
   171  }