191 lines
5.4 KiB
Go
191 lines
5.4 KiB
Go
package hcl2template
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/hcl/v2/gohcl"
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
|
)
|
|
|
|
const (
|
|
buildFromLabel = "from"
|
|
|
|
buildSourceLabel = "source"
|
|
|
|
buildProvisionerLabel = "provisioner"
|
|
|
|
buildErrorCleanupProvisionerLabel = "error-cleanup-provisioner"
|
|
|
|
buildPostProcessorLabel = "post-processor"
|
|
|
|
buildPostProcessorsLabel = "post-processors"
|
|
)
|
|
|
|
var buildSchema = &hcl.BodySchema{
|
|
Blocks: []hcl.BlockHeaderSchema{
|
|
{Type: buildFromLabel, LabelNames: []string{"type"}},
|
|
{Type: sourceLabel, LabelNames: []string{"reference"}},
|
|
{Type: buildProvisionerLabel, LabelNames: []string{"type"}},
|
|
{Type: buildErrorCleanupProvisionerLabel, LabelNames: []string{"type"}},
|
|
{Type: buildPostProcessorLabel, LabelNames: []string{"type"}},
|
|
{Type: buildPostProcessorsLabel, LabelNames: []string{}},
|
|
},
|
|
}
|
|
|
|
var postProcessorsSchema = &hcl.BodySchema{
|
|
Blocks: []hcl.BlockHeaderSchema{
|
|
{Type: buildPostProcessorLabel, LabelNames: []string{"type"}},
|
|
},
|
|
}
|
|
|
|
// BuildBlock references an HCL 'build' block and it content, for example :
|
|
//
|
|
// build {
|
|
// sources = [
|
|
// ...
|
|
// ]
|
|
// provisioner "" { ... }
|
|
// post-processor "" { ... }
|
|
// }
|
|
type BuildBlock struct {
|
|
// Name is a string representing the named build to show in the logs
|
|
Name string
|
|
|
|
// A description of what this build does, it could be used in a inspect
|
|
// call for example.
|
|
Description string
|
|
|
|
// Sources is the list of sources that we want to start in this build block.
|
|
Sources []SourceUseBlock
|
|
|
|
// ProvisionerBlocks references a list of HCL provisioner block that will
|
|
// will be ran against the sources.
|
|
ProvisionerBlocks []*ProvisionerBlock
|
|
|
|
// ErrorCleanupProvisionerBlock references a special provisioner block that
|
|
// will be ran only if the provision step fails.
|
|
ErrorCleanupProvisionerBlock *ProvisionerBlock
|
|
|
|
// PostProcessorLists references the lists of lists of HCL post-processors
|
|
// block that will be run against the artifacts from the provisioning
|
|
// steps.
|
|
PostProcessorsLists [][]*PostProcessorBlock
|
|
|
|
HCL2Ref HCL2Ref
|
|
}
|
|
|
|
type Builds []*BuildBlock
|
|
|
|
// decodeBuildConfig is called when a 'build' block has been detected. It will
|
|
// load the references to the contents of the build block.
|
|
func (p *Parser) decodeBuildConfig(block *hcl.Block, cfg *PackerConfig) (*BuildBlock, hcl.Diagnostics) {
|
|
build := &BuildBlock{}
|
|
|
|
var b struct {
|
|
Name string `hcl:"name,optional"`
|
|
Description string `hcl:"description,optional"`
|
|
FromSources []string `hcl:"sources,optional"`
|
|
Config hcl.Body `hcl:",remain"`
|
|
}
|
|
diags := gohcl.DecodeBody(block.Body, nil, &b)
|
|
if diags.HasErrors() {
|
|
return nil, diags
|
|
}
|
|
|
|
build.Name = b.Name
|
|
build.Description = b.Description
|
|
|
|
for _, buildFrom := range b.FromSources {
|
|
ref := sourceRefFromString(buildFrom)
|
|
|
|
if ref == NoSource ||
|
|
!hclsyntax.ValidIdentifier(ref.Type) ||
|
|
!hclsyntax.ValidIdentifier(ref.Name) {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid " + sourceLabel + " reference",
|
|
Detail: "A " + sourceLabel + " type is made of three parts that are" +
|
|
"split by a dot `.`; each part must start with a letter and " +
|
|
"may contain only letters, digits, underscores, and dashes." +
|
|
"A valid source reference looks like: `source.type.name`",
|
|
Subject: block.DefRange.Ptr(),
|
|
})
|
|
continue
|
|
}
|
|
|
|
// source with no body
|
|
build.Sources = append(build.Sources, SourceUseBlock{SourceRef: ref})
|
|
}
|
|
|
|
content, moreDiags := b.Config.Content(buildSchema)
|
|
diags = append(diags, moreDiags...)
|
|
if diags.HasErrors() {
|
|
return nil, diags
|
|
}
|
|
for _, block := range content.Blocks {
|
|
switch block.Type {
|
|
case sourceLabel:
|
|
ref, moreDiags := p.decodeBuildSource(block)
|
|
diags = append(diags, moreDiags...)
|
|
if moreDiags.HasErrors() {
|
|
continue
|
|
}
|
|
build.Sources = append(build.Sources, ref)
|
|
case buildProvisionerLabel:
|
|
p, moreDiags := p.decodeProvisioner(block, cfg)
|
|
diags = append(diags, moreDiags...)
|
|
if moreDiags.HasErrors() {
|
|
continue
|
|
}
|
|
build.ProvisionerBlocks = append(build.ProvisionerBlocks, p)
|
|
case buildErrorCleanupProvisionerLabel:
|
|
if build.ErrorCleanupProvisionerBlock != nil {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: fmt.Sprintf("Only one " + buildErrorCleanupProvisionerLabel + " is allowed"),
|
|
Subject: block.DefRange.Ptr(),
|
|
})
|
|
continue
|
|
}
|
|
p, moreDiags := p.decodeProvisioner(block, cfg)
|
|
diags = append(diags, moreDiags...)
|
|
if moreDiags.HasErrors() {
|
|
continue
|
|
}
|
|
build.ErrorCleanupProvisionerBlock = p
|
|
case buildPostProcessorLabel:
|
|
pp, moreDiags := p.decodePostProcessor(block)
|
|
diags = append(diags, moreDiags...)
|
|
if moreDiags.HasErrors() {
|
|
continue
|
|
}
|
|
build.PostProcessorsLists = append(build.PostProcessorsLists, []*PostProcessorBlock{pp})
|
|
case buildPostProcessorsLabel:
|
|
|
|
content, moreDiags := block.Body.Content(postProcessorsSchema)
|
|
diags = append(diags, moreDiags...)
|
|
if moreDiags.HasErrors() {
|
|
continue
|
|
}
|
|
|
|
errored := false
|
|
postProcessors := []*PostProcessorBlock{}
|
|
for _, block := range content.Blocks {
|
|
pp, moreDiags := p.decodePostProcessor(block)
|
|
diags = append(diags, moreDiags...)
|
|
if moreDiags.HasErrors() {
|
|
errored = true
|
|
break
|
|
}
|
|
postProcessors = append(postProcessors, pp)
|
|
}
|
|
if errored == false {
|
|
build.PostProcessorsLists = append(build.PostProcessorsLists, postProcessors)
|
|
}
|
|
}
|
|
}
|
|
|
|
return build, diags
|
|
}
|