2024-08-15 10:05:27 -04:00
|
|
|
package common
|
2024-05-01 15:36:39 -04:00
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
2024-09-13 10:06:08 -04:00
|
|
|
"log"
|
2024-05-01 15:36:39 -04:00
|
|
|
"os"
|
2024-08-05 10:44:59 -04:00
|
|
|
"os/exec"
|
2024-05-01 15:36:39 -04:00
|
|
|
"path/filepath"
|
|
|
|
|
"runtime"
|
2024-05-15 16:20:35 -04:00
|
|
|
"strings"
|
2024-05-01 15:36:39 -04:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"github.com/hashicorp/go-version"
|
2024-05-03 16:36:23 -04:00
|
|
|
"github.com/hashicorp/packer-plugin-sdk/plugin"
|
2024-08-15 10:05:27 -04:00
|
|
|
"github.com/hashicorp/packer/packer_test/common/check"
|
2024-05-01 15:36:39 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// LDFlags compiles the ldflags for the plugin to compile based on the information provided.
|
2024-05-03 11:49:13 -04:00
|
|
|
func LDFlags(version *version.Version) string {
|
2024-05-01 15:36:39 -04:00
|
|
|
pluginPackage := "github.com/hashicorp/packer-plugin-tester"
|
|
|
|
|
|
2024-05-03 11:49:13 -04:00
|
|
|
ldflagsArg := fmt.Sprintf("-X %s/version.Version=%s", pluginPackage, version.Core())
|
|
|
|
|
if version.Prerelease() != "" {
|
|
|
|
|
ldflagsArg = fmt.Sprintf("%s -X %s/version.VersionPrerelease=%s", ldflagsArg, pluginPackage, version.Prerelease())
|
2024-05-01 15:36:39 -04:00
|
|
|
}
|
2024-05-03 11:49:13 -04:00
|
|
|
if version.Metadata() != "" {
|
|
|
|
|
ldflagsArg = fmt.Sprintf("%s -X %s/version.VersionMetadata=%s", ldflagsArg, pluginPackage, version.Metadata())
|
2024-05-01 15:36:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ldflagsArg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BinaryName is the raw name of the plugin binary to produce
|
|
|
|
|
//
|
|
|
|
|
// It's expected to be in the "mini-plugin_<version>[-<prerelease>][+<metadata>]" format
|
2024-05-03 11:49:13 -04:00
|
|
|
func BinaryName(version *version.Version) string {
|
|
|
|
|
retStr := fmt.Sprintf("mini-plugin_%s", version.Core())
|
|
|
|
|
if version.Prerelease() != "" {
|
|
|
|
|
retStr = fmt.Sprintf("%s-%s", retStr, version.Prerelease())
|
2024-05-01 15:36:39 -04:00
|
|
|
}
|
2024-05-03 11:49:13 -04:00
|
|
|
if version.Metadata() != "" {
|
|
|
|
|
retStr = fmt.Sprintf("%s+%s", retStr, version.Metadata())
|
2024-05-01 15:36:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return retStr
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-03 16:36:23 -04:00
|
|
|
// ExpectedInstalledName is the expected full name of the plugin once installed.
|
|
|
|
|
func ExpectedInstalledName(versionStr string) string {
|
2024-05-15 16:16:55 -04:00
|
|
|
version.Must(version.NewVersion(versionStr))
|
|
|
|
|
|
|
|
|
|
versionStr = strings.ReplaceAll(versionStr, "v", "")
|
2024-05-03 16:36:23 -04:00
|
|
|
|
|
|
|
|
ext := ""
|
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
|
ext = ".exe"
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-15 16:16:55 -04:00
|
|
|
return fmt.Sprintf("packer-plugin-tester_v%s_x%s.%s_%s_%s%s",
|
|
|
|
|
versionStr,
|
2024-05-03 16:36:23 -04:00
|
|
|
plugin.APIVersionMajor,
|
|
|
|
|
plugin.APIVersionMinor,
|
|
|
|
|
runtime.GOOS, runtime.GOARCH, ext)
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-05 17:33:00 -04:00
|
|
|
// BuildCustomisation is a function that allows you to change things on a plugin's
|
|
|
|
|
// local files, with a way to rollback those changes after the fact.
|
|
|
|
|
//
|
|
|
|
|
// The function is meant to take a path parameter to the directory for the plugin,
|
|
|
|
|
// and returns a function that unravels those changes once the build process is done.
|
|
|
|
|
type BuildCustomisation func(string) (error, func())
|
|
|
|
|
|
|
|
|
|
const SDKModule = "github.com/hashicorp/packer-plugin-sdk"
|
|
|
|
|
|
|
|
|
|
// UseDependency invokes go get and go mod tidy to update a package required
|
|
|
|
|
// by the plugin, and use it to build the plugin with that change.
|
|
|
|
|
func UseDependency(remoteModule, ref string) BuildCustomisation {
|
|
|
|
|
return func(path string) (error, func()) {
|
|
|
|
|
modPath := filepath.Join(path, "go.mod")
|
|
|
|
|
|
|
|
|
|
stat, err := os.Stat(modPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("cannot stat mod file %q: %s", modPath, err), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save old go.mod file from dir
|
|
|
|
|
oldGoMod, err := os.ReadFile(modPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to read current mod file %q: %s", modPath, err), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
modSpec := fmt.Sprintf("%s@%s", remoteModule, ref)
|
|
|
|
|
cmd := exec.Command("go", "get", modSpec)
|
|
|
|
|
cmd.Dir = path
|
|
|
|
|
err = cmd.Run()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to run go get %s: %s", modSpec, err), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cmd = exec.Command("go", "mod", "tidy")
|
|
|
|
|
cmd.Dir = path
|
|
|
|
|
err = cmd.Run()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to run go mod tidy: %s", err), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil, func() {
|
|
|
|
|
err = os.WriteFile(modPath, oldGoMod, stat.Mode())
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Fprintf(os.Stderr, "failed to reset modfile %q: %s; manual cleanup may be needed", modPath, err)
|
|
|
|
|
}
|
|
|
|
|
cmd := exec.Command("go", "mod", "tidy")
|
|
|
|
|
cmd.Dir = path
|
|
|
|
|
_ = cmd.Run()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
packer_test: split BuildPackerPlugin in build/get
The interface for building a plugin through the test suite was
confusing, as it would build the plugin and return its path, cache the
path for the version built, and return the path regardless if something
was built or not.
While in the current state this is harmless as builds are idempotent,
since the state of the plugin package/module does not change, this will
in the future as we introduce customisation techniques on the plugin's
directory and files, making this double-use potentially dangerous.
Furthermore, the current behaviour is unclear, as the function hides
that caching mechanism, which could come as a surprise for users
attempting to build a plugin for the duration of a test, while the built
plugin is linked to the test suite being run, and not the unit test
being evaluated.
Therefore this commit changes the sequence in which plugins are built
and used. Now the `CompilePlugin` function builds a plugin, and does not
return its path anymore, instead terminating the tests immediately if
they fail.
In normal test usage, a new `GetPluginPath` function is introduced,
which looks-up the path in the suite's cache, failing immediately if
invoked before the plugin is built.
With this change, it is heavily advised to build plugins when
initialising the suite, then in the tests, the GetPluginPath function
should be used to get a plugin's path for interacting with packer
commands.
2024-08-14 16:12:53 -04:00
|
|
|
// GetPluginPath gets the path for a pre-compiled plugin in the current test suite.
|
2024-08-05 10:44:59 -04:00
|
|
|
//
|
packer_test: split BuildPackerPlugin in build/get
The interface for building a plugin through the test suite was
confusing, as it would build the plugin and return its path, cache the
path for the version built, and return the path regardless if something
was built or not.
While in the current state this is harmless as builds are idempotent,
since the state of the plugin package/module does not change, this will
in the future as we introduce customisation techniques on the plugin's
directory and files, making this double-use potentially dangerous.
Furthermore, the current behaviour is unclear, as the function hides
that caching mechanism, which could come as a surprise for users
attempting to build a plugin for the duration of a test, while the built
plugin is linked to the test suite being run, and not the unit test
being evaluated.
Therefore this commit changes the sequence in which plugins are built
and used. Now the `CompilePlugin` function builds a plugin, and does not
return its path anymore, instead terminating the tests immediately if
they fail.
In normal test usage, a new `GetPluginPath` function is introduced,
which looks-up the path in the suite's cache, failing immediately if
invoked before the plugin is built.
With this change, it is heavily advised to build plugins when
initialising the suite, then in the tests, the GetPluginPath function
should be used to get a plugin's path for interacting with packer
commands.
2024-08-14 16:12:53 -04:00
|
|
|
// The version only is needed, as the path to a compiled version of the tester
|
|
|
|
|
// plugin will be returned, so it can be installed after the fact.
|
|
|
|
|
//
|
|
|
|
|
// If the version requested does not exist, the function will panic.
|
|
|
|
|
func (ts *PackerTestSuite) GetPluginPath(t *testing.T, version string) string {
|
|
|
|
|
path, ok := ts.compiledPlugins.Load(version)
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Fatalf("tester plugin in version %q was not built, either build it during suite init, or with BuildTestPlugin", version)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return path.(string)
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-13 10:06:08 -04:00
|
|
|
type CompilationResult struct {
|
|
|
|
|
Error error
|
|
|
|
|
Version string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ready processes a series of CompilationResults, as returned by CompilePlugin
|
|
|
|
|
//
|
|
|
|
|
// If any of the jobs requested failed, the test will fail also.
|
|
|
|
|
func Ready(t *testing.T, results []chan CompilationResult) {
|
|
|
|
|
for _, res := range results {
|
|
|
|
|
jobErr := <-res
|
|
|
|
|
empty := CompilationResult{}
|
|
|
|
|
if jobErr != empty {
|
|
|
|
|
t.Errorf("failed to compile plugin at version %s: %s", jobErr.Version, jobErr.Error)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if t.Failed() {
|
|
|
|
|
t.Fatalf("some plugins failed to be compiled, see logs for more info")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type compilationJob struct {
|
|
|
|
|
versionString string
|
|
|
|
|
suite *PackerTestSuite
|
|
|
|
|
done bool
|
|
|
|
|
resultCh chan CompilationResult
|
|
|
|
|
customisations []BuildCustomisation
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CompilationJobs keeps a queue of compilation jobs for plugins
|
|
|
|
|
//
|
|
|
|
|
// This approach allows us to avoid conflicts between compilation jobs.
|
|
|
|
|
// Typically building the plugin with different ldflags is safe to perform
|
|
|
|
|
// in parallel on the same file set, however customisations tend to be more
|
|
|
|
|
// conflictual, as two concurrent compilation jobs may end-up compiling the
|
|
|
|
|
// wrong plugin, which may cause some tests to misbehave, or even compilation
|
|
|
|
|
// jobs to fail.
|
|
|
|
|
//
|
|
|
|
|
// The solution to this approach is to have a global queue for every plugin
|
|
|
|
|
// compilation to be performed safely.
|
|
|
|
|
var CompilationJobs = make(chan compilationJob, 10)
|
|
|
|
|
|
packer_test: split BuildPackerPlugin in build/get
The interface for building a plugin through the test suite was
confusing, as it would build the plugin and return its path, cache the
path for the version built, and return the path regardless if something
was built or not.
While in the current state this is harmless as builds are idempotent,
since the state of the plugin package/module does not change, this will
in the future as we introduce customisation techniques on the plugin's
directory and files, making this double-use potentially dangerous.
Furthermore, the current behaviour is unclear, as the function hides
that caching mechanism, which could come as a surprise for users
attempting to build a plugin for the duration of a test, while the built
plugin is linked to the test suite being run, and not the unit test
being evaluated.
Therefore this commit changes the sequence in which plugins are built
and used. Now the `CompilePlugin` function builds a plugin, and does not
return its path anymore, instead terminating the tests immediately if
they fail.
In normal test usage, a new `GetPluginPath` function is introduced,
which looks-up the path in the suite's cache, failing immediately if
invoked before the plugin is built.
With this change, it is heavily advised to build plugins when
initialising the suite, then in the tests, the GetPluginPath function
should be used to get a plugin's path for interacting with packer
commands.
2024-08-14 16:12:53 -04:00
|
|
|
// CompilePlugin builds a tester plugin with the specified version.
|
|
|
|
|
//
|
|
|
|
|
// The plugin's code is contained in a subdirectory of this file, and lets us
|
2024-08-05 10:44:59 -04:00
|
|
|
// change the attributes of the plugin binary itself, like the SDK version,
|
|
|
|
|
// the plugin's version, etc.
|
|
|
|
|
//
|
|
|
|
|
// The plugin is functional, and can be used to run builds with.
|
|
|
|
|
// There won't be anything substantial created though, its goal is only
|
|
|
|
|
// to validate the core functionality of Packer.
|
|
|
|
|
//
|
|
|
|
|
// The path to the plugin is returned, it won't be removed automatically
|
|
|
|
|
// though, deletion is the caller's responsibility.
|
packer_test: split BuildPackerPlugin in build/get
The interface for building a plugin through the test suite was
confusing, as it would build the plugin and return its path, cache the
path for the version built, and return the path regardless if something
was built or not.
While in the current state this is harmless as builds are idempotent,
since the state of the plugin package/module does not change, this will
in the future as we introduce customisation techniques on the plugin's
directory and files, making this double-use potentially dangerous.
Furthermore, the current behaviour is unclear, as the function hides
that caching mechanism, which could come as a surprise for users
attempting to build a plugin for the duration of a test, while the built
plugin is linked to the test suite being run, and not the unit test
being evaluated.
Therefore this commit changes the sequence in which plugins are built
and used. Now the `CompilePlugin` function builds a plugin, and does not
return its path anymore, instead terminating the tests immediately if
they fail.
In normal test usage, a new `GetPluginPath` function is introduced,
which looks-up the path in the suite's cache, failing immediately if
invoked before the plugin is built.
With this change, it is heavily advised to build plugins when
initialising the suite, then in the tests, the GetPluginPath function
should be used to get a plugin's path for interacting with packer
commands.
2024-08-14 16:12:53 -04:00
|
|
|
//
|
|
|
|
|
// Note: each tester plugin may only be compiled once for a specific version in
|
|
|
|
|
// a test suite. The version may include core (mandatory), pre-release and
|
|
|
|
|
// metadata. Unlike Packer core, metadata does matter for the version being built.
|
2024-09-13 10:06:08 -04:00
|
|
|
//
|
|
|
|
|
// Note: the compilation will process asynchronously, and should be waited upon
|
|
|
|
|
// before tests that use this plugin may proceed. Refer to the `Ready` function
|
|
|
|
|
// for doing that.
|
|
|
|
|
func (ts *PackerTestSuite) CompilePlugin(versionString string, customisations ...BuildCustomisation) chan CompilationResult {
|
|
|
|
|
resultCh := make(chan CompilationResult)
|
|
|
|
|
|
|
|
|
|
CompilationJobs <- compilationJob{
|
|
|
|
|
versionString: versionString,
|
|
|
|
|
suite: ts,
|
|
|
|
|
customisations: customisations,
|
|
|
|
|
done: false,
|
|
|
|
|
resultCh: resultCh,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return resultCh
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
// Run a processor coroutine for the duration of the test.
|
|
|
|
|
//
|
|
|
|
|
// It's simpler to have this occurring on the side at all times, without
|
|
|
|
|
// trying to manage its lifecycle based on the current amount of queued
|
|
|
|
|
// tasks, since this is linked to the test lifecycle, and as it's a single
|
|
|
|
|
// coroutine, we can leave it run until the process exits.
|
|
|
|
|
go func() {
|
|
|
|
|
for job := range CompilationJobs {
|
|
|
|
|
log.Printf("compiling plugin on version %s", job.versionString)
|
|
|
|
|
err := compilePlugin(job.suite, job.versionString, job.customisations...)
|
|
|
|
|
if err != nil {
|
|
|
|
|
job.resultCh <- CompilationResult{
|
|
|
|
|
Error: err,
|
|
|
|
|
Version: job.versionString,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
close(job.resultCh)
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// compilePlugin performs the actual compilation procedure for the plugin, and
|
|
|
|
|
// registers it to the test suite instance passed as a parameter.
|
|
|
|
|
func compilePlugin(ts *PackerTestSuite, versionString string, customisations ...BuildCustomisation) error {
|
packer_test: split BuildPackerPlugin in build/get
The interface for building a plugin through the test suite was
confusing, as it would build the plugin and return its path, cache the
path for the version built, and return the path regardless if something
was built or not.
While in the current state this is harmless as builds are idempotent,
since the state of the plugin package/module does not change, this will
in the future as we introduce customisation techniques on the plugin's
directory and files, making this double-use potentially dangerous.
Furthermore, the current behaviour is unclear, as the function hides
that caching mechanism, which could come as a surprise for users
attempting to build a plugin for the duration of a test, while the built
plugin is linked to the test suite being run, and not the unit test
being evaluated.
Therefore this commit changes the sequence in which plugins are built
and used. Now the `CompilePlugin` function builds a plugin, and does not
return its path anymore, instead terminating the tests immediately if
they fail.
In normal test usage, a new `GetPluginPath` function is introduced,
which looks-up the path in the suite's cache, failing immediately if
invoked before the plugin is built.
With this change, it is heavily advised to build plugins when
initialising the suite, then in the tests, the GetPluginPath function
should be used to get a plugin's path for interacting with packer
commands.
2024-08-14 16:12:53 -04:00
|
|
|
// Fail to build plugin if already built.
|
|
|
|
|
//
|
|
|
|
|
// Especially with customisations being a thing, relying on cache to get and
|
|
|
|
|
// build a plugin at once means that the function is not idempotent anymore,
|
|
|
|
|
// and therefore we cannot rely on it being called twice and producing the
|
|
|
|
|
// same result, so we forbid it.
|
|
|
|
|
if _, ok := ts.compiledPlugins.Load(versionString); ok {
|
2024-09-13 10:06:08 -04:00
|
|
|
return fmt.Errorf("plugin version %q was already built, use GetTestPlugin instead", versionString)
|
2024-08-05 10:44:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v := version.Must(version.NewSemver(versionString))
|
|
|
|
|
|
|
|
|
|
testDir, err := currentDir()
|
|
|
|
|
if err != nil {
|
2024-09-13 10:06:08 -04:00
|
|
|
return fmt.Errorf("failed to compile plugin binary: %s", err)
|
2024-08-05 10:44:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
testerPluginDir := filepath.Join(testDir, "plugin_tester")
|
2024-08-05 17:33:00 -04:00
|
|
|
for _, custom := range customisations {
|
|
|
|
|
err, cleanup := custom(testerPluginDir)
|
|
|
|
|
if err != nil {
|
2024-09-13 10:06:08 -04:00
|
|
|
return fmt.Errorf("failed to prepare plugin workdir: %s", err)
|
2024-08-05 17:33:00 -04:00
|
|
|
}
|
|
|
|
|
defer cleanup()
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-05 10:44:59 -04:00
|
|
|
outBin := filepath.Join(ts.pluginsDirectory, BinaryName(v))
|
|
|
|
|
|
|
|
|
|
compileCommand := exec.Command("go", "build", "-C", testerPluginDir, "-o", outBin, "-ldflags", LDFlags(v), ".")
|
|
|
|
|
logs, err := compileCommand.CombinedOutput()
|
|
|
|
|
if err != nil {
|
2024-09-13 10:06:08 -04:00
|
|
|
return fmt.Errorf("failed to compile plugin binary: %s\ncompiler logs: %s", err, logs)
|
2024-08-05 10:44:59 -04:00
|
|
|
}
|
|
|
|
|
|
2024-08-14 16:04:21 -04:00
|
|
|
ts.compiledPlugins.Store(v.String(), outBin)
|
2024-09-13 10:06:08 -04:00
|
|
|
return nil
|
2024-08-05 10:44:59 -04:00
|
|
|
}
|
|
|
|
|
|
2024-08-15 14:09:33 -04:00
|
|
|
type PluginDirSpec struct {
|
|
|
|
|
dirPath string
|
|
|
|
|
suite *PackerTestSuite
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-01 15:36:39 -04:00
|
|
|
// MakePluginDir installs a list of plugins into a temporary directory and returns its path
|
|
|
|
|
//
|
|
|
|
|
// This can be set in the environment for a test through a function like t.SetEnv(), so
|
|
|
|
|
// packer will be able to use that directory for running its functions.
|
|
|
|
|
//
|
|
|
|
|
// Deletion of the directory is the caller's responsibility.
|
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
|
|
|
//
|
|
|
|
|
// Note: all of the plugin versions specified to be installed in this plugin directory
|
|
|
|
|
// must have been compiled beforehand.
|
2024-08-15 14:09:33 -04:00
|
|
|
func (ts *PackerTestSuite) MakePluginDir() *PluginDirSpec {
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
pluginTempDir, err := os.MkdirTemp("", "packer-plugin-dir-temp-")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2024-05-03 11:54:30 -04:00
|
|
|
|
2024-08-15 14:09:33 -04:00
|
|
|
return &PluginDirSpec{
|
|
|
|
|
dirPath: pluginTempDir,
|
|
|
|
|
suite: ts,
|
2024-05-03 15:35:14 -04:00
|
|
|
}
|
2024-08-15 14:09:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// InstallPluginVersions installs several versions of the tester plugin under
|
|
|
|
|
// github.com/hashicorp/tester.
|
|
|
|
|
//
|
|
|
|
|
// Each version of the plugin needs to have been pre-compiled.
|
|
|
|
|
//
|
|
|
|
|
// If a plugin is missing, the temporary directory will be removed.
|
|
|
|
|
func (ps *PluginDirSpec) InstallPluginVersions(pluginVersions ...string) *PluginDirSpec {
|
|
|
|
|
t := ps.suite.T()
|
2024-05-03 15:35:14 -04:00
|
|
|
|
2024-05-01 15:36:39 -04:00
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
defer func() {
|
2024-08-15 14:09:33 -04:00
|
|
|
if err != nil || t.Failed() {
|
|
|
|
|
rmErr := os.RemoveAll(ps.Dir())
|
|
|
|
|
if rmErr != nil {
|
|
|
|
|
t.Logf("failed to remove temporary plugin directory %q: %s. This may need manual intervention.", ps.Dir(), err)
|
2024-05-01 15:36:39 -04:00
|
|
|
}
|
2024-08-15 14:09:33 -04:00
|
|
|
t.Fatalf("failed to install plugins to temporary plugin directory %q: %s", ps.Dir(), err)
|
2024-05-01 15:36:39 -04:00
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
for _, pluginVersion := range pluginVersions {
|
2024-08-15 14:09:33 -04:00
|
|
|
path := ps.suite.GetPluginPath(t, pluginVersion)
|
|
|
|
|
cmd := ps.suite.PackerCommand().SetArgs("plugins", "install", "--path", path, "github.com/hashicorp/tester").AddEnv("PACKER_PLUGIN_PATH", ps.Dir())
|
2024-08-15 10:05:27 -04:00
|
|
|
cmd.Assert(check.MustSucceed())
|
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
|
|
|
out, stderr, cmdErr := cmd.run()
|
2024-05-01 15:36:39 -04:00
|
|
|
if cmdErr != nil {
|
|
|
|
|
err = fmt.Errorf("failed to install tester plugin version %q: %s\nCommand stdout: %s\nCommand stderr: %s", pluginVersion, err, out, stderr)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-15 14:09:33 -04:00
|
|
|
return ps
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Dir returns the temporary plugin dir for use in other functions
|
|
|
|
|
func (ps PluginDirSpec) Dir() string {
|
|
|
|
|
return ps.dirPath
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ps *PluginDirSpec) Cleanup() {
|
|
|
|
|
pluginDir := ps.Dir()
|
|
|
|
|
if pluginDir == "" {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := os.RemoveAll(pluginDir)
|
|
|
|
|
if err != nil {
|
|
|
|
|
ps.suite.T().Logf("failed to remove temporary plugin directory %q: %s. This may need manual intervention.", pluginDir, err)
|
2024-05-03 11:07:12 -04:00
|
|
|
}
|
2024-05-01 15:36:39 -04:00
|
|
|
}
|
2024-05-03 11:30:15 -04:00
|
|
|
|
2024-05-15 16:20:35 -04:00
|
|
|
// ManualPluginInstall emulates how Packer installs plugins with `packer plugins install`
|
|
|
|
|
//
|
|
|
|
|
// This is used for some tests if we want to install a plugin that cannot be installed
|
|
|
|
|
// through the normal commands (typically because Packer rejects it).
|
|
|
|
|
func ManualPluginInstall(t *testing.T, dest, srcPlugin, versionStr string) {
|
|
|
|
|
err := os.MkdirAll(dest, 0755)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("failed to create destination directories %q: %s", dest, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pluginName := ExpectedInstalledName(versionStr)
|
|
|
|
|
destPath := filepath.Join(dest, pluginName)
|
|
|
|
|
|
|
|
|
|
CopyFile(t, destPath, srcPlugin)
|
|
|
|
|
|
2024-05-15 18:05:03 -04:00
|
|
|
shaPath := fmt.Sprintf("%s_SHA256SUM", destPath)
|
2024-05-15 16:20:35 -04:00
|
|
|
WriteFile(t, shaPath, SHA256Sum(t, destPath))
|
|
|
|
|
}
|