github.com/hashicorp/packer@v1.14.3/command/plugins_remove.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package command 5 6 import ( 7 "context" 8 "crypto/sha256" 9 "fmt" 10 "log" 11 "os" 12 "path/filepath" 13 "runtime" 14 "strings" 15 16 "github.com/hashicorp/go-version" 17 "github.com/hashicorp/hcl/v2" 18 "github.com/hashicorp/packer/hcl2template/addrs" 19 "github.com/hashicorp/packer/packer" 20 plugingetter "github.com/hashicorp/packer/packer/plugin-getter" 21 "github.com/mitchellh/cli" 22 ) 23 24 type PluginsRemoveCommand struct { 25 Meta 26 } 27 28 func (c *PluginsRemoveCommand) Synopsis() string { 29 return "Remove Packer plugins [matching a version]" 30 } 31 32 func (c *PluginsRemoveCommand) Help() string { 33 helpText := ` 34 Usage: packer plugins remove <plugin> [<version constraint>] 35 36 This command will remove one or more installed Packer plugins. 37 38 To remove a plugin matching a version constraint for the current OS and architecture. 39 40 packer plugins remove github.com/hashicorp/happycloud v1.2.3 41 42 To remove all versions of a plugin for the current OS and architecture omit the version constraint. 43 44 packer plugins remove github.com/hashicorp/happycloud 45 46 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. 47 48 packer plugins remove ~/.config/plugins/github.com/hashicorp/happycloud/packer-plugin-happycloud_v1.0.0_x5.0_linux_amd64 49 ` 50 51 return strings.TrimSpace(helpText) 52 } 53 54 func (c *PluginsRemoveCommand) Run(args []string) int { 55 ctx, cleanup := handleTermInterrupt(c.Ui) 56 defer cleanup() 57 58 return c.RunContext(ctx, args) 59 } 60 61 // deletePluginBinary removes a local plugin binary, and its related checksum file. 62 func deletePluginBinary(pluginPath string) error { 63 if err := os.Remove(pluginPath); err != nil { 64 return err 65 } 66 shasumFile := fmt.Sprintf("%s_SHA256SUM", pluginPath) 67 68 if _, err := os.Stat(shasumFile); err != nil { 69 log.Printf("[INFO] No SHA256SUM file to remove for the plugin, ignoring.") 70 return nil 71 } 72 73 return os.Remove(shasumFile) 74 } 75 76 func (c *PluginsRemoveCommand) RunContext(buildCtx context.Context, args []string) int { 77 if len(args) < 1 || len(args) > 2 { 78 return cli.RunResultHelp 79 } 80 81 pluginDir, err := packer.PluginFolder() 82 if err != nil { 83 return writeDiags(c.Ui, nil, hcl.Diagnostics{ 84 &hcl.Diagnostic{ 85 Severity: hcl.DiagError, 86 Summary: "Failed to get the plugin directory", 87 Detail: fmt.Sprintf( 88 "The directory in which plugins are installed could not be fetched from the environment. This is likely a Packer bug. Error: %s", 89 err), 90 }, 91 }) 92 } 93 94 if filepath.IsAbs(args[0]) { 95 if len(args) != 1 { 96 c.Ui.Error("Unsupported: no version constraint may be specified with a local plugin path.\n") 97 return cli.RunResultHelp 98 } 99 100 if !strings.Contains(args[0], pluginDir) { 101 return writeDiags(c.Ui, nil, hcl.Diagnostics{ 102 &hcl.Diagnostic{ 103 Severity: hcl.DiagError, 104 Summary: "Invalid plugin location", 105 Detail: fmt.Sprintf( 106 "The path %q is not under the plugin directory inferred by Packer (%s) and will not be removed.", 107 args[0], 108 pluginDir), 109 }, 110 }) 111 } 112 113 log.Printf("will delete plugin located at %q", args[0]) 114 err := deletePluginBinary(args[0]) 115 if err != nil { 116 return writeDiags(c.Ui, nil, hcl.Diagnostics{ 117 &hcl.Diagnostic{ 118 Severity: hcl.DiagError, 119 Summary: "Failed to delete plugin", 120 Detail: fmt.Sprintf("The plugin %q failed to be deleted with the following error: %q", args[0], err), 121 }, 122 }) 123 } 124 125 c.Ui.Say(args[0]) 126 127 return 0 128 } 129 130 opts := plugingetter.ListInstallationsOptions{ 131 PluginDirectory: c.Meta.CoreConfig.Components.PluginConfig.PluginDirectory, 132 BinaryInstallationOptions: plugingetter.BinaryInstallationOptions{ 133 OS: runtime.GOOS, 134 ARCH: runtime.GOARCH, 135 Checksummers: []plugingetter.Checksummer{ 136 {Type: "sha256", Hash: sha256.New()}, 137 }, 138 }, 139 } 140 141 if runtime.GOOS == "windows" && opts.Ext == "" { 142 opts.BinaryInstallationOptions.Ext = ".exe" 143 } 144 145 plugin, err := addrs.ParsePluginSourceString(args[0]) 146 if err != nil { 147 c.Ui.Errorf("Invalid source string %q: %s", args[0], err) 148 return 1 149 } 150 151 // a plugin requirement that matches them all 152 pluginRequirement := plugingetter.Requirement{ 153 Identifier: plugin, 154 } 155 156 if len(args) > 1 { 157 constraints, err := version.NewConstraint(args[1]) 158 if err != nil { 159 c.Ui.Error(err.Error()) 160 return 1 161 } 162 pluginRequirement.VersionConstraints = constraints 163 } 164 165 installations, err := pluginRequirement.ListInstallations(opts) 166 if err != nil { 167 c.Ui.Error(err.Error()) 168 return 1 169 } 170 for _, installation := range installations { 171 err := deletePluginBinary(installation.BinaryPath) 172 if err != nil { 173 c.Ui.Error(fmt.Sprintf("Failed to remove plugin %q: %q", installation.BinaryPath, err)) 174 continue 175 } 176 c.Ui.Message(installation.BinaryPath) 177 } 178 179 if len(installations) == 0 { 180 errMsg := fmt.Sprintf("No installed plugin found matching the plugin constraints %s", args[0]) 181 if len(args) == 2 { 182 errMsg = fmt.Sprintf("%s %s", errMsg, args[1]) 183 } 184 c.Ui.Error(errMsg) 185 return 1 186 } 187 188 return 0 189 }