command: -var flag works

This commit is contained in:
Mitchell Hashimoto 2014-07-18 11:37:27 -07:00
parent 272632ced0
commit 3534470ca3
12 changed files with 273 additions and 6 deletions

View File

@ -2,7 +2,6 @@ package command
import ( import (
"bytes" "bytes"
"flag"
"fmt" "fmt"
"os" "os"
"sort" "sort"
@ -25,7 +24,7 @@ func (c *ApplyCommand) Run(args []string) int {
args = c.Meta.process(args) args = c.Meta.process(args)
cmdFlags := flag.NewFlagSet("apply", flag.ContinueOnError) cmdFlags := c.Meta.flagSet("apply")
cmdFlags.BoolVar(&init, "init", false, "init") cmdFlags.BoolVar(&init, "init", false, "init")
cmdFlags.StringVar(&statePath, "state", DefaultStateFilename, "path") cmdFlags.StringVar(&statePath, "state", DefaultStateFilename, "path")
cmdFlags.StringVar(&stateOutPath, "state-out", "", "path") cmdFlags.StringVar(&stateOutPath, "state-out", "", "path")
@ -207,6 +206,14 @@ Options:
"-state". This can be used to preserve the old "-state". This can be used to preserve the old
state. state.
-var 'foo=bar' Set a variable in the Terraform configuration. This
flag can be set multiple times.
-var-file=foo Set variables in the Terraform configuration from
a file. If "terraform.tfvars" is present, it will be
automatically loaded if this flag is not specified.
` `
return strings.TrimSpace(helpText) return strings.TrimSpace(helpText)
} }

View File

@ -466,3 +466,41 @@ func TestApply_stateNoExist(t *testing.T) {
t.Fatalf("bad: \n%s", ui.OutputWriter.String()) t.Fatalf("bad: \n%s", ui.OutputWriter.String())
} }
} }
func TestApply_vars(t *testing.T) {
statePath := testTempFile(t)
p := testProvider()
ui := new(cli.MockUi)
c := &ApplyCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
}
actual := ""
p.DiffFn = func(
s *terraform.ResourceState,
c *terraform.ResourceConfig) (*terraform.ResourceDiff, error) {
if v, ok := c.Config["value"]; ok {
actual = v.(string)
}
return nil, nil
}
args := []string{
"-init",
"-var", "foo=bar",
"-state", statePath,
testFixturePath("apply-vars"),
}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
if actual != "bar" {
t.Fatal("didn't work")
}
}

42
command/flag_var.go Normal file
View File

@ -0,0 +1,42 @@
package command
import (
"fmt"
"strings"
)
// FlagVar is a flag.Value implementation for parsing user variables
// from the command-line in the format of '-var key=value'.
type FlagVar map[string]string
func (v *FlagVar) String() string {
return ""
}
func (v *FlagVar) Set(raw string) error {
idx := strings.Index(raw, "=")
if idx == -1 {
return fmt.Errorf("No '=' value in arg: %s", raw)
}
if *v == nil {
*v = make(map[string]string)
}
key, value := raw[0:idx], raw[idx+1:]
(*v)[key] = value
return nil
}
// FlagVarFile is a flag.Value implementation for parsing user variables
// from the command line in the form of files. i.e. '-var-file=foo'
type FlagVarFile map[string]string
func (v *FlagVarFile) String() string {
return ""
}
func (v *FlagVarFile) Set(raw string) error {
// TODO
return nil
}

56
command/flag_var_test.go Normal file
View File

@ -0,0 +1,56 @@
package command
import (
"flag"
"reflect"
"testing"
)
func TestFlagVar_impl(t *testing.T) {
var _ flag.Value = new(FlagVar)
}
func TestFlagVar(t *testing.T) {
cases := []struct {
Input string
Output map[string]string
Error bool
}{
{
"key=value",
map[string]string{"key": "value"},
false,
},
{
"key=",
map[string]string{"key": ""},
false,
},
{
"key=foo=bar",
map[string]string{"key": "foo=bar"},
false,
},
{
"key",
nil,
true,
},
}
for _, tc := range cases {
f := new(FlagVar)
err := f.Set(tc.Input)
if (err != nil) != tc.Error {
t.Fatalf("bad error. Input: %#v", tc.Input)
}
actual := map[string]string(*f)
if !reflect.DeepEqual(actual, tc.Output) {
t.Fatalf("bad: %#v", actual)
}
}
}

View File

@ -1,6 +1,7 @@
package command package command
import ( import (
"flag"
"fmt" "fmt"
"os" "os"
@ -19,6 +20,9 @@ type Meta struct {
// This can be set by the command itself to provide extra hooks. // This can be set by the command itself to provide extra hooks.
extraHooks []terraform.Hook extraHooks []terraform.Hook
// Variables for the context (private)
variables map[string]string
color bool color bool
oldUi cli.Ui oldUi cli.Ui
} }
@ -109,9 +113,29 @@ func (m *Meta) contextOpts() *terraform.ContextOpts {
copy(opts.Hooks[1:], m.ContextOpts.Hooks) copy(opts.Hooks[1:], m.ContextOpts.Hooks)
copy(opts.Hooks[len(m.ContextOpts.Hooks)+1:], m.extraHooks) copy(opts.Hooks[len(m.ContextOpts.Hooks)+1:], m.extraHooks)
println(fmt.Sprintf("%#v", opts.Hooks)) println(fmt.Sprintf("%#v", opts.Hooks))
if len(m.variables) > 0 {
vs := make(map[string]string)
for k, v := range opts.Variables {
vs[k] = v
}
for k, v := range m.variables {
vs[k] = v
}
opts.Variables = vs
}
return &opts return &opts
} }
// flags adds the meta flags to the given FlagSet.
func (m *Meta) flagSet(n string) *flag.FlagSet {
f := flag.NewFlagSet(n, flag.ContinueOnError)
f.Var((*FlagVar)(&m.variables), "var", "variables")
f.Var((*FlagVarFile)(&m.variables), "var-file", "variable file")
return f
}
// process will process the meta-parameters out of the arguments. This // process will process the meta-parameters out of the arguments. This
// will potentially modify the args in-place. It will return the resulting // will potentially modify the args in-place. It will return the resulting
// slice. // slice.

