Files
Packer-Cn/command/plugins_required.go
Lucas Bajolet 56e1f6bf89 packer: make listing installation parallel
Listing installed plugins is a sequential operation that iterates on
each potential plugin binary under PACKER_PLUGIN_PATH.
Each potential candidate may be executed in order to get its
description, and to match that what is reported by the plugin binary
matches what is effectively installed on the filesystem.

This operation may be causing lots of I/O syncs, and if done
sequentially on a rather large number of installed plugins will take
time.

In order to make this faster, we paralellise listing installations, so
we run those commands in parallel in order to minimise idle waiting time
on the main process.
2024-04-17 12:04:53 -04:00

121 lines
3.0 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package command
import (
"context"
"fmt"
"runtime"
"strings"
pluginsdk "github.com/hashicorp/packer-plugin-sdk/plugin"
plugingetter "github.com/hashicorp/packer/packer/plugin-getter"
"github.com/mitchellh/cli"
)
type PluginsRequiredCommand struct {
Meta
}
func (c *PluginsRequiredCommand) Synopsis() string {
return "List plugins required by a config"
}
func (c *PluginsRequiredCommand) Help() string {
helpText := `
Usage: packer plugins required <path>
This command will list every Packer plugin required by a Packer config, in
packer.required_plugins blocks. All binaries matching the required version
constrain and the current OS and Architecture will be listed. The most recent
version (and the first of the list) will be the one picked by Packer during a
build.
Ex: packer plugins required require.pkr.hcl
Ex: packer plugins required path/to/folder/
`
return strings.TrimSpace(helpText)
}
func (c *PluginsRequiredCommand) Run(args []string) int {
ctx, cleanup := handleTermInterrupt(c.Ui)
defer cleanup()
cfg, ret := c.ParseArgs(args)
if ret != 0 {
return ret
}
return c.RunContext(ctx, cfg)
}
func (c *PluginsRequiredCommand) ParseArgs(args []string) (*PluginsRequiredArgs, int) {
var cfg PluginsRequiredArgs
flags := c.Meta.FlagSet("plugins required")
flags.Usage = func() { c.Ui.Say(c.Help()) }
cfg.AddFlagSets(flags)
if err := flags.Parse(args); err != nil {
return &cfg, 1
}
args = flags.Args()
if len(args) != 1 {
return &cfg, cli.RunResultHelp
}
cfg.Path = args[0]
return &cfg, 0
}
func (c *PluginsRequiredCommand) RunContext(buildCtx context.Context, cla *PluginsRequiredArgs) int {
packerStarter, ret := c.GetConfig(&cla.MetaArgs)
if ret != 0 {
return ret
}
// Get plugins requirements
reqs, diags := packerStarter.PluginRequirements()
ret = writeDiags(c.Ui, nil, diags)
if ret != 0 {
return ret
}
opts := plugingetter.ListInstallationsOptions{
PluginDirectory: c.Meta.CoreConfig.Components.PluginConfig.PluginDirectory,
BinaryInstallationOptions: plugingetter.BinaryInstallationOptions{
OS: runtime.GOOS,
ARCH: runtime.GOARCH,
APIVersionMajor: pluginsdk.APIVersionMajor,
APIVersionMinor: pluginsdk.APIVersionMinor,
Checksummers: []plugingetter.Checksummer{
{Type: "sha256"},
},
},
}
for _, pluginRequirement := range reqs {
s := fmt.Sprintf("%s %s %q", pluginRequirement.Accessor, pluginRequirement.Identifier.String(), pluginRequirement.VersionConstraints.String())
installs, err := pluginRequirement.ListInstallations(opts)
if err != nil {
c.Ui.Error(err.Error())
return 1
}
for _, install := range installs {
s += fmt.Sprintf(" %s", install.BinaryPath)
}
c.Ui.Message(s)
}
if len(reqs) == 0 {
c.Ui.Message(`
No plugins requirement found, make sure you reference a Packer config
containing a packer.required_plugins block. See
https://www.packer.io/docs/templates/hcl_templates/blocks/packer
for more info.`)
}
return 0
}