func TestRefreshState_UpdateLocal(t *testing.T) { defer testFixCwd(testDir(t)) rs := terraform.NewState() rs.Serial = 100 remote, srv := testRemote(t, rs) defer srv.Close() local := terraform.NewState() local.Serial = 99 testWriteLocal(t, local) sc, err := RefreshState(remote) if err != nil { t.Fatalf("err: %v", err) } if sc != StateChangeUpdateLocal { t.Fatalf("bad: %s", sc) } // Should update local2 := testReadLocal(t) if local2.Serial != 100 { t.Fatalf("Bad: %#v", local2) } }
func TestPull_local(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) s := terraform.NewState() s.Serial = 10 conf, srv := testRemoteState(t, s, 200) s = terraform.NewState() s.Serial = 5 s.Remote = conf defer srv.Close() // Store the local state buf := bytes.NewBuffer(nil) terraform.WriteState(s, buf) remote.EnsureDirectory() remote.Persist(buf) ui := new(cli.MockUi) c := &PullCommand{ Meta: Meta{ ContextOpts: testCtxConfig(testProvider()), Ui: ui, }, } args := []string{} if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) } }
// Test disabling remote management func TestRemoteConfig_disable(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) // Create remote state file, this should be pulled s := terraform.NewState() s.Serial = 10 conf, srv := testRemoteState(t, s, 200) defer srv.Close() // Persist local remote state s = terraform.NewState() s.Serial = 5 s.Remote = conf // Write the state statePath := filepath.Join(tmp, DefaultDataDir, DefaultStateFilename) state := &state.LocalState{Path: statePath} if err := state.WriteState(s); err != nil { t.Fatalf("err: %s", err) } if err := state.PersistState(); err != nil { t.Fatalf("err: %s", err) } ui := new(cli.MockUi) c := &RemoteConfigCommand{ Meta: Meta{ ContextOpts: testCtxConfig(testProvider()), Ui: ui, }, } args := []string{"-disable"} if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) } // Local state file should be removed and the local cache should exist testRemoteLocal(t, true) testRemoteLocalCache(t, false) // Check that the state file was updated raw, _ := ioutil.ReadFile(DefaultStateFilename) newState, err := terraform.ReadState(bytes.NewReader(raw)) if err != nil { t.Fatalf("err: %v", err) } // Ensure we updated if newState.Remote != nil { t.Fatalf("remote configuration not removed") } }
// initBlank state is used to initialize a blank state that is // remote enabled func (c *RemoteCommand) initBlankState() int { // Validate the remote configuration if err := c.validateRemoteConfig(); err != nil { return 1 } // Make the hidden directory if err := remote.EnsureDirectory(); err != nil { c.Ui.Error(fmt.Sprintf("%s", err)) return 1 } // Make a blank state, attach the remote configuration blank := terraform.NewState() blank.Remote = &c.remoteConf // Persist the state if err := remote.PersistState(blank); err != nil { c.Ui.Error(fmt.Sprintf("Failed to initialize state file: %v", err)) return 1 } // Success! c.Ui.Output("Initialized blank state with remote state enabled!") return 0 }
func TestInit_remoteState(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) s := terraform.NewState() conf, srv := testRemoteState(t, s, 200) defer srv.Close() ui := new(cli.MockUi) c := &InitCommand{ Meta: Meta{ ContextOpts: testCtxConfig(testProvider()), Ui: ui, }, } args := []string{ "-backend", "http", "-backend-config", "address=" + conf.Config["address"], testFixturePath("init"), tmp, } if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) } if _, err := os.Stat(filepath.Join(tmp, "hello.tf")); err != nil { t.Fatalf("err: %s", err) } if _, err := os.Stat(filepath.Join(tmp, DefaultDataDir, DefaultStateFilename)); err != nil { t.Fatalf("missing state: %s", err) } }
func TestPush_plan(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) // Create remote state file, this should be pulled conf, srv := testRemoteState(t, testState(), 200) defer srv.Close() // Persist local remote state s := terraform.NewState() s.Serial = 5 s.Remote = conf testStateFileRemote(t, s) // Create a plan planPath := testPlanFile(t, &terraform.Plan{ Module: testModule(t, "apply"), }) ui := new(cli.MockUi) c := &PushCommand{ Meta: Meta{ ContextOpts: testCtxConfig(testProvider()), Ui: ui, }, } args := []string{planPath} if code := c.Run(args); code != 1 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } }
// initBlank state is used to initialize a blank state that is // remote enabled func (c *RemoteConfigCommand) initBlankState() int { // Validate the remote configuration if err := c.validateRemoteConfig(); err != nil { return 1 } // Make a blank state, attach the remote configuration blank := terraform.NewState() blank.Remote = &c.remoteConf // Persist the state remote := &state.LocalState{Path: c.stateResult.RemotePath} if err := remote.WriteState(blank); err != nil { c.Ui.Error(fmt.Sprintf("Failed to initialize state file: %v", err)) return 1 } if err := remote.PersistState(); err != nil { c.Ui.Error(fmt.Sprintf("Failed to initialize state file: %v", err)) return 1 } // Success! c.Ui.Output("Initialized blank state with remote state enabled!") return 0 }
func TestStateShow_emptyStateWithModule(t *testing.T) { // empty state with empty module state := terraform.NewState() mod := &terraform.ModuleState{ Path: []string{"root", "mod"}, } state.Modules = append(state.Modules, mod) statePath := testStateFile(t, state) p := testProvider() ui := new(cli.MockUi) c := &StateShowCommand{ Meta: Meta{ ContextOpts: testCtxConfig(p), Ui: ui, }, } args := []string{ "-state", statePath, } if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } }
// blankState is used to return a serialized form of a blank state // with only the remote info. func blankState(conf *terraform.RemoteState) ([]byte, error) { blank := terraform.NewState() blank.Remote = conf buf := bytes.NewBuffer(nil) err := terraform.WriteState(blank, buf) return buf.Bytes(), err }
func TestMeta_persistLocal(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) m := new(Meta) s := terraform.NewState() if err := m.persistLocalState(s); err != nil { t.Fatalf("err: %v", err) } exists, err := remote.ExistsFile(m.stateOutPath) if err != nil { t.Fatalf("err: %v", err) } if !exists { t.Fatalf("state should exist") } // Write again, shoudl backup if err := m.persistLocalState(s); err != nil { t.Fatalf("err: %v", err) } exists, err = remote.ExistsFile(m.backupPath) if err != nil { t.Fatalf("err: %v", err) } if !exists { t.Fatalf("backup should exist") } }
func TestMeta_loadState_remote(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) err := remote.EnsureDirectory() if err != nil { t.Fatalf("err: %v", err) } s := terraform.NewState() s.Serial = 1000 conf, srv := testRemoteState(t, s, 200) s.Remote = conf defer srv.Close() s.Serial = 500 if err := remote.PersistState(s); err != nil { t.Fatalf("err: %v", err) } m := new(Meta) s1, err := m.loadState() if err != nil { t.Fatalf("err: %v", err) } if s1.Serial < 1000 { t.Fatalf("Bad: %#v", s1) } if !m.useRemoteState { t.Fatalf("should enable remote") } }
// Test enabling remote state func TestRemoteConfig_enableRemote(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) // Create a non-remote enabled state s := terraform.NewState() s.Serial = 5 // Add the state at the default path fh, err := os.Create(DefaultStateFilename) if err != nil { t.Fatalf("err: %v", err) } err = terraform.WriteState(s, fh) fh.Close() if err != nil { t.Fatalf("err: %v", err) } ui := new(cli.MockUi) c := &RemoteConfigCommand{ Meta: Meta{ ContextOpts: testCtxConfig(testProvider()), Ui: ui, }, } args := []string{ "-backend=http", "-backend-config", "address=http://example.com", "-backend-config", "access_token=test", "-pull=false", } if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) } remotePath := filepath.Join(DefaultDataDir, DefaultStateFilename) ls := &state.LocalState{Path: remotePath} if err := ls.RefreshState(); err != nil { t.Fatalf("err: %s", err) } local := ls.State() if local.Remote.Type != "http" { t.Fatalf("Bad: %#v", local.Remote) } if local.Remote.Config["address"] != "http://example.com" { t.Fatalf("Bad: %#v", local.Remote) } if local.Remote.Config["access_token"] != "test" { t.Fatalf("Bad: %#v", local.Remote) } // Backup file should exist, state file should not testRemoteLocal(t, false) testRemoteLocalBackup(t, true) }
// We want a variable from atlas to fill a missing variable locally func TestPush_inputPartial(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) // Create remote state file, this should be pulled conf, srv := testRemoteState(t, testState(), 200) defer srv.Close() // Persist local remote state s := terraform.NewState() s.Serial = 5 s.Remote = conf testStateFileRemote(t, s) // Path where the archive will be "uploaded" to archivePath := testTempFile(t) defer os.Remove(archivePath) client := &mockPushClient{ File: archivePath, GetResult: map[string]atlas.TFVar{ "foo": atlas.TFVar{Key: "foo", Value: "bar"}, }, } ui := new(cli.MockUi) c := &PushCommand{ Meta: Meta{ ContextOpts: testCtxConfig(testProvider()), Ui: ui, }, client: client, } // Disable test mode so input would be asked and setup the // input reader/writers. test = false defer func() { test = true }() defaultInputReader = bytes.NewBufferString("foo\n") defaultInputWriter = new(bytes.Buffer) args := []string{ "-vcs=false", testFixturePath("push-input-partial"), } if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } expectedTFVars := []atlas.TFVar{ {Key: "bar", Value: "foo"}, {Key: "foo", Value: "bar"}, } if !reflect.DeepEqual(client.UpsertOptions.TFVars, expectedTFVars) { t.Logf("expected: %#v", expectedTFVars) t.Fatalf("got: %#v", client.UpsertOptions.TFVars) } }
func TestRefreshState_NewVersion(t *testing.T) { defer testFixCwd(testDir(t)) rs := terraform.NewState() rs.Serial = 100 rs.Version = terraform.StateVersion + 1 remote, srv := testRemote(t, rs) defer srv.Close() local := terraform.NewState() local.Serial = 99 testWriteLocal(t, local) _, err := RefreshState(remote) if err == nil { t.Fatalf("New version should fail!") } }
func TestPush_good(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) // Create remote state file, this should be pulled conf, srv := testRemoteState(t, testState(), 200) defer srv.Close() // Persist local remote state s := terraform.NewState() s.Serial = 5 s.Remote = conf testStateFileRemote(t, s) // Path where the archive will be "uploaded" to archivePath := testTempFile(t) defer os.Remove(archivePath) client := &mockPushClient{File: archivePath} ui := new(cli.MockUi) c := &PushCommand{ Meta: Meta{ ContextOpts: testCtxConfig(testProvider()), Ui: ui, }, client: client, } args := []string{ "-vcs=false", testFixturePath("push"), } if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } actual := testArchiveStr(t, archivePath) expected := []string{ ".terraform/", ".terraform/terraform.tfstate", "main.tf", } if !reflect.DeepEqual(actual, expected) { t.Fatalf("bad: %#v", actual) } variables := make(map[string]interface{}) if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) { t.Fatalf("bad: %#v", client.UpsertOptions) } if client.UpsertOptions.Name != "foo" { t.Fatalf("bad: %#v", client.UpsertOptions) } }
// Test updating remote config func TestRemoteConfig_updateRemote(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) // Persist local remote state s := terraform.NewState() s.Serial = 5 s.Remote = &terraform.RemoteState{ Type: "invalid", } // Write the state statePath := filepath.Join(tmp, DefaultDataDir, DefaultStateFilename) ls := &state.LocalState{Path: statePath} if err := ls.WriteState(s); err != nil { t.Fatalf("err: %s", err) } if err := ls.PersistState(); err != nil { t.Fatalf("err: %s", err) } ui := new(cli.MockUi) c := &RemoteConfigCommand{ Meta: Meta{ ContextOpts: testCtxConfig(testProvider()), Ui: ui, }, } args := []string{ "-backend=http", "-backend-config", "address=http://example.com", "-backend-config", "access_token=test", "-pull=false", } if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) } remotePath := filepath.Join(DefaultDataDir, DefaultStateFilename) ls = &state.LocalState{Path: remotePath} if err := ls.RefreshState(); err != nil { t.Fatalf("err: %s", err) } local := ls.State() if local.Remote.Type != "http" { t.Fatalf("Bad: %#v", local.Remote) } if local.Remote.Config["address"] != "http://example.com" { t.Fatalf("Bad: %#v", local.Remote) } if local.Remote.Config["access_token"] != "test" { t.Fatalf("Bad: %#v", local.Remote) } }
func TestRefreshState_LocalNewer(t *testing.T) { defer testFixCwd(testDir(t)) rs := terraform.NewState() rs.Serial = 99 remote, srv := testRemote(t, rs) defer srv.Close() local := terraform.NewState() local.Serial = 100 testWriteLocal(t, local) sc, err := RefreshState(remote) if err != nil { t.Fatalf("err: %v", err) } if sc != StateChangeLocalNewer { t.Fatalf("bad: %s", sc) } }
// testPlan returns a non-nil noop plan. func testPlan(t *testing.T) *terraform.Plan { state := terraform.NewState() state.RootModule().Outputs["foo"] = &terraform.OutputState{ Type: "string", Value: "foo", } return &terraform.Plan{ Module: testModule(t, "apply"), State: state, } }
func TestRemotePull_local(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) s := terraform.NewState() s.Serial = 10 conf, srv := testRemoteState(t, s, 200) s = terraform.NewState() s.Serial = 5 s.Remote = conf defer srv.Close() // Store the local state statePath := filepath.Join(tmp, DefaultDataDir, DefaultStateFilename) if err := os.MkdirAll(filepath.Dir(statePath), 0755); err != nil { t.Fatalf("err: %s", err) } f, err := os.Create(statePath) if err != nil { t.Fatalf("err: %s", err) } err = terraform.WriteState(s, f) f.Close() if err != nil { t.Fatalf("err: %s", err) } ui := new(cli.MockUi) c := &RemotePullCommand{ Meta: Meta{ ContextOpts: testCtxConfig(testProvider()), Ui: ui, }, } args := []string{} if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) } }
// Test updating remote config func TestRemote_updateRemote(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) // Persist local remote state s := terraform.NewState() s.Serial = 5 s.Remote = &terraform.RemoteState{ Type: "invalid", } if err := remote.EnsureDirectory(); err != nil { t.Fatalf("err: %v", err) } if err := remote.PersistState(s); err != nil { t.Fatalf("err: %v", err) } ui := new(cli.MockUi) c := &RemoteCommand{ Meta: Meta{ ContextOpts: testCtxConfig(testProvider()), Ui: ui, }, } args := []string{ "-backend=http", "-address", "http://example.com", "-access-token=test", } if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) } local, _, err := remote.ReadLocalState() if err != nil { t.Fatalf("err: %v", err) } if local.Remote.Type != "http" { t.Fatalf("Bad: %#v", local.Remote) } if local.Remote.Config["address"] != "http://example.com" { t.Fatalf("Bad: %#v", local.Remote) } if local.Remote.Config["access_token"] != "test" { t.Fatalf("Bad: %#v", local.Remote) } }
func TestDeleteState(t *testing.T) { defer testFixCwd(testDir(t)) remote, srv := testRemotePush(t, 200) defer srv.Close() local := terraform.NewState() testWriteLocal(t, local) err := DeleteState(remote) if err != nil { t.Fatalf("err: %v", err) } }
func TestRefreshState_Conflict(t *testing.T) { defer testFixCwd(testDir(t)) rs := terraform.NewState() rs.Serial = 50 rs.RootModule().Outputs["foo"] = "bar" remote, srv := testRemote(t, rs) defer srv.Close() local := terraform.NewState() local.Serial = 50 local.RootModule().Outputs["foo"] = "baz" testWriteLocal(t, local) sc, err := RefreshState(remote) if err != nil { t.Fatalf("err: %v", err) } if sc != StateChangeConflict { t.Fatalf("bad: %s", sc) } }
func TestPushState_Conflict(t *testing.T) { defer testFixCwd(testDir(t)) remote, srv := testRemotePush(t, 409) defer srv.Close() local := terraform.NewState() testWriteLocal(t, local) sc, err := PushState(remote, false) if err != nil { t.Fatalf("err: %v", err) } if sc != StateChangeConflict { t.Fatalf("Bad: %v", sc) } }
func TestPushState_Error(t *testing.T) { defer testFixCwd(testDir(t)) remote, srv := testRemotePush(t, 500) defer srv.Close() local := terraform.NewState() testWriteLocal(t, local) sc, err := PushState(remote, false) if err != ErrRemoteInternal { t.Fatalf("err: %v", err) } if sc != StateChangeNoop { t.Fatalf("Bad: %v", sc) } }
func TestPush_name(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) // Create remote state file, this should be pulled conf, srv := testRemoteState(t, testState(), 200) defer srv.Close() // Persist local remote state s := terraform.NewState() s.Serial = 5 s.Remote = conf testStateFileRemote(t, s) // Path where the archive will be "uploaded" to archivePath := testTempFile(t) defer os.Remove(archivePath) client := &mockPushClient{File: archivePath} ui := new(cli.MockUi) c := &PushCommand{ Meta: Meta{ ContextOpts: testCtxConfig(testProvider()), Ui: ui, }, client: client, } args := []string{ "-name", "bar", "-vcs=false", testFixturePath("push"), } if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } if client.UpsertOptions.Name != "bar" { t.Fatalf("bad: %#v", client.UpsertOptions) } }
// Test the case where both managed and non managed state present func TestRemoteConfig_managedAndNonManaged(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) // Persist local remote state s := terraform.NewState() s.Serial = 5 // Write the state statePath := filepath.Join(tmp, DefaultDataDir, DefaultStateFilename) state := &state.LocalState{Path: statePath} if err := state.WriteState(s); err != nil { t.Fatalf("err: %s", err) } if err := state.PersistState(); err != nil { t.Fatalf("err: %s", err) } // Also put a file at the default path fh, err := os.Create(DefaultStateFilename) if err != nil { t.Fatalf("err: %v", err) } err = terraform.WriteState(s, fh) fh.Close() if err != nil { t.Fatalf("err: %v", err) } ui := new(cli.MockUi) c := &RemoteConfigCommand{ Meta: Meta{ ContextOpts: testCtxConfig(testProvider()), Ui: ui, }, } args := []string{} if code := c.Run(args); code != 1 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) } }
func TestMeta_loadState_statePath(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) m := new(Meta) s := terraform.NewState() s.Serial = 1000 if err := m.persistLocalState(s); err != nil { t.Fatalf("err: %v", err) } s1, err := m.loadState() if err != nil { t.Fatalf("err: %v", err) } if s1.Serial < 1000 { t.Fatalf("Bad: %#v", s1) } }
func TestMeta_persistRemote(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) err := remote.EnsureDirectory() if err != nil { t.Fatalf("err: %v", err) } s := terraform.NewState() conf, srv := testRemoteState(t, s, 200) s.Remote = conf defer srv.Close() m := new(Meta) if err := m.persistRemoteState(s); err != nil { t.Fatalf("err: %v", err) } local, _, err := remote.ReadLocalState() if err != nil { t.Fatalf("err: %v", err) } if local == nil { t.Fatalf("state should exist") } if err := m.persistRemoteState(s); err != nil { t.Fatalf("err: %v", err) } backup := remote.LocalDirectory + "/" + remote.BackupHiddenStateFile exists, err := remote.ExistsFile(backup) if err != nil { t.Fatalf("err: %v", err) } if !exists { t.Fatalf("backup should exist") } }
func TestStateShow_emptyState(t *testing.T) { state := terraform.NewState() statePath := testStateFile(t, state) p := testProvider() ui := new(cli.MockUi) c := &StateShowCommand{ Meta: Meta{ ContextOpts: testCtxConfig(p), Ui: ui, }, } args := []string{ "-state", statePath, "test_instance.foo", } if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } }
// Test disabling remote management with a state file in the way func TestRemote_disable_otherState(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) // Persist local remote state s := terraform.NewState() s.Serial = 5 if err := remote.EnsureDirectory(); err != nil { t.Fatalf("err: %v", err) } if err := remote.PersistState(s); err != nil { t.Fatalf("err: %v", err) } // Also put a file at the default path fh, err := os.Create(DefaultStateFilename) if err != nil { t.Fatalf("err: %v", err) } err = terraform.WriteState(s, fh) fh.Close() if err != nil { t.Fatalf("err: %v", err) } ui := new(cli.MockUi) c := &RemoteCommand{ Meta: Meta{ ContextOpts: testCtxConfig(testProvider()), Ui: ui, }, } args := []string{"-disable"} if code := c.Run(args); code != 1 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) } }