Merge pull request #10633 from hashicorp/azr_init_no_magic_host

Packer init: remove host and namespace guessing
This commit is contained in:
Megan Marsh 2021-02-16 14:32:20 -08:00 committed by GitHub
commit 1710590418
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 279 additions and 156 deletions

View File

@ -2,7 +2,7 @@
### FEATURES
** New Command** (HCL only) `packer init` command will download plugins defined
in a new `required_plugins` block [GH-10304]
in a new `required_plugins` block [GH-10304] [GH-10633].
** New Plugin Type** Data sources can be implemented (blog post forthcoming).
[GH-10440]
** New Plugin** Aws Secrets Manager data source [GH-10505] [GH-10467]

View File

@ -109,7 +109,7 @@ func (c *InitCommand) RunContext(buildCtx context.Context, cla *InitArgs) int {
return 1
}
log.Printf("[TRACE] for plugin %s found %d matching installation(s)", pluginRequirement.Identifier.ForDisplay(), len(installs))
log.Printf("[TRACE] for plugin %s found %d matching installation(s)", pluginRequirement.Identifier, len(installs))
if len(installs) > 0 && cla.Upgrade == false {
continue
@ -125,7 +125,7 @@ func (c *InitCommand) RunContext(buildCtx context.Context, cla *InitArgs) int {
ret = 1
}
if newInstall != nil {
msg := fmt.Sprintf("Installed plugin %s %s in %q", pluginRequirement.Identifier.ForDisplay(), newInstall.Version, newInstall.BinaryPath)
msg := fmt.Sprintf("Installed plugin %s %s in %q", pluginRequirement.Identifier, newInstall.Version, newInstall.BinaryPath)
ui.Say(msg)
}
}

View File

