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 }