diff --git a/command/cli.go b/command/cli.go index 4ad80440f..2dfc57dbd 100644 --- a/command/cli.go +++ b/command/cli.go @@ -104,6 +104,7 @@ type BuildArgs struct { func (ia *InitArgs) AddFlagSets(flags *flag.FlagSet) { flags.BoolVar(&ia.Upgrade, "upgrade", false, "upgrade any present plugin to the highest allowed version.") + flags.BoolVar(&ia.Force, "force", false, "force installation of a plugin, even if already installed") ia.MetaArgs.AddFlagSets(flags) } @@ -112,6 +113,7 @@ func (ia *InitArgs) AddFlagSets(flags *flag.FlagSet) { type InitArgs struct { MetaArgs Upgrade bool + Force bool } // PluginsRequiredArgs represents a parsed cli line for a `packer plugins required ` diff --git a/command/init.go b/command/init.go index 71332d57d..2b8cdd759 100644 --- a/command/init.go +++ b/command/init.go @@ -11,6 +11,7 @@ import ( "runtime" "strings" + gversion "github.com/hashicorp/go-version" pluginsdk "github.com/hashicorp/packer-plugin-sdk/plugin" "github.com/hashicorp/packer/packer" plugingetter "github.com/hashicorp/packer/packer/plugin-getter" @@ -120,16 +121,21 @@ for more info.`) return 1 } - log.Printf("[TRACE] for plugin %s found %d matching installation(s)", pluginRequirement.Identifier, len(installs)) + if len(installs) > 0 { + if !cla.Force && !cla.Upgrade { + continue + } - if len(installs) > 0 && cla.Upgrade == false { - continue + if cla.Force && !cla.Upgrade { + pluginRequirement.VersionConstraints, _ = gversion.NewConstraint(fmt.Sprintf("=%s", installs[len(installs)-1].Version)) + } } newInstall, err := pluginRequirement.InstallLatest(plugingetter.InstallOptions{ InFolders: opts.FromFolders, BinaryInstallationOptions: opts.BinaryInstallationOptions, Getters: getters, + Force: cla.Force, }) if err != nil { c.Ui.Error(fmt.Sprintf("Failed getting the %q plugin:", pluginRequirement.Identifier)) @@ -163,6 +169,8 @@ Options: version, if there is a new higher one. Note that this still takes into consideration the version constraint of the config. + -force Forces installation of plugins, even if already + installed. ` return strings.TrimSpace(helpText) diff --git a/command/plugins_install.go b/command/plugins_install.go index e584ea778..fbb2876cd 100644 --- a/command/plugins_install.go +++ b/command/plugins_install.go @@ -50,6 +50,7 @@ Options: installs the plugin where a normal invocation would, but will not try to download it from a web server, but instead directly install the binary for Packer to be able to load it later on. + - force: forces installation of a plugin, even if it is already there. ` return strings.TrimSpace(helpText) @@ -72,10 +73,12 @@ type PluginsInstallArgs struct { PluginName string PluginPath string Version string + Force bool } func (pa *PluginsInstallArgs) AddFlagSets(flags *flag.FlagSet) { flags.StringVar(&pa.PluginPath, "path", "", "install the plugin from a specific path") + flags.BoolVar(&pa.Force, "force", false, "force installation of a plugin, even if already installed") pa.MetaArgs.AddFlagSets(flags) } @@ -168,6 +171,7 @@ func (c *PluginsInstallCommand) RunContext(buildCtx context.Context, args *Plugi InFolders: opts.FromFolders, BinaryInstallationOptions: opts.BinaryInstallationOptions, Getters: getters, + Force: args.Force, }) if err != nil { diff --git a/packer/plugin-getter/plugins.go b/packer/plugin-getter/plugins.go index c9fbd0b9d..29b34da1c 100644 --- a/packer/plugin-getter/plugins.go +++ b/packer/plugin-getter/plugins.go @@ -244,6 +244,9 @@ type InstallOptions struct { // folder of this list. InFolders []string + // Forces installation of the plugin, even if already installed. + Force bool + BinaryInstallationOptions } @@ -582,7 +585,7 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error) log.Printf("[TRACE] found a pre-exising %q checksum file", potentialChecksumer.Type) // if outputFile is there and matches the checksum: do nothing more. - if err := localChecksum.ChecksumFile(localChecksum.Expected, potentialOutputFilename); err == nil { + if err := localChecksum.ChecksumFile(localChecksum.Expected, potentialOutputFilename); err == nil && !opts.Force { log.Printf("[INFO] %s v%s plugin is already correctly installed in %q", pr.Identifier, version, potentialOutputFilename) return nil, nil // success } diff --git a/packer/plugin-getter/plugins_test.go b/packer/plugin-getter/plugins_test.go index dfb287670..513f2c499 100644 --- a/packer/plugin-getter/plugins_test.go +++ b/packer/plugin-getter/plugins_test.go @@ -349,6 +349,7 @@ func TestRequirement_InstallLatest(t *testing.T) { pluginFolderOne, pluginFolderTwo, }, + false, BinaryInstallationOptions{ APIVersionMajor: "5", APIVersionMinor: "0", OS: "darwin", ARCH: "amd64", @@ -385,6 +386,7 @@ func TestRequirement_InstallLatest(t *testing.T) { pluginFolderOne, pluginFolderTwo, }, + false, BinaryInstallationOptions{ APIVersionMajor: "5", APIVersionMinor: "1", OS: "darwin", ARCH: "amd64", @@ -430,6 +432,7 @@ func TestRequirement_InstallLatest(t *testing.T) { pluginFolderOne, pluginFolderTwo, }, + false, BinaryInstallationOptions{ APIVersionMajor: "5", APIVersionMinor: "0", OS: "darwin", ARCH: "amd64", @@ -477,6 +480,7 @@ func TestRequirement_InstallLatest(t *testing.T) { pluginFolderOne, pluginFolderTwo, }, + false, BinaryInstallationOptions{ APIVersionMajor: "6", APIVersionMinor: "1", OS: "darwin", ARCH: "amd64", @@ -527,6 +531,7 @@ func TestRequirement_InstallLatest(t *testing.T) { pluginFolderOne, pluginFolderTwo, }, + false, BinaryInstallationOptions{ APIVersionMajor: "6", APIVersionMinor: "1", OS: "darwin", ARCH: "amd64", @@ -577,6 +582,7 @@ func TestRequirement_InstallLatest(t *testing.T) { pluginFolderOne, pluginFolderTwo, }, + false, BinaryInstallationOptions{ APIVersionMajor: "6", APIVersionMinor: "1", OS: "linux", ARCH: "amd64", @@ -621,6 +627,7 @@ func TestRequirement_InstallLatest(t *testing.T) { pluginFolderOne, pluginFolderTwo, }, + false, BinaryInstallationOptions{ APIVersionMajor: "6", APIVersionMinor: "1", OS: "darwin", ARCH: "amd64", @@ -662,6 +669,7 @@ func TestRequirement_InstallLatest(t *testing.T) { []string{ pluginFolderWrongChecksums, }, + false, BinaryInstallationOptions{ APIVersionMajor: "6", APIVersionMinor: "1", OS: "darwin", ARCH: "amd64",