packer-cn/hcl2template/parser.go

135 lines
3.2 KiB
Go

package hcl2template
import (
"fmt"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclparse"
"github.com/hashicorp/packer/packer"
)
const (
sourceLabel = "source"
variablesLabel = "variables"
buildLabel = "build"
communicatorLabel = "communicator"
)
var configSchema = &hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{Type: sourceLabel, LabelNames: []string{"type", "name"}},
{Type: variablesLabel},
{Type: buildLabel},
{Type: communicatorLabel, LabelNames: []string{"type", "name"}},
},
}
type Parser struct {
*hclparse.Parser
BuilderSchemas packer.BuilderStore
ProvisionersSchemas packer.ProvisionerStore
PostProcessorsSchemas packer.PostProcessorStore
}
const (
hcl2FileExt = ".pkr.hcl"
hcl2JsonFileExt = ".pkr.json"
)
func (p *Parser) parse(filename string) (*PackerConfig, hcl.Diagnostics) {
hclFiles, jsonFiles, diags := GetHCL2Files(filename)
var files []*hcl.File
for _, filename := range hclFiles {
f, moreDiags := p.ParseHCLFile(filename)
diags = append(diags, moreDiags...)
files = append(files, f)
}
for _, filename := range jsonFiles {
f, moreDiags := p.ParseJSONFile(filename)
diags = append(diags, moreDiags...)
files = append(files, f)
}
if diags.HasErrors() {
return nil, diags
}
cfg := &PackerConfig{}
for _, file := range files {
moreDiags := p.parseFile(file, cfg)
diags = append(diags, moreDiags...)
}
return cfg, diags
}
// parseFile filename content into cfg.
//
// parseFile may be called multiple times with the same cfg on a different file.
//
// parseFile returns as complete a config as we can manage, even if there are
// errors, since a partial result can be useful for careful analysis by
// development tools such as text editor extensions.
func (p *Parser) parseFile(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics {
var diags hcl.Diagnostics
content, moreDiags := f.Body.Content(configSchema)
diags = append(diags, moreDiags...)
for _, block := range content.Blocks {
switch block.Type {
case sourceLabel:
source, moreDiags := p.decodeSource(block)
diags = append(diags, moreDiags...)
if moreDiags.HasErrors() {
continue
}
ref := source.Ref()
if existing := cfg.Sources[ref]; existing != nil {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate " + sourceLabel + " block",
Detail: fmt.Sprintf("This "+sourceLabel+" block has the "+
"same builder type and name as a previous block declared "+
"at %s. Each "+sourceLabel+" must have a unique name per builder type.",
existing.block.DefRange.Ptr()),
Subject: source.block.DefRange.Ptr(),
})
continue
}
if cfg.Sources == nil {
cfg.Sources = map[SourceRef]*Source{}
}
cfg.Sources[ref] = source
case variablesLabel:
if cfg.Variables == nil {
cfg.Variables = PackerV1Variables{}
}
moreDiags := cfg.Variables.decodeConfig(block)
if moreDiags.HasErrors() {
continue
}
diags = append(diags, moreDiags...)
case buildLabel:
build, moreDiags := p.decodeBuildConfig(block)
diags = append(diags, moreDiags...)
if moreDiags.HasErrors() {
continue
}
cfg.Builds = append(cfg.Builds, build)
}
}
return diags
}