Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9dfb7a5
hcl2template: remove unused shouldContinue bool
lbajolet-hashicorp Aug 24, 2023
8f22241
hcl2template: simplify datasource evaluation
lbajolet-hashicorp Aug 24, 2023
47d2aff
hcl2template: report localtion for cycle detection
lbajolet-hashicorp Aug 24, 2023
041a5ee
hcl2template: extract attr filter code from ds
lbajolet-hashicorp Aug 24, 2023
87dbdb1
hcl2template: simplify startDatasource function
lbajolet-hashicorp Aug 25, 2023
1617134
hcl2template: fix func to get vars from a config
lbajolet-hashicorp Aug 25, 2023
6979230
hcl2template: rework parsing logic
lbajolet-hashicorp Aug 24, 2023
d5ddb28
hcl2template: make datasources hold pointer refs
lbajolet-hashicorp Sep 1, 2023
891bae7
hcl2template: break down datasource execution
lbajolet-hashicorp Sep 1, 2023
747e26b
hcl2template: split traversal filtering
lbajolet-hashicorp Sep 6, 2023
8fc1a26
hcl2template: breakdown local variable evaluation
lbajolet-hashicorp Sep 6, 2023
a07bd93
hcl2template: remove value validation for locals
lbajolet-hashicorp Sep 6, 2023
58b078f
hcl2template: use diags.Extend instead of append
lbajolet-hashicorp Sep 6, 2023
996c4a8
hcl2template: make consts and functions public
lbajolet-hashicorp Sep 8, 2023
dfbea7f
hcl2template: breakdown CoreBuild creation
lbajolet-hashicorp Sep 11, 2023
c9c1898
hcl2template: track unused only/except options
lbajolet-hashicorp Sep 11, 2023
d4ce57d
hcl2template: make config's cmd arguments public
lbajolet-hashicorp Sep 11, 2023
01651b1
hcl2template: move initialization logic to builds
lbajolet-hashicorp Sep 11, 2023
05199ec
packer: change visibility of builds and variables
lbajolet-hashicorp Sep 13, 2023
0de1e18
hcl2template: move duplicate check to PackerConfig
lbajolet-hashicorp Sep 13, 2023
4be4c6e
hcl2template: keep hcl2 files in a map
lbajolet-hashicorp Sep 18, 2023
0065f73
packer: make Core.Validate public
lbajolet-hashicorp Sep 19, 2023
979601c
command: move command logic to using schedulers
lbajolet-hashicorp Sep 13, 2023
61a3d6a
command: migrate validate to schedulers
lbajolet-hashicorp Sep 19, 2023
3556c55
command: move inspect to schedulers
lbajolet-hashicorp Sep 19, 2023
3be2dea
command: move console to schedulers
lbajolet-hashicorp Sep 19, 2023
9e0c354
command: migrate hcl2_upgrade to schedulers
lbajolet-hashicorp Sep 19, 2023
31c4925
packer: remove Initialise/GetBuilds from handler
lbajolet-hashicorp Sep 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
311 changes: 2 additions & 309 deletions command/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,12 @@ package command
import (
"bytes"
"context"
"errors"
"fmt"
"log"
"math"
"strconv"
"strings"
"sync"
"time"

"github.com/hashicorp/hcl/v2"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer/internal/hcp/registry"
"github.com/hashicorp/packer/packer"
"golang.org/x/sync/semaphore"

"github.com/hako/durafmt"
"github.com/posener/complete"
)

Expand Down Expand Up @@ -93,305 +83,8 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, cla *BuildArgs) int
return ret
}

diags = packerStarter.Initialize(packer.InitializeOptions{})
bundledDiags := c.DetectBundledPlugins(packerStarter)
diags = append(bundledDiags, diags...)
ret = writeDiags(c.Ui, nil, diags)
if ret != 0 {
return ret
}

hcpRegistry, diags := registry.New(packerStarter, c.Ui)
ret = writeDiags(c.Ui, nil, diags)
if ret != 0 {
return ret
}

defer hcpRegistry.IterationStatusSummary()

err := hcpRegistry.PopulateIteration(buildCtx)
if err != nil {
return writeDiags(c.Ui, nil, hcl.Diagnostics{
&hcl.Diagnostic{
Summary: "HCP: populating iteration failed",
Severity: hcl.DiagError,
Detail: err.Error(),
},
})
}

builds, diags := packerStarter.GetBuilds(packer.GetBuildsOptions{
Only: cla.Only,
Except: cla.Except,
Debug: cla.Debug,
Force: cla.Force,
OnError: cla.OnError,
})

// here, something could have gone wrong but we still want to run valid
// builds.
ret = writeDiags(c.Ui, nil, diags)
if len(builds) == 0 && ret != 0 {
return ret
}

if cla.Debug {
c.Ui.Say("Debug mode enabled. Builds will not be parallelized.")
}

