Skip to content

Commit 57cf6b8

Browse files
hcl2template: breakdown CoreBuild creation
When processing builds, we convert build blocks/sources to a series of CoreBuild, which contains the sequence of provisioners and post-processors to run, and the builder to use to boot the instance and create the image from. This process of converting builds used to be completely sequential, and orchestrated by the `GetBuilds' function. This gets broken down into a `ToCoreBuild' function on the build block, whose function is to lift and filter-out (based on --only/--except) the core builds from a valid build block, and this gets invoked from `GetBuilds'. This breakdown is useful as a preparation to the DAG's introduction to the code later on.
1 parent d89a25d commit 57cf6b8

File tree

2 files changed

+130
-146
lines changed

2 files changed

+130
-146
lines changed

hcl2template/types.build.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/hashicorp/hcl/v2/ext/dynblock"
1111
"github.com/hashicorp/hcl/v2/gohcl"
1212
"github.com/hashicorp/hcl/v2/hclsyntax"
13+
"github.com/hashicorp/packer/packer"
1314
"github.com/zclconf/go-cty/cty"
1415
)
1516

@@ -264,3 +265,108 @@ func (build *BuildBlock) finalizeDecode(cfg *PackerConfig) hcl.Diagnostics {
264265

265266
return diags
266267
}
268+
269+
// ToCoreBuilds extracts the core builds from a build block.
270+
//
271+
// Since build blocks can have multiple sources, it can lead to multiple builds
272+
// for each build block.
273+
func (build BuildBlock) ToCoreBuilds(cfg *PackerConfig) ([]*packer.CoreBuild, hcl.Diagnostics) {
274+
var res []*packer.CoreBuild
275+
var diags hcl.Diagnostics
276+
277+
for _, srcUsage := range build.Sources {
278+
_, found := cfg.Sources[srcUsage.SourceRef]
279+
if !found {
280+
diags = append(diags, &hcl.Diagnostic{
281+
Summary: fmt.Sprintf("Unknown %s %s", sourceLabel, srcUsage.String()),
282+
Subject: build.HCL2Ref.DefRange.Ptr(),
283+
Severity: hcl.DiagError,
284+
Detail: fmt.Sprintf("Known: %v", cfg.Sources),
285+
})
286+
continue
287+
}
288+
289+
pcb := &packer.CoreBuild{
290+
BuildName: build.Name,
291+
Type: srcUsage.String(),
292+
}
293+
if !cfg.keepBuild(pcb) {
294+
continue
295+
}
296+
297+
builder, moreDiags, generatedVars := cfg.startBuilder(srcUsage, cfg.EvalContext(BuildContext, nil))
298+
diags = append(diags, moreDiags...)
299+
if moreDiags.HasErrors() {
300+
continue
301+
}
302+
303+
decoded, _ := decodeHCL2Spec(srcUsage.Body, cfg.EvalContext(BuildContext, nil), builder)
304+
pcb.HCLConfig = decoded
305+
306+
// If the builder has provided a list of to-be-generated variables that
307+
// should be made accessible to provisioners, pass that list into
308+
// the provisioner prepare() so that the provisioner can appropriately
309+
// validate user input against what will become available. Otherwise,
310+
// only pass the default variables, using the basic placeholder data.
311+
unknownBuildValues := map[string]cty.Value{}
312+
for _, k := range append(packer.BuilderDataCommonKeys, generatedVars...) {
313+
unknownBuildValues[k] = cty.StringVal("<unknown>")
314+
}
315+
unknownBuildValues["name"] = cty.StringVal(build.Name)
316+
317+
variables := map[string]cty.Value{
318+
sourcesAccessor: cty.ObjectVal(srcUsage.ctyValues()),
319+
buildAccessor: cty.ObjectVal(unknownBuildValues),
320+
}
321+
322+
provisioners, moreDiags := cfg.getCoreBuildProvisioners(srcUsage, build.ProvisionerBlocks, cfg.EvalContext(BuildContext, variables))
323+
diags = append(diags, moreDiags...)
324+
if moreDiags.HasErrors() {
325+
continue
326+
}
327+
pps, moreDiags := cfg.getCoreBuildPostProcessors(srcUsage, build.PostProcessorsLists, cfg.EvalContext(BuildContext, variables))
328+
diags = append(diags, moreDiags...)
329+
if moreDiags.HasErrors() {
330+
continue
331+
}
332+
333+
if build.ErrorCleanupProvisionerBlock != nil &&
334+
!build.ErrorCleanupProvisionerBlock.OnlyExcept.Skip(srcUsage.String()) {
335+
errorCleanupProv, moreDiags := cfg.getCoreBuildProvisioner(srcUsage, build.ErrorCleanupProvisionerBlock, cfg.EvalContext(BuildContext, variables))
336+
diags = append(diags, moreDiags...)
337+
if moreDiags.HasErrors() {
338+
continue
339+
}
340+
pcb.CleanupProvisioner = errorCleanupProv
341+
}
342+
343+
pcb.Builder = builder
344+
pcb.Provisioners = provisioners
345+
pcb.PostProcessors = pps
346+
347+
res = append(res, pcb)
348+
}
349+
350+
return res, diags
351+
}
352+
353+
func (cfg *PackerConfig) keepBuild(cb *packer.CoreBuild) bool {
354+
keep := false
355+
for _, onlyGlob := range cfg.only {
356+
if onlyGlob.Match(cb.Name()) {
357+
keep = true
358+
break
359+
}
360+
}
361+
if !keep && len(cfg.only) > 0 {
362+
return false
363+
}
364+
365+
for _, exceptGlob := range cfg.except {
366+
if exceptGlob.Match(cb.Name()) {
367+
return false
368+
}
369+
}
370+
371+
return true
372+
}

