package command import ( "fmt" "os" "github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/cli" "github.com/mitchellh/colorstring" ) // Meta are the meta-options that are available on all or most commands. type Meta struct { Color bool ContextOpts *terraform.ContextOpts Ui cli.Ui // This can be set by the command itself to provide extra hooks. extraHooks []terraform.Hook color bool oldUi cli.Ui } // Colorize returns the colorization structure for a command. func (m *Meta) Colorize() *colorstring.Colorize { return &colorstring.Colorize{ Colors: colorstring.DefaultColors, Disable: !m.color, Reset: true, } } // Context returns a Terraform Context taking into account the context // options used to initialize this meta configuration. func (m *Meta) Context(path, statePath string, doPlan bool) (*terraform.Context, error) { opts := m.contextOpts() // First try to just read the plan directly from the path given. f, err := os.Open(path) if err == nil { plan, err := terraform.ReadPlan(f) f.Close() if err == nil { return plan.Context(opts), nil } } if statePath != "" { if _, err := os.Stat(statePath); err != nil { return nil, fmt.Errorf( "There was an error reading the state file. The path\n"+ "and error are shown below. If you're trying to build a\n"+ "brand new infrastructure, explicitly pass the '-init'\n"+ "flag to Terraform to tell it it is okay to build new\n"+ "state.\n\n"+ "Path: %s\n"+ "Error: %s", statePath, err) } } // Load up the state var state *terraform.State if statePath != "" { f, err := os.Open(statePath) if err == nil { state, err = terraform.ReadState(f) f.Close() } if err != nil { return nil, fmt.Errorf("Error loading state: %s", err) } } config, err := config.LoadDir(path) if err != nil { return nil, fmt.Errorf("Error loading config: %s", err) } if err := config.Validate(); err != nil { return nil, fmt.Errorf("Error validating config: %s", err) } opts.Config = config opts.State = state ctx := terraform.NewContext(opts) if doPlan { if _, err := ctx.Plan(nil); err != nil { return nil, fmt.Errorf("Error running plan: %s", err) } } return ctx, nil } // contextOpts returns the options to use to initialize a Terraform // context with the settings from this Meta. func (m *Meta) contextOpts() *terraform.ContextOpts { var opts terraform.ContextOpts = *m.ContextOpts opts.Hooks = make( []terraform.Hook, len(m.ContextOpts.Hooks)+len(m.extraHooks)+1) opts.Hooks[0] = m.uiHook() copy(opts.Hooks[1:], m.ContextOpts.Hooks) copy(opts.Hooks[len(m.ContextOpts.Hooks)+1:], m.extraHooks) println(fmt.Sprintf("%#v", opts.Hooks)) return &opts } // process will process the meta-parameters out of the arguments. This // will potentially modify the args in-place. It will return the resulting // slice. func (m *Meta) process(args []string) []string { // We do this so that we retain the ability to technically call // process multiple times, even if we have no plans to do so if m.oldUi != nil { m.Ui = m.oldUi } // Set colorization m.color = m.Color for i, v := range args { if v == "-no-color" { m.Color = false args = append(args[:i], args[i+1:]...) break } } // Set the UI m.oldUi = m.Ui m.Ui = &ColorizeUi{ Colorize: m.Colorize(), ErrorColor: "[red]", Ui: m.oldUi, } return args } // uiHook returns the UiHook to use with the context. func (m *Meta) uiHook() *UiHook { return &UiHook{ Colorize: m.Colorize(), Ui: m.Ui, } }