add hcl2 provisioner type that reprepares itself

This commit is contained in:
Adrien Delorme 2020-07-02 18:02:19 +02:00
parent c88cb098a1
commit db6c3adbba
9 changed files with 129 additions and 50 deletions

View File

@ -129,6 +129,7 @@ func testParse(t *testing.T, tests []parseTest) {
packer.CoreBuildProvisioner{}, packer.CoreBuildProvisioner{},
packer.CoreBuildPostProcessor{}, packer.CoreBuildPostProcessor{},
null.Builder{}, null.Builder{},
HCL2Provisioner{},
), ),
); diff != "" { ); diff != "" {
t.Fatalf("Parser.getBuilds() wrong packer builds. %s", diff) t.Fatalf("Parser.getBuilds() wrong packer builds. %s", diff)
@ -176,7 +177,7 @@ var (
basicMockProvisioner = &MockProvisioner{ basicMockProvisioner = &MockProvisioner{
Config: MockConfig{ Config: MockConfig{
NotSquashed: "value", NotSquashed: "value <UNKNOWN>",
NestedMockConfig: basicNestedMockConfig, NestedMockConfig: basicNestedMockConfig,
Nested: basicNestedMockConfig, Nested: basicNestedMockConfig,
NestedSlice: []NestedMockConfig{ NestedSlice: []NestedMockConfig{

View File

@ -11,7 +11,7 @@ build {
provisioner "shell" { provisioner "shell" {
name = "provisioner that does something" name = "provisioner that does something"
not_squashed = "${var.foo} ${lower(build.ID)}" not_squashed = "${var.foo} ${upper(build.ID)}"
string = "string" string = "string"
int = "${41 + 1}" int = "${41 + 1}"
int64 = "${42 + 1}" int64 = "${42 + 1}"
@ -62,7 +62,7 @@ build {
} }
provisioner "file" { provisioner "file" {
not_squashed = "${var.foo}" not_squashed = "${var.foo} ${upper(build.ID)}"
string = "string" string = "string"
int = 42 int = 42
int64 = 43 int64 = 43

View File

@ -61,7 +61,7 @@ func (p *Parser) decodePostProcessor(block *hcl.Block) (*PostProcessorBlock, hcl
return postProcessor, diags 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 // ProvisionerBlock represents a detected but unparsed provisioner
var diags hcl.Diagnostics var diags hcl.Diagnostics
@ -76,7 +76,7 @@ func (cfg *PackerConfig) startPostProcessor(source SourceBlock, pp *PostProcesso
} }
flatProvisinerCfg, moreDiags := decodeHCL2Spec(pp.Rest, ectx, postProcessor) flatProvisinerCfg, moreDiags := decodeHCL2Spec(pp.Rest, ectx, postProcessor)
diags = append(diags, moreDiags...) diags = append(diags, moreDiags...)
err = postProcessor.Configure(source.builderVariables(), flatProvisinerCfg, generatedVars) err = postProcessor.Configure(source.builderVariables(), flatProvisinerCfg)
if err != nil { if err != nil {
diags = append(diags, &hcl.Diagnostic{ diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError, Severity: hcl.DiagError,

View File

@ -132,7 +132,7 @@ func (p *Parser) decodeProvisioner(block *hcl.Block) (*ProvisionerBlock, hcl.Dia
return provisioner, diags 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 var diags hcl.Diagnostics
provisioner, err := cfg.provisionersSchemas.Start(pb.PType) provisioner, err := cfg.provisionersSchemas.Start(pb.PType)
@ -144,18 +144,13 @@ func (cfg *PackerConfig) startProvisioner(source SourceBlock, pb *ProvisionerBlo
}) })
return nil, diags return nil, diags
} }
flatProvisionerCfg, moreDiags := decodeHCL2Spec(pb.HCL2Ref.Rest, ectx, provisioner) p := &HCL2Provisioner{
diags = append(diags, moreDiags...) Provisioner: provisioner,
if diags.HasErrors() { provisionerBlock: pb,
return nil, diags evalContext: ectx,
builderVariables: source.builderVariables(),
} }
// manipulate generatedVars from builder to add to the interfaces being err = p.HCL2Prepare(generatedVars)
// passed to the provisioner Prepare()
// configs := make([]interface{}, 2)
// configs = append(, flatProvisionerCfg)
// configs = append(configs, generatedVars)
err = provisioner.Prepare(source.builderVariables(), flatProvisionerCfg, generatedVars)
if err != nil { if err != nil {
diags = append(diags, &hcl.Diagnostic{ diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError, Severity: hcl.DiagError,
@ -165,5 +160,5 @@ func (cfg *PackerConfig) startProvisioner(source SourceBlock, pb *ProvisionerBlo
}) })
return nil, diags return nil, diags
} }
return provisioner, diags return p, diags
} }

View File

@ -266,19 +266,23 @@ func TestParse_build(t *testing.T) {
Provisioners: []packer.CoreBuildProvisioner{ Provisioners: []packer.CoreBuildProvisioner{
{ {
PType: "shell", PType: "shell",
Provisioner: &MockProvisioner{ Provisioner: &HCL2Provisioner{
Config: MockConfig{ Provisioner: &MockProvisioner{
NestedMockConfig: NestedMockConfig{Tags: []MockTag{}}, Config: MockConfig{
NestedSlice: []NestedMockConfig{}, NestedMockConfig: NestedMockConfig{Tags: []MockTag{}},
NestedSlice: []NestedMockConfig{},
},
}, },
}, },
}, },
{ {
PType: "file", PType: "file",
Provisioner: &MockProvisioner{ Provisioner: &HCL2Provisioner{
Config: MockConfig{ Provisioner: &MockProvisioner{
NestedMockConfig: NestedMockConfig{Tags: []MockTag{}}, Config: MockConfig{
NestedSlice: []NestedMockConfig{}, NestedMockConfig: NestedMockConfig{Tags: []MockTag{}},
NestedSlice: []NestedMockConfig{},
},
}, },
}, },
}, },
@ -292,19 +296,23 @@ func TestParse_build(t *testing.T) {
Provisioners: []packer.CoreBuildProvisioner{ Provisioners: []packer.CoreBuildProvisioner{
{ {
PType: "file", PType: "file",
Provisioner: &MockProvisioner{ Provisioner: &HCL2Provisioner{
Config: MockConfig{ Provisioner: &MockProvisioner{
NestedMockConfig: NestedMockConfig{Tags: []MockTag{}}, Config: MockConfig{
NestedSlice: []NestedMockConfig{}, NestedMockConfig: NestedMockConfig{Tags: []MockTag{}},
NestedSlice: []NestedMockConfig{},
},
}, },
}, },
}, },
{ {
PType: "shell", PType: "shell",
Provisioner: &MockProvisioner{ Provisioner: &HCL2Provisioner{
Config: MockConfig{ Provisioner: &MockProvisioner{
NestedMockConfig: NestedMockConfig{Tags: []MockTag{}}, Config: MockConfig{
NestedSlice: []NestedMockConfig{}, NestedMockConfig: NestedMockConfig{Tags: []MockTag{}},
NestedSlice: []NestedMockConfig{},
},
}, },
}, },
}, },

View File

@ -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)
}

View File

@ -199,7 +199,7 @@ func (c *PackerConfig) evaluateLocalVariable(local *Local) hcl.Diagnostics {
// getCoreBuildProvisioners takes a list of provisioner block, starts according // getCoreBuildProvisioners takes a list of provisioner block, starts according
// provisioners and sends parsed HCL2 over to it. // 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 var diags hcl.Diagnostics
res := []packer.CoreBuildProvisioner{} res := []packer.CoreBuildProvisioner{}
for _, pb := range blocks { for _, pb := range blocks {
@ -242,7 +242,7 @@ func (cfg *PackerConfig) getCoreBuildProvisioners(source SourceBlock, blocks []*
// getCoreBuildProvisioners takes a list of post processor block, starts // getCoreBuildProvisioners takes a list of post processor block, starts
// according provisioners and sends parsed HCL2 over to it. // 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 var diags hcl.Diagnostics
res := []packer.CoreBuildPostProcessor{} res := []packer.CoreBuildPostProcessor{}
for _, ppb := range blocks { for _, ppb := range blocks {
@ -365,7 +365,7 @@ func (cfg *PackerConfig) GetBuilds(opts packer.GetBuildsOptions) ([]packer.Build
for _, k := range generatedVars { for _, k := range generatedVars {
generatedPlaceholderMap[k] = fmt.Sprintf("Build_%s. "+ generatedPlaceholderMap[k] = fmt.Sprintf("Build_%s. "+
common.PlaceholderMsg, k) common.PlaceholderMsg, k)
unknownBuildValues[k] = cty.StringVal("<unknown_value>") unknownBuildValues[k] = cty.StringVal("<unknown>")
} }
variables := map[string]cty.Value{ variables := map[string]cty.Value{
@ -376,16 +376,16 @@ func (cfg *PackerConfig) GetBuilds(opts packer.GetBuildsOptions) ([]packer.Build
buildAccessor: cty.ObjectVal(unknownBuildValues), 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...) diags = append(diags, moreDiags...)
if moreDiags.HasErrors() { if moreDiags.HasErrors() {
continue 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{} pps := [][]packer.CoreBuildPostProcessor{}
if len(postProcessors) > 0 { if len(postProcessors) > 0 {
pps = [][]packer.CoreBuildPostProcessor{postProcessors} pps = [][]packer.CoreBuildPostProcessor{postProcessors}
} } // TODO(azr): remove this
diags = append(diags, moreDiags...) diags = append(diags, moreDiags...)
if moreDiags.HasErrors() { if moreDiags.HasErrors() {
continue continue

View File

@ -110,11 +110,18 @@ func TestParser_complete(t *testing.T) {
Builder: basicMockBuilder, Builder: basicMockBuilder,
Provisioners: []packer.CoreBuildProvisioner{ Provisioners: []packer.CoreBuildProvisioner{
{ {
PType: "shell", PType: "shell",
PName: "provisioner that does something", PName: "provisioner that does something",
Provisioner: basicMockProvisioner, Provisioner: &HCL2Provisioner{
Provisioner: basicMockProvisioner,
},
},
{
PType: "file",
Provisioner: &HCL2Provisioner{
Provisioner: basicMockProvisioner,
},
}, },
{PType: "file", Provisioner: basicMockProvisioner},
}, },
PostProcessors: [][]packer.CoreBuildPostProcessor{ PostProcessors: [][]packer.CoreBuildPostProcessor{
{ {
@ -146,11 +153,18 @@ func TestParser_complete(t *testing.T) {
}, },
Provisioners: []packer.CoreBuildProvisioner{ Provisioners: []packer.CoreBuildProvisioner{
{ {
PType: "shell", PType: "shell",
PName: "provisioner that does something", PName: "provisioner that does something",
Provisioner: basicMockProvisioner, Provisioner: &HCL2Provisioner{
Provisioner: basicMockProvisioner,
},
},
{
PType: "file",
Provisioner: &HCL2Provisioner{
Provisioner: basicMockProvisioner,
},
}, },
{PType: "file", Provisioner: basicMockProvisioner},
}, },
PostProcessors: [][]packer.CoreBuildPostProcessor{ PostProcessors: [][]packer.CoreBuildPostProcessor{
{ {

View File

@ -51,8 +51,8 @@ type ProvisionHook struct {
// custom generated data could be passed into provisioners from builders to // custom generated data could be passed into provisioners from builders to
// enable specialized builder-specific (but still validated!!) access to builder // enable specialized builder-specific (but still validated!!) access to builder
// data. // data.
func BasicPlaceholderData() map[string]string { func BasicPlaceholderData() map[string]interface{} {
placeholderData := map[string]string{} placeholderData := map[string]interface{}{}
msg := "Build_%s. " + common.PlaceholderMsg msg := "Build_%s. " + common.PlaceholderMsg
placeholderData["ID"] = fmt.Sprintf(msg, "ID") placeholderData["ID"] = fmt.Sprintf(msg, "ID")
// The following correspond to communicator-agnostic functions that are // The following correspond to communicator-agnostic functions that are