// Compile all the UIs for the builds
colors := [5]packer.UiColor{
packer.UiColorGreen,
packer.UiColorCyan,
packer.UiColorMagenta,
packer.UiColorYellow,
packer.UiColorBlue,
}
buildUis := make(map[packersdk.Build]packersdk.Ui)
for i := range builds {
ui := c.Ui
if cla.Color {
// Only set up UI colors if -machine-readable isn't set.
if _, ok := c.Ui.(*packer.MachineReadableUi); !ok {
ui = &packer.ColoredUi{
Color: colors[i%len(colors)],
Ui: ui,
}
ui.Say(fmt.Sprintf("%s: output will be in this color.", builds[i].Name()))
if i+1 == len(builds) {
// Add a newline between the color output and the actual output
c.Ui.Say("")
}
}
}
// Now add timestamps if requested
if cla.TimestampUi {
ui = &packer.TimestampedUi{
Ui: ui,
}
}

buildUis[builds[i]] = ui
}
log.Printf("Build debug mode: %v", cla.Debug)
log.Printf("Force build: %v", cla.Force)
log.Printf("On error: %v", cla.OnError)

if len(builds) == 0 {
return writeDiags(c.Ui, nil, hcl.Diagnostics{
&hcl.Diagnostic{
Summary: "No builds to run",
Detail: "A build command cannot run without at least one build to process. " +
"If the only or except flags have been specified at run time check that" +
" at least one build is selected for execution.",
Severity: hcl.DiagError,
},
})
}

// Get the start of the build command
buildCommandStart := time.Now()

// Run all the builds in parallel and wait for them to complete
var wg sync.WaitGroup
var artifacts = struct {
sync.RWMutex
m map[string][]packersdk.Artifact
}{m: make(map[string][]packersdk.Artifact)}
// Get the builds we care about
var errs = struct {
sync.RWMutex
m map[string]error
}{m: make(map[string]error)}
limitParallel := semaphore.NewWeighted(cla.ParallelBuilds)
for i := range builds {
if err := buildCtx.Err(); err != nil {
log.Println("Interrupted, not going to start any more builds.")
break
}

b := builds[i]
name := b.Name()
ui := buildUis[b]
if err := limitParallel.Acquire(buildCtx, 1); err != nil {
ui.Error(fmt.Sprintf("Build '%s' failed to acquire semaphore: %s", name, err))
errs.Lock()
errs.m[name] = err
errs.Unlock()
break
}
// Increment the waitgroup so we wait for this item to finish properly
wg.Add(1)

// Run the build in a goroutine
go func() {
// Get the start of the build
buildStart := time.Now()

defer wg.Done()

defer limitParallel.Release(1)

err := hcpRegistry.StartBuild(buildCtx, b)
// Seems odd to require this error check here. Now that it is an error we can just exit with diag
if err != nil {
// If the build is already done, we skip without a warning
if errors.As(err, &registry.ErrBuildAlreadyDone{}) {
ui.Say(fmt.Sprintf("skipping already done build %q", name))
return
}
writeDiags(c.Ui, nil, hcl.Diagnostics{
&hcl.Diagnostic{
Summary: fmt.Sprintf(
"hcp: failed to start build %q",
name),
Severity: hcl.DiagError,
Detail: err.Error(),
},
})
return
}

log.Printf("Starting build run: %s", name)
runArtifacts, err := b.Run(buildCtx, ui)

// Get the duration of the build and parse it
buildEnd := time.Now()
buildDuration := buildEnd.Sub(buildStart)
fmtBuildDuration := durafmt.Parse(buildDuration).LimitFirstN(2)

runArtifacts, hcperr := hcpRegistry.CompleteBuild(
buildCtx,
b,
runArtifacts,
err)
if hcperr != nil {
writeDiags(c.Ui, nil, hcl.Diagnostics{
&hcl.Diagnostic{
Summary: fmt.Sprintf(
"failed to complete HCP-enabled build %q",
name),
Severity: hcl.DiagError,
Detail: hcperr.Error(),
},
})
}

if err != nil {
ui.Error(fmt.Sprintf("Build '%s' errored after %s: %s", name, fmtBuildDuration, err))
errs.Lock()
errs.m[name] = err
errs.Unlock()
} else {
ui.Say(fmt.Sprintf("Build '%s' finished after %s.", name, fmtBuildDuration))
if runArtifacts != nil {
artifacts.Lock()
artifacts.m[name] = runArtifacts
artifacts.Unlock()
}
}
}()

if cla.Debug {
log.Printf("Debug enabled, so waiting for build to finish: %s", b.Name())
wg.Wait()
}

if cla.ParallelBuilds == 1 {
log.Printf("Parallelization disabled, waiting for build to finish: %s", b.Name())
wg.Wait()
}
}

// Wait for both the builds to complete and the interrupt handler,
// if it is interrupted.
log.Printf("Waiting on builds to complete...")
wg.Wait()