hcl2template/types.packer_config.go

Lines changed: 24 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ func (cfg *PackerConfig) getCoreBuildProvisioner(source SourceUseBlock, pb *Prov
257257

258258
// getCoreBuildProvisioners takes a list of post processor block, starts
259259
// according provisioners and sends parsed HCL2 over to it.
260-
func (cfg *PackerConfig) getCoreBuildPostProcessors(source SourceUseBlock, blocksList [][]*PostProcessorBlock, ectx *hcl.EvalContext, exceptMatches *int) ([][]packer.CoreBuildPostProcessor, hcl.Diagnostics) {
260+
func (cfg *PackerConfig) getCoreBuildPostProcessors(source SourceUseBlock, blocksList [][]*PostProcessorBlock, ectx *hcl.EvalContext) ([][]packer.CoreBuildPostProcessor, hcl.Diagnostics) {
261261
var diags hcl.Diagnostics
262262
res := [][]packer.CoreBuildPostProcessor{}
263263
for _, blocks := range blocksList {
@@ -276,7 +276,6 @@ func (cfg *PackerConfig) getCoreBuildPostProcessors(source SourceUseBlock, block
276276
for _, exceptGlob := range cfg.except {
277277
if exceptGlob.Match(name) {
278278
exclude = true
279-
*exceptMatches = *exceptMatches + 1
280279
break
281280
}
282281
}
@@ -308,175 +307,54 @@ func (cfg *PackerConfig) getCoreBuildPostProcessors(source SourceUseBlock, block
308307
return res, diags
309308
}
310309

311-
// GetBuilds returns a list of packer Build based on the HCL2 parsed build
312-
// blocks. All Builders, Provisioners and Post Processors will be started and
313-
// configured.
314310
func (cfg *PackerConfig) GetBuilds(opts packer.GetBuildsOptions) ([]packersdk.Build, hcl.Diagnostics) {
315-
res := []packersdk.Build{}
311+
var allBuilds []packersdk.Build
316312
var diags hcl.Diagnostics
317-
possibleBuildNames := []string{}
318-
319-
cfg.debug = opts.Debug
320-
cfg.force = opts.Force
321-
cfg.onError = opts.OnError
322313

323314
if len(cfg.Builds) == 0 {
324-
return res, append(diags, &hcl.Diagnostic{
315+
return nil, append(diags, &hcl.Diagnostic{
325316
Summary: "Missing build block",
326317
Detail: "A build block with one or more sources is required for executing a build.",
327318
Severity: hcl.DiagError,
328319
})
329320
}
330321

331-
for _, build := range cfg.Builds {
332-
for _, srcUsage := range build.Sources {
333-
src, found := cfg.Sources[srcUsage.SourceRef]
334-
if !found {
335-
diags = append(diags, &hcl.Diagnostic{
336-
Summary: "Unknown " + sourceLabel + " " + srcUsage.String(),
337-
Subject: build.HCL2Ref.DefRange.Ptr(),
338-
Severity: hcl.DiagError,
339-
Detail: fmt.Sprintf("Known: %v", cfg.Sources),
340-
})
341-
continue
342-
}
343-
344-
pcb := &packer.CoreBuild{
345-
BuildName: build.Name,
346-
Type: srcUsage.String(),
347-
}
348-
349-
pcb.SetDebug(cfg.debug)
350-
pcb.SetForce(cfg.force)
351-
pcb.SetOnError(cfg.onError)
352-
353-
// Apply the -only and -except command-line options to exclude matching builds.
354-
buildName := pcb.Name()
355-
possibleBuildNames = append(possibleBuildNames, buildName)
356-
// -only
357-
if len(opts.Only) > 0 {
358-
onlyGlobs, diags := convertFilterOption(opts.Only, "only")
359-
if diags.HasErrors() {
360-
return nil, diags
361-
}
362-
cfg.only = onlyGlobs
363-
include := false
364-
for _, onlyGlob := range onlyGlobs {
365-
if onlyGlob.Match(buildName) {
366-
include = true
367-
break
368-
}
369-
}
370-
if !include {
371-
continue
372-
}
373-
opts.OnlyMatches++
374-
}
375-
376-
// -except
377-
if len(opts.Except) > 0 {
378-
exceptGlobs, diags := convertFilterOption(opts.Except, "except")
379-
if diags.HasErrors() {
380-
return nil, diags
381-
}
382-
cfg.except = exceptGlobs
383-
exclude := false
384-
for _, exceptGlob := range exceptGlobs {
385-
if exceptGlob.Match(buildName) {
386-
exclude = true
387-
break
388-
}
389-
}
390-
if exclude {
391-
opts.ExceptMatches++
392-
continue
393-
}
394-
}
395-
396-
builder, moreDiags, generatedVars := cfg.startBuilder(srcUsage, cfg.EvalContext(BuildContext, nil))
397-
diags = append(diags, moreDiags...)
398-
if moreDiags.HasErrors() {
399-
continue
400-
}
401-
402-
decoded, _ := decodeHCL2Spec(srcUsage.Body, cfg.EvalContext(BuildContext, nil), builder)
403-
pcb.HCLConfig = decoded
404-
405-
// If the builder has provided a list of to-be-generated variables that
406-
// should be made accessible to provisioners, pass that list into
407-
// the provisioner prepare() so that the provisioner can appropriately
408-
// validate user input against what will become available. Otherwise,
409-
// only pass the default variables, using the basic placeholder data.
410-
unknownBuildValues := map[string]cty.Value{}
411-
for _, k := range append(packer.BuilderDataCommonKeys, generatedVars...) {
412-
unknownBuildValues[k] = cty.StringVal("<unknown>")
413-
}
414-
unknownBuildValues["name"] = cty.StringVal(build.Name)
415-
416-
variables := map[string]cty.Value{
417-
sourcesAccessor: cty.ObjectVal(srcUsage.ctyValues()),
418-
buildAccessor: cty.ObjectVal(unknownBuildValues),
419-
}
322+
var convertDiags hcl.Diagnostics
323+
cfg.debug = opts.Debug
324+
cfg.except, convertDiags = convertFilterOption(opts.Except, "except")
325+
diags = diags.Extend(convertDiags)
326+
cfg.only, convertDiags = convertFilterOption(opts.Only, "only")
327+
diags = diags.Extend(convertDiags)
328+
cfg.force = opts.Force
329+
cfg.onError = opts.OnError
420330

421-
provisioners, moreDiags := cfg.getCoreBuildProvisioners(srcUsage, build.ProvisionerBlocks, cfg.EvalContext(BuildContext, variables))
422-
diags = append(diags, moreDiags...)
423-
if moreDiags.HasErrors() {
424-
continue
425-
}
426-
pps, moreDiags := cfg.getCoreBuildPostProcessors(srcUsage, build.PostProcessorsLists, cfg.EvalContext(BuildContext, variables), &opts.ExceptMatches)
427-
diags = append(diags, moreDiags...)
428-
if moreDiags.HasErrors() {
429-
continue
430-
}
331+
for _, build := range cfg.Builds {
332+
cbs, cbDiags := build.ToCoreBuilds(cfg)
333+
diags = diags.Extend(cbDiags)
431334

432-
if build.ErrorCleanupProvisionerBlock != nil &&
433-
!build.ErrorCleanupProvisionerBlock.OnlyExcept.Skip(srcUsage.String()) {
434-
errorCleanupProv, moreDiags := cfg.getCoreBuildProvisioner(srcUsage, build.ErrorCleanupProvisionerBlock, cfg.EvalContext(BuildContext, variables))
435-
diags = append(diags, moreDiags...)
436-
if moreDiags.HasErrors() {
437-
continue
438-
}
439-
pcb.CleanupProvisioner = errorCleanupProv
440-
}
335+
for _, cb := range cbs {
336+
cb.SetDebug(opts.Debug)
337+
cb.SetForce(opts.Force)
338+
cb.SetOnError(opts.OnError)
441339

442-
pcb.Builder = builder
443-
pcb.Provisioners = provisioners
444-
pcb.PostProcessors = pps
445-
pcb.Prepared = true
340+
cb.Prepared = true
446341

447342
// Prepare just sets the "prepareCalled" flag on CoreBuild, since
448343
// we did all the prep here.
449-
_, err := pcb.Prepare()
344+
_, err := cb.Prepare()
450345
if err != nil {
451346
diags = append(diags, &hcl.Diagnostic{
452347
Severity: hcl.DiagError,
453-
Summary: fmt.Sprintf("Preparing packer core build %s failed", src.Ref().String()),
348+
Summary: fmt.Sprintf("Preparing packer core build %s failed", cb.Name()),
454349
Detail: err.Error(),
455-
Subject: build.HCL2Ref.DefRange.Ptr(),
456350
})
457-
continue
458351
}
459352

460-
res = append(res, pcb)
353+
allBuilds = append(allBuilds, cb)
461354
}
462355
}
463-
if len(opts.Only) > opts.OnlyMatches {
464-
diags = append(diags, &hcl.Diagnostic{
465-
Severity: hcl.DiagWarning,
466-
Summary: "an 'only' option was passed, but not all matches were found for the given build.",
467-
Detail: fmt.Sprintf("Possible build names: %v.\n"+
468-
"These could also be matched with a glob pattern like: 'happycloud.*'", possibleBuildNames),
469-
})
470-
}
471-
if len(opts.Except) > opts.ExceptMatches {
472-
diags = append(diags, &hcl.Diagnostic{
473-
Severity: hcl.DiagWarning,
474-
Summary: "an 'except' option was passed, but did not match any build.",
475-
Detail: fmt.Sprintf("Possible build names: %v.\n"+
476-
"These could also be matched with a glob pattern like: 'happycloud.*'", possibleBuildNames),
477-
})
478-
}
479-
return res, diags
356+
357+
return allBuilds, diags
480358
}
481359

482360
var PackerConsoleHelp = strings.TrimSpace(`

0 commit comments

Comments
 (0)