diff --git a/command/apply.go b/command/apply.go index 52caa3307..cd6f7af9f 100644 --- a/command/apply.go +++ b/command/apply.go @@ -19,9 +19,11 @@ type ApplyCommand struct { } func (c *ApplyCommand) Run(args []string) int { + var init bool var statePath, stateOutPath string cmdFlags := flag.NewFlagSet("apply", flag.ContinueOnError) + cmdFlags.BoolVar(&init, "init", false, "init") cmdFlags.StringVar(&statePath, "state", "terraform.tfstate", "path") cmdFlags.StringVar(&stateOutPath, "state-out", "", "path") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } @@ -48,6 +50,23 @@ func (c *ApplyCommand) Run(args []string) int { stateOutPath = statePath } + // Load up the state + var state *terraform.State + if !init { + f, err := os.Open(statePath) + if err != nil { + c.Ui.Error(fmt.Sprintf("Error loading state: %s", err)) + return 1 + } + + state, err = terraform.ReadState(f) + f.Close() + if err != nil { + c.Ui.Error(fmt.Sprintf("Error loading state: %s", err)) + return 1 + } + } + b, err := config.Load(args[0]) if err != nil { c.Ui.Error(fmt.Sprintf("Error loading blueprint: %s", err)) @@ -63,13 +82,13 @@ func (c *ApplyCommand) Run(args []string) int { return 1 } - diff, err := tf.Diff(nil) + diff, err := tf.Diff(state) if err != nil { c.Ui.Error(fmt.Sprintf("Error running diff: %s", err)) return 1 } - state, err := tf.Apply(nil, diff) + state, err = tf.Apply(state, diff) if err != nil { c.Ui.Error(fmt.Sprintf("Error applying diff: %s", err)) return 1 diff --git a/command/apply_test.go b/command/apply_test.go index 5f405423c..2ef558589 100644 --- a/command/apply_test.go +++ b/command/apply_test.go @@ -3,6 +3,7 @@ package command import ( "io/ioutil" "os" + "reflect" "testing" "github.com/hashicorp/terraform/terraform" @@ -26,6 +27,7 @@ func TestApply(t *testing.T) { } args := []string{ + "-init", "-state", statePath, testFixturePath("apply"), } @@ -69,6 +71,72 @@ func TestApply_noState(t *testing.T) { } } +func TestApply_state(t *testing.T) { + // Write out some prior state + tf, err := ioutil.TempFile("", "tf") + if err != nil { + t.Fatalf("err: %s", err) + } + statePath := tf.Name() + defer os.Remove(tf.Name()) + + originalState := &terraform.State{ + Resources: map[string]*terraform.ResourceState{ + "test_instance.foo": &terraform.ResourceState{ + ID: "bar", + Type: "test_instance", + }, + }, + } + + err = terraform.WriteState(originalState, tf) + tf.Close() + if err != nil { + t.Fatalf("err: %s", err) + } + + p := testProvider() + ui := new(cli.MockUi) + c := &ApplyCommand{ + TFConfig: testTFConfig(p), + Ui: ui, + } + + // Run the apply command pointing to our existing state + args := []string{ + "-state", statePath, + testFixturePath("apply"), + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + // Verify that the provider was called with the existing state + expectedState := originalState.Resources["test_instance.foo"] + if !reflect.DeepEqual(p.ApplyState, expectedState) { + t.Fatalf("bad: %#v", p.ApplyState) + } + + // Verify a new state exists + 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_stateNoExist(t *testing.T) { p := testProvider() ui := new(cli.MockUi)