-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
packer: log plugins used and their version/path #12567
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package main | ||
|
||
import ( | ||
_ "embed" | ||
"fmt" | ||
"os" | ||
"regexp" | ||
"strings" | ||
|
||
"github.com/hashicorp/packer/packer" | ||
"golang.org/x/mod/modfile" | ||
) | ||
|
||
//go:embed go.mod | ||
var mod string | ||
|
||
var pluginRegex = regexp.MustCompile("packer-plugin-.*$") | ||
|
||
func GetBundledPluginVersions() map[string]packer.PluginSpec { | ||
pluginSpecs := map[string]packer.PluginSpec{} | ||
|
||
mods, err := modfile.Parse("", []byte(mod), nil) | ||
if err != nil { | ||
panic(fmt.Sprintf("failed to parse embedded modfile: %s", err)) | ||
} | ||
|
||
for _, req := range mods.Require { | ||
if pluginRegex.MatchString(req.Mod.Path) { | ||
pluginName := pluginRegex.FindString(req.Mod.Path) | ||
pluginShortName := strings.Replace(pluginName, "packer-plugin-", "", 1) | ||
pluginSpecs[pluginShortName] = packer.PluginSpec{ | ||
Name: pluginShortName, | ||
Version: fmt.Sprintf("bundled (%s)", req.Mod.Version), | ||
Path: os.Args[0], | ||
} | ||
} | ||
} | ||
|
||
return pluginSpecs | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,9 @@ import ( | |
"flag" | ||
"fmt" | ||
"io" | ||
"log" | ||
"os" | ||
"sort" | ||
"strings" | ||
|
||
"github.com/hashicorp/hcl/v2" | ||
|
@@ -257,7 +259,9 @@ func (m *Meta) detectBundledPluginsJSON(core *packer.Core) []string { | |
} | ||
} | ||
|
||
return compileBundledPluginList(bundledPlugins) | ||
bundledPluginList := compileBundledPluginList(bundledPlugins) | ||
|
||
return bundledPluginList | ||
} | ||
|
||
var knownPluginPrefixes = map[string]string{ | ||
|
@@ -354,5 +358,110 @@ func (m *Meta) detectBundledPluginsHCL2(config *hcl2template.PackerConfig) []str | |
} | ||
} | ||
|
||
return compileBundledPluginList(bundledPlugins) | ||
bundledPluginList := compileBundledPluginList(bundledPlugins) | ||
|
||
return bundledPluginList | ||
} | ||
|
||
func (m *Meta) LogPluginUsage(handler packer.Handler) { | ||
switch h := handler.(type) { | ||
case *packer.Core: | ||
m.logPluginUsageJSON(h) | ||
case *hcl2template.PackerConfig: | ||
m.logPluginUsageHCL2(handler.(*hcl2template.PackerConfig)) | ||
} | ||
} | ||
|
||
func (m *Meta) logPluginUsageJSON(c *packer.Core) { | ||
usedPlugins := map[string]packer.PluginSpec{} | ||
|
||
tmpl := c.Template | ||
if tmpl == nil { | ||
panic("No template parsed. This is a Packer bug which should be reported, please open an issue on the project's issue tracker.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like a pattern but I think throwing a panic for failures that are not critical to the execution of Packer can be more a blocker to the user than helpful. Especially, if there are unknown cases that cause an expected failure. For example the panics happening in the tests runs. If we can't get this information for logging or displaying a warning to a user we should log the failure and not stop the build. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with you, but at this time, if the template isn't properly parsed, this means that we've changed something in the logic, and we should be made aware ASAP, not later when we investigate why we don't see a warning/error anymore, and we notice this log. |
||
} | ||
|
||
for _, b := range tmpl.Builders { | ||
// Check since internal components are not registered as components | ||
ps, ok := m.CoreConfig.Components.PluginConfig.GetSpecForPlugin(b.Type) | ||
if ok { | ||
usedPlugins[ps.Name] = ps | ||
} | ||
} | ||
|
||
for _, p := range tmpl.Provisioners { | ||
// Check since internal components are not registered as components | ||
ps, ok := m.CoreConfig.Components.PluginConfig.GetSpecForPlugin(p.Type) | ||
if ok { | ||
usedPlugins[ps.Name] = ps | ||
} | ||
} | ||
|
||
for _, pps := range tmpl.PostProcessors { | ||
for _, pp := range pps { | ||
// Check since internal components are not registered as components | ||
ps, ok := m.CoreConfig.Components.PluginConfig.GetSpecForPlugin(pp.Type) | ||
if ok { | ||
usedPlugins[ps.Name] = ps | ||
} | ||
} | ||
} | ||
|
||
logPlugins(usedPlugins) | ||
} | ||
|
||
func (m *Meta) logPluginUsageHCL2(config *hcl2template.PackerConfig) { | ||
usedPlugins := map[string]packer.PluginSpec{} | ||
|
||
for _, b := range config.Builds { | ||
for _, src := range b.Sources { | ||
ps, ok := m.CoreConfig.Components.PluginConfig.GetSpecForPlugin(src.Type) | ||
if ok { | ||
usedPlugins[ps.Name] = ps | ||
} | ||
} | ||
|
||
for _, p := range b.ProvisionerBlocks { | ||
ps, ok := m.CoreConfig.Components.PluginConfig.GetSpecForPlugin(p.PType) | ||
if ok { | ||
usedPlugins[ps.Name] = ps | ||
} | ||
} | ||
|
||
for _, pps := range b.PostProcessorsLists { | ||
for _, pp := range pps { | ||
ps, ok := m.CoreConfig.Components.PluginConfig.GetSpecForPlugin(pp.PType) | ||
if ok { | ||
usedPlugins[ps.Name] = ps | ||
} | ||
} | ||
} | ||
} | ||
|
||
for _, ds := range config.Datasources { | ||
ps, ok := m.CoreConfig.Components.PluginConfig.GetSpecForPlugin(ds.Type) | ||
if ok { | ||
usedPlugins[ps.Name] = ps | ||
} | ||
} | ||
|
||
logPlugins(usedPlugins) | ||
} | ||
|
||
func logPlugins(usedPlugins map[string]packer.PluginSpec) { | ||
// Could happen if no plugin is loaded and we only rely on internal components | ||
if len(usedPlugins) == 0 { | ||
return | ||
} | ||
|
||
pluginNames := make([]string, 0, len(usedPlugins)) | ||
for pn := range usedPlugins { | ||
pluginNames = append(pluginNames, pn) | ||
} | ||
sort.Strings(pluginNames) | ||
|
||
log.Printf("[INFO] - Used plugins") | ||
for _, pn := range pluginNames { | ||
ps := usedPlugins[pn] | ||
log.Printf(fmt.Sprintf("*\t%s - %s: %s", ps.Name, ps.Version, ps.Path)) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's nice to see how simple this turned out to be but it feels like a short lived change given that their versions haven't changed in the past 4 releases and that we will be removing bundle plugins soon. But I do think logging the versions for external plugins makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In principle I agree with you, so that leaves us with a couple alternatives:
Given those alternatives, I know I would prefer option 3 since the work is already done, the code is straightforward, and concentrated (besides the call to this in main), so when we remove bundled plugins, this will be equally easy to cleanup (at least compared to solution 2).
Let me know what you think on that, I would have liked to include this in 1.9.3, but we can push it to a later version if there are reservations on this implementation.