packer: remove implicit required plugins

Since this feature is no longer something we plan to activate later, as
it contradicts with our efforts to remove bundled plugins, and
encouraging users to move to either manually installing plugins, or
managing them through `packer init', we clean-up the code for this
feature.
pull/12592/head
Lucas Bajolet 3 years ago committed by Lucas Bajolet
parent f961715161
commit eb9e1a4795

@ -124,55 +124,13 @@ 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(fmt.Sprintf("Failed getting the %q plugin:", pluginRequirement.Identifier))
c.Ui.Error(err.Error())
ret = 1
}
c.Ui.Error(fmt.Sprintf("Failed getting the %q plugin:", pluginRequirement.Identifier))
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

@ -77,7 +77,6 @@ func (c *PluginsInstallCommand) RunContext(buildCtx context.Context, args []stri
// a plugin requirement that matches them all
pluginRequirement := plugingetter.Requirement{
Identifier: plugin,
Implicit: false,
}
if len(args) > 1 {

@ -64,7 +64,6 @@ func (c *PluginsInstalledCommand) RunContext(buildCtx context.Context) int {
Accessor: "",
VersionConstraints: nil,
Identifier: nil,
Implicit: false,
}
installations, err := allPlugins.ListInstallations(opts)

@ -74,7 +74,6 @@ func (c *PluginsRemoveCommand) RunContext(buildCtx context.Context, args []strin
// a plugin requirement that matches them all
pluginRequirement := plugingetter.Requirement{
Identifier: plugin,
Implicit: false,
}
if len(args) > 1 {

@ -163,28 +163,14 @@ func (p *Parser) Parse(filename string, varFiles []string, argVars map[string]st
return cfg, diags
}
// Decode required_plugins blocks and create implicit required_plugins
// blocks. Implicit required_plugins blocks happen when a builder or another
// plugin cannot be found, for example if one uses :
// source "amazon-ebs" "example" { ... }
// And no `amazon-ebs` builder can be found. This will then be the
// equivalent of having :
// packer {
// required_plugins {
// amazon = {
// version = "latest"
// source = "github.com/hashicorp/amazon"
// }
// }
// Decode required_plugins blocks.
//
// Note: using `latest` ( or actually an empty string ) in a config file
// does not work and packer will ask you to pick a version
{
for _, file := range files {
diags = append(diags, cfg.decodeRequiredPluginsBlock(file)...)
}
for _, file := range files {
diags = append(diags, cfg.decodeImplicitRequiredPluginsBlocks(file)...)
}
}
// Decode variable blocks so that they are available later on. Here locals

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

@ -9,7 +9,6 @@ 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"
)
@ -44,127 +43,6 @@ func (cfg *PackerConfig) decodeRequiredPluginsBlock(f *hcl.File) hcl.Diagnostics
return diags
}
func (cfg *PackerConfig) decodeImplicitRequiredPluginsBlocks(f *hcl.File) hcl.Diagnostics {
// 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
content, moreDiags := f.Body.Content(configSchema)
diags = append(diags, moreDiags...)
for _, block := range content.Blocks {
switch block.Type {
case sourceLabel:
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 {
@ -177,24 +55,8 @@ 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

@ -46,7 +46,6 @@ func TestPackerConfig_required_plugin_parse(t *testing.T) {
Requirement: VersionConstraint{
Required: mustVersionConstraints(version.NewConstraint("~> v1.2.3")),
},
PluginDependencyReason: PluginDependencyExplicit,
},
}},
},
@ -77,19 +76,13 @@ func TestPackerConfig_required_plugin_parse(t *testing.T) {
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",
}
},
)}, `
parser: getBasicParser(func(p *Parser) {})}, `
packer {
required_plugins {
amazon = {
@ -114,19 +107,13 @@ func TestPackerConfig_required_plugin_parse(t *testing.T) {
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",
}
},
)},
parser: getBasicParser(func(p *Parser) {})},
`
packer {
}`, `
@ -143,202 +130,6 @@ func TestPackerConfig_required_plugin_parse(t *testing.T) {
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) {
@ -351,13 +142,10 @@ func TestPackerConfig_required_plugin_parse(t *testing.T) {
t.Fatal(diags)
}
rest, diags := cfg.parser.ParseHCL([]byte(tt.restOfTemplate), "rest.pkr.hcl")
_, 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)
}

@ -325,72 +325,6 @@ 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",
//"azure-dtl": "github.com/hashicorp/azure",
//"docker": "github.com/hashicorp/docker",
//"exoscale": "github.com/exoscale/exoscale",
//"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",
//"amazon-secretsmanager": "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",
//"exoscale-import": "github.com/exoscale/exoscale",
//"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

@ -43,9 +43,6 @@ 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 {

@ -34,23 +34,6 @@ 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

@ -87,18 +87,6 @@ Directory](/packer/docs/configure#packer-s-plugin-directory).
See [Installing Plugins](/packer/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

Loading…
Cancel
Save