From 8e0728f4a64d22abd8e671ffbf3b71fe99de039e Mon Sep 17 00:00:00 2001 From: Lucas Bajolet Date: Mon, 5 Aug 2024 17:33:00 -0400 Subject: [PATCH] packer_test: add build customisation capabilities When building a plugin, we may want some customisation capabilities beyond changing the version/pre-release/metadata, and instead run commands or change files on the filesystem. To do so, we introduce functions under the BuildCustomisation type, which have two responsabilities: changing the current state of the plugin's directory, and cleaning up afterwards. These customisations are passed as parameters to the BuildSimplePlugin function, and are called one-by-one, deferring their cleanup after the build process is finished. A first implementation of such a customisation is added with this commit, in order to change the version of a module that the plugin depends on, which we'll use to change the version of the plugin SDK in order to test how Packer behaves with different versions of the SDK for a single plugin. --- packer_test/common/plugin.go | 63 +++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) 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), ".")