2014-06-19 06:36:44 +02:00
|
|
|
package command
|
|
|
|
|
|
|
|
import (
|
2014-10-01 17:43:00 +02:00
|
|
|
"bytes"
|
2014-07-08 06:20:48 +02:00
|
|
|
"fmt"
|
2014-07-12 06:30:40 +02:00
|
|
|
"io/ioutil"
|
2014-09-30 00:55:28 +02:00
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
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-09-18 19:40:23 +02:00
|
|
|
"strings"
|
2014-07-08 06:20:48 +02:00
|
|
|
"sync"
|
2015-10-14 19:43:51 +02:00
|
|
|
"sync/atomic"
|
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
|
|
|
|
|
|
|
"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-13 05:37:30 +02:00
|
|
|
Meta: Meta{
|
|
|
|
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-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")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-14 19:43:51 +02:00
|
|
|
func TestApply_parallelism(t *testing.T) {
|
|
|
|
provider := testProvider()
|
2015-10-30 00:06:30 +01:00
|
|
|
statePath := testTempFile(t)
|
2015-05-08 09:01:21 +02:00
|
|
|
|
2015-10-14 19:43:51 +02:00
|
|
|
// This blocks all the appy functions. We close it when we exit so
|
|
|
|
// they end quickly after this test finishes.
|
|
|
|
block := make(chan struct{})
|
|
|
|
defer close(block)
|
2015-05-08 09:01:21 +02:00
|
|
|
|
2015-10-14 19:43:51 +02:00
|
|
|
var runCount uint64
|
|
|
|
provider.ApplyFn = func(
|
|
|
|
i *terraform.InstanceInfo,
|
|
|
|
s *terraform.InstanceState,
|
|
|
|
d *terraform.InstanceDiff) (*terraform.InstanceState, error) {
|
|
|
|
// Increment so we're counting parallelism
|
|
|
|
atomic.AddUint64(&runCount, 1)
|
2015-05-08 09:01:21 +02:00
|
|
|
|
2015-10-14 19:43:51 +02:00
|
|
|
// Block until we're done
|
|
|
|
<-block
|
|
|
|
|
|
|
|
return nil, nil
|
2015-05-08 09:01:21 +02:00
|
|
|
}
|
|
|
|
|
2015-10-14 19:43:51 +02:00
|
|
|
ui := new(cli.MockUi)
|
2015-05-08 09:01:21 +02:00
|
|
|
c := &ApplyCommand{
|
|
|
|
Meta: Meta{
|
2015-10-14 19:43:51 +02:00
|
|
|
ContextOpts: testCtxConfig(provider),
|
2015-05-08 09:01:21 +02:00
|
|
|
Ui: ui,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2015-10-14 19:43:51 +02:00
|
|
|
par := uint64(5)
|
2015-05-08 09:01:21 +02:00
|
|
|
args := []string{
|
2015-10-30 00:06:30 +01:00
|
|
|
"-state", statePath,
|
2015-10-14 19:43:51 +02:00
|
|
|
fmt.Sprintf("-parallelism=%d", par),
|
2015-05-08 09:01:21 +02:00
|
|
|
testFixturePath("parallelism"),
|
|
|
|
}
|
|
|
|
|
2015-10-14 19:43:51 +02:00
|
|
|
// Run in a goroutine. We still try to catch any errors and
|
|
|
|
// get them on the error channel.
|
|
|
|
errCh := make(chan string, 1)
|
|
|
|
go func() {
|
|
|
|
if code := c.Run(args); code != 0 {
|
|
|
|
errCh <- ui.OutputWriter.String()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
select {
|
|
|
|
case <-time.After(1000 * time.Millisecond):
|
|
|
|
case err := <-errCh:
|
|
|
|
t.Fatalf("err: %s", err)
|
2015-05-08 09:01:21 +02:00
|
|
|
}
|
|
|
|
|
2015-10-14 19:43:51 +02:00
|
|
|
// The total in flight should equal the parallelism
|
|
|
|
if rc := atomic.LoadUint64(&runCount); rc != par {
|
|
|
|
t.Fatalf("Expected parallelism: %d, got: %d", par, rc)
|
2015-05-08 09:01:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-03 06:16:36 +02:00
|
|
|
func TestApply_configInvalid(t *testing.T) {
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
2014-07-13 05:37:30 +02:00
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
2014-07-03 06:16:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
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{
|
2014-07-13 05:37:30 +02:00
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
2014-07-12 06:30:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
|
|
|
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{
|
2014-07-13 05:37:30 +02:00
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
2014-07-08 06:20:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var lock sync.Mutex
|
|
|
|
errored := false
|
|
|
|
p.ApplyFn = func(
|
2014-09-17 20:15:07 +02:00
|
|
|
info *terraform.InstanceInfo,
|
|
|
|
s *terraform.InstanceState,
|
2014-09-18 01:33:24 +02:00
|
|
|
d *terraform.InstanceDiff) (*terraform.InstanceState, error) {
|
2014-07-08 06:20:48 +02:00
|
|
|
lock.Lock()
|
|
|
|
defer lock.Unlock()
|
|
|
|
|
|
|
|
if !errored {
|
|
|
|
errored = true
|
|
|
|
return nil, fmt.Errorf("error")
|
|
|
|
}
|
|
|
|
|
2014-09-17 20:15:07 +02:00
|
|
|
return &terraform.InstanceState{ID: "foo"}, nil
|
2014-07-08 06:20:48 +02:00
|
|
|
}
|
|
|
|
p.DiffFn = func(
|
2014-09-17 20:15:07 +02:00
|
|
|
*terraform.InstanceInfo,
|
|
|
|
*terraform.InstanceState,
|
2014-09-18 01:33:24 +02:00
|
|
|
*terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
|
|
|
|
return &terraform.InstanceDiff{
|
2014-07-08 06:20:48 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"ami": &terraform.ResourceAttrDiff{
|
|
|
|
New: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
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")
|
|
|
|
}
|
2014-09-17 20:15:07 +02:00
|
|
|
if len(state.RootModule().Resources) == 0 {
|
2014-07-08 06:20:48 +02:00
|
|
|
t.Fatal("no resources in state")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-30 00:55:28 +02:00
|
|
|
func TestApply_init(t *testing.T) {
|
|
|
|
// Change to the temporary directory
|
|
|
|
cwd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
dir := tempDir(t)
|
|
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if err := os.Chdir(dir); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
defer os.Chdir(cwd)
|
|
|
|
|
|
|
|
// Create the test fixtures
|
|
|
|
statePath := testTempFile(t)
|
|
|
|
ln := testHttpServer(t)
|
|
|
|
defer ln.Close()
|
|
|
|
|
|
|
|
// Initialize the command
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build the URL to the init
|
|
|
|
var u url.URL
|
|
|
|
u.Scheme = "http"
|
|
|
|
u.Host = ln.Addr().String()
|
|
|
|
u.Path = "/header"
|
|
|
|
|
|
|
|
args := []string{
|
|
|
|
"-state", statePath,
|
|
|
|
u.String(),
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code != 0 {
|
|
|
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := os.Stat("hello.tf"); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
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")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-16 19:46:38 +01:00
|
|
|
func TestApply_input(t *testing.T) {
|
|
|
|
// Disable test mode so input would be asked
|
|
|
|
test = false
|
|
|
|
defer func() { test = true }()
|
|
|
|
|
|
|
|
// Set some default reader/writers for the inputs
|
|
|
|
defaultInputReader = bytes.NewBufferString("foo\n")
|
|
|
|
defaultInputWriter = new(bytes.Buffer)
|
|
|
|
|
|
|
|
statePath := testTempFile(t)
|
|
|
|
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
|
|
|
"-state", statePath,
|
|
|
|
testFixturePath("apply-input"),
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code != 0 {
|
|
|
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
if !p.InputCalled {
|
|
|
|
t.Fatal("input should be called")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-12 06:32:34 +02:00
|
|
|
func TestApply_noArgs(t *testing.T) {
|
|
|
|
cwd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if err := os.Chdir(testFixturePath("plan")); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
defer os.Chdir(cwd)
|
|
|
|
|
|
|
|
statePath := testTempFile(t)
|
|
|
|
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
2014-07-13 05:37:30 +02:00
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
2014-07-12 06:32:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
|
|
|
"-state", statePath,
|
|
|
|
}
|
|
|
|
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-06-27 23:43:23 +02:00
|
|
|
func TestApply_plan(t *testing.T) {
|
2014-10-01 17:43:00 +02:00
|
|
|
// Disable test mode so input would be asked
|
|
|
|
test = false
|
|
|
|
defer func() { test = true }()
|
|
|
|
|
|
|
|
// Set some default reader/writers for the inputs
|
|
|
|
defaultInputReader = new(bytes.Buffer)
|
|
|
|
defaultInputWriter = new(bytes.Buffer)
|
|
|
|
|
2014-07-03 20:46:40 +02:00
|
|
|
planPath := testPlanFile(t, &terraform.Plan{
|
2014-09-25 00:48:46 +02:00
|
|
|
Module: testModule(t, "apply"),
|
2014-07-03 20:46:40 +02:00
|
|
|
})
|
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-13 05:37:30 +02:00
|
|
|
Meta: Meta{
|
|
|
|
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 {
|
2014-08-25 06:40:58 +02:00
|
|
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
|
|
|
}
|
|
|
|
|
2014-10-01 17:43:00 +02:00
|
|
|
if p.InputCalled {
|
|
|
|
t.Fatalf("input should not be called for plans")
|
|
|
|
}
|
|
|
|
|
2014-08-25 06:40:58 +02:00
|
|
|
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")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-07 22:08:32 +01:00
|
|
|
func TestApply_plan_remoteState(t *testing.T) {
|
|
|
|
// Disable test mode so input would be asked
|
|
|
|
test = false
|
|
|
|
defer func() { test = true }()
|
|
|
|
tmp, cwd := testCwd(t)
|
|
|
|
defer testFixCwd(t, tmp, cwd)
|
2015-02-22 20:13:21 +01:00
|
|
|
remoteStatePath := filepath.Join(tmp, DefaultDataDir, DefaultStateFilename)
|
|
|
|
if err := os.MkdirAll(filepath.Dir(remoteStatePath), 0755); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
2015-01-07 22:08:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set some default reader/writers for the inputs
|
|
|
|
defaultInputReader = new(bytes.Buffer)
|
|
|
|
defaultInputWriter = new(bytes.Buffer)
|
|
|
|
|
|
|
|
// Create a remote state
|
|
|
|
state := testState()
|
|
|
|
conf, srv := testRemoteState(t, state, 200)
|
|
|
|
defer srv.Close()
|
|
|
|
state.Remote = conf
|
|
|
|
|
|
|
|
planPath := testPlanFile(t, &terraform.Plan{
|
|
|
|
Module: testModule(t, "apply"),
|
|
|
|
State: state,
|
|
|
|
})
|
|
|
|
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
|
|
|
planPath,
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code != 0 {
|
|
|
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
if p.InputCalled {
|
|
|
|
t.Fatalf("input should not be called for plans")
|
|
|
|
}
|
|
|
|
|
|
|
|
// State file should be not be installed
|
2015-02-22 20:13:21 +01:00
|
|
|
if _, err := os.Stat(filepath.Join(tmp, DefaultStateFilename)); err == nil {
|
2015-01-07 22:08:32 +01:00
|
|
|
t.Fatalf("State path should not exist")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for remote state
|
2015-02-22 20:13:21 +01:00
|
|
|
if _, err := os.Stat(remoteStatePath); err != nil {
|
|
|
|
t.Fatalf("missing remote state: %s", err)
|
2015-01-07 22:08:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-25 06:40:58 +02:00
|
|
|
func TestApply_planWithVarFile(t *testing.T) {
|
|
|
|
varFileDir := testTempDir(t)
|
|
|
|
varFilePath := filepath.Join(varFileDir, "terraform.tfvars")
|
|
|
|
if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
planPath := testPlanFile(t, &terraform.Plan{
|
2014-09-25 00:48:46 +02:00
|
|
|
Module: testModule(t, "apply"),
|
2014-08-25 06:40:58 +02:00
|
|
|
})
|
|
|
|
statePath := testTempFile(t)
|
|
|
|
|
|
|
|
cwd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if err := os.Chdir(varFileDir); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
defer os.Chdir(cwd)
|
|
|
|
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
|
|
|
"-state", statePath,
|
|
|
|
planPath,
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code != 0 {
|
2014-06-27 23:43:23 +02:00
|
|
|
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-26 23:32:09 +02:00
|
|
|
func TestApply_planVars(t *testing.T) {
|
|
|
|
planPath := testPlanFile(t, &terraform.Plan{
|
2014-09-25 00:48:46 +02:00
|
|
|
Module: testModule(t, "apply"),
|
2014-07-26 23:32:09 +02:00
|
|
|
})
|
|
|
|
statePath := testTempFile(t)
|
|
|
|
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
|
|
|
"-state", statePath,
|
|
|
|
"-var", "foo=bar",
|
|
|
|
planPath,
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code == 0 {
|
|
|
|
t.Fatal("should've failed")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-27 02:50:13 +02:00
|
|
|
func TestApply_refresh(t *testing.T) {
|
|
|
|
originalState := &terraform.State{
|
2014-09-17 20:15:07 +02:00
|
|
|
Modules: []*terraform.ModuleState{
|
|
|
|
&terraform.ModuleState{
|
|
|
|
Path: []string{"root"},
|
|
|
|
Resources: map[string]*terraform.ResourceState{
|
|
|
|
"test_instance.foo": &terraform.ResourceState{
|
|
|
|
Type: "test_instance",
|
|
|
|
Primary: &terraform.InstanceState{
|
|
|
|
ID: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2014-07-27 02:50:13 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
statePath := testStateFile(t, originalState)
|
|
|
|
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
|
|
|
"-state", statePath,
|
|
|
|
testFixturePath("apply"),
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code != 0 {
|
|
|
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
if !p.RefreshCalled {
|
|
|
|
t.Fatal("should call refresh")
|
|
|
|
}
|
|
|
|
|
|
|
|
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-28 05:38:41 +02:00
|
|
|
|
|
|
|
// Should have a backup file
|
2015-09-11 20:56:20 +02:00
|
|
|
f, err = os.Open(statePath + DefaultBackupExtension)
|
2014-07-28 05:38:41 +02:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
backupState, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
2014-09-19 02:16:09 +02:00
|
|
|
actualStr := strings.TrimSpace(backupState.String())
|
|
|
|
expectedStr := strings.TrimSpace(originalState.String())
|
|
|
|
if actualStr != expectedStr {
|
|
|
|
t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
|
2014-07-28 05:38:41 +02:00
|
|
|
}
|
2014-07-27 02:50:13 +02:00
|
|
|
}
|
|
|
|
|
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-13 05:37:30 +02:00
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
|
|
|
|
|
|
|
ShutdownCh: shutdownCh,
|
2014-07-03 02:01:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
p.DiffFn = func(
|
2014-09-17 20:15:07 +02:00
|
|
|
*terraform.InstanceInfo,
|
|
|
|
*terraform.InstanceState,
|
2014-09-18 01:33:24 +02:00
|
|
|
*terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
|
|
|
|
return &terraform.InstanceDiff{
|
2014-07-03 02:01:02 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"ami": &terraform.ResourceAttrDiff{
|
|
|
|
New: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
p.ApplyFn = func(
|
2014-09-17 20:15:07 +02:00
|
|
|
*terraform.InstanceInfo,
|
|
|
|
*terraform.InstanceState,
|
2014-09-18 01:33:24 +02:00
|
|
|
*terraform.InstanceDiff) (*terraform.InstanceState, error) {
|
2014-07-03 02:01:02 +02:00
|
|
|
if !stopped {
|
|
|
|
stopped = true
|
|
|
|
close(stopCh)
|
|
|
|
<-stopReplyCh
|
|
|
|
}
|
|
|
|
|
2014-09-17 20:15:07 +02:00
|
|
|
return &terraform.InstanceState{
|
2014-07-03 02:01:02 +02:00
|
|
|
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{
|
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")
|
|
|
|
}
|
|
|
|
|
2014-09-17 20:15:07 +02:00
|
|
|
if len(state.RootModule().Resources) != 1 {
|
|
|
|
t.Fatalf("bad: %d", len(state.RootModule().Resources))
|
2014-07-03 02:01:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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{
|
2014-09-17 20:15:07 +02:00
|
|
|
Modules: []*terraform.ModuleState{
|
|
|
|
&terraform.ModuleState{
|
|
|
|
Path: []string{"root"},
|
|
|
|
Resources: map[string]*terraform.ResourceState{
|
|
|
|
"test_instance.foo": &terraform.ResourceState{
|
|
|
|
Type: "test_instance",
|
|
|
|
Primary: &terraform.InstanceState{
|
|
|
|
ID: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2014-06-19 21:12:24 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2014-06-27 23:43:23 +02:00
|
|
|
statePath := testStateFile(t, originalState)
|
2014-06-19 21:12:24 +02:00
|
|
|
|
|
|
|
p := testProvider()
|
2014-09-18 01:33:24 +02:00
|
|
|
p.DiffReturn = &terraform.InstanceDiff{
|
2014-07-01 05:49:49 +02:00
|
|
|
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-13 05:37:30 +02:00
|
|
|
Meta: Meta{
|
|
|
|
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
|
2014-09-18 19:40:23 +02:00
|
|
|
actual := strings.TrimSpace(p.DiffState.String())
|
|
|
|
expected := strings.TrimSpace(testApplyStateDiffStr)
|
|
|
|
if actual != expected {
|
|
|
|
t.Fatalf("bad:\n\n%s", actual)
|
2014-06-19 21:13:47 +02:00
|
|
|
}
|
|
|
|
|
2014-09-18 19:40:23 +02:00
|
|
|
actual = strings.TrimSpace(p.ApplyState.String())
|
|
|
|
expected = strings.TrimSpace(testApplyStateStr)
|
|
|
|
if actual != expected {
|
|
|
|
t.Fatalf("bad:\n\n%s", actual)
|
2014-06-19 21:12:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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-07-28 05:38:41 +02:00
|
|
|
|
|
|
|
// Should have a backup file
|
2015-09-11 20:56:20 +02:00
|
|
|
f, err = os.Open(statePath + DefaultBackupExtension)
|
2014-07-28 05:38:41 +02:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
backupState, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// nil out the ConnInfo since that should not be restored
|
2014-09-17 20:15:07 +02:00
|
|
|
originalState.RootModule().Resources["test_instance.foo"].Primary.Ephemeral.ConnInfo = nil
|
2014-07-28 05:38:41 +02:00
|
|
|
|
2014-09-19 02:16:09 +02:00
|
|
|
actualStr := strings.TrimSpace(backupState.String())
|
|
|
|
expectedStr := strings.TrimSpace(originalState.String())
|
|
|
|
if actualStr != expectedStr {
|
|
|
|
t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
|
2014-07-28 05:38:41 +02:00
|
|
|
}
|
2014-06-19 21:12:24 +02:00
|
|
|
}
|
|
|
|
|
2014-06-19 06:36:44 +02:00
|
|
|
func TestApply_stateNoExist(t *testing.T) {
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
2014-07-13 05:37:30 +02:00
|
|
|
Meta: Meta{
|
|
|
|
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())
|
|
|
|
}
|
|
|
|
}
|
2014-07-18 20:37:27 +02:00
|
|
|
|
2016-05-09 21:46:07 +02:00
|
|
|
func TestApply_sensitiveOutput(t *testing.T) {
|
|
|
|
statePath := testTempFile(t)
|
|
|
|
|
2016-03-11 20:07:54 +01:00
|
|
|
args := []string{
|
|
|
|
"-state", statePath,
|
|
|
|
testFixturePath("apply-sensitive-output"),
|
|
|
|
}
|
|
|
|
|
|
|
|
if code := c.Run(args); code != 0 {
|
|
|
|
t.Fatalf("bad: \n%s", ui.OutputWriter.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
output := ui.OutputWriter.String()
|
|
|
|
if !strings.Contains(output, "notsensitive = Hello world") {
|
|
|
|
t.Fatalf("bad: output should contain 'notsensitive' output\n%s", output)
|
|
|
|
}
|
|
|
|
if !strings.Contains(output, "sensitive = <sensitive>") {
|
|
|
|
t.Fatalf("bad: output should contain 'sensitive' output\n%s", output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestApply_stateFuture(t *testing.T) {
|
|
|
|
originalState := testState()
|
|
|
|
originalState.TFVersion = "99.99.99"
|
|
|
|
statePath := testStateFile(t, originalState)
|
|
|
|
|
2016-05-09 21:46:07 +02:00
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
|
|
|
"-state", statePath,
|
2016-03-11 20:07:54 +01:00
|
|
|
testFixturePath("apply"),
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code == 0 {
|
|
|
|
t.Fatal("should fail")
|
2016-05-09 21:46:07 +02:00
|
|
|
}
|
|
|
|
|
2016-03-11 20:07:54 +01:00
|
|
|
f, err := os.Open(statePath)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
2016-05-09 21:46:07 +02:00
|
|
|
}
|
|
|
|
|
2016-03-11 20:07:54 +01:00
|
|
|
newState, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
2016-05-09 21:46:07 +02:00
|
|
|
}
|
2016-03-11 20:07:54 +01:00
|
|
|
|
|
|
|
if !newState.Equal(originalState) {
|
|
|
|
t.Fatalf("bad: %#v", newState)
|
|
|
|
}
|
|
|
|
if newState.TFVersion != originalState.TFVersion {
|
|
|
|
t.Fatalf("bad: %#v", newState)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestApply_statePast(t *testing.T) {
|
|
|
|
originalState := testState()
|
|
|
|
originalState.TFVersion = "0.1.0"
|
|
|
|
statePath := testStateFile(t, originalState)
|
|
|
|
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
|
|
|
"-state", statePath,
|
|
|
|
testFixturePath("apply"),
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code != 0 {
|
|
|
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
2016-05-09 21:46:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-18 20:37:27 +02:00
|
|
|
func TestApply_vars(t *testing.T) {
|
|
|
|
statePath := testTempFile(t)
|
|
|
|
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
actual := ""
|
|
|
|
p.DiffFn = func(
|
2014-09-17 20:15:07 +02:00
|
|
|
info *terraform.InstanceInfo,
|
|
|
|
s *terraform.InstanceState,
|
2014-09-18 01:33:24 +02:00
|
|
|
c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
|
2014-07-18 20:37:27 +02:00
|
|
|
if v, ok := c.Config["value"]; ok {
|
|
|
|
actual = v.(string)
|
|
|
|
}
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
return &terraform.InstanceDiff{}, nil
|
2014-07-18 20:37:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
|
|
|
"-var", "foo=bar",
|
|
|
|
"-state", statePath,
|
|
|
|
testFixturePath("apply-vars"),
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code != 0 {
|
|
|
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual != "bar" {
|
|
|
|
t.Fatal("didn't work")
|
|
|
|
}
|
|
|
|
}
|
2014-07-18 23:00:40 +02:00
|
|
|
|
|
|
|
func TestApply_varFile(t *testing.T) {
|
|
|
|
varFilePath := testTempFile(t)
|
|
|
|
if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
statePath := testTempFile(t)
|
|
|
|
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
actual := ""
|
|
|
|
p.DiffFn = func(
|
2014-09-17 20:15:07 +02:00
|
|
|
info *terraform.InstanceInfo,
|
|
|
|
s *terraform.InstanceState,
|
2014-09-18 01:33:24 +02:00
|
|
|
c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
|
2014-07-18 23:00:40 +02:00
|
|
|
if v, ok := c.Config["value"]; ok {
|
|
|
|
actual = v.(string)
|
|
|
|
}
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
return &terraform.InstanceDiff{}, nil
|
2014-07-18 23:00:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
|
|
|
"-var-file", varFilePath,
|
|
|
|
"-state", statePath,
|
|
|
|
testFixturePath("apply-vars"),
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code != 0 {
|
|
|
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual != "bar" {
|
|
|
|
t.Fatal("didn't work")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-05 18:32:01 +02:00
|
|
|
func TestApply_varFileDefault(t *testing.T) {
|
|
|
|
varFileDir := testTempDir(t)
|
|
|
|
varFilePath := filepath.Join(varFileDir, "terraform.tfvars")
|
|
|
|
if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
statePath := testTempFile(t)
|
|
|
|
|
|
|
|
cwd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if err := os.Chdir(varFileDir); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
defer os.Chdir(cwd)
|
|
|
|
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
actual := ""
|
|
|
|
p.DiffFn = func(
|
2014-09-17 20:15:07 +02:00
|
|
|
info *terraform.InstanceInfo,
|
|
|
|
s *terraform.InstanceState,
|
2014-09-18 01:33:24 +02:00
|
|
|
c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
|
2014-08-05 18:32:01 +02:00
|
|
|
if v, ok := c.Config["value"]; ok {
|
|
|
|
actual = v.(string)
|
|
|
|
}
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
return &terraform.InstanceDiff{}, nil
|
2014-08-05 18:32:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
|
|
|
"-state", statePath,
|
|
|
|
testFixturePath("apply-vars"),
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code != 0 {
|
|
|
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual != "bar" {
|
|
|
|
t.Fatal("didn't work")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-02 18:21:45 +01:00
|
|
|
func TestApply_varFileDefaultJSON(t *testing.T) {
|
|
|
|
varFileDir := testTempDir(t)
|
|
|
|
varFilePath := filepath.Join(varFileDir, "terraform.tfvars.json")
|
|
|
|
if err := ioutil.WriteFile(varFilePath, []byte(applyVarFileJSON), 0644); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
statePath := testTempFile(t)
|
|
|
|
|
|
|
|
cwd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if err := os.Chdir(varFileDir); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
defer os.Chdir(cwd)
|
|
|
|
|
|
|
|
p := testProvider()
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
actual := ""
|
|
|
|
p.DiffFn = func(
|
|
|
|
info *terraform.InstanceInfo,
|
|
|
|
s *terraform.InstanceState,
|
|
|
|
c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
|
|
|
|
if v, ok := c.Config["value"]; ok {
|
|
|
|
actual = v.(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &terraform.InstanceDiff{}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{
|
|
|
|
"-state", statePath,
|
|
|
|
testFixturePath("apply-vars"),
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code != 0 {
|
|
|
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual != "bar" {
|
|
|
|
t.Fatal("didn't work")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-28 05:38:41 +02:00
|
|
|
func TestApply_backup(t *testing.T) {
|
|
|
|
originalState := &terraform.State{
|
2014-09-17 20:15:07 +02:00
|
|
|
Modules: []*terraform.ModuleState{
|
|
|
|
&terraform.ModuleState{
|
|
|
|
Path: []string{"root"},
|
|
|
|
Resources: map[string]*terraform.ResourceState{
|
|
|
|
"test_instance.foo": &terraform.ResourceState{
|
|
|
|
Type: "test_instance",
|
|
|
|
Primary: &terraform.InstanceState{
|
|
|
|
ID: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2014-07-28 05:38:41 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
statePath := testStateFile(t, originalState)
|
|
|
|
backupPath := testTempFile(t)
|
|
|
|
|
|
|
|
p := testProvider()
|
2014-09-18 01:33:24 +02:00
|
|
|
p.DiffReturn = &terraform.InstanceDiff{
|
2014-07-28 05:38:41 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"ami": &terraform.ResourceAttrDiff{
|
|
|
|
New: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run the apply command pointing to our existing state
|
|
|
|
args := []string{
|
|
|
|
"-state", statePath,
|
|
|
|
"-backup", backupPath,
|
|
|
|
testFixturePath("apply"),
|
|
|
|
}
|
|
|
|
if code := c.Run(args); code != 0 {
|
|
|
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Should have a backup file
|
|
|
|
f, err = os.Open(backupPath)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
backupState, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
2014-09-17 20:15:07 +02:00
|
|
|
actual := backupState.RootModule().Resources["test_instance.foo"]
|
|
|
|
expected := originalState.RootModule().Resources["test_instance.foo"]
|
2014-07-28 05:38:41 +02:00
|
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
|
|
t.Fatalf("bad: %#v %#v", actual, expected)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestApply_disableBackup(t *testing.T) {
|
2014-09-18 19:40:23 +02:00
|
|
|
originalState := testState()
|
2014-07-28 05:38:41 +02:00
|
|
|
statePath := testStateFile(t, originalState)
|
|
|
|
|
|
|
|
p := testProvider()
|
2014-09-18 01:33:24 +02:00
|
|
|
p.DiffReturn = &terraform.InstanceDiff{
|
2014-07-28 05:38:41 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"ami": &terraform.ResourceAttrDiff{
|
|
|
|
New: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
ui := new(cli.MockUi)
|
|
|
|
c := &ApplyCommand{
|
|
|
|
Meta: Meta{
|
|
|
|
ContextOpts: testCtxConfig(p),
|
|
|
|
Ui: ui,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run the apply command pointing to our existing state
|
|
|
|
args := []string{
|
|
|
|
"-state", statePath,
|
|
|
|
"-backup", "-",
|
|
|
|
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
|
2014-09-18 19:40:23 +02:00
|
|
|
actual := strings.TrimSpace(p.DiffState.String())
|
|
|
|
expected := strings.TrimSpace(testApplyDisableBackupStr)
|
|
|
|
if actual != expected {
|
|
|
|
t.Fatalf("bad:\n\n%s", actual)
|
2014-07-28 05:38:41 +02:00
|
|
|
}
|
|
|
|
|
2014-09-18 19:40:23 +02:00
|
|
|
actual = strings.TrimSpace(p.ApplyState.String())
|
|
|
|
expected = strings.TrimSpace(testApplyDisableBackupStateStr)
|
|
|
|
if actual != expected {
|
|
|
|
t.Fatalf("bad:\n\n%s", actual)
|
2014-07-28 05:38:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure there is no backup
|
2015-09-11 20:56:20 +02:00
|
|
|
_, err = os.Stat(statePath + DefaultBackupExtension)
|
2014-07-28 05:38:41 +02:00
|
|
|
if err == nil || !os.IsNotExist(err) {
|
|
|
|
t.Fatalf("backup should not exist")
|
|
|
|
}
|
2015-03-02 18:11:49 +01:00
|
|
|
|
|
|
|
// Ensure there is no literal "-"
|
|
|
|
_, err = os.Stat("-")
|
|
|
|
if err == nil || !os.IsNotExist(err) {
|
|
|
|
t.Fatalf("backup should not exist")
|
|
|
|
}
|
2014-07-28 05:38:41 +02:00
|
|
|
}
|
|
|
|
|
2014-09-30 00:55:28 +02:00
|
|
|
func testHttpServer(t *testing.T) net.Listener {
|
2015-01-28 06:22:35 +01:00
|
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
2014-09-30 00:55:28 +02:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
mux.HandleFunc("/header", testHttpHandlerHeader)
|
|
|
|
|
|
|
|
var server http.Server
|
|
|
|
server.Handler = mux
|
|
|
|
go server.Serve(ln)
|
|
|
|
|
|
|
|
return ln
|
|
|
|
}
|
|
|
|
|
|
|
|
func testHttpHandlerHeader(w http.ResponseWriter, r *http.Request) {
|
|
|
|
var url url.URL
|
|
|
|
url.Scheme = "file"
|
2015-01-28 06:39:16 +01:00
|
|
|
url.Path = filepath.ToSlash(testFixturePath("init"))
|
2014-09-30 00:55:28 +02:00
|
|
|
|
|
|
|
w.Header().Add("X-Terraform-Get", url.String())
|
|
|
|
w.WriteHeader(200)
|
|
|
|
}
|
|
|
|
|
2014-07-18 23:00:40 +02:00
|
|
|
const applyVarFile = `
|
|
|
|
foo = "bar"
|
|
|
|
`
|
2014-09-18 19:40:23 +02:00
|
|
|
|
2015-03-02 18:21:45 +01:00
|
|
|
const applyVarFileJSON = `
|
|
|
|
{ "foo": "bar" }
|
|
|
|
`
|
|
|
|
|
2014-09-18 19:40:23 +02:00
|
|
|
const testApplyDisableBackupStr = `
|
|
|
|
ID = bar
|
|
|
|
`
|
|
|
|
|
|
|
|
const testApplyDisableBackupStateStr = `
|
|
|
|
ID = bar
|
|
|
|
`
|
|
|
|
|
|
|
|
const testApplyStateStr = `
|
|
|
|
ID = bar
|
|
|
|
`
|
|
|
|
|
|
|
|
const testApplyStateDiffStr = `
|
|
|
|
ID = bar
|
|
|
|
`
|