github.com/HashDataInc/packer@v1.3.2/helper/builder/testing/testing.go (about)

     1  package testing
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"log"
     7  	"os"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/hashicorp/packer/packer"
    12  	"github.com/hashicorp/packer/template"
    13  )
    14  
    15  // TestEnvVar must be set to a non-empty value for acceptance tests to run.
    16  const TestEnvVar = "PACKER_ACC"
    17  
    18  // TestCase is a single set of tests to run for a backend. A TestCase
    19  // should generally map 1:1 to each test method for your acceptance
    20  // tests.
    21  type TestCase struct {
    22  	// Precheck, if non-nil, will be called once before the test case
    23  	// runs at all. This can be used for some validation prior to the
    24  	// test running.
    25  	PreCheck func()
    26  
    27  	// Builder is the Builder that will be tested. It will be available
    28  	// as the "test" builder in the template.
    29  	Builder packer.Builder
    30  
    31  	// Template is the template contents to use.
    32  	Template string
    33  
    34  	// Check is called after this step is executed in order to test that
    35  	// the step executed successfully. If this is not set, then the next
    36  	// step will be called
    37  	Check TestCheckFunc
    38  
    39  	// Teardown will be called before the test case is over regardless
    40  	// of if the test succeeded or failed. This should return an error
    41  	// in the case that the test can't guarantee all resources were
    42  	// properly cleaned up.
    43  	Teardown TestTeardownFunc
    44  
    45  	// If SkipArtifactTeardown is true, we will not attempt to destroy the
    46  	// artifact created in this test run.
    47  	SkipArtifactTeardown bool
    48  }
    49  
    50  // TestCheckFunc is the callback used for Check in TestStep.
    51  type TestCheckFunc func([]packer.Artifact) error
    52  
    53  // TestTeardownFunc is the callback used for Teardown in TestCase.
    54  type TestTeardownFunc func() error
    55  
    56  // TestT is the interface used to handle the test lifecycle of a test.
    57  //
    58  // Users should just use a *testing.T object, which implements this.
    59  type TestT interface {
    60  	Error(args ...interface{})
    61  	Fatal(args ...interface{})
    62  	Skip(args ...interface{})
    63  }
    64  
    65  // Test performs an acceptance test on a backend with the given test case.
    66  //
    67  // Tests are not run unless an environmental variable "PACKER_ACC" is
    68  // set to some non-empty value. This is to avoid test cases surprising
    69  // a user by creating real resources.
    70  //
    71  // Tests will fail unless the verbose flag (`go test -v`, or explicitly
    72  // the "-test.v" flag) is set. Because some acceptance tests take quite
    73  // long, we require the verbose flag so users are able to see progress
    74  // output.
    75  func Test(t TestT, c TestCase) {
    76  	// We only run acceptance tests if an env var is set because they're
    77  	// slow and generally require some outside configuration.
    78  	if os.Getenv(TestEnvVar) == "" {
    79  		t.Skip(fmt.Sprintf(
    80  			"Acceptance tests skipped unless env '%s' set",
    81  			TestEnvVar))
    82  		return
    83  	}
    84  
    85  	// We require verbose mode so that the user knows what is going on.
    86  	if !testTesting && !testing.Verbose() {
    87  		t.Fatal("Acceptance tests must be run with the -v flag on tests")
    88  		return
    89  	}
    90  
    91  	// Run the PreCheck if we have it
    92  	if c.PreCheck != nil {
    93  		c.PreCheck()
    94  	}
    95  
    96  	// Parse the template
    97  	log.Printf("[DEBUG] Parsing template...")
    98  	tpl, err := template.Parse(strings.NewReader(c.Template))
    99  	if err != nil {
   100  		t.Fatal(fmt.Sprintf("Failed to parse template: %s", err))
   101  		return
   102  	}
   103  
   104  	// Build the core
   105  	log.Printf("[DEBUG] Initializing core...")
   106  	core, err := packer.NewCore(&packer.CoreConfig{
   107  		Components: packer.ComponentFinder{
   108  			Builder: func(n string) (packer.Builder, error) {
   109  				if n == "test" {
   110  					return c.Builder, nil
   111  				}
   112  
   113  				return nil, nil
   114  			},
   115  		},
   116  		Template: tpl,
   117  	})
   118  	if err != nil {
   119  		t.Fatal(fmt.Sprintf("Failed to init core: %s", err))
   120  		return
   121  	}
   122  
   123  	// Get the build
   124  	log.Printf("[DEBUG] Retrieving 'test' build")
   125  	build, err := core.Build("test")
   126  	if err != nil {
   127  		t.Fatal(fmt.Sprintf("Failed to get 'test' build: %s", err))
   128  		return
   129  	}
   130  
   131  	// Prepare it
   132  	log.Printf("[DEBUG] Preparing 'test' build")
   133  	warnings, err := build.Prepare()
   134  	if err != nil {
   135  		t.Fatal(fmt.Sprintf("Prepare error: %s", err))
   136  		return
   137  	}
   138  	if len(warnings) > 0 {
   139  		t.Fatal(fmt.Sprintf(
   140  			"Prepare warnings:\n\n%s",
   141  			strings.Join(warnings, "\n")))
   142  		return
   143  	}
   144  
   145  	// Run it! We use a temporary directory for caching and discard
   146  	// any UI output. We discard since it shows up in logs anyways.
   147  	log.Printf("[DEBUG] Running 'test' build")
   148  	cache := &packer.FileCache{CacheDir: os.TempDir()}
   149  	ui := &packer.BasicUi{
   150  		Reader:      os.Stdin,
   151  		Writer:      ioutil.Discard,
   152  		ErrorWriter: ioutil.Discard,
   153  	}
   154  	artifacts, err := build.Run(ui, cache)
   155  	if err != nil {
   156  		t.Fatal(fmt.Sprintf("Run error:\n\n%s", err))
   157  		goto TEARDOWN
   158  	}
   159  
   160  	// Check function
   161  	if c.Check != nil {
   162  		log.Printf("[DEBUG] Running check function")
   163  		if err := c.Check(artifacts); err != nil {
   164  			t.Fatal(fmt.Sprintf("Check error:\n\n%s", err))
   165  			goto TEARDOWN
   166  		}
   167  	}
   168  
   169  TEARDOWN:
   170  	if !c.SkipArtifactTeardown {
   171  		// Delete all artifacts
   172  		for _, a := range artifacts {
   173  			if err := a.Destroy(); err != nil {
   174  				t.Error(fmt.Sprintf(
   175  					"!!! ERROR REMOVING ARTIFACT '%s': %s !!!",
   176  					a.String(), err))
   177  			}
   178  		}
   179  	}
   180  
   181  	// Teardown
   182  	if c.Teardown != nil {
   183  		log.Printf("[DEBUG] Running teardown function")
   184  		if err := c.Teardown(); err != nil {
   185  			t.Fatal(fmt.Sprintf("Teardown failure:\n\n%s", err))
   186  			return
   187  		}
   188  	}
   189  }
   190  
   191  // This is for unit tests of this package.
   192  var testTesting = false