mirror of https://github.com/hashicorp/packer
HCL2 only: Make provisioners and post-processors reprepare themselves with build data (#9534)
#9444 allows to share builder infos with provisioners and post-processor by re-starting new provisioners and post-processors once the build has started; #9490 allows to 'excpect/only' on a full build blocks (after naming a block), merged together, the tests of #9490 fail. This means we would have to reimplement the only/except logic. While this could have worked, re-using the already started provisioners and post-processor for HCL2 is better/simpler IMO as we won't have to maintain a feature parity. Also with HCL2 and go-cty all struct ( here provisioner/post-processor) fields are set at the moment of configuring so we should not get weird 'default value' behaviour issue. This PR reverts some parts of #9444 and implement #9444 in a different manner: I created specific HCL2Provisioner and HCL2PostProcessor structs that both re-prepare themselves right before provisioning. I think this makes the code a little simpler and lays a nice ground for improvement.pull/9505/head
commit
741a6e4182
@ -0,0 +1,13 @@
|
||||
package command
|
||||
|
||||
import "strings"
|
||||
|
||||
func init() {
|
||||
spaghettiCarbonara = fixWindowsLineEndings(spaghettiCarbonara)
|
||||
lasagna = fixWindowsLineEndings(lasagna)
|
||||
tiramisu = fixWindowsLineEndings(tiramisu)
|
||||
}
|
||||
|
||||
func fixWindowsLineEndings(s string) string {
|
||||
return strings.ReplaceAll(s, "\n", " \r\n")
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
source "null" "chocolate" {
|
||||
communicator = "none"
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["null.chocolate"]
|
||||
|
||||
provisioner "shell-local" {
|
||||
inline = ["echo hi > provisioner.${build.ID}.txt"]
|
||||
}
|
||||
|
||||
post-processor "shell-local" {
|
||||
inline = ["echo hi > post-processor.${build.ID}.txt"]
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
source "null" "chocolate" {
|
||||
communicator = "none"
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["null.chocolate"]
|
||||
|
||||
provisioner "shell-local" {
|
||||
inline = [
|
||||
"echo hi > provisioner.${build.ID}.txt",
|
||||
"echo hi > provisioner.${upper(build.ID)}.txt"
|
||||
]
|
||||
}
|
||||
|
||||
post-processor "shell-local" {
|
||||
inline = [
|
||||
"echo hi > post-processor.${build.ID}.txt",
|
||||
"echo hi > post-processor.${upper(build.ID)}.txt"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
source "null" "chocolate" {
|
||||
communicator = "none"
|
||||
}
|
||||
|
||||
source "null" "banana" {
|
||||
communicator = "none"
|
||||
}
|
||||
|
||||
build {
|
||||
name = "vanilla"
|
||||
sources = ["null.chocolate"]
|
||||
|
||||
provisioner "shell-local" {
|
||||
inline = ["echo hi > vanilla.chocolate.provisioner.${build.ID}.txt"]
|
||||
}
|
||||
|
||||
post-processor "shell-local" {
|
||||
inline = ["echo hi > vanilla.chocolate.post-processor.${build.ID}.txt"]
|
||||
}
|
||||
}
|
||||
|
||||
build {
|
||||
name = "apple"
|
||||
sources = ["null.chocolate"]
|
||||
|
||||
provisioner "shell-local" {
|
||||
inline = ["echo hi > apple.chocolate.provisioner.${build.ID}.txt"]
|
||||
}
|
||||
|
||||
post-processor "shell-local" {
|
||||
inline = ["echo hi > apple.chocolate.post-processor.${build.ID}.txt"]
|
||||
}
|
||||
}
|
||||
|
||||
build {
|
||||
name = "sugar"
|
||||
sources = ["null.banana"]
|
||||
|
||||
provisioner "shell-local" {
|
||||
inline = ["echo hi > sugar.banana.provisioner.${build.ID}.txt"]
|
||||
}
|
||||
|
||||
post-processor "shell-local" {
|
||||
inline = ["echo hi > sugar.banana.post-processor.${build.ID}.txt"]
|
||||
}
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
source "null" "chocolate" {
|
||||
communicator = "none"
|
||||
}
|
||||
|
||||
source "null" "banana" {
|
||||
communicator = "none"
|
||||
}
|
||||
|
||||
build {
|
||||
name = "vanilla"
|
||||
sources = [
|
||||
"null.chocolate",
|
||||
"null.banana",
|
||||
]
|
||||
|
||||
provisioner "shell-local" {
|
||||
inline = [
|
||||
"echo hi > all.${build.ID}.txt",
|
||||
"echo hi > chocolate.${build.ID}.txt",
|
||||
"echo hi > banana.${build.ID}.txt"
|
||||
]
|
||||
}
|
||||
|
||||
post-processor "shell-local" {
|
||||
only = ["null.chocolate"]
|
||||
inline = ["rm chocolate.${build.ID}.txt"]
|
||||
}
|
||||
post-processor "shell-local" {
|
||||
except = ["null.chocolate"]
|
||||
inline = ["rm banana.${build.ID}.txt"]
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
source "null" "chocolate" {
|
||||
communicator = "none"
|
||||
}
|
||||
|
||||
build {
|
||||
name = "vanilla"
|
||||
sources = ["null.chocolate"]
|
||||
|
||||
provisioner "shell-local" {
|
||||
inline = ["echo hi > provisioner.${build.ID}.txt"]
|
||||
}
|
||||
|
||||
post-processor "shell-local" {
|
||||
inline = ["echo hi > post-processor.${build.ID}.txt"]
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
|
||||
build {
|
||||
source "source.null.base" {
|
||||
name = "tiramisu"
|
||||
// pull me up !
|
||||
}
|
||||
|
||||
provisioner "shell-local" {
|
||||
name = "whipped_york"
|
||||
inline = [ "echo whip_york > ${upper(build.ID)}.${source.name}.txt" ]
|
||||
}
|
||||
provisioner "shell-local" {
|
||||
name = "mascarpone"
|
||||
inline = [ "echo mascarpone >> ${upper(build.ID)}.${source.name}.txt" ]
|
||||
}
|
||||
post-processor "shell-local" {
|
||||
name = "whipped_egg_white"
|
||||
inline = [ "echo whipped_egg_white >> ${upper(build.ID)}.${source.name}.txt" ]
|
||||
}
|
||||
post-processor "shell-local" {
|
||||
name = "dress_with_coffeed_boudoirs"
|
||||
inline = [ "echo dress >> ${upper(build.ID)}.${source.name}.txt" ]
|
||||
}
|
||||
}
|
||||
|
||||
build {
|
||||
name = "recipes"
|
||||
source "source.null.base" {
|
||||
name = "spaghetti_carbonara"
|
||||
}
|
||||
source "source.null.base" {
|
||||
name = "lasagna"
|
||||
}
|
||||
|
||||
provisioner "shell-local" {
|
||||
name = "add_spaghetti"
|
||||
inline = [ "echo spaghetti > ${upper(build.ID)}.${source.name}.txt" ]
|
||||
only = ["null.spaghetti_carbonara"]
|
||||
}
|
||||
|
||||
post-processor "shell-local" {
|
||||
name = "carbonara_it"
|
||||
inline = [ "echo carbonara >> ${upper(build.ID)}.${source.name}.txt" ]
|
||||
except = ["null.lasagna"]
|
||||
}
|
||||
|
||||
|
||||
provisioner "shell-local" {
|
||||
name = "add_lasagna"
|
||||
inline = [ "echo lasagna > ${upper(build.ID)}.${source.name}.txt" ]
|
||||
only = ["null.lasagna"]
|
||||
}
|
||||
|
||||
provisioner "shell-local" {
|
||||
name = "add_tomato"
|
||||
inline = [ "echo tomato >> ${upper(build.ID)}.${source.name}.txt" ]
|
||||
except = ["null.spaghetti_carbonara"]
|
||||
}
|
||||
|
||||
provisioner "shell-local" {
|
||||
name = "add_mozza"
|
||||
inline = [ "echo mozza >> ${upper(build.ID)}.${source.name}.txt" ]
|
||||
except = ["null.spaghetti_carbonara"]
|
||||
}
|
||||
|
||||
post-processor "shell-local" {
|
||||
name = "cook"
|
||||
inline = [ "echo cooking... >> ${upper(build.ID)}.${source.name}.txt" ]
|
||||
except = ["null.spaghetti_carbonara"]
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
source "null" "base" {
|
||||
communicator = "none"
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// HCL2PostProcessor has a reference to the part of the HCL2 body where it is
|
||||
// defined, allowing to completely reconfigure the PostProcessor right before
|
||||
// calling PostProcess: with contextual variables.
|
||||
// This permits using "${build.ID}" values for example.
|
||||
type HCL2PostProcessor struct {
|
||||
PostProcessor packer.PostProcessor
|
||||
postProcessorBlock *PostProcessorBlock
|
||||
evalContext *hcl.EvalContext
|
||||
builderVariables map[string]string
|
||||
}
|
||||
|
||||
func (p *HCL2PostProcessor) ConfigSpec() hcldec.ObjectSpec {
|
||||
return p.PostProcessor.ConfigSpec()
|
||||
}
|
||||
|
||||
func (p *HCL2PostProcessor) 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),
|
||||
}
|
||||
}
|
||||
|
||||
flatPostProcessorCfg, moreDiags := decodeHCL2Spec(p.postProcessorBlock.HCL2Ref.Rest, ectx, p.PostProcessor)
|
||||
diags = append(diags, moreDiags...)
|
||||
if diags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
return p.PostProcessor.Configure(p.builderVariables, flatPostProcessorCfg)
|
||||
}
|
||||
|
||||
func (p *HCL2PostProcessor) Configure(args ...interface{}) error {
|
||||
return p.PostProcessor.Configure(args...)
|
||||
}
|
||||
|
||||
func (p *HCL2PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, bool, error) {
|
||||
generatedData := make(map[string]interface{})
|
||||
if artifactStateData, ok := artifact.State("generated_data").(map[interface{}]interface{}); ok {
|
||||
for k, v := range artifactStateData {
|
||||
generatedData[k.(string)] = v
|
||||
}
|
||||
}
|
||||
|
||||
err := p.HCL2Prepare(generatedData)
|
||||
if err != nil {
|
||||
return nil, false, false, err
|
||||
}
|
||||
return p.PostProcessor.PostProcess(ctx, ui, artifact)
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// HCL2Provisioner has a reference to the part of the HCL2 body where it is
|
||||
// defined, allowing to completely reconfigure the Provisioner right before
|
||||
// calling Provision: with contextual variables.
|
||||
// This permits using "${build.ID}" values for example.
|
||||
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)
|
||||
}
|
||||
Loading…
Reference in new issue