Skip to content

Commit

Permalink
Add packer plugins command and subcommands to interact with plugins (
Browse files Browse the repository at this point in the history
…#11553)

* add basic docs for plugins command

* refactor docs

Co-Authored-By: Wilken Rivera <1749304+nywilken@users.noreply.github.com>

* add plugins command

* add plugins subcommands

they do nothing for now

* add plugins installed command + tests

* add plugins install command

* add remove plugin command

* better docs for the plugins install command

* remove duplicate content

* better output for installed plugins

* add plugins required command

* Update plugins_install.go

* add newline after `Usage:`

* Update plugins_remove.go

* Update plugins_required.go

* Update plugins_remove.go

* Update plugins_installed.go

* Update plugins_install.go

* add docs

* Update plugins_install.go

* fix typos

* Update plugins_test.go

* fix typos

Co-Authored-By: Wilken Rivera <1749304+nywilken@users.noreply.github.com>

* Update core_wrapper.go

Co-Authored-By: Wilken Rivera <1749304+nywilken@users.noreply.github.com>

* Update website/content/docs/commands/plugins/remove.mdx

Co-authored-by: Wilken Rivera <wilken@hashicorp.com>

* Update website/content/docs/commands/plugins/required.mdx

Co-authored-by: Wilken Rivera <wilken@hashicorp.com>

* Update website/content/docs/commands/plugins/required.mdx

Co-authored-by: Wilken Rivera <wilken@hashicorp.com>

* Update plugins_required.go

* Update install.mdx

* Update required.mdx

* plugins requirement, warn when no plugin was found

* Update website/content/docs/commands/plugins/required.mdx

Co-authored-by: Wilken Rivera <wilken@hashicorp.com>

Co-authored-by: Wilken Rivera <1749304+nywilken@users.noreply.github.com>
Co-authored-by: Wilken Rivera <wilken@hashicorp.com>
  • Loading branch information
3 people committed Feb 10, 2022
1 parent 759cada commit 9f4a128
Show file tree
Hide file tree
Showing 16 changed files with 725 additions and 9 deletions.
7 changes: 6 additions & 1 deletion command/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,17 @@ func (ia *InitArgs) AddFlagSets(flags *flag.FlagSet) {
ia.MetaArgs.AddFlagSets(flags)
}

// InitArgs represents a parsed cli line for a `packer build`
// InitArgs represents a parsed cli line for a `packer init <path>`
type InitArgs struct {
MetaArgs
Upgrade bool
}

// PluginsRequiredArgs represents a parsed cli line for a `packer plugins required <path>`
type PluginsRequiredArgs struct {
MetaArgs
}

// ConsoleArgs represents a parsed cli line for a `packer console`
type ConsoleArgs struct {
MetaArgs
Expand Down
4 changes: 2 additions & 2 deletions command/core_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ func (c *CoreWrapper) Initialize(_ packer.InitializeOptions) hcl.Diagnostics {
func (c *CoreWrapper) PluginRequirements() (plugingetter.Requirements, hcl.Diagnostics) {
return nil, hcl.Diagnostics{
&hcl.Diagnostic{
Summary: "Packer init is supported for HCL2 configuration templates only",
Detail: "Please manually install plugins or use a HCL2 configuration that will do that for you.",
Summary: "Packer plugins currently only works with HCL2 configuration templates",
Detail: "Please manually install plugins with the plugins command or use a HCL2 configuration that will do that for you.",
Severity: hcl.DiagError,
},
}
Expand Down
32 changes: 32 additions & 0 deletions command/plugins.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package command

import (
"strings"

"github.com/mitchellh/cli"
)

type PluginsCommand struct {
Meta
}

func (c *PluginsCommand) Synopsis() string {
return "Interact with Packer plugins and catalog"
}

func (c *PluginsCommand) Help() string {
helpText := `
Usage: packer plugins <subcommand> [options] [args]
This command groups subcommands for interacting with Packer plugins.
Related but not under the "plugins" command :
- "packer init <path>" will install all plugins required by a config.
`

return strings.TrimSpace(helpText)
}

func (c *PluginsCommand) Run(args []string) int {
return cli.RunResultHelp
}
124 changes: 124 additions & 0 deletions command/plugins_install.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package command

import (
"context"
"crypto/sha256"
"fmt"
"runtime"
"strings"

"github.com/hashicorp/go-version"
pluginsdk "github.com/hashicorp/packer-plugin-sdk/plugin"
"github.com/hashicorp/packer/hcl2template/addrs"
"github.com/hashicorp/packer/packer"
plugingetter "github.com/hashicorp/packer/packer/plugin-getter"
"github.com/hashicorp/packer/packer/plugin-getter/github"
pkrversion "github.com/hashicorp/packer/version"
"github.com/mitchellh/cli"
)

type PluginsInstallCommand struct {
Meta
}

func (c *PluginsInstallCommand) Synopsis() string {
return "Install latest Packer plugin [matching version constraint]"
}

func (c *PluginsInstallCommand) Help() string {
helpText := `
Usage: packer plugins install <plugin> [<version constraint>]
This command will install the most recent compatible Packer plugin matching
version constraint.
When the version constraint is omitted, the most recent version will be
installed.
Ex: packer plugins install github.com/hashicorp/happycloud v1.2.3
`

return strings.TrimSpace(helpText)
}

func (c *PluginsInstallCommand) Run(args []string) int {
ctx, cleanup := handleTermInterrupt(c.Ui)
defer cleanup()

return c.RunContext(ctx, args)
}

func (c *PluginsInstallCommand) RunContext(buildCtx context.Context, args []string) int {
if len(args) < 1 || len(args) > 2 {
return cli.RunResultHelp
}

opts := plugingetter.ListInstallationsOptions{
FromFolders: c.Meta.CoreConfig.Components.PluginConfig.KnownPluginFolders,
BinaryInstallationOptions: plugingetter.BinaryInstallationOptions{
OS: runtime.GOOS,
ARCH: runtime.GOARCH,
APIVersionMajor: pluginsdk.APIVersionMajor,
APIVersionMinor: pluginsdk.APIVersionMinor,
Checksummers: []plugingetter.Checksummer{
{Type: "sha256", Hash: sha256.New()},
},
},
}

plugin, diags := addrs.ParsePluginSourceString(args[0])
if diags.HasErrors() {
c.Ui.Error(diags.Error())
return 1
}

// a plugin requirement that matches them all
pluginRequirement := plugingetter.Requirement{
Identifier: plugin,
Implicit: false,
}

if len(args) > 1 {
constraints, err := version.NewConstraint(args[1])
if err != nil {
c.Ui.Error(err.Error())
return 1
}
pluginRequirement.VersionConstraints = constraints
}

getters := []plugingetter.Getter{
&github.Getter{
// In the past some terraform plugins downloads were blocked from a
// specific aws region by s3. Changing the user agent unblocked the
// downloads so having one user agent per version will help mitigate
// that a little more. Especially in the case someone forks this
// code to make it more aggressive or something.
// TODO: allow to set this from the config file or an environment
// variable.
UserAgent: "packer-getter-github-" + pkrversion.String(),
},
}

newInstall, err := pluginRequirement.InstallLatest(plugingetter.InstallOptions{
InFolders: opts.FromFolders,
BinaryInstallationOptions: opts.BinaryInstallationOptions,
Getters: getters,
})

if err != nil {
c.Ui.Error(err.Error())
return 1
}

if newInstall != nil {
msg := fmt.Sprintf("Installed plugin %s %s in %q", pluginRequirement.Identifier, newInstall.Version, newInstall.BinaryPath)
ui := &packer.ColoredUi{
Color: packer.UiColorCyan,
Ui: c.Ui,
}
ui.Say(msg)
return 0
}

return 0
}
78 changes: 78 additions & 0 deletions command/plugins_installed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package command

import (
"context"
"crypto/sha256"
"log"
"runtime"
"strings"

plugingetter "github.com/hashicorp/packer/packer/plugin-getter"
)

type PluginsInstalledCommand struct {
Meta
}

func (c *PluginsInstalledCommand) Synopsis() string {
return "List all installed Packer plugin binaries"
}

func (c *PluginsInstalledCommand) Help() string {
helpText := `
Usage: packer plugins installed
This command lists all installed plugin binaries that match with the current
OS and architecture. Packer's API version will be ignored.
`

return strings.TrimSpace(helpText)
}

func (c *PluginsInstalledCommand) Run(args []string) int {
ctx, cleanup := handleTermInterrupt(c.Ui)
defer cleanup()

return c.RunContext(ctx)
}

func (c *PluginsInstalledCommand) RunContext(buildCtx context.Context) int {

opts := plugingetter.ListInstallationsOptions{
FromFolders: c.Meta.CoreConfig.Components.PluginConfig.KnownPluginFolders,
BinaryInstallationOptions: plugingetter.BinaryInstallationOptions{
OS: runtime.GOOS,
ARCH: runtime.GOARCH,
Checksummers: []plugingetter.Checksummer{
{Type: "sha256", Hash: sha256.New()},
},
},
}

if runtime.GOOS == "windows" && opts.Ext == "" {
opts.BinaryInstallationOptions.Ext = ".exe"
}

log.Printf("[TRACE] init: %#v", opts)

// a plugin requirement that matches them all
allPlugins := plugingetter.Requirement{
Accessor: "",
VersionConstraints: nil,
Identifier: nil,
Implicit: false,
}

installations, err := allPlugins.ListInstallations(opts)
if err != nil {
c.Ui.Error(err.Error())
return 1
}

for _, installation := range installations {
c.Ui.Message(installation.BinaryPath)
}

return 0
}
96 changes: 96 additions & 0 deletions command/plugins_remove.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package command

import (
"context"
"crypto/sha256"
"os"
"runtime"
"strings"

"github.com/hashicorp/go-version"
"github.com/hashicorp/packer/hcl2template/addrs"
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>]
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.
Ex: packer plugins remove github.com/hashicorp/happycloud v1.2.3
`

return strings.TrimSpace(helpText)
}

func (c *PluginsRemoveCommand) Run(args []string) int {
ctx, cleanup := handleTermInterrupt(c.Ui)
defer cleanup()

return c.RunContext(ctx, args)
}

func (c *PluginsRemoveCommand) RunContext(buildCtx context.Context, args []string) int {
if len(args) < 1 || len(args) > 2 {
return cli.RunResultHelp
}

opts := plugingetter.ListInstallationsOptions{
FromFolders: c.Meta.CoreConfig.Components.PluginConfig.KnownPluginFolders,
BinaryInstallationOptions: plugingetter.BinaryInstallationOptions{
OS: runtime.GOOS,
ARCH: runtime.GOARCH,
Checksummers: []plugingetter.Checksummer{
{Type: "sha256", Hash: sha256.New()},
},
},
}

plugin, diags := addrs.ParsePluginSourceString(args[0])
if diags.HasErrors() {
c.Ui.Error(diags.Error())
return 1
}

// a plugin requirement that matches them all
pluginRequirement := plugingetter.Requirement{
Identifier: plugin,
Implicit: false,
}

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 {
if err := os.Remove(installation.BinaryPath); err != nil {
c.Ui.Error(err.Error())
return 1
}
c.Ui.Message(installation.BinaryPath)
}

return 0
}
Loading

0 comments on commit 9f4a128

Please sign in to comment.