View File

@ -1,7 +1,6 @@
package command package command
import ( import (
"flag"
"fmt" "fmt"
"log" "log"
"os" "os"
@ -22,7 +21,7 @@ func (c *PlanCommand) Run(args []string) int {
args = c.Meta.process(args) args = c.Meta.process(args)
cmdFlags := flag.NewFlagSet("plan", flag.ContinueOnError) cmdFlags := c.Meta.flagSet("plan")
cmdFlags.BoolVar(&destroy, "destroy", false, "destroy") cmdFlags.BoolVar(&destroy, "destroy", false, "destroy")
cmdFlags.BoolVar(&refresh, "refresh", true, "refresh") cmdFlags.BoolVar(&refresh, "refresh", true, "refresh")
cmdFlags.StringVar(&outPath, "out", "", "path") cmdFlags.StringVar(&outPath, "out", "", "path")
@ -145,6 +144,13 @@ Options:
up Terraform-managed resources. By default it will up Terraform-managed resources. By default it will
use the state "terraform.tfstate" if it exists. use the state "terraform.tfstate" if it exists.
-var 'foo=bar' Set a variable in the Terraform configuration. This
flag can be set multiple times.
-var-file=foo Set variables in the Terraform configuration from
a file. If "terraform.tfvars" is present, it will be
automatically loaded if this flag is not specified.
` `
return strings.TrimSpace(helpText) return strings.TrimSpace(helpText)
} }

View File

@ -281,3 +281,37 @@ func TestPlan_stateDefault(t *testing.T) {
t.Fatalf("bad: %#v", p.DiffState) t.Fatalf("bad: %#v", p.DiffState)
} }
} }
func TestPlan_vars(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
c := &PlanCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
}
actual := ""
p.DiffFn = func(
s *terraform.ResourceState,
c *terraform.ResourceConfig) (*terraform.ResourceDiff, error) {
if v, ok := c.Config["value"]; ok {
actual = v.(string)
}
return nil, nil
}
args := []string{
"-var", "foo=bar",
testFixturePath("plan-vars"),
}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
if actual != "bar" {
t.Fatal("didn't work")
}
}

View File

@ -1,7 +1,6 @@
package command package command
import ( import (
"flag"
"fmt" "fmt"
"log" "log"
"os" "os"
@ -21,7 +20,7 @@ func (c *RefreshCommand) Run(args []string) int {
args = c.Meta.process(args) args = c.Meta.process(args)
cmdFlags := flag.NewFlagSet("refresh", flag.ContinueOnError) cmdFlags := c.Meta.flagSet("refresh")
cmdFlags.StringVar(&statePath, "state", DefaultStateFilename, "path") cmdFlags.StringVar(&statePath, "state", DefaultStateFilename, "path")
cmdFlags.StringVar(&stateOutPath, "state-out", "", "path") cmdFlags.StringVar(&stateOutPath, "state-out", "", "path")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
@ -128,6 +127,13 @@ Options:
-state-out=path Path to write updated state file. By default, the -state-out=path Path to write updated state file. By default, the
"-state" path will be used. "-state" path will be used.
-var 'foo=bar' Set a variable in the Terraform configuration. This
flag can be set multiple times.
-var-file=foo Set variables in the Terraform configuration from
a file. If "terraform.tfvars" is present, it will be
automatically loaded if this flag is not specified.
` `
return strings.TrimSpace(helpText) return strings.TrimSpace(helpText)
} }

View File

@ -296,3 +296,40 @@ func TestRefresh_outPath(t *testing.T) {
t.Fatalf("bad: %#v", actual) t.Fatalf("bad: %#v", actual)
} }
} }
func TestRefresh_var(t *testing.T) {
state := &terraform.State{
Resources: map[string]*terraform.ResourceState{
"test_instance.foo": &terraform.ResourceState{
ID: "bar",
Type: "test_instance",
},
},
}
statePath := testStateFile(t, state)
p := testProvider()
ui := new(cli.MockUi)
c := &RefreshCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
}
args := []string{
"-var", "foo=bar",
"-state", statePath,
testFixturePath("refresh-var"),
}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
if !p.ConfigureCalled {
t.Fatal("configure should be called")
}
if p.ConfigureConfig.Config["value"].(string) != "bar" {
t.Fatalf("bad: %#v", p.ConfigureConfig.Config)
}
}

View File

@ -0,0 +1,5 @@
variable "foo" {}
resource "test_instance" "foo" {
value = "${var.foo}"
}

View File

@ -0,0 +1,5 @@
variable "foo" {}
resource "test_instance" "foo" {
value = "${var.foo}"
}

View File

@ -0,0 +1,7 @@
variable "foo" {}
provider "test" {
value = "${var.foo}"
}
resource "test_instance" "foo" {}