Skip to content

Commit

Permalink
packer: link to docs if a component is missing
Browse files Browse the repository at this point in the history
When a user invokes packer for a build or validation, the template being
processed needs components to be present for Packer to process it
without error.

If the component cannot be found from the plugins loaded (or from the
components bundled with Packer), Packer errors, and the command fails.

This is expected, but the error message does not suggest anything to fix
the error, potantially leaving users confused at the problem.

This commit suggests either a replacement (in case of a typo), or points
to the web documentation for Packer, specifically the integrations, so
they can look for the plugin they're missing, and install it, so
subsequent invocations of Packer work.
  • Loading branch information
lbajolet-hashicorp committed Nov 22, 2023
1 parent 9b2d9f4 commit efe182b
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 5 deletions.
59 changes: 55 additions & 4 deletions hcl2template/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -169,21 +181,47 @@ 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,
})
}
}

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,
})
}
Expand All @@ -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,
})
}
Expand Down
53 changes: 52 additions & 1 deletion packer/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit efe182b

Please sign in to comment.