2014-06-19 06:36:44 +02:00
|
|
|
package command
|
|
|
|
|
|
|
|
import (
|
2014-07-08 06:20:48 +02:00
|
|
|
"fmt"
|
2014-07-12 06:30:40 +02:00
|
|
|
"io/ioutil"
|
2014-06-19 06:36:44 +02:00
|
|
|
"os"
|
2014-07-12 06:30:40 +02:00
|
|
|
"path/filepath"
|
2014-06-19 21:12:24 +02:00
|
|
|
"reflect"
|
2014-07-08 06:20:48 +02:00
|
|
|
"sync"
|
2014-06-19 06:36:44 +02:00
|
|
|
"testing"
|
2014-07-03 02:08:58 +02:00
|
|
|
"time"
|
2014-06-19 06:36:44 +02:00
|
|
|
|
2014-07-03 20:46:40 +02:00
|
|
|
"github.com/hashicorp/terraform/config"
|
2014-06-19 06:36:44 +02:00
|
|
|
"github.com/hashicorp/terraform/terraform"
|
|
|
|
"github.com/mitchellh/cli"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestApply(t *testing.T) {
|
2014-06-27 23:43:23 +02:00
|
|
|
statePath := testTempFile(t)
|
2014-06-19 06:36:44 +02:00
|
|
|
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
2014-07-03 20:46:40 +02:00
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
2014-06-19 06:36:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
2014-06-19 21:12:24 +02:00
|
|
|
"-init",
|
2014-07-12 06:30:40 +02:00
|
|
|
"-state", statePath,
|
2014-06-19 06:36:44 +02:00
|
|
|
testFixturePath("apply"),
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code != 0 {
|
|
|
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := os.Stat(statePath); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.Open(statePath)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
state, err := terraform.ReadState(f)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state should not be nil")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-03 06:16:36 +02:00
|
|
|
func TestApply_configInvalid(t *testing.T) {
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
2014-07-03 20:46:40 +02:00
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
2014-07-03 06:16:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
|
|
|
"-init",
|
2014-07-12 06:30:40 +02:00
|
|
|
"-state", testTempFile(t),
|
2014-07-03 06:16:36 +02:00
|
|
|
testFixturePath("apply-config-invalid"),
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code != 1 {
|
|
|
|
t.Fatalf("bad: \n%s", ui.OutputWriter.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-12 06:30:40 +02:00
|
|
|
func TestApply_defaultState(t *testing.T) {
|
|
|
|
td, err := ioutil.TempDir("", "tf")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
statePath := filepath.Join(td, DefaultStateFilename)
|
|
|
|
|
|
|
|
// Change to the temporary directory
|
|
|
|
cwd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if err := os.Chdir(filepath.Dir(statePath)); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
defer os.Chdir(cwd)
|
|
|
|
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
|
|
|
"-init",
|
|
|
|
testFixturePath("apply"),
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code != 0 {
|
|
|
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := os.Stat(statePath); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.Open(statePath)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
state, err := terraform.ReadState(f)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state should not be nil")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-08 06:20:48 +02:00
|
|
|
func TestApply_error(t *testing.T) {
|
|
|
|
statePath := testTempFile(t)
|
|
|
|
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
}
|
|
|
|
|
|
|
|
var lock sync.Mutex
|
|
|
|
errored := false
|
|
|
|
p.ApplyFn = func(
|
|
|
|
s *terraform.ResourceState,
|
|
|
|
d *terraform.ResourceDiff) (*terraform.ResourceState, error) {
|
|
|
|
lock.Lock()
|
|
|
|
defer lock.Unlock()
|
|
|
|
|
|
|
|
if !errored {
|
|
|
|
errored = true
|
|
|
|
return nil, fmt.Errorf("error")
|
|
|
|
}
|
|
|
|
|
|
|
|
return &terraform.ResourceState{ID: "foo"}, nil
|
|
|
|
}
|
|
|
|
p.DiffFn = func(
|
|
|
|
*terraform.ResourceState,
|
|
|
|
*terraform.ResourceConfig) (*terraform.ResourceDiff, error) {
|
|
|
|
return &terraform.ResourceDiff{
|
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"ami": &terraform.ResourceAttrDiff{
|
|
|
|
New: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
|
|
|
"-init",
|
2014-07-12 06:30:40 +02:00
|
|
|
"-state", statePath,
|
2014-07-08 06:20:48 +02:00
|
|
|
testFixturePath("apply-error"),
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code != 1 {
|
|
|
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := os.Stat(statePath); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.Open(statePath)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
state, err := terraform.ReadState(f)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state should not be nil")
|
|
|
|
}
|
|
|
|
if len(state.Resources) == 0 {
|
|
|
|
t.Fatal("no resources in state")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-27 23:43:23 +02:00
|
|
|
func TestApply_plan(t *testing.T) {
|
2014-07-03 20:46:40 +02:00
|
|
|
planPath := testPlanFile(t, &terraform.Plan{
|
|
|
|
Config: new(config.Config),
|
|
|
|
})
|
2014-06-27 23:43:23 +02:00
|
|
|
statePath := testTempFile(t)
|
|
|
|
|
2014-06-19 06:36:44 +02:00
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
2014-07-03 20:46:40 +02:00
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
2014-06-19 06:36:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
2014-07-12 06:30:40 +02:00
|
|
|
"-state", statePath,
|
2014-06-27 23:43:23 +02:00
|
|
|
planPath,
|
2014-06-19 06:36:44 +02:00
|
|
|
}
|
2014-06-27 23:43:23 +02:00
|
|
|
if code := c.Run(args); code != 0 {
|
|
|
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
2014-06-19 06:36:44 +02:00
|
|
|
}
|
|
|
|
|
2014-06-27 23:43:23 +02:00
|
|
|
if _, err := os.Stat(statePath); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.Open(statePath)
|
2014-06-19 21:12:24 +02:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
2014-06-27 23:43:23 +02:00
|
|
|
defer f.Close()
|
2014-06-19 21:12:24 +02:00
|
|
|
|
2014-06-27 23:43:23 +02:00
|
|
|
state, err := terraform.ReadState(f)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state should not be nil")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-03 02:01:02 +02:00
|
|
|
func TestApply_shutdown(t *testing.T) {
|
|
|
|
stopped := false
|
|
|
|
stopCh := make(chan struct{})
|
|
|
|
stopReplyCh := make(chan struct{})
|
|
|
|
|
|
|
|
statePath := testTempFile(t)
|
|
|
|
|
|
|
|
p := testProvider()
|
|
|
|
shutdownCh := make(chan struct{})
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
2014-07-03 20:46:40 +02:00
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
ShutdownCh: shutdownCh,
|
|
|
|
Ui: ui,
|
2014-07-03 02:01:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
p.DiffFn = func(
|
|
|
|
*terraform.ResourceState,
|
|
|
|
*terraform.ResourceConfig) (*terraform.ResourceDiff, error) {
|
|
|
|
return &terraform.ResourceDiff{
|
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"ami": &terraform.ResourceAttrDiff{
|
|
|
|
New: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
p.ApplyFn = func(
|
|
|
|
*terraform.ResourceState,
|
|
|
|
*terraform.ResourceDiff) (*terraform.ResourceState, error) {
|
|
|
|
if !stopped {
|
|
|
|
stopped = true
|
|
|
|
close(stopCh)
|
|
|
|
<-stopReplyCh
|
|
|
|
}
|
|
|
|
|
|
|
|
return &terraform.ResourceState{
|
|
|
|
ID: "foo",
|
|
|
|
Attributes: map[string]string{
|
|
|
|
"ami": "2",
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
<-stopCh
|
|
|
|
shutdownCh <- struct{}{}
|
2014-07-03 02:08:58 +02:00
|
|
|
|
|
|
|
// This is really dirty, but we have no other way to assure that
|
|
|
|
// tf.Stop() has been called. This doesn't assure it either, but
|
|
|
|
// it makes it much more certain.
|
|
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
|
2014-07-03 02:01:02 +02:00
|
|
|
close(stopReplyCh)
|
|
|
|
}()
|
|
|
|
|
|
|
|
args := []string{
|
|
|
|
"-init",
|
2014-07-12 06:30:40 +02:00
|
|
|
"-state", statePath,
|
2014-07-03 02:01:02 +02:00
|
|
|
testFixturePath("apply-shutdown"),
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code != 0 {
|
|
|
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := os.Stat(statePath); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.Open(statePath)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
state, err := terraform.ReadState(f)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state should not be nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(state.Resources) != 1 {
|
|
|
|
t.Fatalf("bad: %d", len(state.Resources))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-27 23:43:23 +02:00
|
|
|
func TestApply_state(t *testing.T) {
|
2014-06-19 21:12:24 +02:00
|
|
|
originalState := &terraform.State{
|
|
|
|
Resources: map[string]*terraform.ResourceState{
|
|
|
|
"test_instance.foo": &terraform.ResourceState{
|
|
|
|
ID: "bar",
|
|
|
|
Type: "test_instance",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2014-06-27 23:43:23 +02:00
|
|
|
statePath := testStateFile(t, originalState)
|
2014-06-19 21:12:24 +02:00
|
|
|
|
|
|
|
p := testProvider()
|
2014-07-01 05:49:49 +02:00
|
|
|
p.DiffReturn = &terraform.ResourceDiff{
|
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"ami": &terraform.ResourceAttrDiff{
|
|
|
|
New: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2014-06-19 21:12:24 +02:00
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
2014-07-03 20:46:40 +02:00
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
2014-06-19 21:12:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Run the apply command pointing to our existing state
|
|
|
|
args := []string{
|
2014-07-12 06:30:40 +02:00
|
|
|
"-state", statePath,
|
2014-06-19 21:12:24 +02:00
|
|
|
testFixturePath("apply"),
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code != 0 {
|
|
|
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that the provider was called with the existing state
|
|
|
|
expectedState := originalState.Resources["test_instance.foo"]
|
2014-06-19 21:13:47 +02:00
|
|
|
if !reflect.DeepEqual(p.DiffState, expectedState) {
|
|
|
|
t.Fatalf("bad: %#v", p.DiffState)
|
|
|
|
}
|
|
|
|
|
2014-06-19 21:12:24 +02:00
|
|
|
if !reflect.DeepEqual(p.ApplyState, expectedState) {
|
|
|
|
t.Fatalf("bad: %#v", p.ApplyState)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a new state exists
|
|
|
|
if _, err := os.Stat(statePath); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.Open(statePath)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
state, err := terraform.ReadState(f)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state should not be nil")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-19 06:36:44 +02:00
|
|
|
func TestApply_stateNoExist(t *testing.T) {
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
2014-07-03 20:46:40 +02:00
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
2014-06-19 06:36:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
2014-06-27 23:43:23 +02:00
|
|
|
"idontexist.tfstate",
|
2014-06-19 06:36:44 +02:00
|
|
|
testFixturePath("apply"),
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code != 1 {
|
|
|
|
t.Fatalf("bad: \n%s", ui.OutputWriter.String())
|
|
|
|
}
|
|
|
|
}
|