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) }