2023-03-02 15:37:05 -05:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
2023-08-10 18:53:29 -04:00
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
2023-03-02 15:37:05 -05:00
|
|
|
|
2022-02-10 16:53:50 -05:00
|
|
|
package command
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"crypto/sha256"
|
2023-10-25 16:06:45 -04:00
|
|
|
"fmt"
|
2024-03-12 16:16:54 -04:00
|
|
|
"log"
|
2022-02-10 16:53:50 -05:00
|
|
|
"os"
|
2024-03-12 16:16:54 -04:00
|
|
|
"path/filepath"
|
2022-02-10 16:53:50 -05:00
|
|
|
"runtime"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"github.com/hashicorp/go-version"
|
2024-03-12 16:16:54 -04:00
|
|
|
"github.com/hashicorp/hcl/v2"
|
2022-02-10 16:53:50 -05:00
|
|
|
"github.com/hashicorp/packer/hcl2template/addrs"
|
2024-03-12 16:16:54 -04:00
|
|
|
"github.com/hashicorp/packer/packer"
|
2022-02-10 16:53:50 -05:00
|
|
|
plugingetter "github.com/hashicorp/packer/packer/plugin-getter"
|
|
|
|
|
"github.com/mitchellh/cli"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type PluginsRemoveCommand struct {
|
|
|
|
|
Meta
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *PluginsRemoveCommand) Synopsis() string {
|
|
|
|
|
return "Remove Packer plugins [matching a version]"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *PluginsRemoveCommand) Help() string {
|
|
|
|
|
helpText := `
|
|
|
|
|
Usage: packer plugins remove <plugin> [<version constraint>]
|
|
|
|
|
|
2024-03-12 16:16:54 -04:00
|
|
|
This command will remove one or more installed Packer plugins.
|
2022-02-10 16:53:50 -05:00
|
|
|
|
2024-05-13 15:01:17 -04:00
|
|
|
To remove a plugin matching a version constraint for the current OS and architecture.
|
2024-03-12 16:16:54 -04:00
|
|
|
|
|
|
|
|
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
|
2022-02-10 16:53:50 -05:00
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return strings.TrimSpace(helpText)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *PluginsRemoveCommand) Run(args []string) int {
|
|
|
|
|
ctx, cleanup := handleTermInterrupt(c.Ui)
|
|
|
|
|
defer cleanup()
|
|
|
|
|
|
|
|
|
|
return c.RunContext(ctx, args)
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-12 16:16:54 -04:00
|
|
|
// 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)
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-10 16:53:50 -05:00
|
|
|
func (c *PluginsRemoveCommand) RunContext(buildCtx context.Context, args []string) int {
|
|
|
|
|
if len(args) < 1 || len(args) > 2 {
|
|
|
|
|
return cli.RunResultHelp
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-12 16:16:54 -04:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-10 16:53:50 -05:00
|
|
|
opts := plugingetter.ListInstallationsOptions{
|
2024-01-15 14:41:25 -05:00
|
|
|
PluginDirectory: c.Meta.CoreConfig.Components.PluginConfig.PluginDirectory,
|
2022-02-10 16:53:50 -05:00
|
|
|
BinaryInstallationOptions: plugingetter.BinaryInstallationOptions{
|
|
|
|
|
OS: runtime.GOOS,
|
|
|
|
|
ARCH: runtime.GOARCH,
|
|
|
|
|
Checksummers: []plugingetter.Checksummer{
|
|
|
|
|
{Type: "sha256", Hash: sha256.New()},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-04 04:50:26 -05:00
|
|
|
if runtime.GOOS == "windows" && opts.Ext == "" {
|
|
|
|
|
opts.BinaryInstallationOptions.Ext = ".exe"
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-09 17:11:15 -04:00
|
|
|
plugin, err := addrs.ParsePluginSourceString(args[0])
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.Ui.Errorf("Invalid source string %q: %s", args[0], err)
|
2022-02-10 16:53:50 -05:00
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// a plugin requirement that matches them all
|
|
|
|
|
pluginRequirement := plugingetter.Requirement{
|
|
|
|
|
Identifier: plugin,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(args) > 1 {
|
|
|
|
|
constraints, err := version.NewConstraint(args[1])
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.Ui.Error(err.Error())
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
pluginRequirement.VersionConstraints = constraints
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
installations, err := pluginRequirement.ListInstallations(opts)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.Ui.Error(err.Error())
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
for _, installation := range installations {
|
2024-03-12 16:16:54 -04:00
|
|
|
err := deletePluginBinary(installation.BinaryPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.Ui.Error(fmt.Sprintf("Failed to remove plugin %q: %q", installation.BinaryPath, err))
|
|
|
|
|
continue
|
2023-10-25 16:06:45 -04:00
|
|
|
}
|
2022-02-10 16:53:50 -05:00
|
|
|
c.Ui.Message(installation.BinaryPath)
|
|
|
|
|
}
|
|
|
|
|
|
command: error on invalid plugins remove
If a user attempts to remove a plugin through the `packer plugins
remove' subcommand, and the specified plugin is not installed, the
command succeeds, but does nothing, and exits silently.
This is not clear what is happening, and arguably, calling a command
that does nothing, not even explain what went wrong, is not intuitive.
Because of that, this commit changes how the command behaves in this
case, stating what went wrong, and exiting with a non-zero status.
2023-10-26 16:46:15 -04:00
|
|
|
if len(installations) == 0 {
|
|
|
|
|
errMsg := fmt.Sprintf("No installed plugin found matching the plugin constraints %s", args[0])
|
|
|
|
|
if len(args) == 2 {
|
|
|
|
|
errMsg = fmt.Sprintf("%s %s", errMsg, args[1])
|
|
|
|
|
}
|
|
|
|
|
c.Ui.Error(errMsg)
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-10 16:53:50 -05:00
|
|
|
return 0
|
|
|
|
|
}
|