@ -291,6 +291,32 @@ func TestInitCommand_Run(t *testing.T) {
testBuild{want: 1}.fn,
},
},
{
"unsupported-non-github-source-address",
[]func(t *testing.T, tc testCaseInit){
skipInitTestUnlessEnVar(acctest.TestEnvVar).fn,
},
testMetaFile(t),
nil,
"h1:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
map[string]string{
`cfg.pkr.hcl`: `
packer {
required_plugins {
comment = {
source = "example.com/sylviamoss/comment"
version = "v0.2.19"
}
}
}`,
},
cfg.dir("6_pkr_config"),
cfg.dir("6_pkr_user_folder"),
1,
nil,
"h1:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@ -10,17 +10,13 @@ import (
// Plugin encapsulates a single plugin type.
type Plugin struct {
Type string
Namespace string
Hostname string
Namespace string
Type string
}
func (p Plugin) RealRelativePath() string {
ns := DefaultPluginNamespace
if p.Namespace != "" {
ns = p.Namespace
}
return ns + "/packer-plugin-" + p.Type
return p.Namespace + "/packer-plugin-" + p.Type
}
func (p Plugin) Parts() []string {
@ -31,23 +27,6 @@ func (p Plugin) String() string {
return strings.Join(p.Parts(), "/")
}
// ForDisplay returns a user-friendly FQN string, simplified for readability. If
// the plugin is using the default hostname, the hostname is omitted.
func (p *Plugin) ForDisplay() string {
parts := []string{}
if p.Hostname != DefaultPluginHost {
parts = append(parts, p.Hostname)
}
if p.Namespace != DefaultPluginNamespace {
parts = append(parts, p.Namespace)
}
parts = append(parts, p.Type)
return strings.Join(parts, "/")
}
const DefaultPluginHost = "github.com"
const DefaultPluginNamespace = "hashicorp"
// ParsePluginPart processes an addrs.Plugin namespace or type string
// provided by an end-user, producing a normalized version if possible or
// an error if the string contains invalid characters.
@ -120,18 +99,18 @@ func IsPluginPartNormalized(str string) (bool, error) {
// hostname/namespace/name
func ParsePluginSourceString(str string) (*Plugin, hcl.Diagnostics) {
ret := &Plugin{
Hostname: DefaultPluginHost,
Namespace: DefaultPluginNamespace,
Hostname: "",
Namespace: "",
}
var diags hcl.Diagnostics
// split the source string into individual components
parts := strings.Split(str, "/")
if len(parts) == 0 || len(parts) > 3 {
if len(parts) != 3 {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid plugin source string",
Detail: `The "source" attribute must be in the format "[hostname/][namespace/]name"`,
Detail: `The "source" attribute must be in the format "hostname/namespace/name"`,
})
return nil, diags
}
@ -142,7 +121,7 @@ func ParsePluginSourceString(str string) (*Plugin, hcl.Diagnostics) {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid plugin source string",
Detail: `The "source" attribute must be in the format "[hostname/][namespace/]name"`,
Detail: `The "source" attribute must be in the format "hostname/namespace/name"`,
})
return nil, diags
}
@ -161,33 +140,21 @@ func ParsePluginSourceString(str string) (*Plugin, hcl.Diagnostics) {
}
ret.Type = name
if len(parts) == 1 {
return ret, diags
// the namespace is always the second-to-last part
givenNamespace := parts[len(parts)-2]
namespace, err := ParsePluginPart(givenNamespace)
if err != nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid plugin namespace",
Detail: fmt.Sprintf(`Invalid plugin namespace %q in source %q: %s"`, namespace, str, err),
})
return nil, diags
}
ret.Namespace = namespace
if len(parts) >= 2 {
// the namespace is always the second-to-last part
givenNamespace := parts[len(parts)-2]
namespace, err := ParsePluginPart(givenNamespace)
if err != nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid plugin namespace",
Detail: fmt.Sprintf(`Invalid plugin namespace %q in source %q: %s"`, namespace, str, err),
})
return nil, diags
}
ret.Namespace = namespace
}
// Final Case: 3 parts
if len(parts) == 3 {
// the hostname is always the first part in a three-part source string
hostname := parts[0]
// TODO(azr): validate host ? Can this be something else than a
// github.com host for now?
ret.Hostname = hostname
}
// the hostname is always the first part in a three-part source string
ret.Hostname = parts[0]
// Due to how plugin executables are named and plugin git repositories
// are conventionally named, it's a reasonable and
@ -217,7 +184,10 @@ func ParsePluginSourceString(str string) (*Plugin, hcl.Diagnostics) {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid plugin type",
Detail: fmt.Sprintf("Plugin source %q has a type with the prefix %q, which isn't valid. Although that prefix is often used in the names of version control repositories for Packer plugins, plugin source strings should not include it.\n\nDid you mean %q?", ret.ForDisplay(), userErrorPrefix, suggestedAddr.ForDisplay()),
Detail: fmt.Sprintf("Plugin source %q has a type with the prefix %q, which isn't valid. "+
"Although that prefix is often used in the names of version control repositories for Packer plugins, "+
"plugin source strings should not include it.\n"+
"\nDid you mean %q?", ret, userErrorPrefix, suggestedAddr),
})
return nil, diags
}

View File

@ -0,0 +1,32 @@
package addrs
import (
"reflect"
"testing"
)
func TestParsePluginSourceString(t *testing.T) {
type args struct {
str string
}
tests := []struct {
args args
want *Plugin
wantDiags bool
}{
{args{"potato"}, nil, true},
{args{"hashicorp/azr"}, nil, true},
{args{"github.com/hashicorp/azr"}, &Plugin{"github.com", "hashicorp", "azr"}, false},
}
for _, tt := range tests {
t.Run(tt.args.str, func(t *testing.T) {
got, gotDiags := ParsePluginSourceString(tt.args.str)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ParsePluginSourceString() got = %v, want %v", got, tt.want)
}
if tt.wantDiags == (len(gotDiags) == 0) {
t.Errorf("Unexpected diags %s", gotDiags)
}
})
}
}

View File

