diff --git a/CHANGELOG.md b/CHANGELOG.md index 2abecb619..b0a5ac684 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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] diff --git a/command/init.go b/command/init.go index ed5903030..eab38b648 100644 --- a/command/init.go +++ b/command/init.go @@ -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) } } diff --git a/command/init_test.go b/command/init_test.go index 0b86379c7..6a44bc620 100644 --- a/command/init_test.go +++ b/command/init_test.go @@ -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) { diff --git a/hcl2template/addrs/plugin.go b/hcl2template/addrs/plugin.go index 88aa53fc2..8fae62620 100644 --- a/hcl2template/addrs/plugin.go +++ b/hcl2template/addrs/plugin.go @@ -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 - } - - 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 + // 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 } diff --git a/hcl2template/addrs/plugin_test.go b/hcl2template/addrs/plugin_test.go new file mode 100644 index 000000000..191f103fa --- /dev/null +++ b/hcl2template/addrs/plugin_test.go @@ -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) + } + }) + } +} diff --git a/hcl2template/parser.go b/hcl2template/parser.go index 15c6cc37c..6a14fba13 100644 --- a/hcl2template/parser.go +++ b/hcl2template/parser.go @@ -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 diff --git a/hcl2template/plugin.go b/hcl2template/plugin.go index 4b1e81859..b43abdeb5 100644 --- a/hcl2template/plugin.go +++ b/hcl2template/plugin.go @@ -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 diff --git a/hcl2template/testdata/init/duplicate_required_plugins/packer.pkr.hcl b/hcl2template/testdata/init/duplicate_required_plugins/packer.pkr.hcl index b60e10ac0..1cf3f291e 100644 --- a/hcl2template/testdata/init/duplicate_required_plugins/packer.pkr.hcl +++ b/hcl2template/testdata/init/duplicate_required_plugins/packer.pkr.hcl @@ -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" + } } } \ No newline at end of file diff --git a/hcl2template/testdata/init/imports/build.pkr.hcl b/hcl2template/testdata/init/imports/build.pkr.hcl index 453fa045e..7220f3b93 100644 --- a/hcl2template/testdata/init/imports/build.pkr.hcl +++ b/hcl2template/testdata/init/imports/build.pkr.hcl @@ -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" diff --git a/hcl2template/testdata/init/invalid_inexplicit_source.pkr.hcl b/hcl2template/testdata/init/invalid_inexplicit_source.pkr.hcl new file mode 100644 index 000000000..3c84ffc87 --- /dev/null +++ b/hcl2template/testdata/init/invalid_inexplicit_source.pkr.hcl @@ -0,0 +1,8 @@ +packer { + required_plugins { + amazon = { + source = "amazon" + version = ">= v0" + } + } +} \ No newline at end of file diff --git a/hcl2template/testdata/init/invalid_inexplicit_source_2.pkr.hcl b/hcl2template/testdata/init/invalid_inexplicit_source_2.pkr.hcl new file mode 100644 index 000000000..3e71b828e --- /dev/null +++ b/hcl2template/testdata/init/invalid_inexplicit_source_2.pkr.hcl @@ -0,0 +1,8 @@ +packer { + required_plugins { + amazon = { + source = "hashicorp/amazon" + version = ">= v0" + } + } +} \ No newline at end of file diff --git a/hcl2template/testdata/init/invalid_short_source.pkr.hcl b/hcl2template/testdata/init/invalid_short_source.pkr.hcl new file mode 100644 index 000000000..13ea61a42 --- /dev/null +++ b/hcl2template/testdata/init/invalid_short_source.pkr.hcl @@ -0,0 +1,5 @@ +packer { + required_plugins { + amazon = ">= v0" + } +} \ No newline at end of file diff --git a/hcl2template/types.packer_config_test.go b/hcl2template/types.packer_config_test.go index 87c7825bf..5a522b4d0 100644 --- a/hcl2template/types.packer_config_test.go +++ b/hcl2template/types.packer_config_test.go @@ -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) } diff --git a/hcl2template/types.required_plugins.go b/hcl2template/types.required_plugins.go index 072966021..736aeb2c1 100644 --- a/hcl2template/types.required_plugins.go +++ b/hcl2template/types.required_plugins.go @@ -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 } diff --git a/hcl2template/types.variables.go b/hcl2template/types.variables.go index 217ead998..b56b5f5a7 100644 --- a/hcl2template/types.variables.go +++ b/hcl2template/types.variables.go @@ -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) diff --git a/packer/plugin-getter/github/getter.go b/packer/plugin-getter/github/getter.go index c34e092a8..c33c55d77 100644 --- a/packer/plugin-getter/github/getter.go +++ b/packer/plugin-getter/github/getter.go @@ -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 diff --git a/packer/plugin-getter/plugins.go b/packer/plugin-getter/plugins.go index c7909174c..9595b67a0 100644 --- a/packer/plugin-getter/plugins.go +++ b/packer/plugin-getter/plugins.go @@ -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 } diff --git a/packer/plugin-getter/plugins_test.go b/packer/plugin-getter/plugins_test.go index 0fc30fb0a..bbf9745d6 100644 --- a/packer/plugin-getter/plugins_test.go +++ b/packer/plugin-getter/plugins_test.go @@ -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) } diff --git a/website/content/docs/commands/init.mdx b/website/content/docs/commands/init.mdx index 6d1e7c948..e4c3dbad1 100644 --- a/website/content/docs/commands/init.mdx +++ b/website/content/docs/commands/init.mdx @@ -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 diff --git a/website/content/docs/plugins/index.mdx b/website/content/docs/plugins/index.mdx index 0e333a0e0..84daeeac5 100644 --- a/website/content/docs/plugins/index.mdx +++ b/website/content/docs/plugins/index.mdx @@ -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** (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. diff --git a/website/content/docs/templates/hcl_templates/blocks/packer.mdx b/website/content/docs/templates/hcl_templates/blocks/packer.mdx index b68bd1226..b88d761b8 100644 --- a/website/content/docs/templates/hcl_templates/blocks/packer.mdx +++ b/website/content/docs/templates/hcl_templates/blocks/packer.mdx @@ -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" } } ``` diff --git a/website/content/guides/1.7-template-upgrade.mdx b/website/content/guides/1.7-template-upgrade.mdx index 91aff5d32..3f27a894b 100644 --- a/website/content/guides/1.7-template-upgrade.mdx +++ b/website/content/guides/1.7-template-upgrade.mdx @@ -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. - diff --git a/website/content/partials/plugins/plugin-location.mdx b/website/content/partials/plugins/plugin-location.mdx index 76ab8f6e6..3935c2bf9 100644 --- a/website/content/partials/plugins/plugin-location.mdx +++ b/website/content/partials/plugins/plugin-location.mdx @@ -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.