Compare commits

...

50 Commits

Author SHA1 Message Date
Adrien Delorme
780f9c8b41 empty commit 2021-03-24 11:22:30 +01:00
Adrien Delorme
3629aeaa01 Merge remote-tracking branch 'origin/master' into azr_implicit_requried_plugin 2021-03-24 11:08:05 +01:00
Adrien Delorme
c207e4acad Update init.mdx 2021-03-24 10:49:03 +01:00
Adrien Delorme
519dfa1959 Update init.mdx
improve Implicit required plugin section
2021-03-24 10:45:18 +01:00
Adrien Delorme
51024ed507
Update website/content/docs/commands/init.mdx
Co-authored-by: Sylvia Moss <moss@hashicorp.com>
2021-03-23 13:35:05 +01:00
Adrien Delorme
a060cd81a5 Update init.mdx 2021-03-23 13:26:26 +01:00
Adrien Delorme
4ab0d14f8c Update init.mdx 2021-03-23 13:12:22 +01:00
Adrien Delorme
4cd4616641 Update init.mdx 2021-03-23 13:12:05 +01:00
Adrien Delorme
e470ec0ffc Merge remote-tracking branch 'origin/master' into azr_implicit_requried_plugin 2021-03-23 13:09:41 +01:00
Adrien Delorme
e976c30964 Update init.mdx 2021-03-23 13:09:16 +01:00
Adrien Delorme
7ed9e672a9
Update command/init.go
clearer and shorter warning ( using docs link )
2021-03-23 13:07:59 +01:00
Adrien Delorme
ff5b5221c4 Update init.mdx 2021-03-23 13:06:20 +01:00
Adrien Delorme
b345a44a07 Update init.mdx 2021-03-23 13:05:16 +01:00
Adrien Delorme
d8cbfcc075
Update website/content/docs/commands/init.mdx 2021-03-19 11:36:12 +01:00
Adrien Delorme
eba081a155 Update types.required_plugins.go
more comments
2021-03-19 11:18:39 +01:00
Adrien Delorme
7ccfe38475 Update types.required_plugins.go
up comment
2021-03-19 11:16:13 +01:00
Adrien Delorme
dacf52b5b3 check for pre-existing component definition first 2021-03-19 11:13:43 +01:00
Adrien Delorme
1926fd6176
Update command/init.go
better warning

