Merge pull request #10633 from hashicorp/azr_init_no_magic_host
Packer init: remove host and namespace guessing
This commit is contained in:
commit
1710590418
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
packer {
|
||||
required_plugins {
|
||||
amazon = {
|
||||
source = "amazon"
|
||||
version = ">= v0"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
packer {
|
||||
required_plugins {
|
||||
amazon = {
|
||||
source = "hashicorp/amazon"
|
||||
version = ">= v0"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
packer {
|
||||
required_plugins {
|
||||
amazon = ">= v0"
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
Loading…
Reference in New Issue