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.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 <UNKNOWN>",
NestedMockConfig: basicNestedMockConfig,
Nested: basicNestedMockConfig,
NestedSlice: []NestedMockConfig{

View File

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

View File

@ -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,

View File

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

View File

@ -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{},
},
},
},
},

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
// 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("<unknown_value>")
unknownBuildValues[k] = cty.StringVal("<unknown>")
}
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

View File

@ -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{
{

View File

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