Co-authored-by: Sylvia Moss <moss@hashicorp.com>
2021-03-18 16:43:29 +01:00
Adrien Delorme
15f4b93cd2 init.mdx: add short text on implicit plugin requirements 2021-03-17 11:46:42 +01:00
Adrien Delorme
46b6a21a9f Update types.required_plugins.go
better comments
2021-03-17 11:24:37 +01:00
Adrien Delorme
53dc1eb87b better comments 2021-03-17 11:03:42 +01:00
Adrien Delorme
0c45f75bf6 better comments 2021-03-17 11:02:20 +01:00
Adrien Delorme
9b41474ab5 better comments 2021-03-17 10:59:55 +01:00
Adrien Delorme
2f4c5c0a24 test that already defined components won't create implicit required plugins 2021-03-17 10:30:37 +01:00
Adrien Delorme
85aef9d3a6 skip a implictly requiring a plugin if the plugin name of the plugin is being defined in a required_plugin 2021-03-17 10:17:03 +01:00
Adrien Delorme
ad21a101c8 add comments 2021-03-17 10:16:24 +01:00
Adrien Delorme
ef974dd75c add comments 2021-03-17 09:56:00 +01:00
Adrien Delorme
69de942647 requirePluginImplicitly => decodeImplicitRequiredPluginsBlock 2021-03-17 09:54:45 +01:00
Adrien Delorme
2e9307de3f define ComponentKind that help enumerate what kind of components exist in hcl 2021-03-17 09:53:26 +01:00
Adrien Delorme
27fb9b2525 rimplify requirePluginImplicitly a little 2021-03-17 09:52:59 +01:00
Adrien Delorme
47fa09a9c9 hcl2template: Rename Datasource type to DatasourceBlock 2021-03-17 09:42:47 +01:00
Adrien Delorme
7e4948502f add test for a forked plugin 2021-03-17 09:29:36 +01:00
Adrien Delorme
f00ea7a166 more docs 2021-03-17 09:19:14 +01:00
Adrien Delorme
bdc8ac2813 document redirects 2021-03-16 17:59:55 +01:00
Adrien Delorme
7ce5a2b9cf comment redirects for now 2021-03-16 17:59:47 +01:00
Adrien Delorme
2c48e04f1f add all plugins that we plan on moving out 2021-03-16 17:29:29 +01:00
Adrien Delorme
dba5171fa2 improve wording some more 2021-03-16 13:59:24 +01:00
Adrien Delorme
a1de1559bf improve implicit plugin wording 2021-03-16 13:55:01 +01:00
Adrien Delorme
5497139f2a better init wording 2021-03-16 13:36:42 +01:00
Adrien Delorme
172cec7223 better wording for implicitly required plugins 2021-03-15 17:13:58 +01:00
Adrien Delorme
08b0884521 Vet command/init.go 2021-03-09 16:16:08 +01:00
Adrien Delorme
3224438a34 vet 2021-03-09 16:16:08 +01:00
Adrien Delorme
2b4812837a Update main.go
show where to add maps
2021-03-09 16:16:07 +01:00
Adrien Delorme
123517aec1 add warning with what to when calling init on an implicitly required plugin 2021-03-09 16:16:07 +01:00
Adrien Delorme
c4535b2552 Update types.required_plugins_test.go 2021-03-09 16:16:07 +01:00
Adrien Delorme
1d10da1126 better docs 2021-03-09 16:16:07 +01:00
Adrien Delorme
8f7b148cbb Update types.required_plugins.go 2021-03-09 16:16:07 +01:00
Adrien Delorme
924fb0f8f8 make sure renamed plugins don't match if not defined 2021-03-09 16:16:07 +01:00
Adrien Delorme
df4c5a1314 actually parse hcl blocks 2021-03-09 16:16:07 +01:00
Adrien Delorme
37e494a6e8 add basic inferImplicitRequiredPluginFromBlocks + tests
this is not done; I think testing with an actual parsing will be better
2021-03-09 16:16:06 +01:00
12 changed files with 653 additions and 18 deletions

View File

@ -121,13 +121,56 @@ func (c *InitCommand) RunContext(buildCtx context.Context, cla *InitArgs) int {
Getters: getters,
})
if err != nil {
if pluginRequirement.Implicit {
msg := fmt.Sprintf(`
Warning! At least one component used in your config file(s) has moved out of
Packer into the %q plugin.
For that reason, Packer init tried to install the latest version of the %s
plugin. Unfortunately, this failed :
%s`,
pluginRequirement.Identifier,
pluginRequirement.Identifier.Type,
err)
c.Ui.Say(msg)
} else {
c.Ui.Error(err.Error())
ret = 1
}
}
if newInstall != nil {
if pluginRequirement.Implicit {
msg := fmt.Sprintf("Installed implicitly required plugin %s %s in %q", pluginRequirement.Identifier, newInstall.Version, newInstall.BinaryPath)
ui.Say(msg)
warn := fmt.Sprintf(`
Warning, at least one component used in your config file(s) has moved out of
Packer into the %[2]q plugin and is now being implicitly required.
For more details on implicitly required plugins see https://packer.io/docs/commands/init#implicit-required-plugin
To avoid any backward incompatible changes with your
config file you may want to lock the plugin version by pasting the following to your config:
packer {
required_plugins {
%[1]s = {
source = "%[2]s"
version = "~> %[3]s"
}
}
}
`,
pluginRequirement.Identifier.Type,
pluginRequirement.Identifier,
newInstall.Version,
)
ui.Error(warn)
continue
}
msg := fmt.Sprintf("Installed plugin %s %s in %q", pluginRequirement.Identifier, newInstall.Version, newInstall.BinaryPath)
ui.Say(msg)
}
}
return ret
}

