command/e2etest: an initial test for the primary workflow

This e2etest runs an init, plan, apply, destroy sequence against a test
configuration using the real template and null providers downloaded from
the official repository.

This test _does_ trample a bit on the scope of some already-existing
tests, but this is mainly just to check our assumptions about how
Terraform behaves to ensure that we can reach our main conclusion here:
that the main Terraform workflow commands interact correctly with each
other in real use and we can complete the full workflow.
This commit is contained in:
Martin Atkins 2017-07-07 18:54:29 -07:00
parent 52df81ee49
commit 23f9c8785e
2 changed files with 145 additions and 0 deletions

View File

@ -0,0 +1,123 @@
package e2etest
import (
"reflect"
"sort"
"strings"
"testing"
"github.com/davecgh/go-spew/spew"
)
// The tests in this file are for the "primary workflow", which includes
// variants of the following sequence, with different details:
// terraform init
// terraform plan
// terraform apply
// terraform destroy
func TestPrimarySeparatePlan(t *testing.T) {
t.Parallel()
// This test reaches out to releases.hashicorp.com to download the
// template and null providers, so it can only run if network access is
// allowed.
skipIfCannotAccessNetwork(t)
tf := newTerraform("full-workflow-null")
defer tf.Close()
//// INIT
stdout, stderr, err := tf.Run("init")
if err != nil {
t.Fatalf("unexpected init error: %s\nstderr:\n%s", err, stderr)
}
// Make sure we actually downloaded the plugins, rather than picking up
// copies that might be already installed globally on the system.
if !strings.Contains(stdout, "- Downloading plugin for provider \"template\"") {
t.Errorf("template provider download message is missing from init output:\n%s", stdout)
t.Logf("(this can happen if you have a copy of the plugin in one of the global plugin search dirs)")
}
if !strings.Contains(stdout, "- Downloading plugin for provider \"null\"") {
t.Errorf("null provider download message is missing from init output:\n%s", stdout)
t.Logf("(this can happen if you have a copy of the plugin in one of the global plugin search dirs)")
}
//// PLAN
stdout, stderr, err = tf.Run("plan", "-out=tfplan")
if err != nil {
t.Fatalf("unexpected plan error: %s\nstderr:\n%s", err, stderr)
}
if !strings.Contains(stdout, "1 to add, 0 to change, 0 to destroy") {
t.Errorf("incorrect plan tally; want 1 to add:\n%s", stdout)
}
plan, err := tf.Plan("tfplan")
if err != nil {
t.Fatalf("failed to read plan file: %s", err)
}
stateResources := plan.State.RootModule().Resources
diffResources := plan.Diff.RootModule().Resources
if len(stateResources) != 1 || stateResources["data.template_file.test"] == nil {
t.Errorf("incorrect state in plan; want just data.template_file.test to have been rendered, but have:\n%s", spew.Sdump(stateResources))
}
if len(diffResources) != 1 || diffResources["null_resource.test"] == nil {
t.Errorf("incorrect diff in plan; want just null_resource.test to have been rendered, but have:\n%s", spew.Sdump(diffResources))
}
//// APPLY
stdout, stderr, err = tf.Run("apply", "tfplan")
if err != nil {
t.Fatalf("unexpected apply error: %s\nstderr:\n%s", err, stderr)
}
if !strings.Contains(stdout, "Resources: 1 added, 0 changed, 0 destroyed") {
t.Errorf("incorrect apply tally; want 1 added:\n%s", stdout)
}
state, err := tf.LocalState()
if err != nil {
t.Fatalf("failed to read state file: %s", err)
}
stateResources = state.RootModule().Resources
var gotResources []string
for n := range stateResources {
gotResources = append(gotResources, n)
}
sort.Strings(gotResources)
wantResources := []string{
"data.template_file.test",
"null_resource.test",
}
if !reflect.DeepEqual(gotResources, wantResources) {
t.Errorf("wrong resources in state\ngot: %#v\nwant: %#v", gotResources, wantResources)
}
//// DESTROY
stdout, stderr, err = tf.Run("destroy", "-force")
if err != nil {
t.Fatalf("unexpected destroy error: %s\nstderr:\n%s", err, stderr)
}
if !strings.Contains(stdout, "Resources: 2 destroyed") {
t.Errorf("incorrect destroy tally; want 2 destroyed:\n%s", stdout)
}
state, err = tf.LocalState()
if err != nil {
t.Fatalf("failed to read state file after destroy: %s", err)
}
stateResources = state.RootModule().Resources
if len(stateResources) != 0 {
t.Errorf("wrong resources in state after destroy; want none, but still have:%s", spew.Sdump(stateResources))
}
}

View File

@ -0,0 +1,22 @@
variable "name" {
default = "world"
}
data "template_file" "test" {
template = "Hello, $${name}"
vars = {
name = "${var.name}"
}
}
resource "null_resource" "test" {
triggers = {
greeting = "${data.template_file.test.rendered}"
}
}
output "greeting" {
value = "${null_resource.test.triggers["greeting"]}"
}