From 82af81b606569a0782d7b24326b3c75da181fd05 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 18 Jun 2014 21:36:44 -0700 Subject: [PATCH] command: tests for apply --- command/apply.go | 39 ++++++++++++- command/apply_test.go | 89 +++++++++++++++++++++++++++++ command/command_test.go | 35 ++++++++++++ command/test-fixtures/apply/main.tf | 3 + 4 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 command/apply_test.go create mode 100644 command/command_test.go create mode 100644 command/test-fixtures/apply/main.tf diff --git a/command/apply.go b/command/apply.go index 1f36a18c43..52caa33073 100644 --- a/command/apply.go +++ b/command/apply.go @@ -3,6 +3,7 @@ package command import ( "flag" "fmt" + "os" "strings" "github.com/hashicorp/terraform/config" @@ -18,7 +19,11 @@ type ApplyCommand struct { } func (c *ApplyCommand) Run(args []string) int { + var statePath, stateOutPath string + cmdFlags := flag.NewFlagSet("apply", flag.ContinueOnError) + cmdFlags.StringVar(&statePath, "state", "terraform.tfstate", "path") + cmdFlags.StringVar(&stateOutPath, "state-out", "", "path") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 @@ -33,6 +38,16 @@ func (c *ApplyCommand) Run(args []string) int { return 1 } + // TODO: if state, but not exist, -init required + + if statePath == "" { + c.Ui.Error("-state cannot be blank") + return 1 + } + if stateOutPath == "" { + stateOutPath = statePath + } + b, err := config.Load(args[0]) if err != nil { c.Ui.Error(fmt.Sprintf("Error loading blueprint: %s", err)) @@ -60,6 +75,19 @@ func (c *ApplyCommand) Run(args []string) int { return 1 } + // Write state out to the file + f, err := os.Create(stateOutPath) + if err != nil { + c.Ui.Error(fmt.Sprintf("Failed to save state: %s", err)) + return 1 + } + defer f.Close() + + if err := terraform.WriteState(state, f); err != nil { + c.Ui.Error(fmt.Sprintf("Failed to save state: %s", err)) + return 1 + } + c.Ui.Output(strings.TrimSpace(state.String())) return 0 @@ -74,8 +102,15 @@ Usage: terraform apply [terraform.tf] Options: - -init If specified, it is okay to build brand new infrastructure - (with no state file specified). + -init If specified, it is okay to build brand new + infrastructure (with no state file specified). + + -state=terraform.tfstate Path to the state file to build off of. This file + will also be written to with updated state unless + -state-out is specified. + + -state-out=file.tfstate Path to save the new state. If not specified, the + -state value will be used. ` return strings.TrimSpace(helpText) diff --git a/command/apply_test.go b/command/apply_test.go new file mode 100644 index 0000000000..5f405423ce --- /dev/null +++ b/command/apply_test.go @@ -0,0 +1,89 @@ +package command + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/hashicorp/terraform/terraform" + "github.com/mitchellh/cli" +) + +func TestApply(t *testing.T) { + tf, err := ioutil.TempFile("", "tf") + if err != nil { + t.Fatalf("err: %s", err) + } + statePath := tf.Name() + tf.Close() + os.Remove(tf.Name()) + + p := testProvider() + ui := new(cli.MockUi) + c := &ApplyCommand{ + TFConfig: testTFConfig(p), + Ui: ui, + } + + args := []string{ + "-state", statePath, + testFixturePath("apply"), + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + if _, err := os.Stat(statePath); err != nil { + t.Fatalf("err: %s", err) + } + + f, err := os.Open(statePath) + if err != nil { + t.Fatalf("err: %s", err) + } + defer f.Close() + + state, err := terraform.ReadState(f) + if err != nil { + t.Fatalf("err: %s", err) + } + if state == nil { + t.Fatal("state should not be nil") + } +} + +func TestApply_noState(t *testing.T) { + p := testProvider() + ui := new(cli.MockUi) + c := &ApplyCommand{ + TFConfig: testTFConfig(p), + Ui: ui, + } + + args := []string{ + "-state=", + testFixturePath("apply"), + } + if code := c.Run(args); code != 1 { + t.Fatalf("bad: \n%s", ui.OutputWriter.String()) + } +} + +func TestApply_stateNoExist(t *testing.T) { + p := testProvider() + ui := new(cli.MockUi) + c := &ApplyCommand{ + TFConfig: testTFConfig(p), + Ui: ui, + } + + args := []string{ + "-state=idontexist.tfstate", + testFixturePath("apply"), + } + // TODO + return + if code := c.Run(args); code != 1 { + t.Fatalf("bad: \n%s", ui.OutputWriter.String()) + } +} diff --git a/command/command_test.go b/command/command_test.go new file mode 100644 index 0000000000..e151b9de03 --- /dev/null +++ b/command/command_test.go @@ -0,0 +1,35 @@ +package command + +import ( + "path/filepath" + + "github.com/hashicorp/terraform/terraform" +) + +// This is the directory where our test fixtures are. +const fixtureDir = "./test-fixtures" + +func testFixturePath(name string) string { + return filepath.Join(fixtureDir, name, "main.tf") +} + +func testTFConfig(p terraform.ResourceProvider) *terraform.Config { + return &terraform.Config{ + Providers: map[string]terraform.ResourceProviderFactory{ + "test": func() (terraform.ResourceProvider, error) { + return p, nil + }, + }, + } +} + +func testProvider() *terraform.MockResourceProvider { + p := new(terraform.MockResourceProvider) + p.ResourcesReturn = []terraform.ResourceType{ + terraform.ResourceType{ + Name: "test_instance", + }, + } + + return p +} diff --git a/command/test-fixtures/apply/main.tf b/command/test-fixtures/apply/main.tf new file mode 100644 index 0000000000..5794f94d9d --- /dev/null +++ b/command/test-fixtures/apply/main.tf @@ -0,0 +1,3 @@ +resource "test_instance" "foo" { + ami = "bar" +}