View File

@ -20,8 +20,8 @@ import (
const lockedVersion = "v1.5.0"
func getBasicParser() *Parser {
return &Parser{
func getBasicParser(opts ...getParserOption) *Parser {
parser := &Parser{
CorePackerVersion: version.Must(version.NewSemver(lockedVersion)),
CorePackerVersionString: lockedVersion,
Parser: hclparse.NewParser(),
@ -44,8 +44,14 @@ func getBasicParser() *Parser {
},
},
}
for _, configure := range opts {
configure(parser)
}
return parser
}
type getParserOption func(*Parser)
type parseTestArgs struct {
filename string
vars map[string]string
@ -338,7 +344,7 @@ var cmpOpts = []cmp.Option{
PackerConfig{},
Variable{},
SourceBlock{},
Datasource{},
DatasourceBlock{},
ProvisionerBlock{},
PostProcessorBlock{},
packer.CoreBuild{},

View File

@ -0,0 +1,11 @@
package hcl2template
// ComponentKind helps enumerate what kind of components exist in this Package.
type ComponentKind int
const (
Builder ComponentKind = iota
Provisioner
PostProcessor
Datasource
)

View File

@ -41,6 +41,7 @@ func (cfg *PackerConfig) PluginRequirements() (plugingetter.Requirements, hcl.Di
Accessor: name,
Identifier: block.Type,
VersionConstraints: block.Requirement.Required,
Implicit: block.PluginDependencyReason == PluginDependencyImplicit,
})
uniq[name] = block
}

View File

@ -11,8 +11,8 @@ import (
"github.com/zclconf/go-cty/cty"
)
// DataBlock references an HCL 'data' block.
type Datasource struct {
// DatasourceBlock references an HCL 'data' block.
type DatasourceBlock struct {
Type string
Name string
@ -25,9 +25,9 @@ type DatasourceRef struct {
Name string
}
type Datasources map[DatasourceRef]Datasource
type Datasources map[DatasourceRef]DatasourceBlock
func (data *Datasource) Ref() DatasourceRef {
func (data *DatasourceBlock) Ref() DatasourceRef {
return DatasourceRef{
Type: data.Type,
Name: data.Name,
@ -124,9 +124,9 @@ func (cfg *PackerConfig) startDatasource(dataSourceStore packer.DatasourceStore,
return datasource, diags
}
func (p *Parser) decodeDataBlock(block *hcl.Block) (*Datasource, hcl.Diagnostics) {
func (p *Parser) decodeDataBlock(block *hcl.Block) (*DatasourceBlock, hcl.Diagnostics) {
var diags hcl.Diagnostics
r := &Datasource{
r := &DatasourceBlock{
Type: block.Labels[0],
Name: block.Labels[1],
block: block,

View File

@ -131,7 +131,7 @@ func TestParser_complete(t *testing.T) {
},
},
Datasources: Datasources{
DatasourceRef{Type: "amazon-ami", Name: "test"}: Datasource{
DatasourceRef{Type: "amazon-ami", Name: "test"}: DatasourceBlock{
Type: "amazon-ami",
Name: "test",
value: cty.StringVal("foo"),

View File

@ -6,6 +6,7 @@ import (
"github.com/hashicorp/go-version"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/packer/hcl2template/addrs"
"github.com/hashicorp/packer/packer"
"github.com/zclconf/go-cty/cty"
)
@ -41,9 +42,13 @@ func (cfg *PackerConfig) decodeRequiredPluginsBlock(f *hcl.File) hcl.Diagnostics
}
func (cfg *PackerConfig) decodeImplicitRequiredPluginsBlocks(f *hcl.File) hcl.Diagnostics {
// when a plugin is used but not defined in the required plugin blocks, it
// is 'implicitly used'. Here we read common configuration blocks to try to
// guess plugins.
// when a plugin is used but not available it should be 'implicitly
// required'. Here we read common configuration blocks to try to guess
// plugin usages.
// decodeRequiredPluginsBlock needs to be called before
// decodeImplicitRequiredPluginsBlocks; otherwise all required plugins will
// be implicitly required too.
var diags hcl.Diagnostics
@ -51,14 +56,112 @@ func (cfg *PackerConfig) decodeImplicitRequiredPluginsBlocks(f *hcl.File) hcl.Di
diags = append(diags, moreDiags...)
for _, block := range content.Blocks {
switch block.Type {
case sourceLabel:
// TODO
diags = append(diags, cfg.decodeImplicitRequiredPluginsBlock(Builder, block)...)
case dataSourceLabel:
diags = append(diags, cfg.decodeImplicitRequiredPluginsBlock(Datasource, block)...)
case buildLabel:
content, _, moreDiags := block.Body.PartialContent(buildSchema)
diags = append(diags, moreDiags...)
for _, block := range content.Blocks {
switch block.Type {
case buildProvisionerLabel:
diags = append(diags, cfg.decodeImplicitRequiredPluginsBlock(Provisioner, block)...)
case buildPostProcessorLabel:
diags = append(diags, cfg.decodeImplicitRequiredPluginsBlock(PostProcessor, block)...)
case buildPostProcessorsLabel:
content, _, moreDiags := block.Body.PartialContent(postProcessorsSchema)
diags = append(diags, moreDiags...)
for _, block := range content.Blocks {
switch block.Type {
case buildPostProcessorLabel:
diags = append(diags, cfg.decodeImplicitRequiredPluginsBlock(PostProcessor, block)...)
}
}
}
}
}
}
return diags
}
func (cfg *PackerConfig) decodeImplicitRequiredPluginsBlock(k ComponentKind, block *hcl.Block) hcl.Diagnostics {
if len(block.Labels) == 0 {
// malformed block ? Let's not panic :)
return nil
}
// Currently all block types are `type "component-kind" ["name"] {`
// this makes this simple.
componentName := block.Labels[0]
store := map[ComponentKind]packer.BasicStore{
Builder: cfg.parser.PluginConfig.Builders,
PostProcessor: cfg.parser.PluginConfig.PostProcessors,
Provisioner: cfg.parser.PluginConfig.Provisioners,
Datasource: cfg.parser.PluginConfig.DataSources,
}[k]
if store.Has(componentName) {
// If any core or pre-loaded plugin defines the `happycloud-uploader`
// pp, skip. This happens for core and manually installed plugins, as
// they will be listed in the PluginConfig before parsing any HCL.
return nil
}
redirect := map[ComponentKind]map[string]string{
Builder: cfg.parser.PluginConfig.BuilderRedirects,
PostProcessor: cfg.parser.PluginConfig.PostProcessorRedirects,
Provisioner: cfg.parser.PluginConfig.ProvisionerRedirects,
Datasource: cfg.parser.PluginConfig.DatasourceRedirects,
}[k][componentName]
if redirect == "" {
// no known redirect for this component
return nil
}
redirectAddr, diags := addrs.ParsePluginSourceString(redirect)
if diags.HasErrors() {
// This should never happen, since the map is manually filled.
return diags
}
for _, req := range cfg.Packer.RequiredPlugins {
if _, found := req.RequiredPlugins[redirectAddr.Type]; found {
// This could happen if a plugin was forked. For example, I forked
// the github.com/hashicorp/happycloud plugin into
// github.com/azr/happycloud that is required in my config file; and
// am using the `happycloud-uploader` pp component from it. In that
// case - and to avoid miss-requires - we won't implicitly import
// any other `happycloud` plugin.
return nil
}
}
cfg.implicitlyRequirePlugin(redirectAddr)
return nil
}
func (cfg *PackerConfig) implicitlyRequirePlugin(plugin *addrs.Plugin) {
cfg.Packer.RequiredPlugins = append(cfg.Packer.RequiredPlugins, &RequiredPlugins{
RequiredPlugins: map[string]*RequiredPlugin{
plugin.Type: {
Name: plugin.Type,
Source: plugin.String(),
Type: plugin,
Requirement: VersionConstraint{
Required: nil, // means latest
},
PluginDependencyReason: PluginDependencyImplicit,
},
},
})
}
// RequiredPlugin represents a declaration of a dependency on a particular
// Plugin version or source.
type RequiredPlugin struct {
@ -71,8 +174,24 @@ type RequiredPlugin struct {
Type *addrs.Plugin
Requirement VersionConstraint
DeclRange hcl.Range
PluginDependencyReason
}
// PluginDependencyReason is an enumeration of reasons why a dependency might be
// present.
type PluginDependencyReason int
const (
// PluginDependencyExplicit means that there is an explicit
// "required_plugin" block in the configuration.
PluginDependencyExplicit PluginDependencyReason = iota
// PluginDependencyImplicit means that there is no explicit
// "required_plugin" block but there is at least one resource that uses this
// plugin.
PluginDependencyImplicit
)
type RequiredPlugins struct {
RequiredPlugins map[string]*RequiredPlugin
DeclRange hcl.Range

View File

@ -0,0 +1,363 @@
package hcl2template
import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/go-version"
"github.com/hashicorp/packer/hcl2template/addrs"
)
func TestPackerConfig_required_plugin_parse(t *testing.T) {
tests := []struct {
name string
cfg PackerConfig
requirePlugins string
restOfTemplate string
wantDiags bool
wantConfig PackerConfig
}{
{"required_plugin", PackerConfig{parser: getBasicParser()}, `
packer {
required_plugins {
amazon = {
source = "github.com/hashicorp/amazon"
version = "~> v1.2.3"
}
}
} `, `
source "amazon-ebs" "example" {
}
`, false, PackerConfig{
Packer: struct {
VersionConstraints []VersionConstraint
RequiredPlugins []*RequiredPlugins
}{
RequiredPlugins: []*RequiredPlugins{
{RequiredPlugins: map[string]*RequiredPlugin{
"amazon": {
Name: "amazon",
Source: "github.com/hashicorp/amazon",
Type: &addrs.Plugin{Hostname: "github.com", Namespace: "hashicorp", Type: "amazon"},
Requirement: VersionConstraint{
Required: mustVersionConstraints(version.NewConstraint("~> v1.2.3")),
},
PluginDependencyReason: PluginDependencyExplicit,
},
}},
},
},
}},
{"required_plugin_forked_no_redirect", PackerConfig{parser: getBasicParser()}, `
packer {
required_plugins {
amazon = {
source = "github.com/azr/amazon"
version = "~> v1.2.3"
}
}
} `, `
source "amazon-chroot" "example" {
}
`, false, PackerConfig{
Packer: struct {
VersionConstraints []VersionConstraint
RequiredPlugins []*RequiredPlugins
}{
RequiredPlugins: []*RequiredPlugins{
{RequiredPlugins: map[string]*RequiredPlugin{
"amazon": {
Name: "amazon",
Source: "github.com/azr/amazon",
Type: &addrs.Plugin{Hostname: "github.com", Namespace: "azr", Type: "amazon"},
Requirement: VersionConstraint{
Required: mustVersionConstraints(version.NewConstraint("~> v1.2.3")),
},
PluginDependencyReason: PluginDependencyExplicit,
},
}},
},
},
}},
{"required_plugin_forked", PackerConfig{
parser: getBasicParser(func(p *Parser) {
p.PluginConfig.BuilderRedirects = map[string]string{
"amazon-chroot": "github.com/hashicorp/amazon",
}
},
)}, `
packer {
required_plugins {
amazon = {
source = "github.com/azr/amazon"
version = "~> v1.2.3"
}
}
} `, `
source "amazon-chroot" "example" {
}
`, false, PackerConfig{
Packer: struct {
VersionConstraints []VersionConstraint
RequiredPlugins []*RequiredPlugins
}{
RequiredPlugins: []*RequiredPlugins{
{RequiredPlugins: map[string]*RequiredPlugin{
"amazon": {
Name: "amazon",
Source: "github.com/azr/amazon",
Type: &addrs.Plugin{Hostname: "github.com", Namespace: "azr", Type: "amazon"},
Requirement: VersionConstraint{
Required: mustVersionConstraints(version.NewConstraint("~> v1.2.3")),
},
PluginDependencyReason: PluginDependencyExplicit,
},
}},
},
},
}},
{"missing-required-plugin-for-pre-defined-builder", PackerConfig{
parser: getBasicParser(func(p *Parser) {
p.PluginConfig.BuilderRedirects = map[string]string{
"amazon-ebs": "github.com/hashicorp/amazon",
}
},
)},
`
packer {
}`, `
# amazon-ebs is mocked in getBasicParser()
source "amazon-ebs" "example" {
}
`,
false,
PackerConfig{
Packer: struct {
VersionConstraints []VersionConstraint
RequiredPlugins []*RequiredPlugins
}{
RequiredPlugins: nil,
},
}},
{"missing-required-plugin-for-builder", PackerConfig{
parser: getBasicParser(func(p *Parser) {
p.PluginConfig.BuilderRedirects = map[string]string{
"amazon-chroot": "github.com/hashicorp/amazon",
}
},
)},
`
packer {
}`, `
source "amazon-chroot" "example" {
}
`,
false,
PackerConfig{
Packer: struct {
VersionConstraints []VersionConstraint
RequiredPlugins []*RequiredPlugins
}{
RequiredPlugins: []*RequiredPlugins{
{RequiredPlugins: map[string]*RequiredPlugin{
"amazon": {
Name: "amazon",
Source: "github.com/hashicorp/amazon",
Type: &addrs.Plugin{Hostname: "github.com", Namespace: "hashicorp", Type: "amazon"},
Requirement: VersionConstraint{
Required: nil,
},
PluginDependencyReason: PluginDependencyImplicit,
},
}},
},
},
}},
{"missing-required-plugin-for-provisioner", PackerConfig{
parser: getBasicParser(func(p *Parser) {
p.PluginConfig.ProvisionerRedirects = map[string]string{
"ansible-local": "github.com/ansible/ansible",
}
},
)},
`
packer {
}`, `
build {
provisioner "ansible-local" {}
}
`,
false,
PackerConfig{
Packer: struct {
VersionConstraints []VersionConstraint
RequiredPlugins []*RequiredPlugins
}{
RequiredPlugins: []*RequiredPlugins{
{RequiredPlugins: map[string]*RequiredPlugin{
"ansible": {
Name: "ansible",
Source: "github.com/ansible/ansible",
Type: &addrs.Plugin{Hostname: "github.com", Namespace: "ansible", Type: "ansible"},
Requirement: VersionConstraint{
Required: nil,
},
PluginDependencyReason: PluginDependencyImplicit,
},
}},
},
},
}},
{"missing-required-plugin-for-post-processor", PackerConfig{
parser: getBasicParser(func(p *Parser) {
p.PluginConfig.PostProcessorRedirects = map[string]string{
"docker-push": "github.com/hashicorp/docker",
}
},
)},
`
packer {
}`, `
build {
post-processor "docker-push" {}
}
`,
false,
PackerConfig{
Packer: struct {
VersionConstraints []VersionConstraint
RequiredPlugins []*RequiredPlugins
}{
RequiredPlugins: []*RequiredPlugins{
{RequiredPlugins: map[string]*RequiredPlugin{
"docker": {
Name: "docker",
Source: "github.com/hashicorp/docker",
Type: &addrs.Plugin{Hostname: "github.com", Namespace: "hashicorp", Type: "docker"},
Requirement: VersionConstraint{
Required: nil,
},
PluginDependencyReason: PluginDependencyImplicit,
},
}},
},
},
}},
{"missing-required-plugin-for-nested-post-processor", PackerConfig{
parser: getBasicParser(func(p *Parser) {
p.PluginConfig.PostProcessorRedirects = map[string]string{
"docker-push": "github.com/hashicorp/docker",
}
},
)},
`
packer {
}`, `
build {
post-processors {
post-processor "docker-push" {
}
}
}
`,
false,
PackerConfig{
Packer: struct {
VersionConstraints []VersionConstraint
RequiredPlugins []*RequiredPlugins
}{
RequiredPlugins: []*RequiredPlugins{
{RequiredPlugins: map[string]*RequiredPlugin{
"docker": {
Name: "docker",
Source: "github.com/hashicorp/docker",
Type: &addrs.Plugin{Hostname: "github.com", Namespace: "hashicorp", Type: "docker"},
Requirement: VersionConstraint{
Required: nil,
},
PluginDependencyReason: PluginDependencyImplicit,
},
}},
},
},
}},
{"required-plugin-renamed", PackerConfig{
parser: getBasicParser(func(p *Parser) {
p.PluginConfig.BuilderRedirects = map[string]string{
"amazon-chroot": "github.com/hashicorp/amazon",
}
},
)},
`
packer {
required_plugins {
amazon-v1 = {
source = "github.com/hashicorp/amazon"
version = "~> v1.0"
}
}
}`, `
source "amazon-v1-chroot" "example" {
}
source "amazon-chroot" "example" {
}
`,
false,
PackerConfig{
Packer: struct {
VersionConstraints []VersionConstraint
RequiredPlugins []*RequiredPlugins
}{
RequiredPlugins: []*RequiredPlugins{
{RequiredPlugins: map[string]*RequiredPlugin{
"amazon-v1": {
Name: "amazon-v1",
Source: "github.com/hashicorp/amazon",
Type: &addrs.Plugin{Hostname: "github.com", Namespace: "hashicorp", Type: "amazon"},
Requirement: VersionConstraint{
Required: mustVersionConstraints(version.NewConstraint("~> v1.0")),
},
PluginDependencyReason: PluginDependencyExplicit,
},
}},
{RequiredPlugins: map[string]*RequiredPlugin{
"amazon": {
Name: "amazon",
Source: "github.com/hashicorp/amazon",
Type: &addrs.Plugin{Hostname: "github.com", Namespace: "hashicorp", Type: "amazon"},
Requirement: VersionConstraint{
Required: nil,
},
PluginDependencyReason: PluginDependencyImplicit,
},
}},
},
},
}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := tt.cfg
file, diags := cfg.parser.ParseHCL([]byte(tt.requirePlugins), "required_plugins.pkr.hcl")
if len(diags) > 0 {
t.Fatal(diags)
}
if diags := cfg.decodeRequiredPluginsBlock(file); len(diags) > 0 {
t.Fatal(diags)
}
rest, diags := cfg.parser.ParseHCL([]byte(tt.restOfTemplate), "rest.pkr.hcl")
if len(diags) > 0 {
t.Fatal(diags)
}
if gotDiags := cfg.decodeImplicitRequiredPluginsBlocks(rest); (len(gotDiags) > 0) != tt.wantDiags {
t.Fatal(gotDiags)
}
if diff := cmp.Diff(tt.wantConfig, cfg, cmpOpts...); diff != "" {
t.Errorf("PackerConfig.inferImplicitRequiredPluginFromBlocks() unexpected PackerConfig: %v", diff)
}
})
}
}

61
main.go
View File

@ -297,6 +297,67 @@ func loadConfig() (*config, error) {
PluginMinPort: 10000,
PluginMaxPort: 25000,
KnownPluginFolders: packer.PluginFolders("."),
// BuilderRedirects
BuilderRedirects: map[string]string{
// "amazon-chroot": "github.com/hashicorp/amazon",
// "amazon-ebs": "github.com/hashicorp/amazon",
// "amazon-ebssurrogate": "github.com/hashicorp/amazon",
// "amazon-ebsvolume": "github.com/hashicorp/amazon",
// "amazon-instance": "github.com/hashicorp/amazon",
// "azure-arm": "github.com/hashicorp/azure",
// "azure-chroot": "github.com/hashicorp/azure",
// "dtl": "github.com/hashicorp/azure",
// "docker": "github.com/hashicorp/docker",
// "googlecompute": "github.com/hashicorp/googlecompute",
// "parallels-iso": "github.com/hashicorp/parallels",
// "parallels-pvm": "github.com/hashicorp/parallels",
// "qemu": "github.com/hashicorp/qemu",
// "vagrant": "github.com/hashicorp/vagrant",
// "virtualbox-iso": "github.com/hashicorp/virtualbox",
// "virtualbox-ovf": "github.com/hashicorp/virtualbox",
// "virtualbox-vm": "github.com/hashicorp/virtualbox",
// "vmware-iso": "github.com/hashicorp/vmware",
// "vmware-vmx": "github.com/hashicorp/vmware",
// "vsphere-iso": "github.com/hashicorp/vsphere",
// "vsphere-clone": "github.com/hashicorp/vsphere",
},
DatasourceRedirects: map[string]string{
// "amazon-ami": "github.com/hashicorp/amazon",
},
ProvisionerRedirects: map[string]string{
// "ansible": "github.com/hashicorp/ansible",
// "ansible-local": "github.com/hashicorp/ansible",
// "azure-dtlartifact": "github.com/hashicorp/azure",
},
PostProcessorRedirects: map[string]string{
// "amazon-import": "github.com/hashicorp/amazon",
// "docker-import": "github.com/hashicorp/docker",
// "docker-push": "github.com/hashicorp/docker",
// "docker-save": "github.com/hashicorp/docker",
// "docker-tag": "github.com/hashicorp/docker",
// "googlecompute-export": "github.com/hashicorp/googlecompute",
// "googlecompute-import": "github.com/hashicorp/googlecompute",
// "vagrant": "github.com/hashicorp/vagrant",
// "vagrant-cloud": "github.com/hashicorp/vagrant",
// "vsphere": "github.com/hashicorp/vsphere",
// "vsphere-template": "github.com/hashicorp/vsphere",
},
}
if err := config.Plugins.Discover(); err != nil {
return nil, err

View File

@ -38,6 +38,9 @@ type Requirement struct {
// VersionConstraints as defined by user. Empty ( to be avoided ) means
// highest found version.
VersionConstraints version.Constraints
// was this require implicitly guessed ?
Implicit bool
}
type BinaryInstallationOptions struct {

View File

@ -24,6 +24,23 @@ type PluginConfig struct {
Provisioners ProvisionerSet
PostProcessors PostProcessorSet
DataSources DatasourceSet
// Redirects are only set when a plugin was completely moved out; they allow
// telling where a plugin has moved by checking if a known component of this
// plugin is used. For example implicitly require the
// github.com/hashicorp/amazon plugin if it was moved out and the
// "amazon-ebs" plugin is used, but not found.
//
// Redirects will be bypassed if the redirected components are already found
// in their corresponding sets (Builders, Provisioners, PostProcessors,
// DataSources). That is, for example, if you manually put a single
// component plugin in the plugins folder.
//
// Example BuilderRedirects: "amazon-ebs" => "github.com/hashicorp/amazon"
BuilderRedirects map[string]string
DatasourceRedirects map[string]string
ProvisionerRedirects map[string]string
PostProcessorRedirects map[string]string
}
// PACKERSPACE is used to represent the spaces that separate args for a command

View File

@ -67,10 +67,21 @@ plugins will be installed in the [Plugin
Directory](/docs/configure#packer-s-plugin-directory).
See [Installing Plugins](/docs/plugins#installing-plugins) for more information on how plugin installation works.
### Implicit required plugin
This is part of a set of breaking changes made to decouple Packer releases from
plugin releases. To make the transition easier, we will tag components of these
plugins as "moved out". If one of the components of a moved out plugin is used
in a config file, but there is no mention of that plugin in the
"required_plugin" block, then Packer init will automatically download and
install that plugin. Packer will then display a warning and suggest that you
add the plugin to your required_plugin block. We recommend you use the
required_plugin block even if you are only using official plugins, because it
allows you to set the plugin version to avoid surprises in the future.
## Options
- `-upgrade` - On top of installing missing plugins, update installed plugins to
the latest available version, if there is a new higher one. Note that this
still takes into consideration the version constraint of the config.