From 54cc66367db0d655b05592f11b78dee99d6d0f1d Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Sun, 27 Jul 2014 18:09:04 -0400 Subject: [PATCH 1/8] command: Adding backup of state file --- command/apply.go | 28 +++++++++++++++++++++++++++- command/command.go | 3 +++ command/meta.go | 4 ++++ command/plan.go | 27 ++++++++++++++++++++++++++- command/refresh.go | 27 ++++++++++++++++++++++++++- 5 files changed, 86 insertions(+), 3 deletions(-) diff --git a/command/apply.go b/command/apply.go index 62daea2175..00ea50504e 100644 --- a/command/apply.go +++ b/command/apply.go @@ -3,6 +3,7 @@ package command import ( "bytes" "fmt" + "log" "os" "sort" "strings" @@ -20,7 +21,7 @@ type ApplyCommand struct { func (c *ApplyCommand) Run(args []string) int { var refresh bool - var statePath, stateOutPath string + var statePath, stateOutPath, backupPath string args = c.Meta.process(args) @@ -28,6 +29,7 @@ func (c *ApplyCommand) Run(args []string) int { cmdFlags.BoolVar(&refresh, "refresh", true, "refresh") cmdFlags.StringVar(&statePath, "state", DefaultStateFilename, "path") cmdFlags.StringVar(&stateOutPath, "state-out", "", "path") + cmdFlags.StringVar(&backupPath, "backup", "", "path") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 @@ -59,6 +61,12 @@ func (c *ApplyCommand) Run(args []string) int { stateOutPath = statePath } + // If we don't specify a backup path, default to state out with + // the extention + if backupPath == "" { + backupPath = stateOutPath + DefaultBackupExtention + } + // Build the context based on the arguments given ctx, planned, err := c.Context(configPath, statePath) if err != nil { @@ -69,6 +77,20 @@ func (c *ApplyCommand) Run(args []string) int { return 1 } + // Create a backup of the state before updating + if backupPath != "-" { + log.Printf("[INFO] Writing backup state to: %s", backupPath) + f, err := os.Create(backupPath) + if err == nil { + defer f.Close() + err = terraform.WriteState(c.State, f) + } + if err != nil { + c.Ui.Error(fmt.Sprintf("Error writing backup state file: %s", err)) + return 1 + } + } + // Plan if we haven't already if !planned { if refresh { @@ -201,6 +223,10 @@ Usage: terraform apply [options] [dir] Options: + -backup=path Path to backup the existing state file before + modifying. Defaults to the "-state-out" path with + ".backup" extention. Set to "-" to disable backup. + -no-color If specified, output won't contain any color. -refresh=true Update state prior to checking for differences. This diff --git a/command/command.go b/command/command.go index 9d27321d55..faa9cfcc3b 100644 --- a/command/command.go +++ b/command/command.go @@ -10,6 +10,9 @@ import ( // DefaultStateFilename is the default filename used for the state file. const DefaultStateFilename = "terraform.tfstate" +// DefaultBackupExtention is added to the state file to form the path +const DefaultBackupExtention = ".backup" + func validateContext(ctx *terraform.Context, ui cli.Ui) bool { if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 { ui.Output( diff --git a/command/meta.go b/command/meta.go index e0d3acde77..2dda7a9745 100644 --- a/command/meta.go +++ b/command/meta.go @@ -16,6 +16,7 @@ type Meta struct { Color bool ContextOpts *terraform.ContextOpts Ui cli.Ui + State *terraform.State // This can be set by the command itself to provide extra hooks. extraHooks []terraform.Hook @@ -77,6 +78,9 @@ func (m *Meta) Context(path, statePath string) (*terraform.Context, bool, error) } } + // Store the loaded state + m.State = state + config, err := config.LoadDir(path) if err != nil { return nil, false, fmt.Errorf("Error loading config: %s", err) diff --git a/command/plan.go b/command/plan.go index b866482b64..2a5abf3c3d 100644 --- a/command/plan.go +++ b/command/plan.go @@ -17,7 +17,7 @@ type PlanCommand struct { func (c *PlanCommand) Run(args []string) int { var destroy, refresh bool - var outPath, statePath string + var outPath, statePath, backupPath string args = c.Meta.process(args) @@ -26,6 +26,7 @@ func (c *PlanCommand) Run(args []string) int { cmdFlags.BoolVar(&refresh, "refresh", true, "refresh") cmdFlags.StringVar(&outPath, "out", "", "path") cmdFlags.StringVar(&statePath, "state", DefaultStateFilename, "path") + cmdFlags.StringVar(&backupPath, "backup", "", "path") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 @@ -58,6 +59,12 @@ func (c *PlanCommand) Run(args []string) int { } } + // If we don't specify a backup path, default to state out with + // the extention + if backupPath == "" { + backupPath = statePath + DefaultBackupExtention + } + ctx, _, err := c.Context(path, statePath) if err != nil { c.Ui.Error(err.Error()) @@ -68,6 +75,20 @@ func (c *PlanCommand) Run(args []string) int { } if refresh { + // Create a backup of the state before updating + if backupPath != "-" { + log.Printf("[INFO] Writing backup state to: %s", backupPath) + f, err := os.Create(backupPath) + if err == nil { + defer f.Close() + err = terraform.WriteState(c.State, f) + } + if err != nil { + c.Ui.Error(fmt.Sprintf("Error writing backup state file: %s", err)) + return 1 + } + } + c.Ui.Output("Refreshing Terraform state prior to plan...\n") if _, err := ctx.Refresh(); err != nil { c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err)) @@ -130,6 +151,10 @@ Usage: terraform plan [options] [dir] Options: + -backup=path Path to backup the existing state file before + modifying. Defaults to the "-state-out" path with + ".backup" extention. Set to "-" to disable backup. + -destroy If set, a plan will be generated to destroy all resources managed by the given configuration and state. diff --git a/command/refresh.go b/command/refresh.go index 93310efa75..186df8e3ef 100644 --- a/command/refresh.go +++ b/command/refresh.go @@ -16,13 +16,14 @@ type RefreshCommand struct { } func (c *RefreshCommand) Run(args []string) int { - var statePath, stateOutPath string + var statePath, stateOutPath, backupPath string args = c.Meta.process(args) cmdFlags := c.Meta.flagSet("refresh") cmdFlags.StringVar(&statePath, "state", DefaultStateFilename, "path") cmdFlags.StringVar(&stateOutPath, "state-out", "", "path") + cmdFlags.StringVar(&backupPath, "backup", "", "path") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 @@ -50,6 +51,12 @@ func (c *RefreshCommand) Run(args []string) int { stateOutPath = statePath } + // If we don't specify a backup path, default to state out with + // the extention + if backupPath == "" { + backupPath = stateOutPath + DefaultBackupExtention + } + // Verify that the state path exists. The "ContextArg" function below // will actually do this, but we want to provide a richer error message // if possible. @@ -86,6 +93,20 @@ func (c *RefreshCommand) Run(args []string) int { return 1 } + // Create a backup of the state before updating + if backupPath != "-" { + log.Printf("[INFO] Writing backup state to: %s", backupPath) + f, err := os.Create(backupPath) + if err == nil { + defer f.Close() + err = terraform.WriteState(c.State, f) + } + if err != nil { + c.Ui.Error(fmt.Sprintf("Error writing backup state file: %s", err)) + return 1 + } + } + state, err := ctx.Refresh() if err != nil { c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err)) @@ -119,6 +140,10 @@ Usage: terraform refresh [options] [dir] Options: + -backup=path Path to backup the existing state file before + modifying. Defaults to the "-state-out" path with + ".backup" extention. Set to "-" to disable backup. + -no-color If specified, output won't contain any color. -state=path Path to read and save state (unless state-out From f26f432f4c388e57d142ee08aef4a0a500cfeaa0 Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Sun, 27 Jul 2014 22:58:12 -0400 Subject: [PATCH 2/8] command/refresh: Testing backup option --- command/refresh.go | 2 +- command/refresh_test.go | 131 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 1 deletion(-) diff --git a/command/refresh.go b/command/refresh.go index 186df8e3ef..f3038f96ad 100644 --- a/command/refresh.go +++ b/command/refresh.go @@ -94,7 +94,7 @@ func (c *RefreshCommand) Run(args []string) int { } // Create a backup of the state before updating - if backupPath != "-" { + if backupPath != "-" && c.State != nil { log.Printf("[INFO] Writing backup state to: %s", backupPath) f, err := os.Create(backupPath) if err == nil { diff --git a/command/refresh_test.go b/command/refresh_test.go index 9525c68a99..ab345c1696 100644 --- a/command/refresh_test.go +++ b/command/refresh_test.go @@ -221,6 +221,23 @@ func TestRefresh_defaultState(t *testing.T) { if !reflect.DeepEqual(actual, expected) { t.Fatalf("bad: %#v", actual) } + + f, err = os.Open(statePath + DefaultBackupExtention) + if err != nil { + t.Fatalf("err: %s", err) + } + + backupState, err := terraform.ReadState(f) + f.Close() + if err != nil { + t.Fatalf("err: %s", err) + } + + actual = backupState.Resources["test_instance.foo"] + expected = originalState.Resources["test_instance.foo"] + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } } func TestRefresh_outPath(t *testing.T) { @@ -295,6 +312,21 @@ func TestRefresh_outPath(t *testing.T) { if !reflect.DeepEqual(actual, expected) { t.Fatalf("bad: %#v", actual) } + + f, err = os.Open(outPath + DefaultBackupExtention) + if err != nil { + t.Fatalf("err: %s", err) + } + + backupState, err := terraform.ReadState(f) + f.Close() + if err != nil { + t.Fatalf("err: %s", err) + } + + if !reflect.DeepEqual(backupState, state) { + t.Fatalf("bad: %#v", backupState) + } } func TestRefresh_var(t *testing.T) { @@ -376,6 +408,105 @@ func TestRefresh_varFile(t *testing.T) { } } +func TestRefresh_backup(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) + + // Output path + outf, err := ioutil.TempFile("", "tf") + if err != nil { + t.Fatalf("err: %s", err) + } + outPath := outf.Name() + outf.Close() + os.Remove(outPath) + + // Backup path + backupf, err := ioutil.TempFile("", "tf") + if err != nil { + t.Fatalf("err: %s", err) + } + backupPath := backupf.Name() + backupf.Close() + os.Remove(backupPath) + + p := testProvider() + ui := new(cli.MockUi) + c := &RefreshCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(p), + Ui: ui, + }, + } + + p.RefreshFn = nil + p.RefreshReturn = &terraform.ResourceState{ID: "yes"} + + args := []string{ + "-state", statePath, + "-state-out", outPath, + "-backup", backupPath, + testFixturePath("refresh"), + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + f, err := os.Open(statePath) + if err != nil { + t.Fatalf("err: %s", err) + } + + newState, err := terraform.ReadState(f) + f.Close() + if err != nil { + t.Fatalf("err: %s", err) + } + + if !reflect.DeepEqual(newState, state) { + t.Fatalf("bad: %#v", newState) + } + + f, err = os.Open(outPath) + if err != nil { + t.Fatalf("err: %s", err) + } + + newState, err = terraform.ReadState(f) + f.Close() + if err != nil { + t.Fatalf("err: %s", err) + } + + actual := newState.Resources["test_instance.foo"] + expected := p.RefreshReturn + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } + + f, err = os.Open(backupPath) + if err != nil { + t.Fatalf("err: %s", err) + } + + backupState, err := terraform.ReadState(f) + f.Close() + if err != nil { + t.Fatalf("err: %s", err) + } + + if !reflect.DeepEqual(backupState, state) { + t.Fatalf("bad: %#v", backupState) + } +} + const refreshVarFile = ` foo = "bar" ` From 16ef3f57334a04e8b4945d6feaac1d86f577a3a0 Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Sun, 27 Jul 2014 23:38:41 -0400 Subject: [PATCH 3/8] command: Testing the -backup feature --- command/apply.go | 2 +- command/apply_test.go | 189 ++++++++++++++++++++++++++++++++++++++++ command/plan.go | 2 +- command/plan_test.go | 145 ++++++++++++++++++++++++++++++ command/refresh_test.go | 81 +++++++++++++++++ 5 files changed, 417 insertions(+), 2 deletions(-) diff --git a/command/apply.go b/command/apply.go index 00ea50504e..941d1db883 100644 --- a/command/apply.go +++ b/command/apply.go @@ -78,7 +78,7 @@ func (c *ApplyCommand) Run(args []string) int { } // Create a backup of the state before updating - if backupPath != "-" { + if backupPath != "-" && c.State != nil { log.Printf("[INFO] Writing backup state to: %s", backupPath) f, err := os.Create(backupPath) if err == nil { diff --git a/command/apply_test.go b/command/apply_test.go index ab714172dd..7b8289ea24 100644 --- a/command/apply_test.go +++ b/command/apply_test.go @@ -358,6 +358,22 @@ func TestApply_refresh(t *testing.T) { if state == nil { t.Fatal("state should not be nil") } + + // Should have a backup file + f, err = os.Open(statePath + DefaultBackupExtention) + if err != nil { + t.Fatalf("err: %s", err) + } + + backupState, err := terraform.ReadState(f) + f.Close() + if err != nil { + t.Fatalf("err: %s", err) + } + + if !reflect.DeepEqual(backupState, originalState) { + t.Fatalf("bad: %#v", backupState) + } } func TestApply_shutdown(t *testing.T) { @@ -517,6 +533,25 @@ func TestApply_state(t *testing.T) { if state == nil { t.Fatal("state should not be nil") } + + // Should have a backup file + f, err = os.Open(statePath + DefaultBackupExtention) + if err != nil { + t.Fatalf("err: %s", err) + } + + backupState, err := terraform.ReadState(f) + f.Close() + if err != nil { + t.Fatalf("err: %s", err) + } + + // nil out the ConnInfo since that should not be restored + originalState.Resources["test_instance.foo"].ConnInfo = nil + + if !reflect.DeepEqual(backupState, originalState) { + t.Fatalf("bad: %#v", backupState) + } } func TestApply_stateNoExist(t *testing.T) { @@ -617,6 +652,160 @@ func TestApply_varFile(t *testing.T) { } } +func TestApply_backup(t *testing.T) { + originalState := &terraform.State{ + Resources: map[string]*terraform.ResourceState{ + "test_instance.foo": &terraform.ResourceState{ + ID: "bar", + Type: "test_instance", + }, + }, + } + + statePath := testStateFile(t, originalState) + backupPath := testTempFile(t) + + p := testProvider() + p.DiffReturn = &terraform.ResourceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ami": &terraform.ResourceAttrDiff{ + New: "bar", + }, + }, + } + + ui := new(cli.MockUi) + c := &ApplyCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(p), + Ui: ui, + }, + } + + // Run the apply command pointing to our existing state + args := []string{ + "-state", statePath, + "-backup", backupPath, + testFixturePath("apply"), + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + // 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") + } + + // Should have a backup file + f, err = os.Open(backupPath) + if err != nil { + t.Fatalf("err: %s", err) + } + + backupState, err := terraform.ReadState(f) + f.Close() + if err != nil { + t.Fatalf("err: %s", err) + } + + actual := backupState.Resources["test_instance.foo"] + expected := originalState.Resources["test_instance.foo"] + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v %#v", actual, expected) + } +} + +func TestApply_disableBackup(t *testing.T) { + originalState := &terraform.State{ + Resources: map[string]*terraform.ResourceState{ + "test_instance.foo": &terraform.ResourceState{ + ID: "bar", + Type: "test_instance", + ConnInfo: make(map[string]string), + }, + }, + } + + statePath := testStateFile(t, originalState) + + p := testProvider() + p.DiffReturn = &terraform.ResourceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ami": &terraform.ResourceAttrDiff{ + New: "bar", + }, + }, + } + + ui := new(cli.MockUi) + c := &ApplyCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(p), + Ui: ui, + }, + } + + // Run the apply command pointing to our existing state + args := []string{ + "-state", statePath, + "-backup", "-", + 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.DiffState, expectedState) { + t.Fatalf("bad: %#v", p.DiffState) + } + + 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") + } + + // Ensure there is no backup + _, err = os.Stat(statePath + DefaultBackupExtention) + if err == nil || !os.IsNotExist(err) { + t.Fatalf("backup should not exist") + } +} + const applyVarFile = ` foo = "bar" ` diff --git a/command/plan.go b/command/plan.go index 2a5abf3c3d..32ee31527e 100644 --- a/command/plan.go +++ b/command/plan.go @@ -76,7 +76,7 @@ func (c *PlanCommand) Run(args []string) int { if refresh { // Create a backup of the state before updating - if backupPath != "-" { + if backupPath != "-" && c.State != nil { log.Printf("[INFO] Writing backup state to: %s", backupPath) f, err := os.Create(backupPath) if err == nil { diff --git a/command/plan_test.go b/command/plan_test.go index 7a2f171d98..d02dba1b2e 100644 --- a/command/plan_test.go +++ b/command/plan_test.go @@ -78,6 +78,21 @@ func TestPlan_destroy(t *testing.T) { t.Fatalf("bad: %#v", r) } } + + f, err := os.Open(statePath + DefaultBackupExtention) + if err != nil { + t.Fatalf("err: %s", err) + } + + backupState, err := terraform.ReadState(f) + f.Close() + if err != nil { + t.Fatalf("err: %s", err) + } + + if !reflect.DeepEqual(backupState, originalState) { + t.Fatalf("bad: %#v", backupState) + } } func TestPlan_noState(t *testing.T) { p := testProvider() @@ -355,6 +370,136 @@ func TestPlan_varFile(t *testing.T) { } } +func TestPlan_backup(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()) + + // Write out some prior state + backupf, err := ioutil.TempFile("", "tf") + if err != nil { + t.Fatalf("err: %s", err) + } + backupPath := backupf.Name() + backupf.Close() + os.Remove(backupPath) + defer os.Remove(backupPath) + + 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 := &PlanCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(p), + Ui: ui, + }, + } + + args := []string{ + "-state", statePath, + "-backup", backupPath, + testFixturePath("plan"), + } + 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.DiffState, expectedState) { + t.Fatalf("bad: %#v", p.DiffState) + } + + // Verify the backup exist + f, err := os.Open(backupPath) + if err != nil { + t.Fatalf("err: %s", err) + } + + backupState, err := terraform.ReadState(f) + f.Close() + if err != nil { + t.Fatalf("err: %s", err) + } + + if !reflect.DeepEqual(backupState, originalState) { + t.Fatalf("bad: %#v", backupState) + } +} + +func TestPlan_disableBackup(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 := &PlanCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(p), + Ui: ui, + }, + } + + args := []string{ + "-state", statePath, + "-backup", "-", + testFixturePath("plan"), + } + 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.DiffState, expectedState) { + t.Fatalf("bad: %#v", p.DiffState) + } + + // Ensure there is no backup + _, err = os.Stat(statePath + DefaultBackupExtention) + if err == nil || !os.IsNotExist(err) { + t.Fatalf("backup should not exist") + } +} + const planVarFile = ` foo = "bar" ` diff --git a/command/refresh_test.go b/command/refresh_test.go index ab345c1696..976612eaf1 100644 --- a/command/refresh_test.go +++ b/command/refresh_test.go @@ -507,6 +507,87 @@ func TestRefresh_backup(t *testing.T) { } } +func TestRefresh_disableBackup(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) + + // Output path + outf, err := ioutil.TempFile("", "tf") + if err != nil { + t.Fatalf("err: %s", err) + } + outPath := outf.Name() + outf.Close() + os.Remove(outPath) + + p := testProvider() + ui := new(cli.MockUi) + c := &RefreshCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(p), + Ui: ui, + }, + } + + p.RefreshFn = nil + p.RefreshReturn = &terraform.ResourceState{ID: "yes"} + + args := []string{ + "-state", statePath, + "-state-out", outPath, + "-backup", "-", + testFixturePath("refresh"), + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + f, err := os.Open(statePath) + if err != nil { + t.Fatalf("err: %s", err) + } + + newState, err := terraform.ReadState(f) + f.Close() + if err != nil { + t.Fatalf("err: %s", err) + } + + if !reflect.DeepEqual(newState, state) { + t.Fatalf("bad: %#v", newState) + } + + f, err = os.Open(outPath) + if err != nil { + t.Fatalf("err: %s", err) + } + + newState, err = terraform.ReadState(f) + f.Close() + if err != nil { + t.Fatalf("err: %s", err) + } + + actual := newState.Resources["test_instance.foo"] + expected := p.RefreshReturn + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } + + // Ensure there is no backup + _, err = os.Stat(outPath + DefaultBackupExtention) + if err == nil || !os.IsNotExist(err) { + t.Fatalf("backup should not exist") + } +} + const refreshVarFile = ` foo = "bar" ` From 8fbcd65eb1853b8054078d09bf5e10bb3987f8a4 Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Sun, 27 Jul 2014 23:40:18 -0400 Subject: [PATCH 4/8] website: Document the -backup flag --- website/source/docs/commands/apply.html.markdown | 3 +++ website/source/docs/commands/plan.html.markdown | 3 +++ website/source/docs/commands/refresh.html.markdown | 3 +++ 3 files changed, 9 insertions(+) diff --git a/website/source/docs/commands/apply.html.markdown b/website/source/docs/commands/apply.html.markdown index 909770b18e..6338c46976 100644 --- a/website/source/docs/commands/apply.html.markdown +++ b/website/source/docs/commands/apply.html.markdown @@ -21,6 +21,9 @@ execute a pre-determined set of actions. The command-line flags are all optional. The list of available flags are: +* `-backup=path` - Path to the backup file. Defaults to `-state-out` with + the ".backup" extention. Disabled by setting to "-". + * `-no-color` - Disables output with coloring. * `-refresh=true` - Update the state for each resource prior to planning diff --git a/website/source/docs/commands/plan.html.markdown b/website/source/docs/commands/plan.html.markdown index 1435d97c3f..165c8b9db2 100644 --- a/website/source/docs/commands/plan.html.markdown +++ b/website/source/docs/commands/plan.html.markdown @@ -21,6 +21,9 @@ for the configuration and state file to refresh. The command-line flags are all optional. The list of available flags are: +* `-backup=path` - Path to the backup file. Defaults to `-state-out` with + the ".backup" extention. Disabled by setting to "-". + * `-destroy` - If set, generates a plan to destroy all the known resources. * `-no-color` - Disables output with coloring. diff --git a/website/source/docs/commands/refresh.html.markdown b/website/source/docs/commands/refresh.html.markdown index 06f42f7fd2..a6858511c6 100644 --- a/website/source/docs/commands/refresh.html.markdown +++ b/website/source/docs/commands/refresh.html.markdown @@ -24,6 +24,9 @@ for the configuration and state file to refresh. The command-line flags are all optional. The list of available flags are: +* `-backup=path` - Path to the backup file. Defaults to `-state-out` with + the ".backup" extention. Disabled by setting to "-". + * `-no-color` - Disables output with coloring * `-state=path` - Path to read and write the state file to. Defaults to "terraform.tfstate". From 80ce9ad5135bee8672b6534e9198a9ecbb1b62a5 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 27 Jul 2014 22:56:55 -0700 Subject: [PATCH 5/8] command: f.Close instead of defer --- command/apply.go | 2 +- command/plan.go | 2 +- command/refresh.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/command/apply.go b/command/apply.go index 941d1db883..7553da047b 100644 --- a/command/apply.go +++ b/command/apply.go @@ -82,8 +82,8 @@ func (c *ApplyCommand) Run(args []string) int { log.Printf("[INFO] Writing backup state to: %s", backupPath) f, err := os.Create(backupPath) if err == nil { - defer f.Close() err = terraform.WriteState(c.State, f) + f.Close() } if err != nil { c.Ui.Error(fmt.Sprintf("Error writing backup state file: %s", err)) diff --git a/command/plan.go b/command/plan.go index 32ee31527e..67edae2340 100644 --- a/command/plan.go +++ b/command/plan.go @@ -80,8 +80,8 @@ func (c *PlanCommand) Run(args []string) int { log.Printf("[INFO] Writing backup state to: %s", backupPath) f, err := os.Create(backupPath) if err == nil { - defer f.Close() err = terraform.WriteState(c.State, f) + f.Close() } if err != nil { c.Ui.Error(fmt.Sprintf("Error writing backup state file: %s", err)) diff --git a/command/refresh.go b/command/refresh.go index f3038f96ad..69ff456010 100644 --- a/command/refresh.go +++ b/command/refresh.go @@ -98,8 +98,8 @@ func (c *RefreshCommand) Run(args []string) int { log.Printf("[INFO] Writing backup state to: %s", backupPath) f, err := os.Create(backupPath) if err == nil { - defer f.Close() err = terraform.WriteState(c.State, f) + f.Close() } if err != nil { c.Ui.Error(fmt.Sprintf("Error writing backup state file: %s", err)) From b172f45b94904c782a964b25ccc012ad584e7d08 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 27 Jul 2014 22:58:35 -0700 Subject: [PATCH 6/8] command: make state unexported on Meta --- command/apply.go | 4 ++-- command/meta.go | 8 +++++--- command/plan.go | 4 ++-- command/refresh.go | 4 ++-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/command/apply.go b/command/apply.go index 7553da047b..a1453bf99b 100644 --- a/command/apply.go +++ b/command/apply.go @@ -78,11 +78,11 @@ func (c *ApplyCommand) Run(args []string) int { } // Create a backup of the state before updating - if backupPath != "-" && c.State != nil { + if backupPath != "-" && c.state != nil { log.Printf("[INFO] Writing backup state to: %s", backupPath) f, err := os.Create(backupPath) if err == nil { - err = terraform.WriteState(c.State, f) + err = terraform.WriteState(c.state, f) f.Close() } if err != nil { diff --git a/command/meta.go b/command/meta.go index 2dda7a9745..0867ffdda6 100644 --- a/command/meta.go +++ b/command/meta.go @@ -16,7 +16,10 @@ type Meta struct { Color bool ContextOpts *terraform.ContextOpts Ui cli.Ui - State *terraform.State + + // State read when calling `Context`. This is available after calling + // `Context`. + state *terraform.State // This can be set by the command itself to provide extra hooks. extraHooks []terraform.Hook @@ -79,7 +82,7 @@ func (m *Meta) Context(path, statePath string) (*terraform.Context, bool, error) } // Store the loaded state - m.State = state + m.state = state config, err := config.LoadDir(path) if err != nil { @@ -93,7 +96,6 @@ func (m *Meta) Context(path, statePath string) (*terraform.Context, bool, error) opts.State = state ctx := terraform.NewContext(opts) return ctx, false, nil - } // contextOpts returns the options to use to initialize a Terraform diff --git a/command/plan.go b/command/plan.go index 67edae2340..322bc6fa49 100644 --- a/command/plan.go +++ b/command/plan.go @@ -76,11 +76,11 @@ func (c *PlanCommand) Run(args []string) int { if refresh { // Create a backup of the state before updating - if backupPath != "-" && c.State != nil { + if backupPath != "-" && c.state != nil { log.Printf("[INFO] Writing backup state to: %s", backupPath) f, err := os.Create(backupPath) if err == nil { - err = terraform.WriteState(c.State, f) + err = terraform.WriteState(c.state, f) f.Close() } if err != nil { diff --git a/command/refresh.go b/command/refresh.go index 69ff456010..1603a75c85 100644 --- a/command/refresh.go +++ b/command/refresh.go @@ -94,11 +94,11 @@ func (c *RefreshCommand) Run(args []string) int { } // Create a backup of the state before updating - if backupPath != "-" && c.State != nil { + if backupPath != "-" && c.state != nil { log.Printf("[INFO] Writing backup state to: %s", backupPath) f, err := os.Create(backupPath) if err == nil { - err = terraform.WriteState(c.State, f) + err = terraform.WriteState(c.state, f) f.Close() } if err != nil { From 2c22b619c55fe251b221bf3e68f534c1a4128d76 Mon Sep 17 00:00:00 2001 From: Amadeus Demarzi Date: Sun, 27 Jul 2014 23:59:17 -0700 Subject: [PATCH 7/8] A few homepage optimizations. * Improved iOS timing from pop in to breathing. Shouldn't distort as much * Hide tag line when page is loading to not make it pop in then out --- website/source/javascripts/app/Engine.Typewriter.js | 1 + website/source/javascripts/app/Engine.js | 5 +---- website/source/javascripts/app/Init.js | 5 ++--- website/source/stylesheets/_jumbotron.less | 1 + 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/website/source/javascripts/app/Engine.Typewriter.js b/website/source/javascripts/app/Engine.Typewriter.js index 9629a822ab..61762c41c4 100644 --- a/website/source/javascripts/app/Engine.Typewriter.js +++ b/website/source/javascripts/app/Engine.Typewriter.js @@ -6,6 +6,7 @@ Engine.Typewriter = function(element){ this.element = element; this.content = this.element.textContent.split(''); this.element.innerHTML = ''; + this.element.style.visibility = 'visible'; }; Engine.Typewriter.prototype = { diff --git a/website/source/javascripts/app/Engine.js b/website/source/javascripts/app/Engine.js index 8d1e937745..a918be3b00 100644 --- a/website/source/javascripts/app/Engine.js +++ b/website/source/javascripts/app/Engine.js @@ -93,12 +93,9 @@ Engine = Base.extend({ .then(function(){ this.showShapes = true; }, this) - .wait(800) + .wait(1000) .then(function(){ this.logo.startBreathing(); - }, this) - .wait(200) - .then(function(){ this.showGrid = true; }, this) .wait(1000) diff --git a/website/source/javascripts/app/Init.js b/website/source/javascripts/app/Init.js index 6e3ce2653a..074c95d8d9 100644 --- a/website/source/javascripts/app/Init.js +++ b/website/source/javascripts/app/Init.js @@ -66,10 +66,9 @@ var Init = { Pages: { 'page-home': function(){ - var jumbotron; if (isIE) { - jumbotron = document.getElementById('jumbotron'); - jumbotron.className += ' static'; + document.getElementById('jumbotron').className += ' static'; + document.getElementById('tag-line').style.visibility = 'visible'; return; } diff --git a/website/source/stylesheets/_jumbotron.less b/website/source/stylesheets/_jumbotron.less index 21edba78a4..14b33dbbf9 100755 --- a/website/source/stylesheets/_jumbotron.less +++ b/website/source/stylesheets/_jumbotron.less @@ -121,6 +121,7 @@ top:540px; color:#fff; text-align:center; + visibility:hidden; font-family: 'Lato', "Helvetica Neue", Helvetica, Arial, sans-serif; font-weight: 200; From 086ed3dcbf967d7391b5ba806265605fde4d4ce6 Mon Sep 17 00:00:00 2001 From: Amadeus Demarzi Date: Mon, 28 Jul 2014 00:01:05 -0700 Subject: [PATCH 8/8] Updating CSS file from previous commit --- website/source/stylesheets/main.css | 1 + 1 file changed, 1 insertion(+) diff --git a/website/source/stylesheets/main.css b/website/source/stylesheets/main.css index 466b00e4e5..f55beb9441 100644 --- a/website/source/stylesheets/main.css +++ b/website/source/stylesheets/main.css @@ -686,6 +686,7 @@ body.page-home #footer { top: 540px; color: #fff; text-align: center; + visibility: hidden; font-family: 'Lato', "Helvetica Neue", Helvetica, Arial, sans-serif; font-weight: 200; -webkit-transform: translate3d(0, 0, 0);