Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

command: add --path flag to packer plugins install #12643

Merged
merged 6 commits into from
Dec 4, 2023

Conversation

lbajolet-hashicorp
Copy link
Contributor

@lbajolet-hashicorp lbajolet-hashicorp commented Oct 3, 2023

This new flag allows the packer plugins install command to install a plugin from a local binary rather than from Github.

This command will only call describe on the plugin, and won't do any further checks for functionality. The SHA256SUM will be directly computed from the binary, so as with anything manual and potentially sourced by the community, extra care should be applied when invoking this.

NOTE: marking as draft as it is not yet meant to be merged into main, or released soon. This is experimental and we'll decide what to do with it later.

@vercel
Copy link

vercel bot commented Oct 3, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

1 Ignored Deployment
Name Status Preview Updated (UTC)
packer ⬜️ Ignored (Inspect) Oct 3, 2023 3:56pm

@lbajolet-hashicorp lbajolet-hashicorp force-pushed the plugins_promote_proto branch 2 times, most recently from 054aa21 to 4cc8747 Compare October 16, 2023 19:19
@lbajolet-hashicorp lbajolet-hashicorp changed the title command: add packer plugins promote command command: add --path flag to packer plugins install Oct 16, 2023
@nywilken nywilken added enhancement stage/needs-discussion stage/thinking Flagged for internal discussions about possible enhancements and removed stage/needs-discussion labels Oct 23, 2023
@lbajolet-hashicorp lbajolet-hashicorp force-pushed the plugins_promote_proto branch 3 times, most recently from d1d4d45 to acf3ef9 Compare October 26, 2023 14:24
@lbajolet-hashicorp lbajolet-hashicorp force-pushed the plugins_promote_proto branch 2 times, most recently from 0abc8c0 to 7f43b65 Compare November 24, 2023 16:27
Copy link
Contributor

@nywilken nywilken left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the approach and understand why the logic for FlagSet is specified within the install command.

command/init.go Outdated Show resolved Hide resolved
}})
}