// Get the duration of the buildCommand command and parse it
buildCommandEnd := time.Now()
buildCommandDuration := buildCommandEnd.Sub(buildCommandStart)
fmtBuildCommandDuration := durafmt.Parse(buildCommandDuration).LimitFirstN(2)
c.Ui.Say(fmt.Sprintf("\n==> Wait completed after %s", fmtBuildCommandDuration))

if err := buildCtx.Err(); err != nil {
c.Ui.Say("Cleanly cancelled builds after being interrupted.")
return 1
}

if len(errs.m) > 0 {
c.Ui.Machine("error-count", strconv.FormatInt(int64(len(errs.m)), 10))

c.Ui.Error("\n==> Some builds didn't complete successfully and had errors:")
for name, err := range errs.m {
// Create a UI for the machine readable stuff to be targeted
ui := &packer.TargetedUI{
Target: name,
Ui: c.Ui,
}

ui.Machine("error", err.Error())

c.Ui.Error(fmt.Sprintf("--> %s: %s", name, err))
}
}

if len(artifacts.m) > 0 {
c.Ui.Say("\n==> Builds finished. The artifacts of successful builds are:")
for name, buildArtifacts := range artifacts.m {
// Create a UI for the machine readable stuff to be targeted
ui := &packer.TargetedUI{
Target: name,
Ui: c.Ui,
}

// Machine-readable helpful
ui.Machine("artifact-count", strconv.FormatInt(int64(len(buildArtifacts)), 10))

for i, artifact := range buildArtifacts {
var message bytes.Buffer
fmt.Fprintf(&message, "--> %s: ", name)

if artifact != nil {
fmt.Fprint(&message, artifact.String())
} else {
fmt.Fprint(&message, "<nothing>")
}

iStr := strconv.FormatInt(int64(i), 10)
if artifact != nil {
ui.Machine("artifact", iStr, "builder-id", artifact.BuilderId())
ui.Machine("artifact", iStr, "id", artifact.Id())
ui.Machine("artifact", iStr, "string", artifact.String())

files := artifact.Files()
ui.Machine("artifact",
iStr,
"files-count", strconv.FormatInt(int64(len(files)), 10))
for fi, file := range files {
fiStr := strconv.FormatInt(int64(fi), 10)
ui.Machine("artifact", iStr, "file", fiStr, file)
}
} else {
ui.Machine("artifact", iStr, "nil")
}

ui.Machine("artifact", iStr, "end")
c.Ui.Say(message.String())

}

}
} else {
c.Ui.Say("\n==> Builds finished but no artifacts were created.")
}

if len(errs.m) > 0 {
// If any errors occurred, exit with a non-zero exit status
ret = 1
}

return ret
scheduler := NewScheduler(packerStarter, c.Ui, buildCtx)
return scheduler.Build(cla)
}

func (*BuildCommand) Help() string {
Expand Down
2 changes: 1 addition & 1 deletion command/console.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (c *ConsoleCommand) RunContext(ctx context.Context, cla *ConsoleArgs) int {
return ret
}

_ = packerStarter.Initialize(packer.InitializeOptions{})
NewScheduler(packerStarter, c.Ui, ctx).Console(cla)

// Determine if stdin is a pipe. If so, we evaluate directly.
if c.StdinPiped() {
Expand Down
6 changes: 2 additions & 4 deletions command/hcl2_upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ type BlockParser interface {
Write(*bytes.Buffer)
}

func (c *HCL2UpgradeCommand) RunContext(_ context.Context, cla *HCL2UpgradeArgs) int {
func (c *HCL2UpgradeCommand) RunContext(ctx context.Context, cla *HCL2UpgradeArgs) int {
var output io.Writer
if err := os.MkdirAll(filepath.Dir(cla.OutputFile), 0755); err != nil {
c.Ui.Error(fmt.Sprintf("Failed to create output directory: %v", err))
Expand Down Expand Up @@ -163,9 +163,7 @@ func (c *HCL2UpgradeCommand) RunContext(_ context.Context, cla *HCL2UpgradeArgs)
}

core := hdl.(*packer.Core)
if err := core.Initialize(packer.InitializeOptions{}); err != nil {
c.Ui.Error(fmt.Sprintf("Ignoring following initialization error: %v", err))
}
NewScheduler(core, c.Ui, ctx).HCL2Upgrade(cla)
tpl := core.Template

// Parse blocks
Expand Down
8 changes: 1 addition & 7 deletions command/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"context"
"strings"

"github.com/hashicorp/packer/packer"
"github.com/posener/complete"
)

Expand Down Expand Up @@ -48,12 +47,7 @@ func (c *InspectCommand) RunContext(ctx context.Context, cla *InspectArgs) int {
return ret
}

// here we ignore init diags to allow unknown variables to be used
_ = packerStarter.Initialize(packer.InitializeOptions{})

return packerStarter.InspectConfig(packer.InspectConfigOptions{
Ui: c.Ui,
})
return NewScheduler(packerStarter, c.Ui, ctx).Inspect(cla)
}

func (*InspectCommand) Help() string {
Expand Down
Loading