// 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") } }
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()) } }
func TestInit_remoteStateSubdir(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) subdir := filepath.Join(tmp, "subdir") 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"), subdir, } if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) } if _, err := os.Stat(filepath.Join(subdir, "hello.tf")); err != nil { t.Fatalf("err: %s", err) } if _, err := os.Stat(filepath.Join(subdir, DefaultDataDir, DefaultStateFilename)); err != nil { t.Fatalf("missing state: %s", err) } }
// 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 }
// 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) }
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]string) 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 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]string{"foo": "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()) } variables := map[string]string{ "foo": "bar", "bar": "foo", } if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) { t.Fatalf("bad: %#v", client.UpsertOptions) } }
// State implements the TerraformServer interface func (s *Server) State(c context.Context, req *pb.StateRequest) (*pb.StateResponse, error) { resp := new(pb.StateResponse) state, err := json.Marshal(terraform.NewState()) if err != nil { return nil, fmt.Errorf("Error marshalling new state: %v", err) } resp.State = state return resp, nil }
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()) } }
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()) } }
// This tests that the push command prefers Atlas variables over // local ones. func TestPush_preferAtlas(t *testing.T) { // Disable test mode so input would be asked and setup the // input reader/writers. test = false defer func() { test = true }() defaultInputReader = bytes.NewBufferString("nope\n") defaultInputWriter = new(bytes.Buffer) 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} // Provided vars should override existing ones client.GetResult = map[string]string{ "foo": "old", } ui := new(cli.MockUi) c := &PushCommand{ Meta: Meta{ ContextOpts: testCtxConfig(testProvider()), Ui: ui, }, client: client, } path := testFixturePath("push-tfvars") args := []string{ "-var-file", path + "/terraform.tfvars", "-vcs=false", path, } 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", "terraform.tfvars", } if !reflect.DeepEqual(actual, expected) { t.Fatalf("bad: %#v", actual) } if client.UpsertOptions.Name != "foo" { t.Fatalf("bad: %#v", client.UpsertOptions) } variables := map[string]string{ "foo": "old", "bar": "foo", } if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) { t.Fatalf("bad: %#v", client.UpsertOptions) } }