github.com/sl1pm4t/terraform@v0.6.4-0.20170725213156-870617d22df3/backend/local/backend_plan_test.go (about)

     1  package local
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"path/filepath"
     7  	"reflect"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/hashicorp/terraform/backend"
    12  	"github.com/hashicorp/terraform/config/module"
    13  	"github.com/hashicorp/terraform/terraform"
    14  )
    15  
    16  func TestLocal_planBasic(t *testing.T) {
    17  	b := TestLocal(t)
    18  	p := TestLocalProvider(t, b, "test")
    19  
    20  	mod, modCleanup := module.TestTree(t, "./test-fixtures/plan")
    21  	defer modCleanup()
    22  
    23  	op := testOperationPlan()
    24  	op.Module = mod
    25  	op.PlanRefresh = true
    26  
    27  	run, err := b.Operation(context.Background(), op)
    28  	if err != nil {
    29  		t.Fatalf("bad: %s", err)
    30  	}
    31  	<-run.Done()
    32  	if run.Err != nil {
    33  		t.Fatalf("err: %s", err)
    34  	}
    35  
    36  	if !p.DiffCalled {
    37  		t.Fatal("diff should be called")
    38  	}
    39  }
    40  
    41  func TestLocal_planNoConfig(t *testing.T) {
    42  	b := TestLocal(t)
    43  	TestLocalProvider(t, b, "test")
    44  
    45  	op := testOperationPlan()
    46  	op.Module = nil
    47  	op.PlanRefresh = true
    48  
    49  	run, err := b.Operation(context.Background(), op)
    50  	if err != nil {
    51  		t.Fatalf("bad: %s", err)
    52  	}
    53  	<-run.Done()
    54  
    55  	err = run.Err
    56  	if err == nil {
    57  		t.Fatal("should error")
    58  	}
    59  	if !strings.Contains(err.Error(), "configuration") {
    60  		t.Fatalf("bad: %s", err)
    61  	}
    62  }
    63  
    64  func TestLocal_planRefreshFalse(t *testing.T) {
    65  	b := TestLocal(t)
    66  	p := TestLocalProvider(t, b, "test")
    67  	terraform.TestStateFile(t, b.StatePath, testPlanState())
    68  
    69  	mod, modCleanup := module.TestTree(t, "./test-fixtures/plan")
    70  	defer modCleanup()
    71  
    72  	op := testOperationPlan()
    73  	op.Module = mod
    74  
    75  	run, err := b.Operation(context.Background(), op)
    76  	if err != nil {
    77  		t.Fatalf("bad: %s", err)
    78  	}
    79  	<-run.Done()
    80  	if run.Err != nil {
    81  		t.Fatalf("err: %s", err)
    82  	}
    83  
    84  	if p.RefreshCalled {
    85  		t.Fatal("refresh should not be called")
    86  	}
    87  
    88  	if !run.PlanEmpty {
    89  		t.Fatal("plan should be empty")
    90  	}
    91  }
    92  
    93  func TestLocal_planDestroy(t *testing.T) {
    94  	b := TestLocal(t)
    95  	p := TestLocalProvider(t, b, "test")
    96  	terraform.TestStateFile(t, b.StatePath, testPlanState())
    97  
    98  	mod, modCleanup := module.TestTree(t, "./test-fixtures/plan")
    99  	defer modCleanup()
   100  
   101  	outDir := testTempDir(t)
   102  	defer os.RemoveAll(outDir)
   103  	planPath := filepath.Join(outDir, "plan.tfplan")
   104  
   105  	op := testOperationPlan()
   106  	op.Destroy = true
   107  	op.PlanRefresh = true
   108  	op.Module = mod
   109  	op.PlanOutPath = planPath
   110  
   111  	run, err := b.Operation(context.Background(), op)
   112  	if err != nil {
   113  		t.Fatalf("bad: %s", err)
   114  	}
   115  	<-run.Done()
   116  	if run.Err != nil {
   117  		t.Fatalf("err: %s", err)
   118  	}
   119  
   120  	if !p.RefreshCalled {
   121  		t.Fatal("refresh should be called")
   122  	}
   123  
   124  	if run.PlanEmpty {
   125  		t.Fatal("plan should not be empty")
   126  	}
   127  
   128  	plan := testReadPlan(t, planPath)
   129  	for _, m := range plan.Diff.Modules {
   130  		for _, r := range m.Resources {
   131  			if !r.Destroy {
   132  				t.Fatalf("bad: %#v", r)
   133  			}
   134  		}
   135  	}
   136  }
   137  
   138  func TestLocal_planDestroyNoConfig(t *testing.T) {
   139  	b := TestLocal(t)
   140  	p := TestLocalProvider(t, b, "test")
   141  	terraform.TestStateFile(t, b.StatePath, testPlanState())
   142  
   143  	outDir := testTempDir(t)
   144  	defer os.RemoveAll(outDir)
   145  	planPath := filepath.Join(outDir, "plan.tfplan")
   146  
   147  	op := testOperationPlan()
   148  	op.Destroy = true
   149  	op.PlanRefresh = true
   150  	op.Module = nil
   151  	op.PlanOutPath = planPath
   152  
   153  	run, err := b.Operation(context.Background(), op)
   154  	if err != nil {
   155  		t.Fatalf("bad: %s", err)
   156  	}
   157  	<-run.Done()
   158  	if run.Err != nil {
   159  		t.Fatalf("err: %s", err)
   160  	}
   161  
   162  	if !p.RefreshCalled {
   163  		t.Fatal("refresh should be called")
   164  	}
   165  
   166  	if run.PlanEmpty {
   167  		t.Fatal("plan should not be empty")
   168  	}
   169  
   170  	plan := testReadPlan(t, planPath)
   171  	for _, m := range plan.Diff.Modules {
   172  		for _, r := range m.Resources {
   173  			if !r.Destroy {
   174  				t.Fatalf("bad: %#v", r)
   175  			}
   176  		}
   177  	}
   178  }
   179  
   180  func TestLocal_planOutPathNoChange(t *testing.T) {
   181  	b := TestLocal(t)
   182  	TestLocalProvider(t, b, "test")
   183  	terraform.TestStateFile(t, b.StatePath, testPlanState())
   184  
   185  	mod, modCleanup := module.TestTree(t, "./test-fixtures/plan")
   186  	defer modCleanup()
   187  
   188  	outDir := testTempDir(t)
   189  	defer os.RemoveAll(outDir)
   190  	planPath := filepath.Join(outDir, "plan.tfplan")
   191  
   192  	op := testOperationPlan()
   193  	op.Module = mod
   194  	op.PlanOutPath = planPath
   195  
   196  	run, err := b.Operation(context.Background(), op)
   197  	if err != nil {
   198  		t.Fatalf("bad: %s", err)
   199  	}
   200  	<-run.Done()
   201  	if run.Err != nil {
   202  		t.Fatalf("err: %s", err)
   203  	}
   204  
   205  	plan := testReadPlan(t, planPath)
   206  	if !plan.Diff.Empty() {
   207  		t.Fatalf("expected empty plan to be written")
   208  	}
   209  }
   210  
   211  // TestLocal_planScaleOutNoDupeCount tests a Refresh/Plan sequence when a
   212  // resource count is scaled out. The scaled out node needs to exist in the
   213  // graph and run through a plan-style sequence during the refresh phase, but
   214  // can conflate the count if its post-diff count hooks are not skipped. This
   215  // checks to make sure the correct resource count is ultimately given to the
   216  // UI.
   217  func TestLocal_planScaleOutNoDupeCount(t *testing.T) {
   218  	b := TestLocal(t)
   219  	TestLocalProvider(t, b, "test")
   220  	state := &terraform.State{
   221  		Version: 2,
   222  		Modules: []*terraform.ModuleState{
   223  			&terraform.ModuleState{
   224  				Path: []string{"root"},
   225  				Resources: map[string]*terraform.ResourceState{
   226  					"test_instance.foo.0": &terraform.ResourceState{
   227  						Type: "test_instance",
   228  						Primary: &terraform.InstanceState{
   229  							ID: "bar",
   230  						},
   231  					},
   232  					"test_instance.foo.1": &terraform.ResourceState{
   233  						Type: "test_instance",
   234  						Primary: &terraform.InstanceState{
   235  							ID: "bar",
   236  						},
   237  					},
   238  				},
   239  			},
   240  		},
   241  	}
   242  	terraform.TestStateFile(t, b.StatePath, state)
   243  
   244  	actual := new(CountHook)
   245  	b.ContextOpts.Hooks = append(b.ContextOpts.Hooks, actual)
   246  
   247  	mod, modCleanup := module.TestTree(t, "./test-fixtures/plan-scaleout")
   248  	defer modCleanup()
   249  
   250  	outDir := testTempDir(t)
   251  	defer os.RemoveAll(outDir)
   252  
   253  	op := testOperationPlan()
   254  	op.Module = mod
   255  	op.PlanRefresh = true
   256  
   257  	run, err := b.Operation(context.Background(), op)
   258  	if err != nil {
   259  		t.Fatalf("bad: %s", err)
   260  	}
   261  	<-run.Done()
   262  	if run.Err != nil {
   263  		t.Fatalf("err: %s", err)
   264  	}
   265  
   266  	expected := new(CountHook)
   267  	expected.ToAdd = 1
   268  	expected.ToChange = 0
   269  	expected.ToRemoveAndAdd = 0
   270  	expected.ToRemove = 0
   271  
   272  	if !reflect.DeepEqual(expected, actual) {
   273  		t.Fatalf("Expected %#v, got %#v instead.",
   274  			expected, actual)
   275  	}
   276  }
   277  
   278  func testOperationPlan() *backend.Operation {
   279  	return &backend.Operation{
   280  		Type: backend.OperationTypePlan,
   281  	}
   282  }
   283  
   284  // testPlanState is just a common state that we use for testing refresh.
   285  func testPlanState() *terraform.State {
   286  	return &terraform.State{
   287  		Version: 2,
   288  		Modules: []*terraform.ModuleState{
   289  			&terraform.ModuleState{
   290  				Path: []string{"root"},
   291  				Resources: map[string]*terraform.ResourceState{
   292  					"test_instance.foo": &terraform.ResourceState{
   293  						Type: "test_instance",
   294  						Primary: &terraform.InstanceState{
   295  							ID: "bar",
   296  						},
   297  					},
   298  				},
   299  			},
   300  		},
   301  	}
   302  }
   303  
   304  func testReadPlan(t *testing.T, path string) *terraform.Plan {
   305  	f, err := os.Open(path)
   306  	if err != nil {
   307  		t.Fatalf("err: %s", err)
   308  	}
   309  	defer f.Close()
   310  
   311  	p, err := terraform.ReadPlan(f)
   312  	if err != nil {
   313  		t.Fatalf("err: %s", err)
   314  	}
   315  
   316  	return p
   317  }