2026-06-03 05:07:46 -04:00
|
|
|
// Copyright IBM Corp. 2024, 2025
|
2023-08-10 18:53:29 -04:00
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
2023-03-02 15:37:05 -05:00
|
|
|
|
2021-02-02 12:05:04 -05:00
|
|
|
package hcl2template
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"crypto/sha256"
|
|
|
|
|
"fmt"
|
|
|
|
|
"log"
|
|
|
|
|
"runtime"
|
2023-07-17 11:25:42 -04:00
|
|
|
"strings"
|
2021-02-02 12:05:04 -05:00
|
|
|
|
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
2021-03-30 09:53:04 -04:00
|
|
|
"github.com/hashicorp/packer-plugin-sdk/didyoumean"
|
2021-02-02 12:05:04 -05:00
|
|
|
pluginsdk "github.com/hashicorp/packer-plugin-sdk/plugin"
|
|
|
|
|
plugingetter "github.com/hashicorp/packer/packer/plugin-getter"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// PluginRequirements returns a sorted list of plugin requirements.
|
|
|
|
|
func (cfg *PackerConfig) PluginRequirements() (plugingetter.Requirements, hcl.Diagnostics) {
|
|
|
|
|
|
|
|
|
|
var diags hcl.Diagnostics
|
|
|
|
|
var reqs plugingetter.Requirements
|
|
|
|
|
reqPluginsBlocks := cfg.Packer.RequiredPlugins
|
|
|
|
|
|
|
|
|
|
// Take all required plugins, make sure there are no conflicting blocks
|
|
|
|
|
// and append them to the list.
|
|
|
|
|
uniq := map[string]*RequiredPlugin{}
|
|
|
|
|
for _, requiredPluginsBlock := range reqPluginsBlocks {
|
|
|
|
|
for name, block := range requiredPluginsBlock.RequiredPlugins {
|
|
|
|
|
|
|
|
|
|
if previouslySeenBlock, found := uniq[name]; found {
|
|
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
Summary: fmt.Sprintf("Duplicate required_plugin.%q block", name),
|
|
|
|
|
Detail: fmt.Sprintf("Block previously seen at %s is already named %q.\n", previouslySeenBlock.DeclRange, name) +
|
|
|
|
|
"Names at the left hand side of required_plugins are made available to use in your HCL2 configurations.\n" +
|
|
|
|
|
"To allow to calling to their features correctly two plugins have to have different accessors.",
|
|
|
|
|
Context: &block.DeclRange,
|
|
|
|
|
})
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reqs = append(reqs, &plugingetter.Requirement{
|
|
|
|
|
Accessor: name,
|
|
|
|
|
Identifier: block.Type,
|
|
|
|
|
VersionConstraints: block.Requirement.Required,
|
|
|
|
|
})
|
|
|
|
|
uniq[name] = block
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return reqs, diags
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-11 15:41:37 -04:00
|
|
|
func (cfg *PackerConfig) DetectPluginBinaries() hcl.Diagnostics {
|
main: move Discover to DetectPluginBinaries
When Packer is loaded, we used to perform plugin discovery.
This was done for every call to Packer, including when it is executed as
a plugin, arguably against what the comments document.
Doing this as early in the loading process makes it harder to change
this behaviour, as we'd need to introduce flags aside from the rest, and
handle them manually, which is not optimal.
Therefore, we change this: now when Packer starts executing, it will not
attempt to discover installed plugins anymore, and instead will only try
to load them when a configuration has been parsed, and is being used to
perform actions (typically build/validate).
2024-02-02 10:02:40 -05:00
|
|
|
// Then we can apply any constraint from the template, if any
|
2021-02-02 12:05:04 -05:00
|
|
|
opts := plugingetter.ListInstallationsOptions{
|
2024-01-15 14:41:25 -05:00
|
|
|
PluginDirectory: cfg.parser.PluginConfig.PluginDirectory,
|
2021-02-02 12:05:04 -05:00
|
|
|
BinaryInstallationOptions: plugingetter.BinaryInstallationOptions{
|
|
|
|
|
OS: runtime.GOOS,
|
|
|
|
|
ARCH: runtime.GOARCH,
|
|
|
|
|
APIVersionMajor: pluginsdk.APIVersionMajor,
|
|
|
|
|
APIVersionMinor: pluginsdk.APIVersionMinor,
|
|
|
|
|
Checksummers: []plugingetter.Checksummer{
|
|
|
|
|
{Type: "sha256", Hash: sha256.New()},
|
|
|
|
|
},
|
2024-02-02 10:34:52 -05:00
|
|
|
ReleasesOnly: cfg.parser.PluginConfig.ReleasesOnly,
|
2021-02-02 12:05:04 -05:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if runtime.GOOS == "windows" && opts.Ext == "" {
|
|
|
|
|
opts.BinaryInstallationOptions.Ext = ".exe"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pluginReqs, diags := cfg.PluginRequirements()
|
|
|
|
|
if diags.HasErrors() {
|
|
|
|
|
return diags
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-17 11:25:42 -04:00
|
|
|
uninstalledPlugins := map[string]string{}
|
|
|
|
|
|
2021-02-02 12:05:04 -05:00
|
|
|
for _, pluginRequirement := range pluginReqs {
|
|
|
|
|
sortedInstalls, err := pluginRequirement.ListInstallations(opts)
|
|
|
|
|
if err != nil {
|
|
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
|
|
|
Severity: hcl.DiagError,
|
2021-02-15 09:32:42 -05:00
|
|
|
Summary: fmt.Sprintf("Failed to list installation for %s", pluginRequirement.Identifier),
|
2021-02-02 12:05:04 -05:00
|
|
|
Detail: err.Error(),
|
|
|
|
|
})
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if len(sortedInstalls) == 0 {
|
2023-07-17 11:25:42 -04:00
|
|
|
uninstalledPlugins[pluginRequirement.Identifier.String()] = pluginRequirement.VersionConstraints.String()
|
2021-02-02 12:05:04 -05:00
|
|
|
continue
|
|
|
|
|
}
|
2021-02-15 09:32:42 -05:00
|
|
|
log.Printf("[TRACE] Found the following %q installations: %v", pluginRequirement.Identifier, sortedInstalls)
|
2024-02-01 15:01:37 -05:00
|
|
|
install := sortedInstalls[len(sortedInstalls)-1]
|
2021-02-02 12:05:04 -05:00
|
|
|
err = cfg.parser.PluginConfig.DiscoverMultiPlugin(pluginRequirement.Accessor, install.BinaryPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
|
|
|
Severity: hcl.DiagError,
|
2021-02-15 09:32:42 -05:00
|
|
|
Summary: fmt.Sprintf("Error discovering plugin %s", pluginRequirement.Identifier),
|
2021-02-09 11:07:59 -05:00
|
|
|
Detail: err.Error(),
|
2021-02-02 12:05:04 -05:00
|
|
|
})
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-17 11:25:42 -04:00
|
|
|
if len(uninstalledPlugins) > 0 {
|
|
|
|
|
detailMessage := &strings.Builder{}
|
|
|
|
|
detailMessage.WriteString("The following plugins are required, but not installed:\n\n")
|
|
|
|
|
for pluginName, pluginVersion := range uninstalledPlugins {
|
|
|
|
|
fmt.Fprintf(detailMessage, "* %s %s\n", pluginName, pluginVersion)
|
|
|
|
|
}
|
|
|
|
|
detailMessage.WriteString("\nDid you run packer init for this project ?")
|
|
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
Summary: "Missing plugins",
|
|
|
|
|
Detail: detailMessage.String(),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-17 17:24:47 -04:00
|
|
|
// Do a second pass to discover the remaining installed plugins
|
|
|
|
|
err := cfg.parser.PluginConfig.Discover()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return (hcl.Diagnostics{}).Append(&hcl.Diagnostic{
|
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
Summary: "Failed to discover installed plugins",
|
|
|
|
|
Detail: err.Error(),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-02 12:05:04 -05:00
|
|
|
return diags
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics {
|
2021-03-30 09:53:04 -04:00
|
|
|
// verify that all used plugins do exist
|
2021-02-02 12:05:04 -05:00
|
|
|
var diags hcl.Diagnostics
|
|
|
|
|
|
|
|
|
|
for _, build := range cfg.Builds {
|
|
|
|
|
for i := range build.Sources {
|
|
|
|
|
// here we grab a pointer to the source usage because we will set
|
|
|
|
|
// its body.
|
|
|
|
|
srcUsage := &(build.Sources[i])
|
|
|
|
|
if !cfg.parser.PluginConfig.Builders.Has(srcUsage.Type) {
|
packer: link to docs if a component is missing
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.
2023-11-22 14:57:29 -05:00
|
|
|
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)
|
|
|
|
|
}
|
2021-02-02 12:05:04 -05:00
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
|
|
|
Summary: "Unknown " + buildSourceLabel + " type " + srcUsage.Type,
|
|
|
|
|
Subject: &build.HCL2Ref.DefRange,
|
packer: link to docs if a component is missing
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.
2023-11-22 14:57:29 -05:00
|
|
|
Detail: detail,
|
2021-02-02 12:05:04 -05:00
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
})
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sourceDefinition, found := cfg.Sources[srcUsage.SourceRef]
|
|
|
|
|
if !found {
|
2021-03-30 09:53:04 -04:00
|
|
|
availableSrcs := listAvailableSourceNames(cfg.Sources)
|
|
|
|
|
detail := fmt.Sprintf("Known: %v", availableSrcs)
|
|
|
|
|
if sugg := didyoumean.NameSuggestion(srcUsage.SourceRef.String(), availableSrcs); sugg != "" {
|
|
|
|
|
detail = fmt.Sprintf("Did you mean to use %q?", sugg)
|
|
|
|
|
}
|
2021-02-02 12:05:04 -05:00
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
2021-03-30 09:53:04 -04:00
|
|
|
Summary: "Unknown " + sourceLabel + " " + srcUsage.SourceRef.String(),
|
2021-02-02 12:05:04 -05:00
|
|
|
Subject: build.HCL2Ref.DefRange.Ptr(),
|
|
|
|
|
Severity: hcl.DiagError,
|
2021-03-30 09:53:04 -04:00
|
|
|
Detail: detail,
|
2021-02-02 12:05:04 -05:00
|
|
|
})
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
body := sourceDefinition.block.Body
|
|
|
|
|
if srcUsage.Body != nil {
|
|
|
|
|
// merge additions into source definition to get a new body.
|
|
|
|
|
body = hcl.MergeBodies([]hcl.Body{body, srcUsage.Body})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srcUsage.Body = body
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, provBlock := range build.ProvisionerBlocks {
|
|
|
|
|
if !cfg.parser.PluginConfig.Provisioners.Has(provBlock.PType) {
|
packer: link to docs if a component is missing
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.
2023-11-22 14:57:29 -05:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-02 12:05:04 -05:00
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
|
|
|
Summary: fmt.Sprintf("Unknown "+buildProvisionerLabel+" type %q", provBlock.PType),
|
|
|
|
|
Subject: provBlock.HCL2Ref.TypeRange.Ptr(),
|
packer: link to docs if a component is missing
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.
2023-11-22 14:57:29 -05:00
|
|
|
Detail: detail,
|
2021-02-02 12:05:04 -05:00
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-11 04:23:15 -05:00
|
|
|
if build.ErrorCleanupProvisionerBlock != nil {
|
|
|
|
|
if !cfg.parser.PluginConfig.Provisioners.Has(build.ErrorCleanupProvisionerBlock.PType) {
|
packer: link to docs if a component is missing
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.
2023-11-22 14:57:29 -05:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-11 04:23:15 -05:00
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
|
|
|
Summary: fmt.Sprintf("Unknown "+buildErrorCleanupProvisionerLabel+" type %q", build.ErrorCleanupProvisionerBlock.PType),
|
|
|
|
|
Subject: build.ErrorCleanupProvisionerBlock.HCL2Ref.TypeRange.Ptr(),
|
packer: link to docs if a component is missing
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.
2023-11-22 14:57:29 -05:00
|
|
|
Detail: detail,
|
2021-02-11 04:23:15 -05:00
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-02 12:05:04 -05:00
|
|
|
for _, ppList := range build.PostProcessorsLists {
|
|
|
|
|
for _, ppBlock := range ppList {
|
|
|
|
|
if !cfg.parser.PluginConfig.PostProcessors.Has(ppBlock.PType) {
|
packer: link to docs if a component is missing
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.
2023-11-22 14:57:29 -05:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-02 12:05:04 -05:00
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
|
|
|
Summary: fmt.Sprintf("Unknown "+buildPostProcessorLabel+" type %q", ppBlock.PType),
|
|
|
|
|
Subject: ppBlock.HCL2Ref.TypeRange.Ptr(),
|
packer: link to docs if a component is missing
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.
2023-11-22 14:57:29 -05:00
|
|
|
Detail: detail,
|
2021-02-02 12:05:04 -05:00
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return diags
|
|
|
|
|
}
|