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 }