@ -156,7 +156,9 @@ func (p *Parser) Parse(filename string, varFiles []string, argVars map[string]st
// equivalent of having :
// packer {
// required_plugins {
// amazon = "latest"
// amazon = {
// version = "latest"
// source = "github.com/hashicorp/amazon"
// }
// }
// Note: using `latest` ( or actually an empty string ) in a config file

View File

@ -78,7 +78,7 @@ func (cfg *PackerConfig) detectPluginBinaries() hcl.Diagnostics {
if err != nil {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: fmt.Sprintf("Failed to list installation for %s", pluginRequirement.Identifier.ForDisplay()),
Summary: fmt.Sprintf("Failed to list installation for %s", pluginRequirement.Identifier),
Detail: err.Error(),
})
continue
@ -86,18 +86,18 @@ func (cfg *PackerConfig) detectPluginBinaries() hcl.Diagnostics {
if len(sortedInstalls) == 0 {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: fmt.Sprintf("no plugin installed for %s %v", pluginRequirement.Identifier.ForDisplay(), pluginRequirement.VersionConstraints.String()),
Summary: fmt.Sprintf("no plugin installed for %s %v", pluginRequirement.Identifier, pluginRequirement.VersionConstraints.String()),
Detail: "Did you run packer init for this project ?",
})
continue
}
log.Printf("[TRACE] Found the following %q installations: %v", pluginRequirement.Identifier.ForDisplay(), sortedInstalls)
log.Printf("[TRACE] Found the following %q installations: %v", pluginRequirement.Identifier, sortedInstalls)
install := sortedInstalls[len(sortedInstalls)-1]
err = cfg.parser.PluginConfig.DiscoverMultiPlugin(pluginRequirement.Accessor, install.BinaryPath)
if err != nil {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: fmt.Sprintf("Error discovering plugin %s", pluginRequirement.Identifier.ForDisplay()),
Summary: fmt.Sprintf("Error discovering plugin %s", pluginRequirement.Identifier),
Detail: err.Error(),
})
continue

View File

@ -1,7 +1,13 @@
packer {
required_plugins {
amazon = ">= v0"
amazon = ">= v4"
amazon = {
source = "github.com/hashicorp/amazon"
version = ">= v0"
}
amazon = {
source = "github.com/hashicorp/amazon"
version = ">= v4"
}
}
}

View File

