2025-11-30 18:32:15 +00:00
|
|
|
// Copyright IBM Corp. 2013, 2025
|
2023-08-10 15:53:29 -07:00
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
2023-03-02 15:37:05 -05:00
|
|
|
|
2014-10-27 20:21:13 -07:00
|
|
|
package command
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
2019-03-22 14:53:28 +01:00
|
|
|
"context"
|
2022-09-06 17:26:49 -04:00
|
|
|
"errors"
|
2014-10-27 20:21:13 -07:00
|
|
|
"fmt"
|
|
|
|
|
"log"
|
2019-04-13 22:24:35 +10:00
|
|
|
"math"
|
2014-10-27 20:21:13 -07:00
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"sync"
|
2020-08-11 11:22:40 -07:00
|
|
|
"time"
|
2015-05-23 16:30:45 -07:00
|
|
|
|
2019-12-17 11:25:56 +01:00
|
|
|
"github.com/hashicorp/hcl/v2"
|
2020-12-17 13:29:25 -08:00
|
|
|
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
2022-11-07 14:16:04 -05:00
|
|
|
"github.com/hashicorp/packer/internal/hcp/registry"
|
2017-04-04 13:39:01 -07:00
|
|
|
"github.com/hashicorp/packer/packer"
|
2019-04-13 22:24:35 +10:00
|
|
|
"golang.org/x/sync/semaphore"
|
2017-10-13 11:42:22 -07:00
|
|
|
|
2020-08-11 11:22:40 -07:00
|
|
|
"github.com/hako/durafmt"
|
2017-10-13 11:42:22 -07:00
|
|
|
"github.com/posener/complete"
|
2014-10-27 20:21:13 -07:00
|
|
|
)
|
|
|
|
|
|
2024-01-25 11:32:18 -05:00
|
|
|
const (
|
|
|
|
|
hcpReadyIntegrationURL = "https://developer.hashicorp.com/packer/integrations?flags=hcp-ready"
|
|
|
|
|
)
|
|
|
|
|
|
2014-10-27 20:21:13 -07:00
|
|
|
type BuildCommand struct {
|
|
|
|
|
Meta
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-13 11:41:42 -07:00
|
|
|
func (c *BuildCommand) Run(args []string) int {
|
2020-05-12 11:24:22 +02:00
|
|
|
ctx, cleanup := handleTermInterrupt(c.Ui)
|
2020-05-08 12:05:14 +02:00
|
|
|
defer cleanup()
|
2019-05-06 12:19:59 +02:00
|
|
|
|
2020-05-08 12:05:14 +02:00
|
|
|
cfg, ret := c.ParseArgs(args)
|
|
|
|
|
if ret != 0 {
|
|
|
|
|
return ret
|
|
|
|
|
}
|
2019-05-06 12:19:59 +02:00
|
|
|
|
2020-05-12 11:24:22 +02:00
|
|
|
return c.RunContext(ctx, cfg)
|
2019-05-07 11:43:18 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-08 16:41:47 +02:00
|
|
|
func (c *BuildCommand) ParseArgs(args []string) (*BuildArgs, int) {
|
2020-05-08 17:46:33 +02:00
|
|
|
var cfg BuildArgs
|
2023-10-06 10:48:31 -04:00
|
|
|
flags := c.Meta.FlagSet("build")
|
2015-05-23 16:30:45 -07:00
|
|
|
flags.Usage = func() { c.Ui.Say(c.Help()) }
|
2020-05-08 16:41:47 +02:00
|
|
|
cfg.AddFlagSets(flags)
|
2015-05-23 16:30:45 -07:00
|
|
|
if err := flags.Parse(args); err != nil {
|
2020-05-08 17:46:33 +02:00
|
|
|
return &cfg, 1
|
2014-10-27 20:21:13 -07:00
|
|
|
}
|
|
|
|
|
|
2019-05-07 11:43:18 +02:00
|
|
|
if cfg.ParallelBuilds < 1 {
|
|
|
|
|
cfg.ParallelBuilds = math.MaxInt64
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-07 11:51:21 +02:00
|
|
|
args = flags.Args()
|
|
|
|
|
if len(args) != 1 {
|
2015-05-23 16:30:45 -07:00
|
|
|
flags.Usage()
|
2020-05-08 17:46:33 +02:00
|
|
|
return &cfg, 1
|
2019-05-07 11:43:18 +02:00
|
|
|
}
|
2019-05-07 11:51:21 +02:00
|
|
|
cfg.Path = args[0]
|
2020-05-08 17:46:33 +02:00
|
|
|
return &cfg, 0
|
2019-05-07 11:43:18 +02:00
|
|
|
}
|
|
|
|
|
|
2020-11-19 11:54:31 -08:00
|
|
|
func writeDiags(ui packersdk.Ui, files map[string]*hcl.File, diags hcl.Diagnostics) int {
|
2020-05-08 16:41:47 +02:00
|
|
|
// write HCL errors/diagnostics if any.
|
|
|
|
|
b := bytes.NewBuffer(nil)
|
|
|
|
|
err := hcl.NewDiagnosticTextWriter(b, files, 80, false).WriteDiagnostics(diags)
|
|
|
|
|
if err != nil {
|
|
|
|
|
ui.Error("could not write diagnostic: " + err.Error())
|
|
|
|
|
return 1
|
2019-12-17 11:25:56 +01:00
|
|
|
}
|
2020-05-08 16:41:47 +02:00
|
|
|
if b.Len() != 0 {
|
2020-04-30 16:36:01 +02:00
|
|
|
if diags.HasErrors() {
|
2020-05-08 16:41:47 +02:00
|
|
|
ui.Error(b.String())
|
|
|
|
|
return 1
|
2020-04-29 16:36:40 +02:00
|
|
|
}
|
2020-05-08 16:41:47 +02:00
|
|
|
ui.Say(b.String())
|
2019-12-17 11:25:56 +01:00
|
|
|
}
|
2020-05-08 16:41:47 +02:00
|
|
|
return 0
|
2019-12-17 11:25:56 +01:00
|
|
|
}
|
|
|
|
|
|
2020-05-11 17:56:14 +02:00
|
|
|
func (c *BuildCommand) RunContext(buildCtx context.Context, cla *BuildArgs) int {
|
2024-02-02 10:41:31 -05:00
|
|
|
// Set the release only flag if specified as argument
|
|
|
|
|
//
|
|
|
|
|
// This deactivates the capacity for Packer to load development binaries.
|
|
|
|
|
c.CoreConfig.Components.PluginConfig.ReleasesOnly = cla.ReleaseOnly
|
|
|
|
|
|
2020-05-11 17:56:14 +02:00
|
|
|
packerStarter, ret := c.GetConfig(&cla.MetaArgs)
|
2019-12-17 11:25:56 +01:00
|
|
|
if ret != 0 {
|
|
|
|
|
return ret
|
|
|
|
|
}
|
2021-08-05 09:25:19 -04:00
|
|
|
|
2023-07-11 15:41:37 -04:00
|
|
|
diags := packerStarter.DetectPluginBinaries()
|
|
|
|
|
ret = writeDiags(c.Ui, nil, diags)
|
|
|
|
|
if ret != 0 {
|
|
|
|
|
return ret
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-30 14:38:32 -04:00
|
|
|
diags = packerStarter.Initialize(packer.InitializeOptions{
|
|
|
|
|
UseSequential: cla.UseSequential,
|
|
|
|
|
})
|
2024-07-24 16:58:13 -04:00
|
|
|
|
|
|
|
|
if packer.PackerUseProto {
|
|
|
|
|
log.Printf("[TRACE] Using protobuf for communication with plugins")
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-24 10:58:03 +02:00
|
|
|
ret = writeDiags(c.Ui, nil, diags)
|
|
|
|
|
if ret != 0 {
|
|
|
|
|
return ret
|
|
|
|
|
}
|
2019-12-17 11:25:56 +01:00
|
|
|
|
hcp: generate fingerprints on each new build
Fingerprints are how we link a packer build to an iteration on HCP.
These are computed automatically from the Git SHA in the current state,
and are unique to the bucket/iteration.
The main problem with this approach is that while sound in theory, it
quickly falls apart when users want to run the same build configuration
twice, but expect a new image to be created.
With the current model, this fails, as the iteration with the current
SHA already exists.
While this is solvable through environment variables, or by committing a
change to the repository, we think this is not clear enough, and causes
an extra step to what should otherwise be a simple process.
Therefore, to lower the barrier of entry into HCP, we change this
behaviour with this commit.
Now, fingerprints are randomly generated ULIDs instead of a git SHA, and
a new one is always generated, unless one is already specified in the
environment.
This makes continuation of an existing iteration a conscious choice
rather than something automatic, and virtually eliminates conflicts such
as the ones described above.
2022-12-19 15:50:29 -05:00
|
|
|
hcpRegistry, diags := registry.New(packerStarter, c.Ui)
|
2022-07-26 14:59:17 -04:00
|
|
|
ret = writeDiags(c.Ui, nil, diags)
|
|
|
|
|
if ret != 0 {
|
|
|
|
|
return ret
|
|
|
|
|
}
|
2024-07-22 23:08:14 +05:30
|
|
|
hcpRegistry.Metadata().Gather(GetCleanedBuildArgs(cla))
|
2022-07-26 14:59:17 -04:00
|
|
|
|
2024-01-24 13:17:35 -05:00
|
|
|
defer hcpRegistry.VersionStatusSummary()
|
hcp: generate fingerprints on each new build
Fingerprints are how we link a packer build to an iteration on HCP.
These are computed automatically from the Git SHA in the current state,
and are unique to the bucket/iteration.
The main problem with this approach is that while sound in theory, it
quickly falls apart when users want to run the same build configuration
twice, but expect a new image to be created.
With the current model, this fails, as the iteration with the current
SHA already exists.
While this is solvable through environment variables, or by committing a
change to the repository, we think this is not clear enough, and causes
an extra step to what should otherwise be a simple process.
Therefore, to lower the barrier of entry into HCP, we change this
behaviour with this commit.
Now, fingerprints are randomly generated ULIDs instead of a git SHA, and
a new one is always generated, unless one is already specified in the
environment.
This makes continuation of an existing iteration a conscious choice
rather than something automatic, and virtually eliminates conflicts such
as the ones described above.
2022-12-19 15:50:29 -05:00
|
|
|
|
2024-01-24 13:17:35 -05:00
|
|
|
err := hcpRegistry.PopulateVersion(buildCtx)
|
2022-09-06 17:26:49 -04:00
|
|
|
if err != nil {
|
|
|
|
|
return writeDiags(c.Ui, nil, hcl.Diagnostics{
|
|
|
|
|
&hcl.Diagnostic{
|
2024-01-24 13:17:35 -05:00
|
|
|
Summary: "HCP: populating version failed",
|
2022-09-06 17:26:49 -04:00
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
Detail: err.Error(),
|
|
|
|
|
},
|
|
|
|
|
})
|
2021-10-08 15:05:35 -07:00
|
|
|
}
|
|
|
|
|
|
2022-12-05 16:06:07 -05:00
|
|
|
builds, diags := packerStarter.GetBuilds(packer.GetBuildsOptions{
|
2020-05-14 16:22:51 -07:00
|
|
|
Only: cla.Only,
|
|
|
|
|
Except: cla.Except,
|
|
|
|
|
Debug: cla.Debug,
|
|
|
|
|
Force: cla.Force,
|
|
|
|
|
OnError: cla.OnError,
|
2020-04-30 16:36:01 +02:00
|
|
|
})
|
2014-10-27 20:21:13 -07:00
|
|
|
|
2020-05-08 18:01:34 +02:00
|
|
|
// here, something could have gone wrong but we still want to run valid
|
|
|
|
|
// builds.
|
|
|
|
|
ret = writeDiags(c.Ui, nil, diags)
|
2022-09-20 17:27:42 -04:00
|
|
|
if len(builds) == 0 && ret != 0 {
|
|
|
|
|
return ret
|
|
|
|
|
}
|
2014-10-27 20:21:13 -07:00
|
|
|
|
2020-05-11 17:56:14 +02:00
|
|
|
if cla.Debug {
|
2015-05-23 16:30:45 -07:00
|
|
|
c.Ui.Say("Debug mode enabled. Builds will not be parallelized.")
|
2014-10-27 20:21:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compile all the UIs for the builds
|
|
|
|
|
colors := [5]packer.UiColor{
|
|
|
|
|
packer.UiColorGreen,
|
|
|
|
|
packer.UiColorCyan,
|
|
|
|
|
packer.UiColorMagenta,
|
|
|
|
|
packer.UiColorYellow,
|
|
|
|
|
packer.UiColorBlue,
|
|
|
|
|
}
|
2024-03-01 11:21:02 -05:00
|
|
|
buildUis := make(map[*packer.CoreBuild]packersdk.Ui)
|
2019-12-17 11:25:56 +01:00
|
|
|
for i := range builds {
|
|
|
|
|
ui := c.Ui
|
2020-05-11 17:56:14 +02:00
|
|
|
if cla.Color {
|
2020-06-17 11:57:12 -07:00
|
|
|
// Only set up UI colors if -machine-readable isn't set.
|
2017-02-09 17:41:42 -08:00
|
|
|
if _, ok := c.Ui.(*packer.MachineReadableUi); !ok {
|
2020-06-17 11:57:12 -07:00
|
|
|
ui = &packer.ColoredUi{
|
|
|
|
|
Color: colors[i%len(colors)],
|
|
|
|
|
Ui: ui,
|
|
|
|
|
}
|
2019-12-17 11:25:56 +01:00
|
|
|
ui.Say(fmt.Sprintf("%s: output will be in this color.", builds[i].Name()))
|
|
|
|
|
if i+1 == len(builds) {
|
2017-02-09 17:45:42 -08:00
|
|
|
// Add a newline between the color output and the actual output
|
|
|
|
|
c.Ui.Say("")
|
|
|
|
|
}
|
2019-06-19 15:04:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Now add timestamps if requested
|
2020-05-11 17:56:14 +02:00
|
|
|
if cla.TimestampUi {
|
2019-06-19 15:04:13 +02:00
|
|
|
ui = &packer.TimestampedUi{
|
|
|
|
|
Ui: ui,
|
2017-02-09 17:41:42 -08:00
|
|
|
}
|
2014-10-27 20:21:13 -07:00
|
|
|
}
|
|
|
|
|
|
2019-12-17 11:25:56 +01:00
|
|
|
buildUis[builds[i]] = ui
|
2014-10-27 20:21:13 -07:00
|
|
|
}
|
2020-05-11 17:56:14 +02:00
|
|
|
log.Printf("Build debug mode: %v", cla.Debug)
|
|
|
|
|
log.Printf("Force build: %v", cla.Force)
|
|
|
|
|
log.Printf("On error: %v", cla.OnError)
|
2014-10-27 20:21:13 -07:00
|
|
|
|
2022-09-08 10:12:12 -04:00
|
|
|
if len(builds) == 0 {
|
|
|
|
|
return writeDiags(c.Ui, nil, hcl.Diagnostics{
|
|
|
|
|
&hcl.Diagnostic{
|
2022-09-22 14:35:43 -04:00
|
|
|
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.",
|
2022-09-08 10:12:12 -04:00
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-11 11:22:40 -07:00
|
|
|
// Get the start of the build command
|
|
|
|
|
buildCommandStart := time.Now()
|
|
|
|
|
|
2014-10-27 20:21:13 -07:00
|
|
|
// Run all the builds in parallel and wait for them to complete
|
2019-05-06 12:19:59 +02:00
|
|
|
var wg sync.WaitGroup
|
2015-10-14 08:25:13 -07:00
|
|
|
var artifacts = struct {
|
|
|
|
|
sync.RWMutex
|
2020-11-19 12:17:11 -08:00
|
|
|
m map[string][]packersdk.Artifact
|
|
|
|
|
}{m: make(map[string][]packersdk.Artifact)}
|
2019-12-17 11:25:56 +01:00
|
|
|
// Get the builds we care about
|
2022-09-06 17:26:49 -04:00
|
|
|
var errs = struct {
|
2019-12-17 11:25:56 +01:00
|
|
|
sync.RWMutex
|
|
|
|
|
m map[string]error
|
|
|
|
|
}{m: make(map[string]error)}
|
2020-05-11 17:56:14 +02:00
|
|
|
limitParallel := semaphore.NewWeighted(cla.ParallelBuilds)
|
2024-01-25 11:32:18 -05:00
|
|
|
|
2019-05-02 16:34:48 +02:00
|
|
|
for i := range builds {
|
2019-05-06 12:19:59 +02:00
|
|
|
if err := buildCtx.Err(); err != nil {
|
|
|
|
|
log.Println("Interrupted, not going to start any more builds.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-02 16:34:48 +02:00
|
|
|
b := builds[i]
|
|
|
|
|
name := b.Name()
|
2019-12-17 11:25:56 +01:00
|
|
|
ui := buildUis[b]
|
2019-05-02 16:34:48 +02:00
|
|
|
if err := limitParallel.Acquire(buildCtx, 1); err != nil {
|
|
|
|
|
ui.Error(fmt.Sprintf("Build '%s' failed to acquire semaphore: %s", name, err))
|
2022-09-06 17:26:49 -04:00
|
|
|
errs.Lock()
|
|
|
|
|
errs.m[name] = err
|
|
|
|
|
errs.Unlock()
|
2019-05-02 16:34:48 +02:00
|
|
|
break
|
|
|
|
|
}
|
2019-05-03 07:46:26 +02:00
|
|
|
// Increment the waitgroup so we wait for this item to finish properly
|
|
|
|
|
wg.Add(1)
|
2014-10-27 20:21:13 -07:00
|
|
|
|
|
|
|
|
// Run the build in a goroutine
|
2019-05-02 16:34:48 +02:00
|
|
|
go func() {
|
2020-08-11 11:22:40 -07:00
|
|
|
// Get the start of the build
|
|
|
|
|
buildStart := time.Now()
|
|
|
|
|
|
2014-10-27 20:21:13 -07:00
|
|
|
defer wg.Done()
|
|
|
|
|
|
2019-04-19 17:10:36 +10:00
|
|
|
defer limitParallel.Release(1)
|
|
|
|
|
|
2022-12-05 16:06:07 -05:00
|
|
|
err := hcpRegistry.StartBuild(buildCtx, b)
|
2022-11-07 14:16:04 -05:00
|
|
|
// Seems odd to require this error check here. Now that it is an error we can just exit with diag
|
2022-09-06 17:26:49 -04:00
|
|
|
if err != nil {
|
2022-11-07 14:16:04 -05:00
|
|
|
// If the build is already done, we skip without a warning
|
|
|
|
|
if errors.As(err, ®istry.ErrBuildAlreadyDone{}) {
|
|
|
|
|
ui.Say(fmt.Sprintf("skipping already done build %q", name))
|
2022-09-06 17:26:49 -04:00
|
|
|
return
|
2022-08-29 17:28:22 -04:00
|
|
|
}
|
2022-11-07 14:16:04 -05:00
|
|
|
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
|
2022-08-29 17:28:22 -04:00
|
|
|
}
|
|
|
|
|
|
2019-04-19 17:10:36 +10:00
|
|
|
log.Printf("Starting build run: %s", name)
|
2019-03-22 14:53:28 +01:00
|
|
|
runArtifacts, err := b.Run(buildCtx, ui)
|
2014-10-27 20:21:13 -07:00
|
|
|
|
2020-08-11 11:22:40 -07:00
|
|
|
// Get the duration of the build and parse it
|
|
|
|
|
buildEnd := time.Now()
|
|
|
|
|
buildDuration := buildEnd.Sub(buildStart)
|
|
|
|
|
fmtBuildDuration := durafmt.Parse(buildDuration).LimitFirstN(2)
|
|
|
|
|
|
2022-11-07 14:16:04 -05:00
|
|
|
runArtifacts, hcperr := hcpRegistry.CompleteBuild(
|
2022-09-06 17:26:49 -04:00
|
|
|
buildCtx,
|
2022-12-05 16:06:07 -05:00
|
|
|
b,
|
2022-09-06 17:26:49 -04:00
|
|
|
runArtifacts,
|
|
|
|
|
err)
|
|
|
|
|
if hcperr != nil {
|
build: don't suggest lack of HCP support on fail
When running a build with HCP Packer enabled, Packer attempts to push
the build status to HCP.
If the build fails, we update the status to BUILD_FAILED, and that's the
end of it.
If however the build succeeds, Packer attempts to get the HCP artifact
from the builder, which will only succeed if the builder supports it.
Otherwise, we'll get either nil, or an artifact type that is not
compatible with what is expected for HCP support.
When either of those happens, we warn that the builder may not support
HCP Packer at all, so users are aware of the problem.
However, when the error was introduced, it only looked at the fact that
an error was produced, independently of the type of error. This caused
legitimate errors while building to be reported as potential
incompatibility between the builder and HCP, which was confusing to
users.
This commit changes this by introducing a new error type, only produced
when the artifact either is nil, or failed to be deserialised into a HCP
artifact, which lets us produce the incompatibility warning with more
accuracy.
2024-02-07 11:22:26 -05:00
|
|
|
if _, ok := hcperr.(*registry.NotAHCPArtifactError); ok {
|
|
|
|
|
writeDiags(c.Ui, nil, hcl.Diagnostics{
|
|
|
|
|
&hcl.Diagnostic{
|
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
Summary: fmt.Sprintf("The %q builder produced an artifact that cannot be pushed to HCP Packer", b.Name()),
|
|
|
|
|
Detail: fmt.Sprintf(
|
|
|
|
|
`%s
|
|
|
|
|
Check that you are using an HCP Ready integration before trying again:
|
|
|
|
|
%s`,
|
|
|
|
|
hcperr, hcpReadyIntegrationURL),
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
writeDiags(c.Ui, nil, hcl.Diagnostics{
|
|
|
|
|
&hcl.Diagnostic{
|
|
|
|
|
Summary: fmt.Sprintf(
|
|
|
|
|
"publishing build metadata to HCP Packer for %q failed",
|
|
|
|
|
name),
|
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
Detail: hcperr.Error(),
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
2022-08-29 17:28:22 -04:00
|
|
|
}
|
|
|
|
|
|
2014-10-27 20:21:13 -07:00
|
|
|
if err != nil {
|
2020-08-11 11:22:40 -07:00
|
|
|
ui.Error(fmt.Sprintf("Build '%s' errored after %s: %s", name, fmtBuildDuration, err))
|
2022-09-06 17:26:49 -04:00
|
|
|
errs.Lock()
|
|
|
|
|
errs.m[name] = err
|
|
|
|
|
errs.Unlock()
|
2014-10-27 20:21:13 -07:00
|
|
|
} else {
|
2020-08-11 11:22:40 -07:00
|
|
|
ui.Say(fmt.Sprintf("Build '%s' finished after %s.", name, fmtBuildDuration))
|
2021-08-05 09:25:19 -04:00
|
|
|
if runArtifacts != nil {
|
2019-03-10 15:38:38 +01:00
|
|
|
artifacts.Lock()
|
|
|
|
|
artifacts.m[name] = runArtifacts
|
|
|
|
|
artifacts.Unlock()
|
|
|
|
|
}
|
2014-10-27 20:21:13 -07:00
|
|
|
}
|
2024-11-13 15:36:45 -05:00
|
|
|
|
|
|
|
|
// If the build succeeded but uploading to HCP failed,
|
|
|
|
|
// Packer should exit non-zero, so we re-assign the
|
|
|
|
|
// error to account for this case.
|
|
|
|
|
if hcperr != nil && err == nil {
|
|
|
|
|
errs.Lock()
|
|
|
|
|
errs.m[name] = hcperr
|
|
|
|
|
errs.Unlock()
|
|
|
|
|
}
|
2019-05-02 16:34:48 +02:00
|
|
|
}()
|
2014-10-27 20:21:13 -07:00
|
|
|
|
2020-05-11 17:56:14 +02:00
|
|
|
if cla.Debug {
|
2014-10-27 20:21:13 -07:00
|
|
|
log.Printf("Debug enabled, so waiting for build to finish: %s", b.Name())
|
|
|
|
|
wg.Wait()
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-11 17:56:14 +02:00
|
|
|
if cla.ParallelBuilds == 1 {
|
2014-10-27 20:21:13 -07:00
|
|
|
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()
|
|
|
|
|
|
2020-08-11 11:22:40 -07:00
|
|
|
// 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))
|
|
|
|
|
|
2019-05-06 12:19:59 +02:00
|
|
|
if err := buildCtx.Err(); err != nil {
|
2015-05-23 16:30:45 -07:00
|
|
|
c.Ui.Say("Cleanly cancelled builds after being interrupted.")
|
2014-10-27 20:21:13 -07:00
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-06 17:26:49 -04:00
|
|
|
if len(errs.m) > 0 {
|
|
|
|
|
c.Ui.Machine("error-count", strconv.FormatInt(int64(len(errs.m)), 10))
|
2014-10-27 20:21:13 -07:00
|
|
|
|
2015-05-23 16:30:45 -07:00
|
|
|
c.Ui.Error("\n==> Some builds didn't complete successfully and had errors:")
|
2022-09-06 17:26:49 -04:00
|
|
|
for name, err := range errs.m {
|
2017-03-28 17:45:01 -07:00
|
|
|
// Create a UI for the machine readable stuff to be targeted
|
2017-03-29 12:44:42 -07:00
|
|
|
ui := &packer.TargetedUI{
|
2014-10-27 20:21:13 -07:00
|
|
|
Target: name,
|
2015-05-23 16:30:45 -07:00
|
|
|
Ui: c.Ui,
|
2014-10-27 20:21:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ui.Machine("error", err.Error())
|
|
|
|
|
|
2015-05-23 16:30:45 -07:00
|
|
|
c.Ui.Error(fmt.Sprintf("--> %s: %s", name, err))
|
2014-10-27 20:21:13 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-14 08:25:13 -07:00
|
|
|
if len(artifacts.m) > 0 {
|
2015-05-23 16:30:45 -07:00
|
|
|
c.Ui.Say("\n==> Builds finished. The artifacts of successful builds are:")
|
2015-10-14 08:25:13 -07:00
|
|
|
for name, buildArtifacts := range artifacts.m {
|
2017-03-28 17:45:01 -07:00
|
|
|
// Create a UI for the machine readable stuff to be targeted
|
2017-03-29 12:44:42 -07:00
|
|
|
ui := &packer.TargetedUI{
|
2014-10-27 20:21:13 -07:00
|
|
|
Target: name,
|
2015-05-23 16:30:45 -07:00
|
|
|
Ui: c.Ui,
|
2014-10-27 20:21:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 {
|
2016-04-05 22:41:26 -07:00
|
|
|
fmt.Fprint(&message, artifact.String())
|
2014-10-27 20:21:13 -07:00
|
|
|
} 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")
|
2015-05-23 16:30:45 -07:00
|
|
|
c.Ui.Say(message.String())
|
2021-08-05 09:25:19 -04:00
|
|
|
|
2014-10-27 20:21:13 -07:00
|
|
|
}
|
2021-08-05 09:25:19 -04:00
|
|
|
|
2014-10-27 20:21:13 -07:00
|
|
|
}
|
|
|
|
|
} else {
|
2015-05-23 16:30:45 -07:00
|
|
|
c.Ui.Say("\n==> Builds finished but no artifacts were created.")
|
2014-10-27 20:21:13 -07:00
|
|
|
}
|
|
|
|
|
|
2022-09-06 17:26:49 -04:00
|
|
|
if len(errs.m) > 0 {
|
2014-10-27 20:21:13 -07:00
|
|
|
// If any errors occurred, exit with a non-zero exit status
|
2019-12-17 11:25:56 +01:00
|
|
|
ret = 1
|
2014-10-27 20:21:13 -07:00
|
|
|
}
|
|
|
|
|
|
2019-12-17 11:25:56 +01:00
|
|
|
return ret
|
2014-10-27 20:21:13 -07:00
|
|
|
}
|
|
|
|
|
|
2017-10-13 11:41:42 -07:00
|
|
|
func (*BuildCommand) Help() string {
|
2014-10-27 20:21:13 -07:00
|
|
|
helpText := `
|
|
|
|
|
Usage: packer build [options] TEMPLATE
|
|
|
|
|
|
|
|
|
|
Will execute multiple builds in parallel as defined in the template.
|
|
|
|
|
The various artifacts created by the template will be outputted.
|
|
|
|
|
|
|
|
|
|
Options:
|
|
|
|
|
|
2018-10-10 21:34:35 -04:00
|
|
|
-color=false Disable color output. (Default: color)
|
|
|
|
|
-debug Debug mode enabled for builds.
|
2020-10-08 13:58:22 +01:00
|
|
|
-except=foo,bar,baz Run all builds and post-processors other than these.
|
2018-10-10 21:34:35 -04:00
|
|
|
-only=foo,bar,baz Build only the specified builds.
|
|
|
|
|
-force Force a build to continue if artifacts exist, deletes existing artifacts.
|
|
|
|
|
-machine-readable Produce machine-readable output.
|
2020-06-16 15:29:56 +02:00
|
|
|
-on-error=[cleanup|abort|ask|run-cleanup-provisioner] If the build fails do: clean up (default), abort, ask, or run-cleanup-provisioner.
|
2020-05-12 13:03:43 +02:00
|
|
|
-parallel-builds=1 Number of builds to run in parallel. 1 disables parallelization. 0 means no limit (Default: 0)
|
2018-10-03 14:40:13 +02:00
|
|
|
-timestamp-ui Enable prefixing of each ui output with an RFC3339 timestamp.
|
2018-09-01 10:32:23 +03:00
|
|
|
-var 'key=value' Variable for templates, can be used multiple times.
|
2022-11-14 17:06:45 -05:00
|
|
|
-var-file=path JSON or HCL2 file containing user variables, can be used multiple times.
|
|
|
|
|
-warn-on-undeclared-var Display warnings for user variable files containing undeclared variables.
|
2024-03-12 11:27:25 -04:00
|
|
|
-ignore-prerelease-plugins Disable the loading of prerelease plugin binaries (x.y.z-dev).
|
2024-08-30 14:38:32 -04:00
|
|
|
-use-sequential-evaluation Fallback to using a sequential approach for local/datasource evaluation.
|
2014-10-27 20:21:13 -07:00
|
|
|
`
|
|
|
|
|
|
|
|
|
|
return strings.TrimSpace(helpText)
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-13 11:41:42 -07:00
|
|
|
func (*BuildCommand) Synopsis() string {
|
2014-10-27 20:21:13 -07:00
|
|
|
return "build image(s) from template"
|
|
|
|
|
}
|
2017-10-13 11:42:22 -07:00
|
|
|
|
|
|
|
|
func (*BuildCommand) AutocompleteArgs() complete.Predictor {
|
|
|
|
|
return complete.PredictNothing
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (*BuildCommand) AutocompleteFlags() complete.Flags {
|
|
|
|
|
return complete.Flags{
|
|
|
|
|
"-color": complete.PredictNothing,
|
|
|
|
|
"-debug": complete.PredictNothing,
|
|
|
|
|
"-except": complete.PredictNothing,
|
|
|
|
|
"-only": complete.PredictNothing,
|
|
|
|
|
"-force": complete.PredictNothing,
|
|
|
|
|
"-machine-readable": complete.PredictNothing,
|
|
|
|
|
"-on-error": complete.PredictNothing,
|
|
|
|
|
"-parallel": complete.PredictNothing,
|
2018-10-03 14:40:13 +02:00
|
|
|
"-timestamp-ui": complete.PredictNothing,
|
2017-10-13 11:42:22 -07:00
|
|
|
"-var": complete.PredictNothing,
|
|
|
|
|
"-var-file": complete.PredictNothing,
|
|
|
|
|
}
|
|
|
|
|
}
|