diff --git a/acctest/plugin/bundled_plugin_test.go b/acctest/plugin/bundled_plugin_test.go new file mode 100644 index 00000000000..e9308a59869 --- /dev/null +++ b/acctest/plugin/bundled_plugin_test.go @@ -0,0 +1,181 @@ +package plugin + +import ( + _ "embed" + "errors" + "fmt" + "os" + "os/exec" + "strings" + "testing" + + "github.com/hashicorp/go-multierror" + amazonacc "github.com/hashicorp/packer-plugin-amazon/builder/ebs/acceptance" + "github.com/hashicorp/packer-plugin-sdk/acctest" + "github.com/hashicorp/packer/hcl2template/addrs" +) + +//go:embed test-fixtures/basic_amazon_bundled.pkr.hcl +var basicAmazonBundledEbsTemplate string + +func TestAccBuildBundledPlugins(t *testing.T) { + plugin := addrs.Plugin{ + Hostname: "github.com", + Namespace: "hashicorp", + Type: "amazon", + } + testCase := &acctest.PluginTestCase{ + Name: "amazon-ebs_bundled_test", + Setup: func() error { + return cleanupPluginInstallation(plugin) + }, + Teardown: func() error { + helper := amazonacc.AMIHelper{ + Region: "us-east-1", + Name: "packer-plugin-bundled-amazon-ebs-test", + } + return helper.CleanUpAmi() + }, + Template: basicAmazonBundledEbsTemplate, + Type: "amazon-ebs", + Init: false, + Check: func(buildCommand *exec.Cmd, logfile string) error { + if buildCommand.ProcessState != nil { + if buildCommand.ProcessState.ExitCode() != 0 { + return fmt.Errorf("Bad exit code. Logfile: %s", logfile) + } + } + + rawLogs, err := os.ReadFile(logfile) + if err != nil { + return fmt.Errorf("failed to read logs: %s", err) + } + + var errs error + + logs := string(rawLogs) + + if !strings.Contains(logs, "Warning: Bundled plugins used") { + errs = multierror.Append(errs, errors.New("expected warning about bundled plugins used, did not find it")) + } + + if !strings.Contains(logs, "Then run 'packer init' to manage installation of the plugins") { + errs = multierror.Append(errs, errors.New("expected suggestion about packer init in logs, did not find it.")) + } + + return errs + }, + } + + acctest.TestPlugin(t, testCase) +} + +//go:embed test-fixtures/basic_amazon_with_required_plugins.pkr.hcl +var basicAmazonRequiredPluginEbsTemplate string + +func TestAccBuildBundledPluginsWithRequiredPlugins(t *testing.T) { + plugin := addrs.Plugin{ + Hostname: "github.com", + Namespace: "hashicorp", + Type: "amazon", + } + testCase := &acctest.PluginTestCase{ + Name: "amazon-ebs_with_required_plugins_test", + Setup: func() error { + return cleanupPluginInstallation(plugin) + }, + Teardown: func() error { + helper := amazonacc.AMIHelper{ + Region: "us-east-1", + Name: "packer-plugin-required-plugin-amazon-ebs-test", + } + return helper.CleanUpAmi() + }, + Template: basicAmazonRequiredPluginEbsTemplate, + Type: "amazon-ebs", + Init: false, + Check: func(buildCommand *exec.Cmd, logfile string) error { + if buildCommand.ProcessState != nil { + if buildCommand.ProcessState.ExitCode() != 1 { + return fmt.Errorf("Bad exit code. Logfile: %s", logfile) + } + } + + rawLogs, err := os.ReadFile(logfile) + if err != nil { + return fmt.Errorf("failed to read logs: %s", err) + } + + var errs error + + logs := string(rawLogs) + + if strings.Contains(logs, "Warning: Bundled plugins used") { + errs = multierror.Append(errs, errors.New("did not expect warning about bundled plugins used")) + } + + if !strings.Contains(logs, "Missing plugins") { + errs = multierror.Append(errs, errors.New("expected error about plugins required and not installed, did not find it")) + } + + return errs + }, + } + + acctest.TestPlugin(t, testCase) +} + +//go:embed test-fixtures/basic_amazon_bundled.json +var basicAmazonBundledEbsTemplateJSON string + +func TestAccBuildBundledPluginsJSON(t *testing.T) { + plugin := addrs.Plugin{ + Hostname: "github.com", + Namespace: "hashicorp", + Type: "amazon", + } + testCase := &acctest.PluginTestCase{ + Name: "amazon-ebs_bundled_test_json", + Setup: func() error { + return cleanupPluginInstallation(plugin) + }, + Teardown: func() error { + helper := amazonacc.AMIHelper{ + Region: "us-east-1", + Name: "packer-plugin-bundled-amazon-ebs-test-json", + } + return helper.CleanUpAmi() + }, + Template: basicAmazonBundledEbsTemplateJSON, + Type: "amazon-ebs", + Init: false, + Check: func(buildCommand *exec.Cmd, logfile string) error { + if buildCommand.ProcessState != nil { + if buildCommand.ProcessState.ExitCode() != 0 { + return fmt.Errorf("Bad exit code. Logfile: %s", logfile) + } + } + + rawLogs, err := os.ReadFile(logfile) + if err != nil { + return fmt.Errorf("failed to read logs: %s", err) + } + + var errs error + + logs := string(rawLogs) + + if !strings.Contains(logs, "Warning: Bundled plugins used") { + errs = multierror.Append(errs, errors.New("expected warning about bundled plugins, did not find it.")) + } + + if !strings.Contains(logs, "plugins with the 'packer plugins install' command") { + errs = multierror.Append(errs, errors.New("expected suggestion about packer plugins install in logs, did not find it.")) + } + + return errs + }, + } + + acctest.TestPlugin(t, testCase) +} diff --git a/acctest/plugin/test-fixtures/basic_amazon_bundled.json b/acctest/plugin/test-fixtures/basic_amazon_bundled.json new file mode 100644 index 00000000000..f674cd130d1 --- /dev/null +++ b/acctest/plugin/test-fixtures/basic_amazon_bundled.json @@ -0,0 +1,10 @@ +{ + "builders": [{ + "type": "amazon-ebs", + "region": "us-east-1", + "instance_type": "m3.medium", + "source_ami": "ami-76b2a71e", + "ssh_username": "ubuntu", + "ami_name": "packer-plugin-bundled-amazon-ebs-test-json" + }] +} diff --git a/acctest/plugin/test-fixtures/basic_amazon_bundled.pkr.hcl b/acctest/plugin/test-fixtures/basic_amazon_bundled.pkr.hcl new file mode 100644 index 00000000000..c17a09df04f --- /dev/null +++ b/acctest/plugin/test-fixtures/basic_amazon_bundled.pkr.hcl @@ -0,0 +1,11 @@ +source "amazon-ebs" "basic-test" { + region = "us-east-1" + instance_type = "m3.medium" + source_ami = "ami-76b2a71e" + ssh_username = "ubuntu" + ami_name = "packer-plugin-bundled-amazon-ebs-test" +} + +build { + sources = ["source.amazon-ebs.basic-test"] +} diff --git a/acctest/plugin/test-fixtures/basic_amazon_with_required_plugins.pkr.hcl b/acctest/plugin/test-fixtures/basic_amazon_with_required_plugins.pkr.hcl new file mode 100644 index 00000000000..f9ba5a55b2c --- /dev/null +++ b/acctest/plugin/test-fixtures/basic_amazon_with_required_plugins.pkr.hcl @@ -0,0 +1,20 @@ +packer { + required_plugins { + amazon = { + source = "github.com/hashicorp/amazon", + version = "~> 1" + } + } +} + +source "amazon-ebs" "basic-test" { + region = "us-east-1" + instance_type = "m3.medium" + source_ami = "ami-76b2a71e" + ssh_username = "ubuntu" + ami_name = "packer-plugin-bundled-amazon-ebs-test" +} + +build { + sources = ["source.amazon-ebs.basic-test"] +} diff --git a/command/build.go b/command/build.go index b707fe22d6f..842938adcb9 100644 --- a/command/build.go +++ b/command/build.go @@ -87,7 +87,15 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, cla *BuildArgs) int return ret } - diags := packerStarter.Initialize(packer.InitializeOptions{}) + diags := packerStarter.DetectPluginBinaries() + ret = writeDiags(c.Ui, nil, diags) + if ret != 0 { + return ret + } + + diags = packerStarter.Initialize(packer.InitializeOptions{}) + bundledDiags := c.DetectBundledPlugins(packerStarter) + diags = append(bundledDiags, diags...) ret = writeDiags(c.Ui, nil, diags) if ret != 0 { return ret diff --git a/command/meta.go b/command/meta.go index 99123fd8ec7..1579d152237 100644 --- a/command/meta.go +++ b/command/meta.go @@ -9,7 +9,9 @@ import ( "fmt" "io" "os" + "strings" + "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclparse" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/hashicorp/packer-plugin-sdk/template" @@ -179,3 +181,176 @@ func (m *Meta) GetConfigFromJSON(cla *MetaArgs) (packer.Handler, int) { } return core, ret } + +func (m *Meta) DetectBundledPlugins(handler packer.Handler) hcl.Diagnostics { + var plugins []string + + switch h := handler.(type) { + case *packer.Core: + plugins = m.detectBundledPluginsJSON(h) + case *hcl2template.PackerConfig: + plugins = m.detectBundledPluginsHCL2(handler.(*hcl2template.PackerConfig)) + } + + if len(plugins) == 0 { + return nil + } + + buf := &strings.Builder{} + buf.WriteString("This template relies on the use of plugins bundled into the Packer binary.\n") + buf.WriteString("The practice of bundling external plugins into Packer will be removed in an upcoming version.\n\n") + switch h := handler.(type) { + case *packer.Core: + buf.WriteString("To remove this warning and ensure builds keep working you can install these external plugins with the 'packer plugins install' command\n\n") + + for _, plugin := range plugins { + fmt.Fprintf(buf, "* packer plugins install %s\n", plugin) + } + + buf.WriteString("\nAlternatively, if you upgrade your templates to HCL2, you can use 'packer init' with a 'required_plugins' block to automatically install external plugins.\n\n") + fmt.Fprintf(buf, "You can try HCL2 by running 'packer hcl2_upgrade %s'", h.Template.Path) + case *hcl2template.PackerConfig: + buf.WriteString("To remove this warning, add the following section to your template:\n") + buf.WriteString(m.fixRequiredPlugins(h)) + buf.WriteString("\nThen run 'packer init' to manage installation of the plugins") + } + + return hcl.Diagnostics{ + &hcl.Diagnostic{ + Severity: hcl.DiagWarning, + Summary: "Bundled plugins used", + Detail: buf.String(), + }, + } +} + +func (m *Meta) detectBundledPluginsJSON(core *packer.Core) []string { + bundledPlugins := map[string]struct{}{} + + tmpl := core.Template + if tmpl == nil { + panic("No template parsed. This is a Packer bug which should be reported, please open an issue on the project's issue tracker.") + } + + for _, b := range tmpl.Builders { + builderType := fmt.Sprintf("packer-builder-%s", b.Type) + if bundledStatus[builderType] { + bundledPlugins[builderType] = struct{}{} + } + } + + for _, p := range tmpl.Provisioners { + provisionerType := fmt.Sprintf("packer-provisioner-%s", p.Type) + if bundledStatus[provisionerType] { + bundledPlugins[provisionerType] = struct{}{} + } + } + + for _, pps := range tmpl.PostProcessors { + for _, pp := range pps { + postProcessorType := fmt.Sprintf("packer-post-processor-%s", pp.Type) + if bundledStatus[postProcessorType] { + bundledPlugins[postProcessorType] = struct{}{} + } + } + } + + return compileBundledPluginList(bundledPlugins) +} + +var knownPluginPrefixes = map[string]string{ + "amazon": "github.com/hashicorp/amazon", + "ansible": "github.com/hashicorp/ansible", + "azure": "github.com/hashicorp/azure", + "docker": "github.com/hashicorp/docker", + "googlecompute": "github.com/hashicorp/googlecompute", + "qemu": "github.com/hashicorp/qemu", + "vagrant": "github.com/hashicorp/vagrant", + "vmware": "github.com/hashicorp/vmware", + "vsphere": "github.com/hashicorp/vsphere", +} + +func (m *Meta) fixRequiredPlugins(config *hcl2template.PackerConfig) string { + plugins := map[string]struct{}{} + + for _, b := range config.Builds { + for _, b := range b.Sources { + for prefix, plugin := range knownPluginPrefixes { + if strings.HasPrefix(b.Type, prefix) { + plugins[plugin] = struct{}{} + } + } + } + + for _, p := range b.ProvisionerBlocks { + for prefix, plugin := range knownPluginPrefixes { + if strings.HasPrefix(p.PType, prefix) { + plugins[plugin] = struct{}{} + } + } + } + + for _, pps := range b.PostProcessorsLists { + for _, pp := range pps { + for prefix, plugin := range knownPluginPrefixes { + if strings.HasPrefix(pp.PType, prefix) { + plugins[plugin] = struct{}{} + } + } + } + } + } + + for _, ds := range config.Datasources { + for prefix, plugin := range knownPluginPrefixes { + if strings.HasPrefix(ds.Type, prefix) { + plugins[plugin] = struct{}{} + } + } + } + + retPlugins := make([]string, 0, len(plugins)) + for plugin := range plugins { + retPlugins = append(retPlugins, plugin) + } + + return generateRequiredPluginsBlock(retPlugins) +} + +func (m *Meta) detectBundledPluginsHCL2(config *hcl2template.PackerConfig) []string { + bundledPlugins := map[string]struct{}{} + + for _, b := range config.Builds { + for _, src := range b.Sources { + builderType := fmt.Sprintf("packer-builder-%s", src.Type) + if bundledStatus[builderType] { + bundledPlugins[builderType] = struct{}{} + } + } + + for _, p := range b.ProvisionerBlocks { + provisionerType := fmt.Sprintf("packer-provisioner-%s", p.PType) + if bundledStatus[provisionerType] { + bundledPlugins[provisionerType] = struct{}{} + } + } + + for _, pps := range b.PostProcessorsLists { + for _, pp := range pps { + postProcessorType := fmt.Sprintf("packer-post-processor-%s", pp.PType) + if bundledStatus[postProcessorType] { + bundledPlugins[postProcessorType] = struct{}{} + } + } + } + } + + for _, ds := range config.Datasources { + dsType := fmt.Sprintf("packer-datasource-%s", ds.Type) + if bundledStatus[dsType] { + bundledPlugins[dsType] = struct{}{} + } + } + + return compileBundledPluginList(bundledPlugins) +} diff --git a/command/validate.go b/command/validate.go index 1bc4cc1d7eb..2b7e2107b0d 100644 --- a/command/validate.go +++ b/command/validate.go @@ -65,9 +65,17 @@ func (c *ValidateCommand) RunContext(ctx context.Context, cla *ValidateArgs) int return 0 } - diags := packerStarter.Initialize(packer.InitializeOptions{ + diags := packerStarter.DetectPluginBinaries() + ret = writeDiags(c.Ui, nil, diags) + if ret != 0 { + return ret + } + + diags = packerStarter.Initialize(packer.InitializeOptions{ SkipDatasourcesExecution: !cla.EvaluateDatasources, }) + bundledDiags := c.DetectBundledPlugins(packerStarter) + diags = append(bundledDiags, diags...) ret = writeDiags(c.Ui, nil, diags) if ret != 0 { return ret diff --git a/command/vendored_plugins.go b/command/vendored_plugins.go index 0718b91a7c5..d6e5f836338 100644 --- a/command/vendored_plugins.go +++ b/command/vendored_plugins.go @@ -4,6 +4,10 @@ package command import ( + "fmt" + "log" + "strings" + packersdk "github.com/hashicorp/packer-plugin-sdk/packer" // Previously core-bundled components, split into their own plugins but @@ -102,6 +106,170 @@ var VendoredPostProcessors = map[string]packersdk.PostProcessor{ "vsphere": new(vspherepostprocessor.PostProcessor), } +// bundledStatus is used to know if one of the bundled components is loaded from +// an external plugin, or from the bundled plugins. +// +// We keep track of this to produce a warning if a user relies on one +// such plugin, as they will be removed in a later version of Packer. +var bundledStatus = map[string]bool{ + "packer-builder-amazon-ebs": false, + "packer-builder-amazon-chroot": false, + "packer-builder-amazon-ebssurrogate": false, + "packer-builder-amazon-ebsvolume": false, + "packer-builder-amazon-instance": false, + "packer-post-processor-amazon-import": false, + "packer-datasource-amazon-ami": false, + "packer-datasource-amazon-secretsmanager": false, + + "packer-provisioner-ansible": false, + "packer-provisioner-ansible-local": false, + + "packer-provisioner-azure-dtlartifact": false, + "packer-builder-azure-arm": false, + "packer-builder-azure-chroot": false, + "packer-builder-azure-dtl": false, + + "packer-builder-docker": false, + "packer-post-processor-docker-import": false, + "packer-post-processor-docker-push": false, + "packer-post-processor-docker-save": false, + "packer-post-processor-docker-tag": false, + + "packer-builder-googlecompute": false, + "packer-post-processor-googlecompute-export": false, + "packer-post-processor-googlecompute-import": false, + + "packer-builder-qemu": false, + + "packer-builder-vagrant": false, + "packer-post-processor-vagrant": false, + "packer-post-processor-vagrant-cloud": false, + + "packer-builder-virtualbox-iso": false, + "packer-builder-virtualbox-ovf": false, + "packer-builder-virtualbox-vm": false, + + "packer-builder-vmware-iso": false, + "packer-builder-vmware-vmx": false, + + "packer-builder-vsphere-clone": false, + "packer-builder-vsphere-iso": false, + "packer-post-processor-vsphere-template": false, + "packer-post-processor-vsphere": false, +} + +// TrackBundledPlugin marks a component as loaded from Packer's bundled plugins +// instead of from an externally loaded plugin. +// +// NOTE: `pluginName' must be in the format `packer--' +func TrackBundledPlugin(pluginName string) { + _, exists := bundledStatus[pluginName] + if !exists { + return + } + + bundledStatus[pluginName] = true +} + +var componentPluginMap = map[string]string{ + "packer-builder-amazon-ebs": "github.com/hashicorp/amazon", + "packer-builder-amazon-chroot": "github.com/hashicorp/amazon", + "packer-builder-amazon-ebssurrogate": "github.com/hashicorp/amazon", + "packer-builder-amazon-ebsvolume": "github.com/hashicorp/amazon", + "packer-builder-amazon-instance": "github.com/hashicorp/amazon", + "packer-post-processor-amazon-import": "github.com/hashicorp/amazon", + "packer-datasource-amazon-ami": "github.com/hashicorp/amazon", + "packer-datasource-amazon-secretsmanager": "github.com/hashicorp/amazon", + + "packer-provisioner-ansible": "github.com/hashicorp/ansible", + "packer-provisioner-ansible-local": "github.com/hashicorp/ansible", + + "packer-provisioner-azure-dtlartifact": "github.com/hashicorp/azure", + "packer-builder-azure-arm": "github.com/hashicorp/azure", + "packer-builder-azure-chroot": "github.com/hashicorp/azure", + "packer-builder-azure-dtl": "github.com/hashicorp/azure", + + "packer-builder-docker": "github.com/hashicorp/docker", + "packer-post-processor-docker-import": "github.com/hashicorp/docker", + "packer-post-processor-docker-push": "github.com/hashicorp/docker", + "packer-post-processor-docker-save": "github.com/hashicorp/docker", + "packer-post-processor-docker-tag": "github.com/hashicorp/docker", + + "packer-builder-googlecompute": "github.com/hashicorp/googlecompute", + "packer-post-processor-googlecompute-export": "github.com/hashicorp/googlecompute", + "packer-post-processor-googlecompute-import": "github.com/hashicorp/googlecompute", + + "packer-builder-qemu": "github.com/hashicorp/qemu", + + "packer-builder-vagrant": "github.com/hashicorp/vagrant", + "packer-post-processor-vagrant": "github.com/hashicorp/vagrant", + "packer-post-processor-vagrant-cloud": "github.com/hashicorp/vagrant", + + "packer-builder-virtualbox-iso": "github.com/hashicorp/virtualbox", + "packer-builder-virtualbox-ovf": "github.com/hashicorp/virtualbox", + "packer-builder-virtualbox-vm": "github.com/hashicorp/virtualbox", + + "packer-builder-vmware-iso": "github.com/hashicorp/vmware", + "packer-builder-vmware-vmx": "github.com/hashicorp/vmware", + + "packer-builder-vsphere-clone": "github.com/hashicorp/vsphere", + "packer-builder-vsphere-iso": "github.com/hashicorp/vsphere", + "packer-post-processor-vsphere-template": "github.com/hashicorp/vsphere", + "packer-post-processor-vsphere": "github.com/hashicorp/vsphere", +} + +// compileBundledPluginList returns a list of plugins to import in a config +// +// This only works on bundled plugins and serves as a way to inform users that +// they should not rely on a bundled plugin anymore, but give them recommendations +// on how to manage those plugins instead. +func compileBundledPluginList(componentMap map[string]struct{}) []string { + plugins := map[string]struct{}{} + for component := range componentMap { + plugin, ok := componentPluginMap[component] + if !ok { + log.Printf("Unknown bundled plugin component: %q", component) + continue + } + + plugins[plugin] = struct{}{} + } + + pluginList := make([]string, 0, len(plugins)) + for plugin := range plugins { + pluginList = append(pluginList, plugin) + } + + return pluginList +} + +func generateRequiredPluginsBlock(plugins []string) string { + if len(plugins) == 0 { + return "" + } + + buf := &strings.Builder{} + buf.WriteString(` +packer { + required_plugins {`) + + for _, plugin := range plugins { + pluginName := strings.Replace(plugin, "github.com/hashicorp/", "", 1) + fmt.Fprintf(buf, ` + %s = { + source = %q + version = "~> 1" + }`, pluginName, plugin) + } + + buf.WriteString(` + } +} +`) + + return buf.String() +} + // Upon init lets load up any plugins that were vendored manually into the default // set of plugins. func init() { diff --git a/config.go b/config.go index 6b59b98fe63..af679dd8e0b 100644 --- a/config.go +++ b/config.go @@ -153,6 +153,7 @@ func (c *config) discoverInternalComponents() error { for builder := range command.Builders { builder := builder if !c.Plugins.Builders.Has(builder) { + command.TrackBundledPlugin(fmt.Sprintf("packer-builder-%s", builder)) bin := fmt.Sprintf("%s%splugin%spacker-builder-%s", packerPath, PACKERSPACE, PACKERSPACE, builder) c.Plugins.Builders.Set(builder, func() (packersdk.Builder, error) { @@ -164,6 +165,7 @@ func (c *config) discoverInternalComponents() error { for provisioner := range command.Provisioners { provisioner := provisioner if !c.Plugins.Provisioners.Has(provisioner) { + command.TrackBundledPlugin(fmt.Sprintf("packer-provisioner-%s", provisioner)) bin := fmt.Sprintf("%s%splugin%spacker-provisioner-%s", packerPath, PACKERSPACE, PACKERSPACE, provisioner) c.Plugins.Provisioners.Set(provisioner, func() (packersdk.Provisioner, error) { @@ -175,6 +177,7 @@ func (c *config) discoverInternalComponents() error { for postProcessor := range command.PostProcessors { postProcessor := postProcessor if !c.Plugins.PostProcessors.Has(postProcessor) { + command.TrackBundledPlugin(fmt.Sprintf("packer-post-processor-%s", postProcessor)) bin := fmt.Sprintf("%s%splugin%spacker-post-processor-%s", packerPath, PACKERSPACE, PACKERSPACE, postProcessor) c.Plugins.PostProcessors.Set(postProcessor, func() (packersdk.PostProcessor, error) { @@ -186,6 +189,7 @@ func (c *config) discoverInternalComponents() error { for dataSource := range command.Datasources { dataSource := dataSource if !c.Plugins.DataSources.Has(dataSource) { + command.TrackBundledPlugin(fmt.Sprintf("packer-datasource-%s", dataSource)) bin := fmt.Sprintf("%s%splugin%spacker-datasource-%s", packerPath, PACKERSPACE, PACKERSPACE, dataSource) c.Plugins.DataSources.Set(dataSource, func() (packersdk.Datasource, error) { diff --git a/hcl2template/parser.go b/hcl2template/parser.go index 2246131fa88..a724bb807bd 100644 --- a/hcl2template/parser.go +++ b/hcl2template/parser.go @@ -308,19 +308,8 @@ func filterVarsFromLogs(inputOrLocal Variables) { } func (cfg *PackerConfig) Initialize(opts packer.InitializeOptions) hcl.Diagnostics { - var diags hcl.Diagnostics - - // enable packer to start plugins requested in required_plugins. - moreDiags := cfg.detectPluginBinaries() - diags = append(diags, moreDiags...) - if moreDiags.HasErrors() { - return diags - } - - moreDiags = cfg.InputVariables.ValidateValues() - diags = append(diags, moreDiags...) - moreDiags = cfg.LocalVariables.ValidateValues() - diags = append(diags, moreDiags...) + diags := cfg.InputVariables.ValidateValues() + diags = append(diags, cfg.LocalVariables.ValidateValues()...) diags = append(diags, cfg.evaluateDatasources(opts.SkipDatasourcesExecution)...) diags = append(diags, checkForDuplicateLocalDefinition(cfg.LocalBlocks)...) diags = append(diags, cfg.evaluateLocalVariables(cfg.LocalBlocks)...) diff --git a/hcl2template/plugin.go b/hcl2template/plugin.go index 1c32ef7779e..4a5c0186647 100644 --- a/hcl2template/plugin.go +++ b/hcl2template/plugin.go @@ -55,7 +55,7 @@ func (cfg *PackerConfig) PluginRequirements() (plugingetter.Requirements, hcl.Di return reqs, diags } -func (cfg *PackerConfig) detectPluginBinaries() hcl.Diagnostics { +func (cfg *PackerConfig) DetectPluginBinaries() hcl.Diagnostics { opts := plugingetter.ListInstallationsOptions{ FromFolders: cfg.parser.PluginConfig.KnownPluginFolders, BinaryInstallationOptions: plugingetter.BinaryInstallationOptions{ diff --git a/packer/core.go b/packer/core.go index 4d62cce6bc3..b711af080bc 100644 --- a/packer/core.go +++ b/packer/core.go @@ -132,6 +132,12 @@ func NewCore(c *CoreConfig) *Core { return core } +// DetectPluginBinaries is used to load required plugins from the template, +// since it is unsupported in JSON, this is essentially a no-op. +func (c *Core) DetectPluginBinaries() hcl.Diagnostics { + return nil +} + func (c *Core) Initialize(_ InitializeOptions) hcl.Diagnostics { err := c.initialize() if err != nil { diff --git a/packer/run_interfaces.go b/packer/run_interfaces.go index 5bf1151529c..253998c20a4 100644 --- a/packer/run_interfaces.go +++ b/packer/run_interfaces.go @@ -40,6 +40,12 @@ type InitializeOptions struct { SkipDatasourcesExecution bool } +type PluginBinaryDetector interface { + // DetectPluginBinaries is used only for HCL2 templates, and loads required + // plugins if specified. + DetectPluginBinaries() hcl.Diagnostics +} + // The Handler handles all Packer things. This interface reflects the Packer // commands, ex: init, console ( evaluate ), fix config, inspect config, etc. To // run a build we will start the builds and then the core of Packer handles @@ -53,6 +59,7 @@ type Handler interface { BuildGetter ConfigFixer ConfigInspector + PluginBinaryDetector } //go:generate enumer -type FixConfigMode