diff --git a/hcl2template/common_test.go b/hcl2template/common_test.go index 5db6dc6c2..610e178ce 100644 --- a/hcl2template/common_test.go +++ b/hcl2template/common_test.go @@ -129,6 +129,7 @@ func testParse(t *testing.T, tests []parseTest) { packer.CoreBuildProvisioner{}, packer.CoreBuildPostProcessor{}, null.Builder{}, + HCL2Provisioner{}, ), ); diff != "" { t.Fatalf("Parser.getBuilds() wrong packer builds. %s", diff) @@ -176,7 +177,7 @@ var ( basicMockProvisioner = &MockProvisioner{ Config: MockConfig{ - NotSquashed: "value", + NotSquashed: "value ", NestedMockConfig: basicNestedMockConfig, Nested: basicNestedMockConfig, NestedSlice: []NestedMockConfig{ diff --git a/hcl2template/testdata/complete/build.pkr.hcl b/hcl2template/testdata/complete/build.pkr.hcl index ef73e65f9..5d6745a01 100644 --- a/hcl2template/testdata/complete/build.pkr.hcl +++ b/hcl2template/testdata/complete/build.pkr.hcl @@ -11,7 +11,7 @@ build { provisioner "shell" { name = "provisioner that does something" - not_squashed = "${var.foo} ${lower(build.ID)}" + not_squashed = "${var.foo} ${upper(build.ID)}" string = "string" int = "${41 + 1}" int64 = "${42 + 1}" @@ -62,7 +62,7 @@ build { } provisioner "file" { - not_squashed = "${var.foo}" + not_squashed = "${var.foo} ${upper(build.ID)}" string = "string" int = 42 int64 = 43 diff --git a/hcl2template/types.build.post-processor.go b/hcl2template/types.build.post-processor.go index b45b682f5..f80b40ecf 100644 --- a/hcl2template/types.build.post-processor.go +++ b/hcl2template/types.build.post-processor.go @@ -61,7 +61,7 @@ func (p *Parser) decodePostProcessor(block *hcl.Block) (*PostProcessorBlock, hcl return postProcessor, diags } -func (cfg *PackerConfig) startPostProcessor(source SourceBlock, pp *PostProcessorBlock, ectx *hcl.EvalContext, generatedVars map[string]string) (packer.PostProcessor, hcl.Diagnostics) { +func (cfg *PackerConfig) startPostProcessor(source SourceBlock, pp *PostProcessorBlock, ectx *hcl.EvalContext, generatedVars map[string]interface{}) (packer.PostProcessor, hcl.Diagnostics) { // ProvisionerBlock represents a detected but unparsed provisioner var diags hcl.Diagnostics @@ -76,7 +76,7 @@ func (cfg *PackerConfig) startPostProcessor(source SourceBlock, pp *PostProcesso } flatProvisinerCfg, moreDiags := decodeHCL2Spec(pp.Rest, ectx, postProcessor) diags = append(diags, moreDiags...) - err = postProcessor.Configure(source.builderVariables(), flatProvisinerCfg, generatedVars) + err = postProcessor.Configure(source.builderVariables(), flatProvisinerCfg) if err != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, diff --git a/hcl2template/types.build.provisioners.go b/hcl2template/types.build.provisioners.go index 70756f39e..19b699155 100644 --- a/hcl2template/types.build.provisioners.go +++ b/hcl2template/types.build.provisioners.go @@ -132,7 +132,7 @@ func (p *Parser) decodeProvisioner(block *hcl.Block) (*ProvisionerBlock, hcl.Dia return provisioner, diags } -func (cfg *PackerConfig) startProvisioner(source SourceBlock, pb *ProvisionerBlock, ectx *hcl.EvalContext, generatedVars map[string]string) (packer.Provisioner, hcl.Diagnostics) { +func (cfg *PackerConfig) startProvisioner(source SourceBlock, pb *ProvisionerBlock, ectx *hcl.EvalContext, generatedVars map[string]interface{}) (packer.Provisioner, hcl.Diagnostics) { var diags hcl.Diagnostics provisioner, err := cfg.provisionersSchemas.Start(pb.PType) @@ -144,18 +144,13 @@ func (cfg *PackerConfig) startProvisioner(source SourceBlock, pb *ProvisionerBlo }) return nil, diags } - flatProvisionerCfg, moreDiags := decodeHCL2Spec(pb.HCL2Ref.Rest, ectx, provisioner) - diags = append(diags, moreDiags...) - if diags.HasErrors() { - return nil, diags + p := &HCL2Provisioner{ + Provisioner: provisioner, + provisionerBlock: pb, + evalContext: ectx, + builderVariables: source.builderVariables(), } - // manipulate generatedVars from builder to add to the interfaces being - // passed to the provisioner Prepare() - - // configs := make([]interface{}, 2) - // configs = append(, flatProvisionerCfg) - // configs = append(configs, generatedVars) - err = provisioner.Prepare(source.builderVariables(), flatProvisionerCfg, generatedVars) + err = p.HCL2Prepare(generatedVars) if err != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, @@ -165,5 +160,5 @@ func (cfg *PackerConfig) startProvisioner(source SourceBlock, pb *ProvisionerBlo }) return nil, diags } - return provisioner, diags + return p, diags } diff --git a/hcl2template/types.build_test.go b/hcl2template/types.build_test.go index c3d5dc9e5..f7a69e844 100644 --- a/hcl2template/types.build_test.go +++ b/hcl2template/types.build_test.go @@ -266,19 +266,23 @@ func TestParse_build(t *testing.T) { Provisioners: []packer.CoreBuildProvisioner{ { PType: "shell", - Provisioner: &MockProvisioner{ - Config: MockConfig{ - NestedMockConfig: NestedMockConfig{Tags: []MockTag{}}, - NestedSlice: []NestedMockConfig{}, + Provisioner: &HCL2Provisioner{ + Provisioner: &MockProvisioner{ + Config: MockConfig{ + NestedMockConfig: NestedMockConfig{Tags: []MockTag{}}, + NestedSlice: []NestedMockConfig{}, + }, }, }, }, { PType: "file", - Provisioner: &MockProvisioner{ - Config: MockConfig{ - NestedMockConfig: NestedMockConfig{Tags: []MockTag{}}, - NestedSlice: []NestedMockConfig{}, + Provisioner: &HCL2Provisioner{ + Provisioner: &MockProvisioner{ + Config: MockConfig{ + NestedMockConfig: NestedMockConfig{Tags: []MockTag{}}, + NestedSlice: []NestedMockConfig{}, + }, }, }, }, @@ -292,19 +296,23 @@ func TestParse_build(t *testing.T) { Provisioners: []packer.CoreBuildProvisioner{ { PType: "file", - Provisioner: &MockProvisioner{ - Config: MockConfig{ - NestedMockConfig: NestedMockConfig{Tags: []MockTag{}}, - NestedSlice: []NestedMockConfig{}, + Provisioner: &HCL2Provisioner{ + Provisioner: &MockProvisioner{ + Config: MockConfig{ + NestedMockConfig: NestedMockConfig{Tags: []MockTag{}}, + NestedSlice: []NestedMockConfig{}, + }, }, }, }, { PType: "shell", - Provisioner: &MockProvisioner{ - Config: MockConfig{ - NestedMockConfig: NestedMockConfig{Tags: []MockTag{}}, - NestedSlice: []NestedMockConfig{}, + Provisioner: &HCL2Provisioner{ + Provisioner: &MockProvisioner{ + Config: MockConfig{ + NestedMockConfig: NestedMockConfig{Tags: []MockTag{}}, + NestedSlice: []NestedMockConfig{}, + }, }, }, }, diff --git a/hcl2template/types.hcl_provisioner.go b/hcl2template/types.hcl_provisioner.go new file mode 100644 index 000000000..795497874 --- /dev/null +++ b/hcl2template/types.hcl_provisioner.go @@ -0,0 +1,61 @@ +package hcl2template + +import ( + "context" + "fmt" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/packer" + "github.com/zclconf/go-cty/cty" +) + +type HCL2Provisioner struct { + Provisioner packer.Provisioner + provisionerBlock *ProvisionerBlock + evalContext *hcl.EvalContext + builderVariables map[string]string +} + +func (p *HCL2Provisioner) ConfigSpec() hcldec.ObjectSpec { + return p.Provisioner.ConfigSpec() +} + +func (p *HCL2Provisioner) HCL2Prepare(buildVars map[string]interface{}) error { + var diags hcl.Diagnostics + ectx := p.evalContext + if len(buildVars) > 0 { + ectx = p.evalContext.NewChild() + buildValues := map[string]cty.Value{} + for k, v := range buildVars { + switch v := v.(type) { + case string: + buildValues[k] = cty.StringVal(v) + default: + return fmt.Errorf("unhandled builvar type: %T", v) + } + } + ectx.Variables = map[string]cty.Value{ + buildAccessor: cty.ObjectVal(buildValues), + } + } + + flatProvisionerCfg, moreDiags := decodeHCL2Spec(p.provisionerBlock.HCL2Ref.Rest, ectx, p.Provisioner) + diags = append(diags, moreDiags...) + if diags.HasErrors() { + return diags + } + return p.Provisioner.Prepare(p.builderVariables, flatProvisionerCfg) +} + +func (p *HCL2Provisioner) Prepare(args ...interface{}) error { + return p.Provisioner.Prepare(args...) +} + +func (p *HCL2Provisioner) Provision(ctx context.Context, ui packer.Ui, c packer.Communicator, vars map[string]interface{}) error { + err := p.HCL2Prepare(vars) + if err != nil { + return err + } + return p.Provisioner.Provision(ctx, ui, c, vars) +} diff --git a/hcl2template/types.packer_config.go b/hcl2template/types.packer_config.go index 3908c91fe..069175ca2 100644 --- a/hcl2template/types.packer_config.go +++ b/hcl2template/types.packer_config.go @@ -199,7 +199,7 @@ func (c *PackerConfig) evaluateLocalVariable(local *Local) hcl.Diagnostics { // getCoreBuildProvisioners takes a list of provisioner block, starts according // provisioners and sends parsed HCL2 over to it. -func (cfg *PackerConfig) getCoreBuildProvisioners(source SourceBlock, blocks []*ProvisionerBlock, ectx *hcl.EvalContext, generatedVars map[string]string) ([]packer.CoreBuildProvisioner, hcl.Diagnostics) { +func (cfg *PackerConfig) getCoreBuildProvisioners(source SourceBlock, blocks []*ProvisionerBlock, ectx *hcl.EvalContext, generatedVars map[string]interface{}) ([]packer.CoreBuildProvisioner, hcl.Diagnostics) { var diags hcl.Diagnostics res := []packer.CoreBuildProvisioner{} for _, pb := range blocks { @@ -242,7 +242,7 @@ func (cfg *PackerConfig) getCoreBuildProvisioners(source SourceBlock, blocks []* // getCoreBuildProvisioners takes a list of post processor block, starts // according provisioners and sends parsed HCL2 over to it. -func (cfg *PackerConfig) getCoreBuildPostProcessors(source SourceBlock, blocks []*PostProcessorBlock, ectx *hcl.EvalContext, generatedVars map[string]string) ([]packer.CoreBuildPostProcessor, hcl.Diagnostics) { +func (cfg *PackerConfig) getCoreBuildPostProcessors(source SourceBlock, blocks []*PostProcessorBlock, ectx *hcl.EvalContext, generatedVars map[string]interface{}) ([]packer.CoreBuildPostProcessor, hcl.Diagnostics) { var diags hcl.Diagnostics res := []packer.CoreBuildPostProcessor{} for _, ppb := range blocks { @@ -365,7 +365,7 @@ func (cfg *PackerConfig) GetBuilds(opts packer.GetBuildsOptions) ([]packer.Build for _, k := range generatedVars { generatedPlaceholderMap[k] = fmt.Sprintf("Build_%s. "+ common.PlaceholderMsg, k) - unknownBuildValues[k] = cty.StringVal("") + unknownBuildValues[k] = cty.StringVal("") } variables := map[string]cty.Value{ @@ -376,16 +376,16 @@ func (cfg *PackerConfig) GetBuilds(opts packer.GetBuildsOptions) ([]packer.Build buildAccessor: cty.ObjectVal(unknownBuildValues), } - provisioners, moreDiags := cfg.getCoreBuildProvisioners(src, build.ProvisionerBlocks, cfg.EvalContext(variables), generatedPlaceholderMap) + provisioners, moreDiags := cfg.getCoreBuildProvisioners(src, build.ProvisionerBlocks, cfg.EvalContext(variables), nil) diags = append(diags, moreDiags...) if moreDiags.HasErrors() { continue } - postProcessors, moreDiags := cfg.getCoreBuildPostProcessors(src, build.PostProcessors, cfg.EvalContext(variables), generatedPlaceholderMap) + postProcessors, moreDiags := cfg.getCoreBuildPostProcessors(src, build.PostProcessors, cfg.EvalContext(variables), nil) pps := [][]packer.CoreBuildPostProcessor{} if len(postProcessors) > 0 { pps = [][]packer.CoreBuildPostProcessor{postProcessors} - } + } // TODO(azr): remove this diags = append(diags, moreDiags...) if moreDiags.HasErrors() { continue diff --git a/hcl2template/types.packer_config_test.go b/hcl2template/types.packer_config_test.go index 74df297e4..9849f06df 100644 --- a/hcl2template/types.packer_config_test.go +++ b/hcl2template/types.packer_config_test.go @@ -110,11 +110,18 @@ func TestParser_complete(t *testing.T) { Builder: basicMockBuilder, Provisioners: []packer.CoreBuildProvisioner{ { - PType: "shell", - PName: "provisioner that does something", - Provisioner: basicMockProvisioner, + PType: "shell", + PName: "provisioner that does something", + Provisioner: &HCL2Provisioner{ + Provisioner: basicMockProvisioner, + }, + }, + { + PType: "file", + Provisioner: &HCL2Provisioner{ + Provisioner: basicMockProvisioner, + }, }, - {PType: "file", Provisioner: basicMockProvisioner}, }, PostProcessors: [][]packer.CoreBuildPostProcessor{ { @@ -146,11 +153,18 @@ func TestParser_complete(t *testing.T) { }, Provisioners: []packer.CoreBuildProvisioner{ { - PType: "shell", - PName: "provisioner that does something", - Provisioner: basicMockProvisioner, + PType: "shell", + PName: "provisioner that does something", + Provisioner: &HCL2Provisioner{ + Provisioner: basicMockProvisioner, + }, + }, + { + PType: "file", + Provisioner: &HCL2Provisioner{ + Provisioner: basicMockProvisioner, + }, }, - {PType: "file", Provisioner: basicMockProvisioner}, }, PostProcessors: [][]packer.CoreBuildPostProcessor{ { diff --git a/packer/provisioner.go b/packer/provisioner.go index 905b28971..8caffc1a9 100644 --- a/packer/provisioner.go +++ b/packer/provisioner.go @@ -51,8 +51,8 @@ type ProvisionHook struct { // custom generated data could be passed into provisioners from builders to // enable specialized builder-specific (but still validated!!) access to builder // data. -func BasicPlaceholderData() map[string]string { - placeholderData := map[string]string{} +func BasicPlaceholderData() map[string]interface{} { + placeholderData := map[string]interface{}{} msg := "Build_%s. " + common.PlaceholderMsg placeholderData["ID"] = fmt.Sprintf(msg, "ID") // The following correspond to communicator-agnostic functions that are