diff --git a/hcl2template/plugin.go b/hcl2template/plugin.go index 323263596f8..321616017b6 100644 --- a/hcl2template/plugin.go +++ b/hcl2template/plugin.go @@ -133,10 +133,22 @@ func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics { // its body. srcUsage := &(build.Sources[i]) if !cfg.parser.PluginConfig.Builders.Has(srcUsage.Type) { + detail := fmt.Sprintf( + "The %s %s is unknown by Packer, and is likely part of a plugin that is not installed.\n"+ + "You may find the needed plugin along with installation instructions documented on the Packer integrations page.\n\n"+ + "https://developer.hashicorp.com/packer/integrations?filter=%s", + buildSourceLabel, + srcUsage.Type, + strings.Split(srcUsage.Type, "-")[0], + ) + + if sugg := didyoumean.NameSuggestion(srcUsage.Type, cfg.parser.PluginConfig.Builders.List()); sugg != "" { + detail = fmt.Sprintf("Did you mean to use %q?", sugg) + } diags = append(diags, &hcl.Diagnostic{ Summary: "Unknown " + buildSourceLabel + " type " + srcUsage.Type, Subject: &build.HCL2Ref.DefRange, - Detail: fmt.Sprintf("known builders: %v", cfg.parser.PluginConfig.Builders.List()), + Detail: detail, Severity: hcl.DiagError, }) continue @@ -169,10 +181,23 @@ func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics { for _, provBlock := range build.ProvisionerBlocks { if !cfg.parser.PluginConfig.Provisioners.Has(provBlock.PType) { + detail := fmt.Sprintf( + "The %s %s is unknown by Packer, and is likely part of a plugin that is not installed.\n"+ + "You may find the needed plugin along with installation instructions documented on the Packer integrations page.\n\n"+ + "https://developer.hashicorp.com/packer/integrations?filter=%s", + buildProvisionerLabel, + provBlock.PType, + strings.Split(provBlock.PType, "-")[0], + ) + + if sugg := didyoumean.NameSuggestion(provBlock.PType, cfg.parser.PluginConfig.Provisioners.List()); sugg != "" { + detail = fmt.Sprintf("Did you mean to use %q?", sugg) + } + diags = append(diags, &hcl.Diagnostic{ Summary: fmt.Sprintf("Unknown "+buildProvisionerLabel+" type %q", provBlock.PType), Subject: provBlock.HCL2Ref.TypeRange.Ptr(), - Detail: fmt.Sprintf("known "+buildProvisionerLabel+"s: %v", cfg.parser.PluginConfig.Provisioners.List()), + Detail: detail, Severity: hcl.DiagError, }) } @@ -180,10 +205,23 @@ func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics { if build.ErrorCleanupProvisionerBlock != nil { if !cfg.parser.PluginConfig.Provisioners.Has(build.ErrorCleanupProvisionerBlock.PType) { + detail := fmt.Sprintf( + "The %s %s is unknown by Packer, and is likely part of a plugin that is not installed.\n"+ + "You may find the needed plugin along with installation instructions documented on the Packer integrations page.\n\n"+ + "https://developer.hashicorp.com/packer/integrations?filter=%s", + buildErrorCleanupProvisionerLabel, + build.ErrorCleanupProvisionerBlock.PType, + strings.Split(build.ErrorCleanupProvisionerBlock.PType, "-")[0], + ) + + if sugg := didyoumean.NameSuggestion(build.ErrorCleanupProvisionerBlock.PType, cfg.parser.PluginConfig.Provisioners.List()); sugg != "" { + detail = fmt.Sprintf("Did you mean to use %q?", sugg) + } + diags = append(diags, &hcl.Diagnostic{ Summary: fmt.Sprintf("Unknown "+buildErrorCleanupProvisionerLabel+" type %q", build.ErrorCleanupProvisionerBlock.PType), Subject: build.ErrorCleanupProvisionerBlock.HCL2Ref.TypeRange.Ptr(), - Detail: fmt.Sprintf("known "+buildErrorCleanupProvisionerLabel+"s: %v", cfg.parser.PluginConfig.Provisioners.List()), + Detail: detail, Severity: hcl.DiagError, }) } @@ -192,10 +230,23 @@ func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics { for _, ppList := range build.PostProcessorsLists { for _, ppBlock := range ppList { if !cfg.parser.PluginConfig.PostProcessors.Has(ppBlock.PType) { + detail := fmt.Sprintf( + "The %s %s is unknown by Packer, and is likely part of a plugin that is not installed.\n"+ + "You may find the needed plugin along with installation instructions documented on the Packer integrations page.\n\n"+ + "https://developer.hashicorp.com/packer/integrations?filter=%s", + buildPostProcessorLabel, + ppBlock.PType, + strings.Split(ppBlock.PType, "-")[0], + ) + + if sugg := didyoumean.NameSuggestion(ppBlock.PType, cfg.parser.PluginConfig.PostProcessors.List()); sugg != "" { + detail = fmt.Sprintf("Did you mean to use %q?", sugg) + } + diags = append(diags, &hcl.Diagnostic{ Summary: fmt.Sprintf("Unknown "+buildPostProcessorLabel+" type %q", ppBlock.PType), Subject: ppBlock.HCL2Ref.TypeRange.Ptr(), - Detail: fmt.Sprintf("known "+buildPostProcessorLabel+"s: %v", cfg.parser.PluginConfig.PostProcessors.List()), + Detail: detail, Severity: hcl.DiagError, }) } diff --git a/packer/core.go b/packer/core.go index 732f026360f..4803f1de474 100644 --- a/packer/core.go +++ b/packer/core.go @@ -18,6 +18,7 @@ import ( multierror "github.com/hashicorp/go-multierror" version "github.com/hashicorp/go-version" hcl "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/packer-plugin-sdk/didyoumean" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/hashicorp/packer-plugin-sdk/template" "github.com/hashicorp/packer-plugin-sdk/template/interpolate" @@ -218,15 +219,33 @@ func (c *Core) BuildNames(only, except []string) []string { func (c *Core) generateCoreBuildProvisioner(rawP *template.Provisioner, rawName string) (CoreBuildProvisioner, error) { // Get the provisioner cbp := CoreBuildProvisioner{} + + if !c.components.PluginConfig.Provisioners.Has(rawP.Type) { + err := fmt.Errorf( + "The provisioner %s is unknown by Packer, and is likely part of a plugin that is not installed.\n"+ + "You may find the needed plugin along with installation instructions documented on the Packer integrations page.\n\n"+ + "https://developer.hashicorp.com/packer/integrations?filter=%s", + rawP.Type, + strings.Split(rawP.Type, "-")[0], + ) + + if sugg := didyoumean.NameSuggestion(rawP.Type, c.components.PluginConfig.Builders.List()); sugg != "" { + err = fmt.Errorf("Did you mean to use %q?", sugg) + } + + return cbp, err + } + provisioner, err := c.components.PluginConfig.Provisioners.Start(rawP.Type) if err != nil { return cbp, fmt.Errorf( "error initializing provisioner '%s': %s", rawP.Type, err) } + // Seems unlikely that a provisioner doesn't start successfully without error if provisioner == nil { return cbp, fmt.Errorf( - "provisioner type not found: %s", rawP.Type) + "provisioner failed to be started and did not error: %s", rawP.Type) } // Get the configuration @@ -335,6 +354,22 @@ func (c *Core) Build(n string) (packersdk.Build, error) { // For reference, the builtin BuilderStore is generated in // packer/config.go in the Discover() func. + if !c.components.PluginConfig.Builders.Has(configBuilder.Type) { + err := fmt.Errorf( + "The builder %s is unknown by Packer, and is likely part of a plugin that is not installed.\n"+ + "You may find the needed plugin along with installation instructions documented on the Packer integrations page.\n\n"+ + "https://developer.hashicorp.com/packer/integrations?filter=%s", + configBuilder.Type, + strings.Split(configBuilder.Type, "-")[0], + ) + + if sugg := didyoumean.NameSuggestion(configBuilder.Type, c.components.PluginConfig.Builders.List()); sugg != "" { + err = fmt.Errorf("Did you mean to use %q?", sugg) + } + + return nil, err + } + // the Start command launches the builder plugin of the given type without // calling Prepare() or passing any build-specific details. builder, err := c.components.PluginConfig.Builders.Start(configBuilder.Type) @@ -396,6 +431,22 @@ func (c *Core) Build(n string) (packersdk.Build, error) { break } + if !c.components.PluginConfig.PostProcessors.Has(rawP.Type) { + err := fmt.Errorf( + "The post-processor %s is unknown by Packer, and is likely part of a plugin that is not installed.\n"+ + "You may find the needed plugin along with installation instructions documented on the Packer integrations page.\n\n"+ + "https://developer.hashicorp.com/packer/integrations?filter=%s", + rawP.Type, + strings.Split(rawP.Type, "-")[0], + ) + + if sugg := didyoumean.NameSuggestion(rawP.Type, c.components.PluginConfig.PostProcessors.List()); sugg != "" { + err = fmt.Errorf("Did you mean to use %q?", sugg) + } + + return nil, err + } + // Get the post-processor postProcessor, err := c.components.PluginConfig.PostProcessors.Start(rawP.Type) if err != nil {