From c71a79218623edb453a72a15750ce005dd9bae06 Mon Sep 17 00:00:00 2001 From: Adrien Delorme Date: Thu, 30 Apr 2020 16:36:01 +0200 Subject: [PATCH] simplify/refactor core for build & validate --- command/build.go | 115 ++++++++++++++++++++++++++------------------ command/meta.go | 48 ------------------ command/validate.go | 2 +- packer/core.go | 15 +++++- 4 files changed, 83 insertions(+), 97 deletions(-) diff --git a/command/build.go b/command/build.go index 9017ae471..d815f6f26 100644 --- a/command/build.go +++ b/command/build.go @@ -96,25 +96,25 @@ func (c *BuildCommand) ParseArgs(args []string) (Config, int) { return cfg, 0 } -func (c *BuildCommand) GetBuildsFromHCL(path string) ([]packer.Build, int) { +func (m *Meta) GetConfigFromHCL(path string) (BuildStarter, int) { parser := &hcl2template.Parser{ Parser: hclparse.NewParser(), - BuilderSchemas: c.CoreConfig.Components.BuilderStore, - ProvisionersSchemas: c.CoreConfig.Components.ProvisionerStore, - PostProcessorsSchemas: c.CoreConfig.Components.PostProcessorStore, + BuilderSchemas: m.CoreConfig.Components.BuilderStore, + ProvisionersSchemas: m.CoreConfig.Components.ProvisionerStore, + PostProcessorsSchemas: m.CoreConfig.Components.PostProcessorStore, } - cfg, diags := parser.Parse(path, c.varFiles, c.flagVars) + cfg, diags := parser.Parse(path, m.varFiles, m.flagVars) { // write HCL errors/diagnostics if any. b := bytes.NewBuffer(nil) err := hcl.NewDiagnosticTextWriter(b, parser.Files(), 80, false).WriteDiagnostics(diags) if err != nil { - c.Ui.Error("could not write diagnostic: " + err.Error()) + m.Ui.Error("could not write diagnostic: " + err.Error()) return nil, 1 } if b.Len() != 0 { - c.Ui.Message(b.String()) + m.Ui.Message(b.String()) } } ret := 0 @@ -124,75 +124,88 @@ func (c *BuildCommand) GetBuildsFromHCL(path string) ([]packer.Build, int) { // working; should this be an option ? } - builds, diags := cfg.GetBuilds(c.CoreConfig.Only, c.CoreConfig.Except) - { - // write HCL errors/diagnostics if any. - b := bytes.NewBuffer(nil) - err := hcl.NewDiagnosticTextWriter(b, parser.Files(), 80, false).WriteDiagnostics(diags) - if err != nil { - c.Ui.Error("could not write diagnostic: " + err.Error()) - return nil, 1 + return func(opts buildStarterOptions) ([]packer.Build, int) { + builds, diags := cfg.GetBuilds(opts.only, opts.except) + { + // write HCL errors/diagnostics if any. + b := bytes.NewBuffer(nil) + err := hcl.NewDiagnosticTextWriter(b, parser.Files(), 80, false).WriteDiagnostics(diags) + if err != nil { + m.Ui.Error("could not write diagnostic: " + err.Error()) + return nil, 1 + } + if b.Len() != 0 { + m.Ui.Message(b.String()) + } } - if b.Len() != 0 { - c.Ui.Message(b.String()) + if diags.HasErrors() { + ret = 1 } - } - if diags.HasErrors() { - ret = 1 - } - return builds, ret + return builds, ret + }, ret } -func (c *BuildCommand) GetBuilds(path string) ([]packer.Build, int) { +// GetBuilds will start all packer plugins ( builder, provisioner and +// post-processor ) referenced in the config. These plugins will be in a +// waiting to execute mode. Upon error a non nil error will be returned. +type BuildStarter func(buildStarterOptions) ([]packer.Build, int) +type buildStarterOptions struct { + except, only []string +} + +func (m *Meta) GetConfig(path string) (BuildStarter, int) { isHCLLoaded, err := isHCLLoaded(path) if path != "-" && err != nil { - c.Ui.Error(fmt.Sprintf("could not tell whether %s is hcl enabled: %s", path, err)) + m.Ui.Error(fmt.Sprintf("could not tell whether %s is hcl enabled: %s", path, err)) return nil, 1 } if isHCLLoaded { - return c.GetBuildsFromHCL(path) + return m.GetConfigFromHCL(path) } - // TODO: uncomment in v1.5.1 once we've polished HCL a bit more. + // TODO: uncomment once we've polished HCL a bit more. // c.Ui.Say(`Legacy JSON Configuration Will Be Used. // The template will be parsed in the legacy configuration style. This style // will continue to work but users are encouraged to move to the new style. // See: https://packer.io/guides/hcl // `) + return m.GetConfigFromJSON(path) +} +func (m *Meta) GetConfigFromJSON(path string) (BuildStarter, int) { // Parse the template - var tpl *template.Template - tpl, err = template.ParseFile(path) + tpl, err := template.ParseFile(path) if err != nil { - c.Ui.Error(fmt.Sprintf("Failed to parse template: %s", err)) + m.Ui.Error(fmt.Sprintf("Failed to parse template: %s", err)) return nil, 1 } // Get the core - core, err := c.Meta.Core(tpl) + core, err := m.Core(tpl) if err != nil { - c.Ui.Error(err.Error()) + m.Ui.Error(err.Error()) return nil, 1 } + return func(opts buildStarterOptions) ([]packer.Build, int) { + ret := 0 + buildNames := core.BuildNames(opts.only, opts.except) + builds := make([]packer.Build, 0, len(buildNames)) + for _, n := range buildNames { + b, err := core.Build(n) + if err != nil { + m.Ui.Error(fmt.Sprintf( + "Failed to initialize build '%s': %s", + n, err)) + ret = 1 + continue + } - ret := 0 - buildNames := c.Meta.BuildNames(core) - builds := make([]packer.Build, 0, len(buildNames)) - for _, n := range buildNames { - b, err := core.Build(n) - if err != nil { - c.Ui.Error(fmt.Sprintf( - "Failed to initialize build '%s': %s", - n, err)) - ret = 1 - continue + builds = append(builds, b) } - - builds = append(builds, b) - } - return builds, ret + return builds, ret + }, 0 } func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { @@ -201,7 +214,15 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { return ret } - builds, ret := c.GetBuilds(cfg.Path) + packerStarter, ret := c.GetConfig(cfg.Path) + if ret != 0 { + return ret + } + + builds, ret := packerStarter(buildStarterOptions{ + except: c.CoreConfig.Except, + only: c.CoreConfig.Only, + }) if cfg.Debug { c.Ui.Say("Debug mode enabled. Builds will not be parallelized.") diff --git a/command/meta.go b/command/meta.go index 58d9a9359..a42a463f0 100644 --- a/command/meta.go +++ b/command/meta.go @@ -73,54 +73,6 @@ func (m *Meta) Core(tpl *template.Template) (*packer.Core, error) { return core, nil } -// BuildNames returns the list of builds that are in the given core -// that we care about taking into account the only and except flags. -func (m *Meta) BuildNames(c *packer.Core) []string { - // TODO: test - - // Filter the "only" - if len(m.CoreConfig.Only) > 0 { - // Build a set of all the available names - nameSet := make(map[string]struct{}) - for _, n := range c.BuildNames() { - nameSet[n] = struct{}{} - } - - // Build our result set which we pre-allocate some sane number - result := make([]string, 0, len(m.CoreConfig.Only)) - for _, n := range m.CoreConfig.Only { - if _, ok := nameSet[n]; ok { - result = append(result, n) - } - } - - return result - } - - // Filter the "except" - if len(m.CoreConfig.Except) > 0 { - // Build a set of the things we don't want - nameSet := make(map[string]struct{}) - for _, n := range m.CoreConfig.Except { - nameSet[n] = struct{}{} - } - - // Build our result set which is the names of all builds except - // those in the given set. - names := c.BuildNames() - result := make([]string, 0, len(names)) - for _, n := range names { - if _, ok := nameSet[n]; !ok { - result = append(result, n) - } - } - return result - } - - // We care about everything - return c.BuildNames() -} - // FlagSet returns a FlagSet with the common flags that every // command implements. The exact behavior of FlagSet can be configured // using the flags as the second parameter, for example to disable diff --git a/command/validate.go b/command/validate.go index a84eb6637..8f08b44bc 100644 --- a/command/validate.go +++ b/command/validate.go @@ -57,7 +57,7 @@ func (c *ValidateCommand) Run(args []string) int { warnings := make(map[string][]string) // Get the builds we care about - buildNames := c.Meta.BuildNames(core) + buildNames := core.BuildNames(c.CoreConfig.Only, c.CoreConfig.Except) builds := make([]packer.Build, 0, len(buildNames)) for _, n := range buildNames { b, err := core.Build(n) diff --git a/packer/core.go b/packer/core.go index 05cb720ff..04be127c3 100644 --- a/packer/core.go +++ b/packer/core.go @@ -126,9 +126,22 @@ func NewCore(c *CoreConfig) (*Core, error) { } // BuildNames returns the builds that are available in this configured core. -func (c *Core) BuildNames() []string { +func (c *Core) BuildNames(only, except []string) []string { + + sort.Strings(only) + sort.Strings(except) + r := make([]string, 0, len(c.builds)) for n := range c.builds { + onlyPos := sort.SearchStrings(only, n) + foundInOnly := onlyPos < len(only) && only[onlyPos] == n + if len(only) > 0 && !foundInOnly { + continue + } + + if pos := sort.SearchStrings(except, n); pos < len(except) && except[pos] == n { + continue + } r = append(r, n) } sort.Strings(r)