Files
Lucas Bajolet 8df9ff3ab1 command: introduce schedulers and centralise logic
As we move on to introduce a DAG into Packer, we are introducing a
Scheduler interface, as well as a first implementation of it which
replicates the current scheduling algorithm, but centralises all the
code related to evaluation and execution of components.

This won't pass tests in the current state as only the logic has
changed, and tests haven't been updated yet. This will come later in the
refactoring process.
2023-08-11 16:01:51 -04:00

125 lines
3.9 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package hcl2template
import (
"crypto/sha256"
"fmt"
"log"
"runtime"
"strings"
"github.com/hashicorp/hcl/v2"
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,
Implicit: block.PluginDependencyReason == PluginDependencyImplicit,
})
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
}
uninstalledPlugins := map[string]string{}
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),
Detail: err.Error(),
})
continue
}
if len(sortedInstalls) == 0 {
uninstalledPlugins[pluginRequirement.Identifier.String()] = pluginRequirement.VersionConstraints.String()
continue
}
log.Printf("[TRACE] Found the following %q installations: %v", pluginRequirement.Identifier, 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),
Detail: err.Error(),
})
continue
}
}
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(),
})
}
return diags
}