@ -3,29 +3,26 @@ packer {
required_version = ">= v1"
required_plugins {
amazon = ">= v0"
amazon = {
source = "github.com/hashicorp/amazon"
version = ">= v0"
}
amazon-v1 = {
source = "amazon"
source = "github.com/hashicorp/amazon"
version = ">= v1"
}
amazon-v2 = {
source = "amazon"
source = "github.com/hashicorp/amazon"
version = ">= v2"
}
amazon-v3 = {
source = "hashicorp/amazon"
source = "github.com/hashicorp/amazon"
version = ">= v3"
}
amazon-v3-azr = {
source = "azr/amazon"
source = "github.com/azr/amazon"
version = ">= v3"
}
amazon-v4 = {
source = "github.com/hashicorp/amazon"
version = ">= v4"

View File

@ -0,0 +1,8 @@
packer {
required_plugins {
amazon = {
source = "amazon"
version = ">= v0"
}
}
}

View File

@ -0,0 +1,8 @@
packer {
required_plugins {
amazon = {
source = "hashicorp/amazon"
version = ">= v0"
}
}
}

View File

@ -0,0 +1,5 @@
packer {
required_plugins {
amazon = ">= v0"
}
}

View File

@ -1,6 +1,7 @@
package hcl2template
import (
"path/filepath"
"testing"
"github.com/hashicorp/go-version"
@ -431,7 +432,7 @@ func TestParser_no_init(t *testing.T) {
RequiredPlugins: map[string]*RequiredPlugin{
"amazon": {
Name: "amazon",
Source: "",
Source: "github.com/hashicorp/amazon",
Type: &addrs.Plugin{
Type: "amazon",
Namespace: "hashicorp",
@ -443,7 +444,7 @@ func TestParser_no_init(t *testing.T) {
},
"amazon-v1": {
Name: "amazon-v1",
Source: "amazon",
Source: "github.com/hashicorp/amazon",
Type: &addrs.Plugin{
Type: "amazon",
Namespace: "hashicorp",
@ -455,7 +456,7 @@ func TestParser_no_init(t *testing.T) {
},
"amazon-v2": {
Name: "amazon-v2",
Source: "amazon",
Source: "github.com/hashicorp/amazon",
Type: &addrs.Plugin{
Type: "amazon",
Namespace: "hashicorp",
@ -467,7 +468,7 @@ func TestParser_no_init(t *testing.T) {
},
"amazon-v3": {
Name: "amazon-v3",
Source: "hashicorp/amazon",
Source: "github.com/hashicorp/amazon",
Type: &addrs.Plugin{
Type: "amazon",
Namespace: "hashicorp",
@ -479,7 +480,7 @@ func TestParser_no_init(t *testing.T) {
},
"amazon-v3-azr": {
Name: "amazon-v3-azr",
Source: "azr/amazon",
Source: "github.com/azr/amazon",
Type: &addrs.Plugin{
Type: "amazon",
Namespace: "azr",
@ -610,6 +611,66 @@ func TestParser_no_init(t *testing.T) {
[]packersdk.Build{},
false,
},
{"invalid_inexplicit_source.pkr.hcl",
defaultParser,
parseTestArgs{"testdata/init/invalid_inexplicit_source.pkr.hcl", nil, nil},
&PackerConfig{
Packer: struct {
VersionConstraints []VersionConstraint
RequiredPlugins []*RequiredPlugins
}{
VersionConstraints: nil,
RequiredPlugins: []*RequiredPlugins{
{},
},
},
CorePackerVersionString: lockedVersion,
Basedir: filepath.Clean("testdata/init"),
},
true, true,
[]packersdk.Build{},
false,
},
{"invalid_short_source.pkr.hcl",
defaultParser,
parseTestArgs{"testdata/init/invalid_short_source.pkr.hcl", nil, nil},
&PackerConfig{
Packer: struct {
VersionConstraints []VersionConstraint
RequiredPlugins []*RequiredPlugins
}{
VersionConstraints: nil,
RequiredPlugins: []*RequiredPlugins{
{},
},
},
CorePackerVersionString: lockedVersion,
Basedir: filepath.Clean("testdata/init"),
},
true, true,
[]packersdk.Build{},
false,
},
{"invalid_inexplicit_source_2.pkr.hcl",
defaultParser,
parseTestArgs{"testdata/init/invalid_inexplicit_source_2.pkr.hcl", nil, nil},
&PackerConfig{
Packer: struct {
VersionConstraints []VersionConstraint
RequiredPlugins []*RequiredPlugins
}{
VersionConstraints: nil,
RequiredPlugins: []*RequiredPlugins{
{},
},
},
CorePackerVersionString: lockedVersion,
Basedir: filepath.Clean("testdata/init"),
},
true, true,
[]packersdk.Build{},
false,
},
}
testParse_only_Parse(t, tests)
}

View File

@ -62,7 +62,11 @@ func (cfg *PackerConfig) decodeImplicitRequiredPluginsBlocks(f *hcl.File) hcl.Di
// RequiredPlugin represents a declaration of a dependency on a particular
// Plugin version or source.
type RequiredPlugin struct {
Name string
Name string
// Source used to be able to tell how the template referenced this source,
// for example, "awesomecloud" instead of github.com/awesome/awesomecloud.
// This one is left here in case we want to go back to allowing inexplicit
// source url definitions.
Source string
Type *addrs.Plugin
Requirement VersionConstraint
@ -77,7 +81,7 @@ type RequiredPlugins struct {
func decodeRequiredPluginsBlock(block *hcl.Block) (*RequiredPlugins, hcl.Diagnostics) {
attrs, diags := block.Body.JustAttributes()
ret := &RequiredPlugins{
RequiredPlugins: make(map[string]*RequiredPlugin),
RequiredPlugins: nil,
DeclRange: block.DefRange,
}
for name, attr := range attrs {
@ -96,18 +100,24 @@ func decodeRequiredPluginsBlock(block *hcl.Block) (*RequiredPlugins, hcl.Diagnos
switch {
case expr.Type().IsPrimitiveType():
vc, reqDiags := decodeVersionConstraint(attr)
diags = append(diags, reqDiags...)
rp.Requirement = vc
rp.Type, err = addrs.ParsePluginSourceString(name)
if err != nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid plugin type",
Detail: fmt.Sprintf(`Invalid plugin type %q: %s"`, name, err),
})
c := "version"
if cs, _ := decodeVersionConstraint(attr); len(cs.Required) > 0 {
c = cs.Required.String()
}
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid plugin requirement",
Detail: fmt.Sprintf(`'%s = "%s"' plugin requirement calls are not possible.`+
` You must define a whole block. For example:`+"\n"+
`%[1]s = {`+"\n"+
` source = "github.com/hashicorp/%[1]s"`+"\n"+
` version = "%[2]s"`+"\n"+`}`,
name, c),
Subject: attr.Range.Ptr(),
})
continue
case expr.Type().IsObjectType():
if !expr.Type().HasAttribute("version") {
diags = append(diags, &hcl.Diagnostic{
@ -140,8 +150,10 @@ func decodeRequiredPluginsBlock(block *hcl.Block) (*RequiredPlugins, hcl.Diagnos
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid version constraint",
Detail: "This string does not use correct version constraint syntax. See https://www.packer.io/docs/templates/hcl_templates/blocks/packer#version-constraint-syntax for docs.",
Subject: attr.Expr.Range().Ptr(),
Detail: "This string does not use correct version constraint syntax. " +
"See https://www.packer.io/docs/templates/hcl_templates/blocks/packer#version-constraint-syntax for docs.\n" +
err.Error(),
Subject: attr.Expr.Range().Ptr(),
})
continue
}
@ -179,6 +191,7 @@ func decodeRequiredPluginsBlock(block *hcl.Block) (*RequiredPlugins, hcl.Diagnos
}
}
diags = append(diags, sourceDiags...)
continue
} else {
rp.Type = p
}
@ -207,6 +220,9 @@ func decodeRequiredPluginsBlock(block *hcl.Block) (*RequiredPlugins, hcl.Diagnos
})
}
if ret.RequiredPlugins == nil {
ret.RequiredPlugins = make(map[string]*RequiredPlugin)
}
ret.RequiredPlugins[rp.Name] = rp
}

View File

@ -82,7 +82,6 @@ func (v *Variable) GoString() string {
// validateValue ensures that all of the configured custom validations for a
// variable value are passing.
//
func (v *Variable) validateValue(val VariableAssignment) (diags hcl.Diagnostics) {
if len(v.Validations) == 0 {
log.Printf("[TRACE] validateValue: not active for %s, so skipping", v.Name)

View File

@ -23,6 +23,7 @@ import (
const (
ghTokenAccessor = "PACKER_GITHUB_API_TOKEN"
defaultUserAgent = "packer-plugin-getter"
defaultHostname = "github.com"
)
type Getter struct {
@ -154,6 +155,11 @@ func (t *HostSpecificTokenAuthTransport) base() http.RoundTripper {
}
func (g *Getter) Get(what string, opts plugingetter.GetOptions) (io.ReadCloser, error) {
if opts.PluginRequirement.Identifier.Hostname != defaultHostname {
s := opts.PluginRequirement.Identifier.String() + " doesn't appear to be a valid " + defaultHostname + " source address; check source and try again."
return nil, errors.New(s)
}
ctx := context.TODO()
if g.Client == nil {
var tc *http.Client

View File

@ -27,7 +27,7 @@ type Requirements []*Requirement
type Requirement struct {
// Plugin accessor as defined in the config file.
// For Packer, using :
// required_plugins { amazon = ">= v0" }
// required_plugins { amazon = {...} }
// Will set Accessor to `amazon`.
Accessor string
@ -81,7 +81,7 @@ func (pr Requirement) ListInstallations(opts ListInstallationsOptions) (InstallL
res := InstallList{}
FilenamePrefix := pr.FilenamePrefix()
filenameSuffix := opts.filenameSuffix()
log.Printf("[TRACE] listing potential installations for %q that match %q. %#v", pr.Identifier.ForDisplay(), pr.VersionConstraints, opts)
log.Printf("[TRACE] listing potential installations for %q that match %q. %#v", pr.Identifier, pr.VersionConstraints, opts)
for _, knownFolder := range opts.FromFolders {
glob := filepath.Join(knownFolder, pr.Identifier.Hostname, pr.Identifier.Namespace, pr.Identifier.Type, FilenamePrefix+"*"+filenameSuffix)
@ -345,7 +345,7 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error)
getters := opts.Getters
fail := fmt.Errorf("could not find a local nor a remote checksum for plugin %q %q", pr.Identifier, pr.VersionConstraints)
log.Printf("[TRACE] getting available versions for the the %s plugin", pr.Identifier.ForDisplay())
log.Printf("[TRACE] getting available versions for the %s plugin", pr.Identifier)
versions := version.Collection{}
for _, getter := range getters {
@ -397,7 +397,7 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error)
log.Printf("[DEBUG] will try to install: %s", versions)
if len(versions) == 0 {
err := fmt.Errorf("no release version found for the %s plugin matching the constraint(s): %q", pr.Identifier.ForDisplay(), pr.VersionConstraints.String())
err := fmt.Errorf("no release version found for the %s plugin matching the constraint(s): %q", pr.Identifier, pr.VersionConstraints.String())
return nil, err
}
@ -411,7 +411,7 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error)
filepath.Join(pr.Identifier.Parts()...),
)
log.Printf("[TRACE] fetching checksums file for the %q version of the %s plugin in %q...", version, pr.Identifier.ForDisplay(), outputFolder)
log.Printf("[TRACE] fetching checksums file for the %q version of the %s plugin in %q...", version, pr.Identifier, outputFolder)
var checksum *FileChecksum
for _, getter := range getters {
@ -428,7 +428,7 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error)
version: version,
})
if err != nil {
err := fmt.Errorf("could not get %s checksum file for %s version %s. Is the file present on the release and correctly named ? %s", checksummer.Type, pr.Identifier.ForDisplay(), version, err)
err := fmt.Errorf("could not get %s checksum file for %s version %s. Is the file present on the release and correctly named ? %s", checksummer.Type, pr.Identifier, version, err)
log.Printf("[TRACE] %s", err.Error())
return nil, err
}
@ -486,7 +486,7 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error)
log.Printf("[TRACE] found a pre-exising %q checksum file", potentialChecksumer.Type)
// if outputFile is there and matches the checksum: do nothing more.
if err := localChecksum.ChecksumFile(localChecksum.Expected, potentialOutputFilename); err == nil {
log.Printf("[INFO] %s v%s plugin is already correctly installed in %q", pr.Identifier.ForDisplay(), version, potentialOutputFilename)
log.Printf("[INFO] %s v%s plugin is already correctly installed in %q", pr.Identifier, version, potentialOutputFilename)
return nil, nil
}
}
@ -519,7 +519,7 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error)
expectedZipFilename: expectedZipFilename,
})
if err != nil {
err := fmt.Errorf("could not get binary for %s version %s. Is the file present on the release and correctly named ? %s", pr.Identifier.ForDisplay(), version, err)
err := fmt.Errorf("could not get binary for %s version %s. Is the file present on the release and correctly named ? %s", pr.Identifier, version, err)
log.Printf("[TRACE] %v", err)
continue
}

View File

@ -43,7 +43,7 @@ func TestPlugin_ListInstallations(t *testing.T) {
{
"darwin_amazon_prot_5.0",
fields{
Identifier: "amazon",
Identifier: "github.com/hashicorp/amazon",
},
ListInstallationsOptions{
[]string{
@ -80,7 +80,7 @@ func TestPlugin_ListInstallations(t *testing.T) {
{
"darwin_amazon_prot_5.1",
fields{
Identifier: "amazon",
Identifier: "github.com/hashicorp/amazon",
},
ListInstallationsOptions{
[]string{
@ -121,7 +121,7 @@ func TestPlugin_ListInstallations(t *testing.T) {
{
"windows_amazon",
fields{
Identifier: "amazon",
Identifier: "github.com/hashicorp/amazon",
},
ListInstallationsOptions{
[]string{
@ -159,7 +159,7 @@ func TestPlugin_ListInstallations(t *testing.T) {
{
"windows_google_multifolder",
fields{
Identifier: "hashicorp/google",
Identifier: "github.com/hashicorp/google",
},
ListInstallationsOptions{
[]string{
@ -542,7 +542,7 @@ func TestRequirement_InstallLatest(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
log.Printf("starting %s test", tt.name)
identifier, diags := addrs.ParsePluginSourceString(tt.fields.Identifier)
identifier, diags := addrs.ParsePluginSourceString("github.com/hashicorp/" + tt.fields.Identifier)
if len(diags) != 0 {
t.Fatalf("ParsePluginSourceString(%q): %v", tt.fields.Identifier, diags)
}

View File

@ -44,11 +44,10 @@ block :
```hcl
packer {
required_plugins {
myawesomecloud = {
happycloud = {
version = ">= 2.7.0"
source = "azr/myawesomecloud"
source = "github.com/azr/happycloud"
}
happycloud = ">= 2.7.0"
}
}
```
@ -57,8 +56,8 @@ HashiCorp does not officially verify third party Packer plugins, plugins not und
## Plugin Selection
Plugin selection depends on the source and version constraints defined within the `required_plugins` block.
For each of the required plugins Packer will query the source repository `azr/myawesomecloud` whose fully qualified address
is `https://github.com/azr/packer-plugin-myawesomecloud` for a plugin matching the version constraints for the host operating system.
For each of the required plugins Packer will query the source repository `github.com/azr/happycloud` whose fully qualified address
is `https://github.com/azr/packer-plugin-happycloud` for a plugin matching the version constraints for the host operating system.
Packer init will install the latest found version matching the version selection
in the `required_plugins` section. Make sure to set a correct [version

View File

@ -76,11 +76,11 @@ Here is an example `required_plugins` block:
required_plugins {
myawesomecloud = {
version = ">= 2.7.0"
source = "azr/myawesomecloud"
source = "github.com/azr/myawesomecloud"
}
happycloud = {
version = ">= 1.1.3"
source = "azr/happycloud"
source = "github.com/azr/happycloud"
}
}
}
@ -130,7 +130,7 @@ If we change the required_plugins block to use a different local name "foo":
required_plugins {
foo = {
version = ">= 2.7.0"
source = "azr/myawesomecloud"
source = "github.com/azr/myawesomecloud"
}
}
```
@ -151,37 +151,29 @@ to download it.
Source addresses consist of three parts delimited by slashes (`/`), as
follows:
`[<HOSTNAME>/]<NAMESPACE>/<TYPE>`
`<HOSTNAME>/<NAMESPACE>/<TYPE>`
* **Hostname** (optional): The hostname of the location/service that
distributes the plugin. If omitted, this defaults to
`github.com`, we recommend explicitly setting the hostname. Currently, the
only valid "hostname" is github.com, but we plan to eventually support plugins
downloaded from other domains.
* **Hostname:** The hostname of the location/service that
distributes the plugin. Currently, the only valid "hostname" is github.com,
but we plan to eventually support plugins downloaded from other domains.
* **Namespace:** An organizational namespace within the specified host.
This often is the organization that publishes the plugin. If omitted, this
defaults to `hashicorp`. We recommend explicitly setting the namespace.
This often is the organization that publishes the plugin.
* **Type:** A short name for the platform or system the plugin manages. The
type is usually the plugin's preferred local name.
For example, the fictional `myawesomecloud` plugin could belong to the
`hashicorp` namespace on `github.com`, so its `source` could be
`github.com/hashicorp/myawesomecloud`, `hashicorp/myawesomecloud` or
`myawesomecloud`. Note: the actual _repository_ that myawesomecloud comes from
must always have the name format
`www.github.com/hashicorp/packer-plugin-myawesomecloud`, but the
`github.com/hashicorp/myawesomecloud`,
Note: the actual _repository_ that myawesomecloud comes from must always have
the name format `github.com/hashicorp/packer-plugin-myawesomecloud`, but the
`required_plugins` block omits the redundant `packer-plugin-` repository prefix
for brevity.
The source address with all three components given explicitly is called the
plugin's _fully-qualified address_. You will see fully-qualified address in
various outputs, like error messages, but in most cases a simplified display
version is used. Therefore you may see the shortened version `"myawesomecloud"`
instead of `"github.com/hashicorp/myawesomecloud"`.
-> **Note:** We recommend using explicit source addresses for all plugins.
various outputs, like error messages.
## Plugin location
@ -192,17 +184,15 @@ instead of `"github.com/hashicorp/myawesomecloud"`.
Using the following example :
```hcl
required_plugins {
myawesomecloud = {
happycloud = {
version = ">= 2.7.0"
source = "azr/myawesomecloud"
source = "github.com/azr/happycloud"
}
happycloud = ">= 2.7.0"
}
```
The plugin getter will look for plugins located at:
* github.com/azr/packer-plugin-myawesomecloud
* github.com/hashicorp/packer-plugin-happycloud
* github.com/azr/packer-plugin-happycloud
Packer will error if you set the `packer-plugin-` prefix in a `source`. This
will avoid conflicting with other plugins for other tools, like Terraform.

View File

@ -59,11 +59,10 @@ version constraint.
```hcl
packer {
required_plugins {
myawesomecloud = {
happycloud = {
version = ">= 2.7.0"
source = "hashicorp/myawesomecloud"
source = "github.com/hashicorp/happycloud"
}
happycloud = ">= 2.7.0"
}
}
```

View File

@ -135,10 +135,12 @@ in the rest of the template.
Here it is a brief explanation of each field:
- `version` - Should follow the [version constraints](/docs/templates/hcl_templates/blocks/packer#version-constraints).
- `source` - Should have the plugin's organizational namespace on GitHub and its short name. Packer will be responsible
for determining the full GitHub address.
For example, if the source is `sylviamoss/comment`, Packer will download the binaries from `github.com/sylviamoss/packer-plugin-comment`.
To learn more about the source field, check out the [Source Address](/docs/plugins#source-addresses) documentation.
- `source` - Should have the GitHub hostname, the plugin's organizational namespace, and its short name.
Packer will be responsible for determining the full GitHub
address. For example, if the source is `github.com/sylviamoss/comment`, Packer
will download the binaries from `github.com/sylviamoss/packer-plugin-comment`.
To learn more about the source field, check out the [Source
Address](/docs/plugins#source-addresses) documentation.
- `local_name`- Can be replaced with whatever you want, and the new value will become the name of the plugin.
For example:
```hcl
@ -180,4 +182,3 @@ and won't need to run `init` again unless you want to upgrade the plugin version
A template with a `required_plugins` block should **always** be initialised at least once with `packer init` before
`packer build`. If the template is built before init, Packer will fail and ask for initialisation.

View File

@ -24,28 +24,26 @@ colon (`:`) on other systems. The order priority will be kept.
Using the following example :
```hcl
required_plugins {
myawesomecloud = {
happycloud = {
version = ">= 2.7.0"
source = "azr/myawesomecloud"
source = "github.com/azr/happycloud"
}
happycloud = ">= 2.7.0"
}
```
The plugin getter will then install the binaries in the following location for a
system with no `PACKER_PLUGIN_PATH` env var set.
* `PACKER_HOME_DIR/plugins/github.com/azr/myawesomecloud/`
* `PACKER_HOME_DIR/plugins/github.com/hashicorp/happycloud/`
During initialization, on a `darwin_amd64` system, Packer will look-up for the
following files:
* `PACKER_EXEC_DIR/github.com/azr/myawesomecloud/packer-plugin-myawesomecloud_*_darwin_amd64_x5`
* `./github.com/azr/myawesomecloud/packer-plugin-myawesomecloud_*_darwin_amd64_x5`
* `PACKER_HOME_DIR/plugins/github.com/azr/myawesomecloud/packer-plugin-myawesomecloud_*_darwin_amd64_x5`
* `PACKER_PLUGIN_PATH/github.com/azr/myawesomecloud/packer-plugin-myawesomecloud_*_darwin_amd64_x5`
* `./packer-plugin-myawesomecloud`
* `PACKER_EXEC_DIR/github.com/azr/happycloud/packer-plugin-happycloud_*_darwin_amd64_x5.0`
* `./github.com/azr/happycloud/packer-plugin-happycloud_*_darwin_amd64_x5.0`
* `PACKER_HOME_DIR/plugins/github.com/azr/happycloud/packer-plugin-happycloud_*_darwin_amd64_x5.0`
* `PACKER_PLUGIN_PATH/github.com/azr/happycloud/packer-plugin-happycloud_*_darwin_amd64_x5.0`
* `./packer-plugin-happycloud`
The first plugin-name/version files found will take precedence.