binaryPath := fmt.Sprintf(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For windows binaries we will need the .exe extension as well.

@@ -128,3 +198,167 @@ func (c *PluginsInstallCommand) RunContext(buildCtx context.Context, args []stri

return 0
}

func (c *PluginsInstallCommand) InstallFromBinary(args *PluginsInstallArgs) int {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you are doing here. Is there a way to reuse the Checksummer for obtaining and write the checksum file, the InFolders, and BinaryInstallOptions for generating file names.

I have to think through this a little but would there be any benefit to adding this as a method on plugingetter.Requirement?

You can write the code to be something like the following and reuse a bit of the logic from InstallLatest along with the call to describe you have in InstallFromBinary.

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

	if args.Version != "" {
		constraints, err := version.NewConstraint(args.Version)
		if err != nil {
			c.Ui.Error(err.Error())
			return 1
		}
		pluginRequirement.VersionConstraints = constraints
	}

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

         var newInstall *plugingetter.Installation
         var err error
	// If we did specify a binary to install the plugin from, we ignore
	// the Github-based getter in favour of installing it directly.
	if args.PluginPath != "" {
		newInstall, err :=  pluignRequirement.InstallLocalBinary(args.PluginPath, plugingetter.InstallOptions{
		InFolders:                 opts.FromFolders,
		BinaryInstallationOptions: opts.BinaryInstallationOptions,
		Force:                     args.Force, //This may not be needed.
	       })
	} else {

	getters := []plugingetter.Getter{
		&github.Getter{
			UserAgent: "packer-getter-github-" + pkrversion.String(),
		},
	}

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

command/plugins_install.go Show resolved Hide resolved
This new flag allows the `packer plugins install' command to install a
plugin from a local binary rather than from Github.

This command will only call `describe' on the plugin, and won't do any
further checks for functionality. The SHA256SUM will be directly
computed from the binary, so as with anything manual and potentially
sourced by the community, extra care should be applied when invoking
this.
The --force option for packer init and packer plugins install enforces
installation of a plugin, even if it is already locally installed.

This will become useful if for some reason a pre-existing plugin
binary/version is already installed, and we want to overwrite it.
To avoid plugins being installed with a specific version when a path is
used for installing a plugin from a locally sourced plugin binary, we
explicitly reject the combination of both a path and a version for
plugins install.
When installing a plugin with packer plugins install --path, we only
accept release versions of a plugin, as otherwise the loading can be
inconsistent if for example a user specifies a required_plugins block in
their template, in which case the plugins will be ignored.

Until we have a simpler loading scheme then, we will reject non-release
versions of plugins to avoid confusion.
@lbajolet-hashicorp lbajolet-hashicorp marked this pull request as ready for review December 1, 2023 18:19
@lbajolet-hashicorp lbajolet-hashicorp requested a review from a team as a code owner December 1, 2023 18:19
Copy link
Contributor

@nywilken nywilken left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great. Glad we were able to refactor for consistency I left a few suggestions to consolidate code a bit more and tighten up the scope of a few variables.

}

func (c *PluginsInstallCommand) RunContext(buildCtx context.Context, args []string) int {
func (pa *PluginsInstallArgs) AddFlagSets(flags *flag.FlagSet) {
flags.StringVar(&pa.PluginPath, "path", "", "install the plugin from a specific path")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
flags.StringVar(&pa.PluginPath, "path", "", "install the plugin from a specific path")
flags.StringVar(&pa.PluginPath, "path", "", "install the binary specified by path as a Packer plugin.")

func (c *PluginsInstallCommand) RunContext(buildCtx context.Context, args []string) int {
func (pa *PluginsInstallArgs) AddFlagSets(flags *flag.FlagSet) {
flags.StringVar(&pa.PluginPath, "path", "", "install the plugin from a specific path")
flags.BoolVar(&pa.Force, "force", false, "force installation of a plugin, even if already installed")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
flags.BoolVar(&pa.Force, "force", false, "force installation of a plugin, even if already installed")
flags.BoolVar(&pa.Force, "force", false, "force installation of the specified plugin, even if already installed.")

}

if pa.Path != "" && pa.Version != "" {
c.Ui.Error("Invalid arguments: a version cannot be specified with --path")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
c.Ui.Error("Invalid arguments: a version cannot be specified with --path")
c.Ui.Error("Invalid arguments: a version cannot be specified when using --path to install a local plugin binary")

}

pa.PluginName = args[0]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

@@ -68,30 +133,36 @@ func (c *PluginsInstallCommand) RunContext(buildCtx context.Context, args []stri
},
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

return writeDiags(c.Ui, nil, hcl.Diagnostics{&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Unable to find plugin to promote",
Detail: fmt.Sprintf("The plugin %q failed to be opened because of an error: %s", args.PluginName, err),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Detail: fmt.Sprintf("The plugin %q failed to be opened because of an error: %s", args.PluginName, err),
Detail: fmt.Sprintf("The plugin %q failed to be opened because of an error: %s", pluginIdentifier, err),

Detail: fmt.Sprintf("Failed to read plugin binary from %q: %s", args.PluginPath, err),
}})
}
_ = pluginBinary.Close()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should handle this error in case close fails.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eh, the chances of Close failing are excessively thin, and will be closed anyway when the process terminates, so we probably can overlook this one (we would too if we deferred)

outputPrefix+opts.BinaryInstallationOptions.FilenameSuffix(),
)

outputPlugin, err := os.OpenFile(binaryPath, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0755)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the same permissions we use inside of InstallLatest?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe so? If not we should probably settle for 755 for both, since read/execute makes sense for eveyone else, and all perms for the owner

command/plugins_install.go Show resolved Hide resolved
command/cli.go Show resolved Hide resolved
Comment on lines 281 to 282
pluginContents := &bytes.Buffer{}
_, err = io.Copy(pluginContents, pluginBinary)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: We only need the pointer for copying.

Suggested change
pluginContents := &bytes.Buffer{}
_, err = io.Copy(pluginContents, pluginBinary)
pluginContents := bytes.Buffer{}
_, err = io.Copy(&pluginContents, pluginBinary)

return writeDiags(c.Ui, nil, hcl.Diagnostics{&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid version",
Detail: fmt.Sprintf("Packer can only install plugin releases with this command (ex: 1.0.0), the binary's reported version is %q", desc.Version),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't solve the problem for folks using dev builds right.

Suggested change
Detail: fmt.Sprintf("Packer can only install plugin releases with this command (ex: 1.0.0), the binary's reported version is %q", desc.Version),
Detail: fmt.Sprintf("Installation of prerelease binaries with this command is not allowed, the binary's reported version is %q", desc.Version),
~>  packer plugins install --path ~/.packer.d/plugins/packer-plugin-amazon github.com/hashicorp/happycloud
Error: Invalid version

Packer can only install plugin releases with this command (ex: 1.0.0), the
binary's reported version is "1.2.9-dev"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not indeed for now, it's one of the things we need to address/fix for 1.11.0

Copy link
Contributor

@nywilken nywilken left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something seems off

~>  tree ~/.packer.d/plugins/github.com/hashicorp/
/Users/dev/.packer.d/plugins/github.com/hashicorp/
├── amazon
│   ├── packer-plugin-amazon_v1.2.8_x5.0_darwin_arm64
│   └── packer-plugin-amazon_v1.2.8_x5.0_darwin_arm64_SHA256SUM
└── happycloud
    ├── packer-plugin-happycloud_v1.2.8_x5.0_darwin_arm64
    └── packer-plugin-happycloud_v1.2.8_x5.0_darwin_arm64_SHA256SUM

2 directories, 4 files
~>  packer plugins installed
/Users/dev/.packer.d/plugins/github.com/hashicorp/amazon/packer-plugin-amazon_v1.2.8_x5.0_darwin_arm64

Read original binary into memory to fix case when installation
destination and source were the same, resulting in an empty binary.
@lbajolet-hashicorp lbajolet-hashicorp added backport/1.10.x Backport PR changes to `release/1.10.x` and removed stage/thinking Flagged for internal discussions about possible enhancements labels Dec 4, 2023
Copy link
Contributor

@nywilken nywilken left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice updates ✅

Copy link

github-actions bot commented Jan 4, 2024

I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.
If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jan 4, 2024
@lbajolet-hashicorp lbajolet-hashicorp deleted the plugins_promote_proto branch July 17, 2024 18:08
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
backport/1.10.x Backport PR changes to `release/1.10.x` enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants