2024-08-15 10:05:27 -04:00
|
|
|
package common
|
2024-05-01 15:36:39 -04:00
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
2024-05-15 17:34:15 -04:00
|
|
|
"os"
|
2024-05-01 15:36:39 -04:00
|
|
|
"os/exec"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
2024-08-15 10:05:27 -04:00
|
|
|
|
|
|
|
|
"github.com/hashicorp/packer/packer_test/common/check"
|
2024-05-01 15:36:39 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type packerCommand struct {
|
packer_test: add func to change assert behaviour
When a command asserts its output with checkers, by default it will
register errors through a t.Errorf.
While this works, in some cases we would want to stop execution
immediately if a function's Assert fails, as the rest of the test may
depend on the assertion being valid.
In the current state, this means either getting the result of the run to
check if an error was returned (not fully reliable as if the command was
run multiple times, and the last run succeeded, we won't get an error),
or relying on t.IsFailed() (completely reliable).
Instead, we introduce a new function on packerCommand, that lets users
change how Assert behaves, so that if an error was reported, instead of
logging the error and flagging the test as failed, we can use t.Fatalf,
so that the test immedately fails and stops execution.
2024-08-15 10:21:35 -04:00
|
|
|
runs int
|
|
|
|
|
packerPath string
|
|
|
|
|
args []string
|
|
|
|
|
env map[string]string
|
|
|
|
|
stdin string
|
|
|
|
|
stderr *strings.Builder
|
|
|
|
|
stdout *strings.Builder
|
|
|
|
|
workdir string
|
|
|
|
|
err error
|
|
|
|
|
t *testing.T
|
|
|
|
|
fatalfAssert bool
|
2024-05-01 15:36:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PackerCommand creates a skeleton of packer command with the ability to execute gadgets on the outputs of the command.
|
|
|
|
|
func (ts *PackerTestSuite) PackerCommand() *packerCommand {
|
|
|
|
|
return &packerCommand{
|
|
|
|
|
packerPath: ts.packerPath,
|
2024-06-11 16:30:37 -04:00
|
|
|
runs: 1,
|
2024-05-01 15:36:39 -04:00
|
|
|
env: map[string]string{
|
|
|
|
|
"PACKER_LOG": "1",
|
2024-05-15 17:34:15 -04:00
|
|
|
// Required for Windows, otherwise since we overwrite all
|
|
|
|
|
// the envvars for the test and Go relies on that envvar
|
|
|
|
|
// being set in order to return another path than
|
|
|
|
|
// C:\Windows by default
|
|
|
|
|
//
|
|
|
|
|
// If we don't have it, Packer immediately errors upon
|
|
|
|
|
// invocation as the temporary logfile that we write in
|
|
|
|
|
// case of Panic will fail to be created (unless tests
|
|
|
|
|
// are running as Administrator, but please don't).
|
|
|
|
|
"TMP": os.TempDir(),
|
2024-06-13 10:49:03 -04:00
|
|
|
// Since those commands are used to run tests, we want to
|
|
|
|
|
// make them as self-contained and quick as possible.
|
|
|
|
|
// Removing telemetry here is probably for the best.
|
|
|
|
|
"CHECKPOINT_DISABLE": "1",
|
2024-05-01 15:36:39 -04:00
|
|
|
},
|
2024-06-11 16:30:37 -04:00
|
|
|
t: ts.T(),
|
2024-05-01 15:36:39 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-06 16:45:45 -04:00
|
|
|
// NoVerbose removes the `PACKER_LOG=1` environment variable from the command
|
|
|
|
|
func (pc *packerCommand) NoVerbose() *packerCommand {
|
|
|
|
|
_, ok := pc.env["PACKER_LOG"]
|
|
|
|
|
if ok {
|
|
|
|
|
delete(pc.env, "PACKER_LOG")
|
|
|
|
|
}
|
|
|
|
|
return pc
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-07 10:58:43 -04:00
|
|
|
// SetWD changes the directory Packer is invoked from
|
|
|
|
|
func (pc *packerCommand) SetWD(dir string) *packerCommand {
|
|
|
|
|
pc.workdir = dir
|
|
|
|
|
return pc
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-03 15:23:30 -04:00
|
|
|
// UsePluginDir sets the plugin directory in the environment to `dir`
|
2024-08-15 14:09:33 -04:00
|
|
|
func (pc *packerCommand) UsePluginDir(dir *PluginDirSpec) *packerCommand {
|
|
|
|
|
return pc.UseRawPluginDir(dir.dirPath)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UseRawPluginDir is meant to be used for setting the plugin directory with a
|
|
|
|
|
// raw directory path instead of a PluginDirSpec.
|
|
|
|
|
func (pc *packerCommand) UseRawPluginDir(dirPath string) *packerCommand {
|
|
|
|
|
return pc.AddEnv("PACKER_PLUGIN_PATH", dirPath)
|
2024-05-03 15:23:30 -04:00
|
|
|
}
|
|
|
|
|
|
2024-05-01 15:36:39 -04:00
|
|
|
func (pc *packerCommand) SetArgs(args ...string) *packerCommand {
|
|
|
|
|
pc.args = args
|
|
|
|
|
return pc
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (pc *packerCommand) AddEnv(key, val string) *packerCommand {
|
|
|
|
|
pc.env[key] = val
|
|
|
|
|
return pc
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-11 16:30:37 -04:00
|
|
|
// Runs changes the number of times the command is run.
|
|
|
|
|
//
|
|
|
|
|
// This is useful for testing non-deterministic bugs, which we can reasonably
|
|
|
|
|
// execute multiple times and expose a dysfunctional run.
|
|
|
|
|
//
|
|
|
|
|
// This is not necessarily a guarantee that the code is sound, but so long as
|
|
|
|
|
// we run the test enough times, we can be decently confident the problem has
|
|
|
|
|
// been solved.
|
|
|
|
|
func (pc *packerCommand) Runs(runs int) *packerCommand {
|
|
|
|
|
if runs <= 0 {
|
|
|
|
|
panic(fmt.Sprintf("cannot set command runs to %d", runs))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pc.runs = runs
|
|
|
|
|
return pc
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-12 09:59:35 -04:00
|
|
|
// Stdin changes the contents of the stdin for the command.
|
|
|
|
|
//
|
|
|
|
|
// Each run will be populated with a copy of this string, and wait for the
|
|
|
|
|
// command to terminate.
|
|
|
|
|
//
|
|
|
|
|
// Note: this could lead to a deadlock if the command doesn't support stdin
|
|
|
|
|
// closing after it's finished feeding the inputs.
|
|
|
|
|
func (pc *packerCommand) Stdin(in string) *packerCommand {
|
|
|
|
|
pc.stdin = in
|
|
|
|
|
return pc
|
|
|
|
|
}
|
|
|
|
|
|
packer_test: add func to change assert behaviour
When a command asserts its output with checkers, by default it will
register errors through a t.Errorf.
While this works, in some cases we would want to stop execution
immediately if a function's Assert fails, as the rest of the test may
depend on the assertion being valid.
In the current state, this means either getting the result of the run to
check if an error was returned (not fully reliable as if the command was
run multiple times, and the last run succeeded, we won't get an error),
or relying on t.IsFailed() (completely reliable).
Instead, we introduce a new function on packerCommand, that lets users
change how Assert behaves, so that if an error was reported, instead of
logging the error and flagging the test as failed, we can use t.Fatalf,
so that the test immedately fails and stops execution.
2024-08-15 10:21:35 -04:00
|
|
|
// SetAssertFatal allows changing how Assert behaves when reporting an error.
|
|
|
|
|
//
|
|
|
|
|
// By default Assert will invoke t.Errorf with the error details, but this can be
|
|
|
|
|
// changed to a t.Fatalf so that if the assertion fails, the test invoking it will
|
|
|
|
|
// also immediately fail and stop execution.
|
|
|
|
|
func (pc *packerCommand) SetAssertFatal() *packerCommand {
|
|
|
|
|
pc.fatalfAssert = true
|
|
|
|
|
return pc
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-01 15:36:39 -04:00
|
|
|
// Run executes the packer command with the args/env requested and returns the
|
|
|
|
|
// output streams (stdout, stderr)
|
|
|
|
|
//
|
2024-06-11 16:30:37 -04:00
|
|
|
// Note: while originally "Run" was designed to be idempotent, with the
|
|
|
|
|
// introduction of multiple runs for a command, this is not the case anymore
|
|
|
|
|
// and the function should not be considered thread-safe anymore.
|
packer_test: hide run and introduce Output
When using a PackerCommand, the Run function was made public as a way to
access the contents of an execution.
This was clumsy as it had too many responsabilities, and was not needed
strictly as Assert was performing the executions, as many times as
required.
This could introduce cases in which one run as spent by the caller, then
the remainder were executed through Assert.
Therefore, we change this convention.
Now, run is private to the type, and only through Assert can a command
be executed.
If a test needs access to a command's output, stderr, or error, it can
do so through the Output function, which requires Assert to be called
first.
2024-08-15 10:25:43 -04:00
|
|
|
func (pc *packerCommand) run() (string, string, error) {
|
2024-06-11 16:30:37 -04:00
|
|
|
if pc.runs <= 0 {
|
|
|
|
|
return pc.stdout.String(), pc.stderr.String(), pc.err
|
|
|
|
|
}
|
|
|
|
|
pc.runs--
|
2024-05-01 15:36:39 -04:00
|
|
|
|
2024-06-11 16:30:37 -04:00
|
|
|
pc.stdout = &strings.Builder{}
|
|
|
|
|
pc.stderr = &strings.Builder{}
|
2024-05-01 15:36:39 -04:00
|
|
|
|
2024-05-07 15:56:19 -04:00
|
|
|
cmd := exec.Command(pc.packerPath, pc.args...)
|
2024-05-01 15:36:39 -04:00
|
|
|
for key, val := range pc.env {
|
|
|
|
|
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, val))
|
|
|
|
|
}
|
|
|
|
|
cmd.Stdout = pc.stdout
|
|
|
|
|
cmd.Stderr = pc.stderr
|
|
|
|
|
|
2024-06-12 09:59:35 -04:00
|
|
|
if pc.stdin != "" {
|
|
|
|
|
cmd.Stdin = strings.NewReader(pc.stdin)
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-07 10:58:43 -04:00
|
|
|
if pc.workdir != "" {
|
|
|
|
|
cmd.Dir = pc.workdir
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-01 15:36:39 -04:00
|
|
|
pc.err = cmd.Run()
|
2024-06-11 16:30:37 -04:00
|
|
|
|
packer_test: check for a panic during execution
When a command is run, it is the expectation that no test should make
Packer panic. If it did, something is wrong and Packer should be fixed
so it doesn't panic anymore in that situation.
The way we did the check before was adding a PanicCheck after the
command ran, so we could make sure of that during `Assert`.
However, since we introduced the possibility to have multiple runs,
having this addition as part of the run loop meant that the PanicCheck
would be run as many times as there were runs.
While this worked, this implied that we'd do the same check multiple
times on a single command output, which is not optimal.
Instead, this commit moves the check to within the `Run` function, this
way for each run of the command we do the check once, and then we can
assert the results of the command on what output it produced.
2024-06-17 12:19:35 -04:00
|
|
|
// Check that the command didn't panic, and if it did, we can immediately error
|
2024-08-15 10:05:27 -04:00
|
|
|
panicErr := check.PanicCheck{}.Check(pc.stdout.String(), pc.stderr.String(), pc.err)
|
packer_test: check for a panic during execution
When a command is run, it is the expectation that no test should make
Packer panic. If it did, something is wrong and Packer should be fixed
so it doesn't panic anymore in that situation.
The way we did the check before was adding a PanicCheck after the
command ran, so we could make sure of that during `Assert`.
However, since we introduced the possibility to have multiple runs,
having this addition as part of the run loop meant that the PanicCheck
would be run as many times as there were runs.
While this worked, this implied that we'd do the same check multiple
times on a single command output, which is not optimal.
Instead, this commit moves the check to within the `Run` function, this
way for each run of the command we do the check once, and then we can
assert the results of the command on what output it produced.
2024-06-17 12:19:35 -04:00
|
|
|
if panicErr != nil {
|
|
|
|
|
pc.t.Fatalf("Packer panicked during execution: %s", panicErr)
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-11 16:30:37 -04:00
|
|
|
return pc.stdout.String(), pc.stderr.String(), pc.err
|
2024-05-01 15:36:39 -04:00
|
|
|
}
|
|
|
|
|
|
packer_test: hide run and introduce Output
When using a PackerCommand, the Run function was made public as a way to
access the contents of an execution.
This was clumsy as it had too many responsabilities, and was not needed
strictly as Assert was performing the executions, as many times as
required.
This could introduce cases in which one run as spent by the caller, then
the remainder were executed through Assert.
Therefore, we change this convention.
Now, run is private to the type, and only through Assert can a command
be executed.
If a test needs access to a command's output, stderr, or error, it can
do so through the Output function, which requires Assert to be called
first.
2024-08-15 10:25:43 -04:00
|
|
|
// Output returns the results of the latest Run that was executed.
|
|
|
|
|
//
|
|
|
|
|
// In general there is only one run of the command, but as it can be changed
|
|
|
|
|
// through the Runs function, only the latest run will be returned.
|
|
|
|
|
//
|
|
|
|
|
// If the command was not run (through Assert), this will make the test fail
|
|
|
|
|
// immediately.
|
|
|
|
|
func (pc *packerCommand) Output() (string, string, error) {
|
|
|
|
|
if pc.runs > 0 {
|
|
|
|
|
pc.t.Fatalf("command was not run, invoke Assert first, then Output.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pc.stdout.String(), pc.stderr.String(), pc.err
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-15 10:05:27 -04:00
|
|
|
func (pc *packerCommand) Assert(checks ...check.Checker) {
|
2024-06-11 16:30:37 -04:00
|
|
|
attempt := 0
|
|
|
|
|
for pc.runs > 0 {
|
|
|
|
|
attempt++
|
packer_test: hide run and introduce Output
When using a PackerCommand, the Run function was made public as a way to
access the contents of an execution.
This was clumsy as it had too many responsabilities, and was not needed
strictly as Assert was performing the executions, as many times as
required.
This could introduce cases in which one run as spent by the caller, then
the remainder were executed through Assert.
Therefore, we change this convention.
Now, run is private to the type, and only through Assert can a command
be executed.
If a test needs access to a command's output, stderr, or error, it can
do so through the Output function, which requires Assert to be called
first.
2024-08-15 10:25:43 -04:00
|
|
|
stdout, stderr, err := pc.run()
|
2024-06-11 16:30:37 -04:00
|
|
|
|
2024-08-15 10:05:27 -04:00
|
|
|
for _, checker := range checks {
|
|
|
|
|
checkErr := checker.Check(stdout, stderr, err)
|
2024-06-11 16:30:37 -04:00
|
|
|
if checkErr != nil {
|
2024-08-15 10:05:27 -04:00
|
|
|
checkerName := check.InferName(checker)
|
packer_test: add func to change assert behaviour
When a command asserts its output with checkers, by default it will
register errors through a t.Errorf.
While this works, in some cases we would want to stop execution
immediately if a function's Assert fails, as the rest of the test may
depend on the assertion being valid.
In the current state, this means either getting the result of the run to
check if an error was returned (not fully reliable as if the command was
run multiple times, and the last run succeeded, we won't get an error),
or relying on t.IsFailed() (completely reliable).
Instead, we introduce a new function on packerCommand, that lets users
change how Assert behaves, so that if an error was reported, instead of
logging the error and flagging the test as failed, we can use t.Fatalf,
so that the test immedately fails and stops execution.
2024-08-15 10:21:35 -04:00
|
|
|
|
2024-06-11 16:30:37 -04:00
|
|
|
pc.t.Errorf("check %q failed: %s", checkerName, checkErr)
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-01 15:36:39 -04:00
|
|
|
|
2024-06-11 16:30:37 -04:00
|
|
|
if pc.t.Failed() {
|
|
|
|
|
pc.t.Errorf("attempt %d failed validation", attempt)
|
2024-06-12 11:27:41 -04:00
|
|
|
|
|
|
|
|
pc.t.Logf("dumping stdout: %s", stdout)
|
|
|
|
|
pc.t.Logf("dumping stdout: %s", stderr)
|
|
|
|
|
|
packer_test: add func to change assert behaviour
When a command asserts its output with checkers, by default it will
register errors through a t.Errorf.
While this works, in some cases we would want to stop execution
immediately if a function's Assert fails, as the rest of the test may
depend on the assertion being valid.
In the current state, this means either getting the result of the run to
check if an error was returned (not fully reliable as if the command was
run multiple times, and the last run succeeded, we won't get an error),
or relying on t.IsFailed() (completely reliable).
Instead, we introduce a new function on packerCommand, that lets users
change how Assert behaves, so that if an error was reported, instead of
logging the error and flagging the test as failed, we can use t.Fatalf,
so that the test immedately fails and stops execution.
2024-08-15 10:21:35 -04:00
|
|
|
if pc.fatalfAssert {
|
|
|
|
|
pc.t.Fatalf("stopping test now because of failures reported")
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-11 16:30:37 -04:00
|
|
|
break
|
2024-05-01 15:36:39 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|