packer-cn/hcl2template/plugin.go

197 lines
6.9 KiB
Go

package hcl2template
import (
"crypto/sha256"
"fmt"
"log"
"runtime"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/ext/dynblock"
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
}
func (cfg *PackerConfig) detectPluginBinaries() hcl.Diagnostics {
opts := plugingetter.ListInstallationsOptions{
FromFolders: cfg.parser.PluginConfig.KnownPluginFolders,
BinaryInstallationOptions: plugingetter.BinaryInstallationOptions{
OS: runtime.GOOS,
ARCH: runtime.GOARCH,
APIVersionMajor: pluginsdk.APIVersionMajor,
APIVersionMinor: pluginsdk.APIVersionMinor,
Checksummers: []plugingetter.Checksummer{
{Type: "sha256", Hash: sha256.New()},
},
},
}
if runtime.GOOS == "windows" && opts.Ext == "" {
opts.BinaryInstallationOptions.Ext = ".exe"
}
pluginReqs, diags := cfg.PluginRequirements()
if diags.HasErrors() {
return diags
}
for _, pluginRequirement := range pluginReqs {
sortedInstalls, err := pluginRequirement.ListInstallations(opts)
if err != nil {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: fmt.Sprintf("Failed to list installation for %s", pluginRequirement.Identifier.ForDisplay()),
Detail: err.Error(),
})
continue
}
if len(sortedInstalls) == 0 {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: fmt.Sprintf("no plugin installed for %s %v", pluginRequirement.Identifier.ForDisplay(), pluginRequirement.VersionConstraints.String()),
Detail: "Did you run packer init for this project ?",
})
continue
}
log.Printf("[TRACE] Found the following %q installations: %v", pluginRequirement.Identifier.ForDisplay(), sortedInstalls)
install := sortedInstalls[len(sortedInstalls)-1]
err = cfg.parser.PluginConfig.DiscoverMultiPlugin(pluginRequirement.Accessor, install.BinaryPath)
if err != nil {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: fmt.Sprintf("Error discovering plugin %s", pluginRequirement.Identifier.ForDisplay()),
Detail: err.Error(),
})
continue
}
}
return diags
}
func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics {
// verify that all used plugins do exist and expand dynamic bodies
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) {
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()),
Severity: hcl.DiagError,
})
continue
}
sourceDefinition, found := cfg.Sources[srcUsage.SourceRef]
if !found {
diags = append(diags, &hcl.Diagnostic{
Summary: "Unknown " + sourceLabel + " " + srcUsage.String(),
Subject: build.HCL2Ref.DefRange.Ptr(),
Severity: hcl.DiagError,
Detail: fmt.Sprintf("Known: %v", cfg.Sources),
// TODO: show known sources as a string slice here ^.
})
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})
}
// expand any dynamic block.
body = dynblock.Expand(body, cfg.EvalContext(BuildContext, nil))
srcUsage.Body = body
}
for _, provBlock := range build.ProvisionerBlocks {
if !cfg.parser.PluginConfig.Provisioners.Has(provBlock.PType) {
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()),
Severity: hcl.DiagError,
})
}
// Allow rest of the body to have dynamic blocks
provBlock.HCL2Ref.Rest = dynblock.Expand(provBlock.HCL2Ref.Rest, cfg.EvalContext(BuildContext, nil))
}
if build.ErrorCleanupProvisionerBlock != nil {
if !cfg.parser.PluginConfig.Provisioners.Has(build.ErrorCleanupProvisionerBlock.PType) {
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()),
Severity: hcl.DiagError,
})
}
// Allow rest of the body to have dynamic blocks
build.ErrorCleanupProvisionerBlock.HCL2Ref.Rest = dynblock.Expand(build.ErrorCleanupProvisionerBlock.HCL2Ref.Rest, cfg.EvalContext(BuildContext, nil))
}
for _, ppList := range build.PostProcessorsLists {
for _, ppBlock := range ppList {
if !cfg.parser.PluginConfig.PostProcessors.Has(ppBlock.PType) {
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()),
Severity: hcl.DiagError,
})
}
// Allow the rest of the body to have dynamic blocks
ppBlock.HCL2Ref.Rest = dynblock.Expand(ppBlock.HCL2Ref.Rest, cfg.EvalContext(BuildContext, nil))
}
}
}
return diags
}