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