// 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 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()) } }
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 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 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") } }
// Test the case where both managed and non managed state present func TestRemote_managedAndNonManaged(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{} if code := c.Run(args); code != 1 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) } }
func TestMeta_loadState_conflict(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) err := remote.EnsureDirectory() if err != nil { t.Fatalf("err: %v", err) } m := new(Meta) s := terraform.NewState() if err := remote.PersistState(s); err != nil { t.Fatalf("err: %v", err) } if err := m.persistLocalState(s); err != nil { t.Fatalf("err: %v", err) } _, err = m.loadState() if err == nil { t.Fatalf("should error with conflict") } }
// Test disabling remote management without pulling func TestRemote_disable_noPull(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 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{"-disable", "-pull=false"} if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) } // Local state file should be removed haveLocal, err := remote.HaveLocalState() if err != nil { t.Fatalf("err: %v", err) } if haveLocal { t.Fatalf("should be disabled") } // New state file should be installed exists, err := remote.ExistsFile(DefaultStateFilename) if err != nil { t.Fatalf("err: %v", err) } if !exists { t.Fatalf("failed to make state file") } // 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 DIDNT updated // TODO: Should be 5, but WriteState currently increments // this which is incorrect. if newState.Serial != 7 { t.Fatalf("state file updated: %#v", newState) } if newState.Remote != nil { t.Fatalf("remote configuration not removed") } }
// enableRemoteState is used to enable remote state management // and to move a state file into place func (c *RemoteCommand) enableRemoteState() 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 } // Read the provided state file raw, err := ioutil.ReadFile(c.conf.statePath) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to read '%s': %v", c.conf.statePath, err)) return 1 } state, err := terraform.ReadState(bytes.NewReader(raw)) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to decode '%s': %v", c.conf.statePath, err)) return 1 } // Backup the state file before we modify it backupPath := c.conf.backupPath if backupPath != "-" { // Provide default backup path if none provided if backupPath == "" { backupPath = c.conf.statePath + DefaultBackupExtention } log.Printf("[INFO] Writing backup state to: %s", backupPath) f, err := os.Create(backupPath) if err == nil { err = terraform.WriteState(state, f) f.Close() } if err != nil { c.Ui.Error(fmt.Sprintf("Error writing backup state file: %s", err)) return 1 } } // Update the local configuration, move into place state.Remote = &c.remoteConf if err := remote.PersistState(state); err != nil { c.Ui.Error(fmt.Sprintf("%s", err)) return 1 } // Remove the state file log.Printf("[INFO] Removing state file: %s", c.conf.statePath) if err := os.Remove(c.conf.statePath); err != nil { c.Ui.Error(fmt.Sprintf("Failed to remove state file '%s': %v", c.conf.statePath, err)) return 1 } // Success! c.Ui.Output("Remote state management enabled") return 0 }