diff --git a/packer_test/common/plugin.go b/packer_test/common/plugin.go index 2e0a3b5bebc..90d29647abf 100644 --- a/packer_test/common/plugin.go +++ b/packer_test/common/plugin.go @@ -62,6 +62,59 @@ func ExpectedInstalledName(versionStr string) string { runtime.GOOS, runtime.GOARCH, ext) } +// 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() + } + } +} + // GetPluginPath gets the path for a pre-compiled plugin in the current test suite. // // The version only is needed, as the path to a compiled version of the tester @@ -93,7 +146,7 @@ func (ts *PackerTestSuite) GetPluginPath(t *testing.T, version string) string { // 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. -func (ts *PackerTestSuite) CompilePlugin(t *testing.T, versionString string) { +func (ts *PackerTestSuite) CompilePlugin(t *testing.T, versionString string, customisations ...BuildCustomisation) { // Fail to build plugin if already built. // // Especially with customisations being a thing, relying on cache to get and @@ -114,6 +167,14 @@ func (ts *PackerTestSuite) CompilePlugin(t *testing.T, versionString string) { } testerPluginDir := filepath.Join(testDir, "plugin_tester") + for _, custom := range customisations { + err, cleanup := custom(testerPluginDir) + if err != nil { + t.Fatalf("failed to prepare plugin workdir: %s", err) + } + defer cleanup() + } + outBin := filepath.Join(ts.pluginsDirectory, BinaryName(v)) compileCommand := exec.Command("go", "build", "-C", testerPluginDir, "-o", outBin, "-ldflags", LDFlags(v), ".")