// 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")
	}
}
예제 #2
0
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())
	}
}
예제 #3
0
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)
	}
}
예제 #4
0
// 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)
}
예제 #6
0
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)
	}
}
예제 #8
0
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)
	}
}
예제 #9
0
// 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
}
예제 #10
0
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())
	}
}
예제 #11
0
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())
	}
}
예제 #13
0
// 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)
	}
}