diff --git a/command/hcl2_upgrade.go b/command/hcl2_upgrade.go index 8295e4e8ae0..3393ee86bad 100644 --- a/command/hcl2_upgrade.go +++ b/command/hcl2_upgrade.go @@ -29,42 +29,6 @@ import ( "github.com/zclconf/go-cty/cty" ) -type HCL2UpgradeCommand struct { - Meta -} - -func (c *HCL2UpgradeCommand) Run(args []string) int { - ctx, cleanup := handleTermInterrupt(c.Ui) - defer cleanup() - - cfg, ret := c.ParseArgs(args) - if ret != 0 { - return ret - } - - return c.RunContext(ctx, cfg) -} - -func (c *HCL2UpgradeCommand) ParseArgs(args []string) (*HCL2UpgradeArgs, int) { - var cfg HCL2UpgradeArgs - flags := c.Meta.FlagSet("hcl2_upgrade") - flags.Usage = func() { c.Ui.Say(c.Help()) } - cfg.AddFlagSets(flags) - if err := flags.Parse(args); err != nil { - return &cfg, 1 - } - args = flags.Args() - if len(args) != 1 { - flags.Usage() - return &cfg, 1 - } - cfg.Path = args[0] - if cfg.OutputFile == "" { - cfg.OutputFile = cfg.Path + ".pkr.hcl" - } - return &cfg, 0 -} - const ( hcl2UpgradeFileHeader = `# This file was autogenerated by the 'packer hcl2_upgrade' command. We # recommend double checking that everything is correct before going forward. We @@ -130,6 +94,59 @@ var ( strftime = false ) +// knownPlugins represent the HashiCorp maintained plugins the we can confidently +// construct a required plugins block for. +var knownPlugins = 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", +} + +// unknownPluginName represents any plugin not in knownPlugins or bundled into Packer +const unknownPluginName string = "unknown" + +type HCL2UpgradeCommand struct { + Meta +} + +func (c *HCL2UpgradeCommand) Run(args []string) int { + ctx, cleanup := handleTermInterrupt(c.Ui) + defer cleanup() + + cfg, ret := c.ParseArgs(args) + if ret != 0 { + return ret + } + + return c.RunContext(ctx, cfg) +} + +func (c *HCL2UpgradeCommand) ParseArgs(args []string) (*HCL2UpgradeArgs, int) { + var cfg HCL2UpgradeArgs + flags := c.Meta.FlagSet("hcl2_upgrade") + flags.Usage = func() { c.Ui.Say(c.Help()) } + cfg.AddFlagSets(flags) + if err := flags.Parse(args); err != nil { + return &cfg, 1 + } + args = flags.Args() + if len(args) != 1 { + flags.Usage() + return &cfg, 1 + } + cfg.Path = args[0] + if cfg.OutputFile == "" { + cfg.OutputFile = cfg.Path + ".pkr.hcl" + } + return &cfg, 0 +} + type BlockParser interface { Parse(*template.Template) error Write(*bytes.Buffer) @@ -169,7 +186,6 @@ func (c *HCL2UpgradeCommand) RunContext(_ context.Context, cla *HCL2UpgradeArgs) tpl := core.Template // Parse blocks - packerBlock := &PackerParser{ WithAnnotations: cla.WithAnnotations, } @@ -827,28 +843,28 @@ func gatherPluginsFromTemplate(tpl *template.Template) []string { plugins := map[string]struct{}{} for _, b := range tpl.Builders { - for prefix, plugin := range knownPluginPrefixes { - if strings.HasPrefix(b.Type, prefix) { - plugins[plugin] = struct{}{} - } + name := knownPluginComponent(b.Type) + if name == unknownPluginName { + continue } + plugins[knownPlugins[name]] = struct{}{} } for _, p := range tpl.Provisioners { - for prefix, plugin := range knownPluginPrefixes { - if strings.HasPrefix(p.Type, prefix) { - plugins[plugin] = struct{}{} - } + name := knownPluginComponent(p.Type) + if name == unknownPluginName { + continue } + plugins[knownPlugins[name]] = struct{}{} } for _, pps := range tpl.PostProcessors { for _, pp := range pps { - for prefix, plugin := range knownPluginPrefixes { - if strings.HasPrefix(pp.Type, prefix) { - plugins[plugin] = struct{}{} - } + name := knownPluginComponent(pp.Type) + if name == unknownPluginName { + continue } + plugins[knownPlugins[name]] = struct{}{} } } @@ -1182,18 +1198,17 @@ type SourceParser struct { } func (p *SourceParser) Parse(tpl *template.Template) error { - var unknownBuilders []string if p.out == nil { p.out = []byte{} } + + var unknownBuilders []string for i, builderCfg := range p.Builders { sourcesContent := hclwrite.NewEmptyFile() body := sourcesContent.Body() - body.AppendNewline() - if !p.BuilderPlugins.Has(builderCfg.Type) { + if !p.BuilderPlugins.Has(builderCfg.Type) && knownPluginComponent(builderCfg.Type) == unknownPluginName { unknownBuilders = append(unknownBuilders, builderCfg.Type) - } if builderCfg.Name == "" || builderCfg.Name == builderCfg.Type { builderCfg.Name = fmt.Sprintf("autogenerated_%d", i+1) @@ -1206,9 +1221,11 @@ func (p *SourceParser) Parse(tpl *template.Template) error { p.out = append(p.out, transposeTemplatingCalls(sourcesContent.Bytes())...) } + // TODO update to output to stderr as opposed to having the command exit 1 if len(unknownBuilders) > 0 { return fmt.Errorf("unknown builder type(s): %v\n", unknownBuilders) } + return nil } @@ -1412,3 +1429,12 @@ func fixQuoting(old string) string { return string(body) } + +func knownPluginComponent(component string) string { + for prefix := range knownPlugins { + if strings.HasPrefix(component, prefix) { + return prefix + } + } + return unknownPluginName +} diff --git a/command/hcl2_upgrade_test.go b/command/hcl2_upgrade_test.go index 487d99999cb..9a3833f909e 100644 --- a/command/hcl2_upgrade_test.go +++ b/command/hcl2_upgrade_test.go @@ -19,22 +19,22 @@ func Test_hcl2_upgrade(t *testing.T) { exitCode int exitEarly bool }{ - {folder: "unknown_builder", flags: []string{}, exitCode: 1}, - {folder: "complete", flags: []string{"-with-annotations"}}, - {folder: "without-annotations", flags: []string{}}, - {folder: "minimal", flags: []string{"-with-annotations"}}, - {folder: "source-name", flags: []string{"-with-annotations"}}, - {folder: "error-cleanup-provisioner", flags: []string{"-with-annotations"}}, - {folder: "aws-access-config", flags: []string{}}, - {folder: "escaping", flags: []string{}}, - {folder: "vsphere_linux_options_and_network_interface", exitCode: 1, flags: []string{}}, + {folder: "unknown_builder", flags: []string{}, exitCode: 1}, // warn for unknown components not tracked in knownPluginPrefixes + {folder: "complete", flags: []string{"-with-annotations"}, exitCode: 0}, + {folder: "without-annotations", flags: []string{}, exitCode: 0}, + {folder: "minimal", flags: []string{"-with-annotations"}, exitCode: 0}, + {folder: "source-name", flags: []string{"-with-annotations"}, exitCode: 0}, + {folder: "error-cleanup-provisioner", flags: []string{"-with-annotations"}, exitCode: 0}, + {folder: "aws-access-config", flags: []string{}, exitCode: 0}, + {folder: "escaping", flags: []string{}, exitCode: 0}, + {folder: "vsphere_linux_options_and_network_interface", flags: []string{}, exitCode: 0}, //do not warn for known uninstalled plugins components {folder: "nonexistent", flags: []string{}, exitCode: 1, exitEarly: true}, {folder: "placeholders", flags: []string{}, exitCode: 0}, {folder: "ami_test", flags: []string{}, exitCode: 0}, {folder: "azure_shg", flags: []string{}, exitCode: 0}, - {folder: "variables-only", flags: []string{}}, - {folder: "variables-with-variables", flags: []string{}}, - {folder: "complete-variables-with-template-engine", flags: []string{}}, + {folder: "variables-only", flags: []string{}, exitCode: 0}, + {folder: "variables-with-variables", flags: []string{}, exitCode: 0}, + {folder: "complete-variables-with-template-engine", flags: []string{}, exitCode: 0}, {folder: "undeclared-variables", flags: []string{}, exitCode: 0}, {folder: "varfile-with-no-variables-block", flags: []string{}, exitCode: 0}, {folder: "bundled-plugin-used", flags: []string{}, exitCode: 0}, diff --git a/command/meta.go b/command/meta.go index 0e464ccc36d..9807df78b5d 100644 --- a/command/meta.go +++ b/command/meta.go @@ -9,7 +9,6 @@ import ( "fmt" "io" "os" - "strings" "github.com/hashicorp/hcl/v2/hclparse" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" @@ -167,89 +166,3 @@ func (m *Meta) GetConfigFromJSON(cla *MetaArgs) (packer.Handler, int) { } return core, ret } - -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 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() -}