mirror of
https://github.com/hashicorp/packer.git
synced 2026-09-19 14:31:40 -04:00
Packer can only install plugins from github.com, with releases.hashicorp.com consulted first for HashiCorp-published plugins. This has been a long-standing gap for air-gapped and policy-restricted environments (#11164): the source address parser already accepts any hostname, but both existing getters reject non-github.com sources at install time. Add a remote plugin getter that installs plugins from the host named in a required_plugins source address. The host serves the directory structure of releases.hashicorp.com under the source's path: an index.json listing versions, and per version a SHA256SUMS file, the zips it lists, and - when the zip names carry no plugin protocol version - the version's manifest.json. A plugin published on releases.hashicorp.com is therefore mirrored as a verbatim copy of its tree, with every checksum file and signature upstream-authored. A plugin published as GitHub release assets is mirrored by copying each release's assets into a version directory, renaming their SHA256SUMS file to the unprefixed convention with content unchanged, and writing an index.json listing the versions. Both kinds of content can be served side by side by one host. Getter selection happens per source address: github.com sources keep the release and github getters unchanged, while any other host is served by the remote getter over HTTPS. Sources with three or more components are supported, up to the existing 16-component limit, so nested artifact-repository paths and hosts that embed the upstream origin in their path all resolve. Version discovery, constraint solving, checksum verification, and the binary naming rules match the existing getters. The installed filename is rebuilt from validated checksum-file fields and never taken from the server's response, checksum entries matching neither known naming shape are rejected rather than guessed at, and nothing the remote metadata supplies is used to fetch from another origin or path. index.json parsing is covered by fixtures captured from the live releases API, so a format change there fails tests rather than user installs. Closes #11164
86 lines
3.3 KiB
Go
86 lines
3.3 KiB
Go
// Copyright IBM Corp. 2024, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package plugin_tests
|
|
|
|
import (
|
|
"github.com/hashicorp/packer/packer_test/common/check"
|
|
)
|
|
|
|
func (ts *PackerPluginTestSuite) TestPackerInitForce() {
|
|
ts.SkipNoAcc()
|
|
|
|
pluginPath := ts.MakePluginDir()
|
|
defer pluginPath.Cleanup()
|
|
|
|
ts.Run("installs any missing plugins", func() {
|
|
ts.PackerCommand().UsePluginDir(pluginPath).
|
|
SetArgs("init", "--force", "./templates/init/hashicups.pkr.hcl").
|
|
Assert(check.MustSucceed(), check.Grep("Installed plugin github.com/hashicorp/hashicups v1.0.2", check.GrepStdout))
|
|
})
|
|
|
|
ts.Run("reinstalls plugins matching version constraints", func() {
|
|
ts.PackerCommand().UsePluginDir(pluginPath).
|
|
SetArgs("init", "--force", "./templates/init/hashicups.pkr.hcl").
|
|
Assert(check.MustSucceed(), check.Grep("Installed plugin github.com/hashicorp/hashicups v1.0.2", check.GrepStdout))
|
|
})
|
|
}
|
|
|
|
func (ts *PackerPluginTestSuite) TestPackerInitUpgrade() {
|
|
ts.SkipNoAcc()
|
|
|
|
pluginPath := ts.MakePluginDir()
|
|
defer pluginPath.Cleanup()
|
|
|
|
cmd := ts.PackerCommand().UsePluginDir(pluginPath)
|
|
cmd.SetArgs("plugins", "install", "github.com/hashicorp/hashicups", "1.0.1")
|
|
cmd.SetAssertFatal()
|
|
cmd.Assert(check.MustSucceed(), check.Grep("Installed plugin github.com/hashicorp/hashicups v1.0.1", check.GrepStdout))
|
|
|
|
ts.Run("upgrades a plugin to the latest matching version constraints", func() {
|
|
ts.PackerCommand().UsePluginDir(pluginPath).
|
|
SetArgs("init", "--upgrade", "./templates/init/hashicups.pkr.hcl").
|
|
Assert(check.MustSucceed(), check.Grep("Installed plugin github.com/hashicorp/hashicups v1.0.2", check.GrepStdout))
|
|
})
|
|
}
|
|
|
|
func (ts *PackerPluginTestSuite) TestPackerInitWithNonGithubSource() {
|
|
pluginPath := ts.MakePluginDir()
|
|
defer pluginPath.Cleanup()
|
|
|
|
ts.Run("try installing from an unreachable non-github source, should fail", func() {
|
|
// The .invalid TLD never resolves (RFC 6761), so the remote getter
|
|
// fails deterministically without depending on a live external host.
|
|
ts.PackerCommand().UsePluginDir(pluginPath).
|
|
SetArgs("init", "./templates/init/non_gh.pkr.hcl").
|
|
Assert(check.MustFail(), check.Grep(`failed to fetch https://hubgit.invalid/hashicorp/packer-plugin-tester/index.json`, check.GrepStdout))
|
|
})
|
|
|
|
ts.Run("manually install plugin to the expected source", func() {
|
|
ts.PackerCommand().UsePluginDir(pluginPath).
|
|
SetArgs("plugins", "install", "--path", ts.GetPluginPath(ts.T(), "1.0.10"), "hubgit.invalid/hashicorp/tester").
|
|
Assert(check.MustSucceed(), check.Grep("packer-plugin-tester_v1.0.10", check.GrepStdout))
|
|
})
|
|
|
|
ts.Run("re-run packer init on same template, should succeed silently", func() {
|
|
ts.PackerCommand().UsePluginDir(pluginPath).
|
|
SetArgs("init", "./templates/init/non_gh.pkr.hcl").
|
|
Assert(check.MustSucceed(),
|
|
check.MkPipeCheck("no output in stdout").SetTester(check.ExpectEmptyInput()).SetStream(check.OnlyStdout))
|
|
})
|
|
}
|
|
|
|
func (ts *PackerPluginTestSuite) TestPackerInitWithMixedVersions() {
|
|
ts.SkipNoAcc()
|
|
|
|
pluginPath := ts.MakePluginDir()
|
|
defer pluginPath.Cleanup()
|
|
|
|
ts.Run("skips the plugin installation with mixed versions before exiting with an error", func() {
|
|
ts.PackerCommand().UsePluginDir(pluginPath).
|
|
SetArgs("init", "./templates/init/mixed_versions.pkr.hcl").
|
|
Assert(check.MustFail(),
|
|
check.Grep("binary reported a pre-release version of 10.7.3-dev", check.GrepStdout))
|
|
})
|
|
}
|