/
pluginacc.go
163 lines (145 loc) · 5.24 KB
/
pluginacc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package acctest
import (
"bytes"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"testing"
)
// TestEnvVar must be set to a non-empty value for acceptance tests to run.
const TestEnvVar = "PACKER_ACC"
// PluginTestCase is a single set of tests to run for a plugin.
// A PluginTestCase should generally map 1:1 to each test method for your
// acceptance tests.
// Requirements:
// - If not using 'packer init', the plugin must be previously installed
// - Packer must be installed locally
type PluginTestCase struct {
// Init, if true `packer init` will be executed prior to `packer build`.
Init bool
// BuildExtraArgs is the list of arguments to be passed as extra to the packer build
// command.
// These arguments are injected before the already present `--machine-readable` option.
BuildExtraArgs []string
// CheckInit is called after packer init step is executed in order to test that
// the step executed successfully. If this is not set, then the next
// step will be called
CheckInit func(*exec.Cmd, string) error
// Check is called after this step is executed in order to test that
// the step executed successfully. If this is not set, then the next
// step will be called
Check func(*exec.Cmd, string) error
// Name is the name of the test case. Be simple but unique and descriptive.
Name string
// Setup, if non-nil, will be called once before the test case
// runs. This can be used for some setup like setting environment
// variables, or for validation prior to the
// test running. For example, you can use this to make sure certain
// binaries are installed, or text fixtures are in place.
Setup func() error
// Teardown will be called before the test case is over regardless
// of if the test succeeded or failed. This should return an error
// in the case that the test can't guarantee all resources were
// properly cleaned up.
Teardown TestTeardownFunc
// Template is the testing HCL2 template to use.
Template string
// Type is the type of the plugin.
Type string
}
// TestTeardownFunc is the callback used for Teardown in TestCase.
type TestTeardownFunc func() error
//nolint:errcheck
func TestPlugin(t *testing.T, testCase *PluginTestCase) {
if os.Getenv(TestEnvVar) == "" {
t.Skipf("Acceptance tests skipped unless env '%s' set", TestEnvVar)
return
}
if testCase.Setup != nil {
err := testCase.Setup()
if err != nil {
t.Fatalf("test %s setup failed: %s", testCase.Name, err)
}
}
logfile := fmt.Sprintf("packer_log_%s.txt", testCase.Name)
extension := ".pkr.hcl"
if err := json.Unmarshal([]byte(testCase.Template), &(map[string]interface{}{})); err == nil {
extension = ".json"
}
templatePath := fmt.Sprintf("./%s%s", testCase.Name, extension)
// Write config hcl2 template
out := bytes.NewBuffer(nil)
fmt.Fprintf(out, testCase.Template)
outputFile, err := os.Create(templatePath)
if err != nil {
t.Fatalf("bad: failed to create template file: %s", err.Error())
}
_, err = outputFile.Write(out.Bytes())
if err != nil {
t.Fatalf("bad: failed to write template file: %s", err.Error())
}
outputFile.Sync()
// Make sure packer is installed:
packerbin, err := exec.LookPath("packer")
if err != nil {
t.Fatalf("Couldn't find packer binary installed on system: %s", err.Error())
}
if testCase.Init {
initLogfile := fmt.Sprintf("packer_init_log_%s.txt", testCase.Name)
initCommand := exec.Command(packerbin, "init", templatePath)
initCommand.Env = append(initCommand.Env, os.Environ()...)
initCommand.Env = append(initCommand.Env, "PACKER_LOG=1", fmt.Sprintf("PACKER_LOG_PATH=%s", initLogfile))
initCommand.Run()
if testCase.CheckInit != nil {
if err := testCase.CheckInit(initCommand, initLogfile); err != nil {
cwd, _ := os.Getwd()
t.Fatalf(fmt.Sprintf("Error running plugin acceptance"+
" tests: %s\nLogs can be found at %s\nand the "+
"acceptance test template can be found at %s",
err.Error(), filepath.Join(cwd, initLogfile),
filepath.Join(cwd, templatePath)))
} else {
os.Remove(initLogfile)
}
}
}
buildArgs := []string{"build"}
for _, arg := range testCase.BuildExtraArgs {
buildArgs = append(buildArgs, arg)
}
buildArgs = append(buildArgs, "--machine-readable", templatePath)
// Run build
buildCommand := exec.Command(packerbin, buildArgs...)
buildCommand.Env = append(buildCommand.Env, os.Environ()...)
buildCommand.Env = append(buildCommand.Env, "PACKER_LOG=1",
fmt.Sprintf("PACKER_LOG_PATH=%s", logfile))
buildCommand.Run()
// Check for test custom pass/fail before we clean up
var checkErr error
if testCase.Check != nil {
checkErr = testCase.Check(buildCommand, logfile)
}
// Clean up anything created in the plugin run
if testCase.Teardown != nil {
cleanErr := testCase.Teardown()
if cleanErr != nil {
t.Logf("bad: failed to clean up test-created resources: %s", cleanErr.Error())
}
}
// Fail test if check failed.
if checkErr != nil {
cwd, _ := os.Getwd()
t.Fatalf(fmt.Sprintf("Error running plugin acceptance"+
" tests: %s\nLogs can be found at %s\nand the "+
"acceptance test template can be found at %s",
checkErr.Error(), filepath.Join(cwd, logfile),
filepath.Join(cwd, templatePath)))
} else {
os.Remove(templatePath)
os.Remove(logfile)
}
}