From 930b6c3e2b528f19114678599193ddeb82d725d4 Mon Sep 17 00:00:00 2001 From: Lucas Bajolet Date: Tue, 12 Mar 2024 16:16:54 -0400 Subject: [PATCH] command: support local paths for plugins remove The packer plugins remove command allows users to delete plugins installed locally. Previous versions of the command only allowed for the plugins to be removed using the source for a plugin, and the versions to remove, optionally. This commit adds the capability for the plugins to be removed using their local path, in addition to the regular source+version method, that way we are able to pipe the results of `packer plugins installed' into the plugins remove command for quick plugin removal. --- command/plugins_remove.go | 96 ++++++++++++++++++++++++++++++++++----- 1 file changed, 84 insertions(+), 12 deletions(-) diff --git a/command/plugins_remove.go b/command/plugins_remove.go index 7b29ffa7b..1fc17416a 100644 --- a/command/plugins_remove.go +++ b/command/plugins_remove.go @@ -7,12 +7,16 @@ import ( "context" "crypto/sha256" "fmt" + "log" "os" + "path/filepath" "runtime" "strings" "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl/v2" "github.com/hashicorp/packer/hcl2template/addrs" + "github.com/hashicorp/packer/packer" plugingetter "github.com/hashicorp/packer/packer/plugin-getter" "github.com/mitchellh/cli" ) @@ -29,11 +33,19 @@ func (c *PluginsRemoveCommand) Help() string { helpText := ` Usage: packer plugins remove [] - This command will remove all Packer plugins matching the version constraint - for the current OS and architecture. - When the version is omitted all installed versions will be removed. + This command will remove one or more installed Packer plugins. - Ex: packer plugins remove github.com/hashicorp/happycloud v1.2.3 + To remove a plugin matching a version contraint for the current OS and architecture. + + packer plugins remove github.com/hashicorp/happycloud v1.2.3 + + To remove all versions of a plugin for the current OS and architecture omit the version constraint. + + packer plugins remove github.com/hashicorp/happycloud + + To remove a single plugin binary from the Packer plugin directory specify the absolute path to an installed binary. This syntax does not allow for version matching. + + packer plugins remove ~/.config/plugins/github.com/hashicorp/happycloud/packer-plugin-happycloud_v1.0.0_x5.0_linux_amd64 ` return strings.TrimSpace(helpText) @@ -46,11 +58,75 @@ func (c *PluginsRemoveCommand) Run(args []string) int { return c.RunContext(ctx, args) } +// deletePluginBinary removes a local plugin binary, and its related checksum file. +func deletePluginBinary(pluginPath string) error { + if err := os.Remove(pluginPath); err != nil { + return err + } + shasumFile := fmt.Sprintf("%s_SHA256SUM", pluginPath) + + if _, err := os.Stat(shasumFile); err != nil { + log.Printf("[INFO] No SHA256SUM file to remove for the plugin, ignoring.") + return nil + } + + return os.Remove(shasumFile) +} + func (c *PluginsRemoveCommand) RunContext(buildCtx context.Context, args []string) int { if len(args) < 1 || len(args) > 2 { return cli.RunResultHelp } + pluginDir, err := packer.PluginFolder() + if err != nil { + return writeDiags(c.Ui, nil, hcl.Diagnostics{ + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Failed to get the plugin directory", + Detail: fmt.Sprintf( + "The directory in which plugins are installed could not be fetched from the environment. This is likely a Packer bug. Error: %s", + err), + }, + }) + } + + if filepath.IsAbs(args[0]) { + if len(args) != 1 { + c.Ui.Error("Unsupported: no version constraint may be specified with a local plugin path.\n") + return cli.RunResultHelp + } + + if !strings.Contains(args[0], pluginDir) { + return writeDiags(c.Ui, nil, hcl.Diagnostics{ + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid plugin location", + Detail: fmt.Sprintf( + "The path %q is not under the plugin directory inferred by Packer (%s) and will not be removed.", + args[0], + pluginDir), + }, + }) + } + + log.Printf("will delete plugin located at %q", args[0]) + err := deletePluginBinary(args[0]) + if err != nil { + return writeDiags(c.Ui, nil, hcl.Diagnostics{ + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Failed to delete plugin", + Detail: fmt.Sprintf("The plugin %q failed to be deleted with the following error: %q", args[0], err), + }, + }) + } + + c.Ui.Say(args[0]) + + return 0 + } + opts := plugingetter.ListInstallationsOptions{ PluginDirectory: c.Meta.CoreConfig.Components.PluginConfig.PluginDirectory, BinaryInstallationOptions: plugingetter.BinaryInstallationOptions{ @@ -92,14 +168,10 @@ func (c *PluginsRemoveCommand) RunContext(buildCtx context.Context, args []strin return 1 } for _, installation := range installations { - if err := os.Remove(installation.BinaryPath); err != nil { - c.Ui.Error(err.Error()) - return 1 - } - shasumFile := fmt.Sprintf("%s_SHA256SUM", installation.BinaryPath) - if err := os.Remove(shasumFile); err != nil { - c.Ui.Error(fmt.Sprintf("failed to remove %s: %s", shasumFile, err)) - c.Ui.Error("You may need to remove it manually") + err := deletePluginBinary(installation.BinaryPath) + if err != nil { + c.Ui.Error(fmt.Sprintf("Failed to remove plugin %q: %q", installation.BinaryPath, err)) + continue } c.Ui.Message(installation.BinaryPath) }