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_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 enabling remote state func TestRemote_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 := &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) } // Backup file should exist exist, err := remote.ExistsFile(DefaultStateFilename + DefaultBackupExtention) if err != nil { t.Fatalf("err: %v", err) } if !exist { t.Fatalf("backup should exist") } // State file should not exist, err = remote.ExistsFile(DefaultStateFilename) if err != nil { t.Fatalf("err: %v", err) } if exist { t.Fatalf("state file should not exist") } }
// 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") } }
func (c *RemoteCommand) Run(args []string) int { args = c.Meta.process(args, false) var address, accessToken, name, path string cmdFlags := flag.NewFlagSet("remote", flag.ContinueOnError) cmdFlags.BoolVar(&c.conf.disableRemote, "disable", false, "") cmdFlags.BoolVar(&c.conf.pullOnDisable, "pull", true, "") cmdFlags.StringVar(&c.conf.statePath, "state", DefaultStateFilename, "path") cmdFlags.StringVar(&c.conf.backupPath, "backup", "", "path") cmdFlags.StringVar(&c.remoteConf.Type, "backend", "atlas", "") cmdFlags.StringVar(&address, "address", "", "") cmdFlags.StringVar(&accessToken, "access-token", "", "") cmdFlags.StringVar(&name, "name", "", "") cmdFlags.StringVar(&path, "path", "", "") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 } // Show help if given no inputs if !c.conf.disableRemote && c.remoteConf.Type == "atlas" && name == "" && accessToken == "" { cmdFlags.Usage() return 1 } // Populate the various configurations c.remoteConf.Config = map[string]string{ "address": address, "access_token": accessToken, "name": name, "path": path, } // Check if have an existing local state file haveLocal, err := remote.HaveLocalState() if err != nil { c.Ui.Error(fmt.Sprintf("Failed to check for local state: %v", err)) return 1 } // Check if we have the non-managed state file haveNonManaged, err := remote.ExistsFile(c.conf.statePath) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to check for state file: %v", err)) return 1 } // Check if remote state is being disabled if c.conf.disableRemote { if !haveLocal { c.Ui.Error(fmt.Sprintf("Remote state management not enabled! Aborting.")) return 1 } if haveNonManaged { c.Ui.Error(fmt.Sprintf("State file already exists at '%s'. Aborting.", c.conf.statePath)) return 1 } return c.disableRemoteState() } // Ensure there is no conflict switch { case haveLocal && haveNonManaged: c.Ui.Error(fmt.Sprintf("Remote state is enabled, but non-managed state file '%s' is also present!", c.conf.statePath)) return 1 case !haveLocal && !haveNonManaged: // If we don't have either state file, initialize a blank state file return c.initBlankState() case haveLocal && !haveNonManaged: // Update the remote state target potentially return c.updateRemoteConfig() case !haveLocal && haveNonManaged: // Enable remote state management return c.enableRemoteState() default: panic("unhandled case") } return 0 }