Share .Device and .MountPath between builders, provisioners and post-processors (#8621)

This commit is contained in:
Sylvia Moss 2020-01-20 16:29:38 +01:00 committed by GitHub
parent d25b4cf514
commit 0b7251a4bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 131 additions and 19 deletions

View File

@ -10,6 +10,7 @@ package chroot
import ( import (
"context" "context"
"errors" "errors"
"github.com/hashicorp/packer/builder"
"runtime" "runtime"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
@ -326,7 +327,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
} }
packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token) packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token)
return nil, warns, nil generatedData := []string{"SourceAMIName", "Device", "MountPath"}
return generatedData, warns, nil
} }
func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
@ -356,6 +358,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
state.Put("hook", hook) state.Put("hook", hook)
state.Put("ui", ui) state.Put("ui", ui)
state.Put("wrappedCommand", common.CommandWrapper(wrappedCommand)) state.Put("wrappedCommand", common.CommandWrapper(wrappedCommand))
generatedData := &builder.GeneratedData{State: state}
// Build the steps // Build the steps
steps := []multistep.Step{ steps := []multistep.Step{
@ -381,7 +384,9 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
steps = append(steps, steps = append(steps,
&StepFlock{}, &StepFlock{},
&StepPrepareDevice{}, &StepPrepareDevice{
GeneratedData: generatedData,
},
&StepCreateVolume{ &StepCreateVolume{
RootVolumeType: b.config.RootVolumeType, RootVolumeType: b.config.RootVolumeType,
RootVolumeSize: b.config.RootVolumeSize, RootVolumeSize: b.config.RootVolumeSize,
@ -396,6 +401,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&StepMountDevice{ &StepMountDevice{
MountOptions: b.config.MountOptions, MountOptions: b.config.MountOptions,
MountPartition: b.config.MountPartition, MountPartition: b.config.MountPartition,
GeneratedData: generatedData,
}, },
&chroot.StepPostMountCommands{ &chroot.StepPostMountCommands{
Commands: b.config.PostMountCommands, Commands: b.config.PostMountCommands,
@ -439,6 +445,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
SnapshotUsers: b.config.SnapshotUsers, SnapshotUsers: b.config.SnapshotUsers,
SnapshotGroups: b.config.SnapshotGroups, SnapshotGroups: b.config.SnapshotGroups,
Ctx: b.config.ctx, Ctx: b.config.ctx,
GeneratedData: generatedData,
}, },
&awscommon.StepCreateTags{ &awscommon.StepCreateTags{
Tags: b.config.AMITags, Tags: b.config.AMITags,
@ -466,6 +473,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
Amis: state.Get("amis").(map[string]string), Amis: state.Get("amis").(map[string]string),
BuilderIdValue: BuilderId, BuilderIdValue: BuilderId,
Session: session, Session: session,
StateData: map[string]interface{}{"generated_data": state.Get("generated_data")},
} }
return artifact, nil return artifact, nil

View File

@ -209,3 +209,28 @@ func TestBuilderPrepare_RootDeviceNameNoAMIMappings(t *testing.T) {
t.Fatalf("should have error") t.Fatalf("should have error")
} }
} }
func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
var b Builder
config := testConfig()
generatedData, warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if len(generatedData) == 0 {
t.Fatalf("Generated data should not be empty")
}
if generatedData[0] != "SourceAMIName" {
t.Fatalf("Generated data should contain SourceAMIName")
}
if generatedData[1] != "Device" {
t.Fatalf("Generated data should contain Device")
}
if generatedData[2] != "MountPath" {
t.Fatalf("Generated data should contain MountPath")
}
}

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"fmt" "fmt"
"github.com/hashicorp/packer/builder"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
@ -29,7 +30,8 @@ type StepMountDevice struct {
MountOptions []string MountOptions []string
MountPartition string MountPartition string
mountPath string mountPath string
GeneratedData *builder.GeneratedData
} }
func (s *StepMountDevice) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepMountDevice) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
@ -118,6 +120,7 @@ func (s *StepMountDevice) Run(ctx context.Context, state multistep.StateBag) mul
// Set the mount path so we remember to unmount it later // Set the mount path so we remember to unmount it later
s.mountPath = mountPath s.mountPath = mountPath
state.Put("mount_path", s.mountPath) state.Put("mount_path", s.mountPath)
s.GeneratedData.Put("MountPath", s.mountPath)
state.Put("mount_device_cleanup", s) state.Put("mount_device_cleanup", s)
return multistep.ActionContinue return multistep.ActionContinue

View File

@ -3,6 +3,7 @@ package chroot
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/hashicorp/packer/builder"
"log" "log"
"os" "os"
@ -12,6 +13,7 @@ import (
// StepPrepareDevice finds an available device and sets it. // StepPrepareDevice finds an available device and sets it.
type StepPrepareDevice struct { type StepPrepareDevice struct {
GeneratedData *builder.GeneratedData
} }
func (s *StepPrepareDevice) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepPrepareDevice) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
@ -40,6 +42,7 @@ func (s *StepPrepareDevice) Run(ctx context.Context, state multistep.StateBag) m
log.Printf("Device: %s", device) log.Printf("Device: %s", device)
state.Put("device", device) state.Put("device", device)
s.GeneratedData.Put("Device", device)
return multistep.ActionContinue return multistep.ActionContinue
} }

View File

@ -3,6 +3,7 @@ package common
import ( import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/builder"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
) )
@ -15,7 +16,7 @@ type BuildInfoTemplate struct {
SourceAMITags map[string]string SourceAMITags map[string]string
} }
func extractBuildInfo(region string, state multistep.StateBag) *BuildInfoTemplate { func extractBuildInfo(region string, state multistep.StateBag, generatedData *builder.GeneratedData) *BuildInfoTemplate {
rawSourceAMI, hasSourceAMI := state.GetOk("source_image") rawSourceAMI, hasSourceAMI := state.GetOk("source_image")
if !hasSourceAMI { if !hasSourceAMI {
return &BuildInfoTemplate{ return &BuildInfoTemplate{
@ -37,7 +38,6 @@ func extractBuildInfo(region string, state multistep.StateBag) *BuildInfoTemplat
SourceAMIOwnerName: aws.StringValue(sourceAMI.ImageOwnerAlias), SourceAMIOwnerName: aws.StringValue(sourceAMI.ImageOwnerAlias),
SourceAMITags: sourceAMITags, SourceAMITags: sourceAMITags,
} }
state.Put("generated_data", map[string]interface{}{"SourceAMIName": buildInfoTemplate.SourceAMIName}) generatedData.Put("SourceAMIName", buildInfoTemplate.SourceAMIName)
return buildInfoTemplate return buildInfoTemplate
} }

View File

@ -1,6 +1,7 @@
package common package common
import ( import (
"github.com/hashicorp/packer/builder"
"reflect" "reflect"
"testing" "testing"
@ -33,9 +34,15 @@ func testState() multistep.StateBag {
return state return state
} }
func testGeneratedData(state multistep.StateBag) builder.GeneratedData {
generatedData := builder.GeneratedData{State: state}
return generatedData
}
func TestInterpolateBuildInfo_extractBuildInfo_noSourceImage(t *testing.T) { func TestInterpolateBuildInfo_extractBuildInfo_noSourceImage(t *testing.T) {
state := testState() state := testState()
buildInfo := extractBuildInfo("foo", state) generatedData := testGeneratedData(state)
buildInfo := extractBuildInfo("foo", state, &generatedData)
expected := BuildInfoTemplate{ expected := BuildInfoTemplate{
BuildRegion: "foo", BuildRegion: "foo",
@ -48,7 +55,8 @@ func TestInterpolateBuildInfo_extractBuildInfo_noSourceImage(t *testing.T) {
func TestInterpolateBuildInfo_extractBuildInfo_withSourceImage(t *testing.T) { func TestInterpolateBuildInfo_extractBuildInfo_withSourceImage(t *testing.T) {
state := testState() state := testState()
state.Put("source_image", testImage()) state.Put("source_image", testImage())
buildInfo := extractBuildInfo("foo", state) generatedData := testGeneratedData(state)
buildInfo := extractBuildInfo("foo", state, &generatedData)
expected := BuildInfoTemplate{ expected := BuildInfoTemplate{
BuildRegion: "foo", BuildRegion: "foo",
@ -69,11 +77,12 @@ func TestInterpolateBuildInfo_extractBuildInfo_withSourceImage(t *testing.T) {
func TestInterpolateBuildInfo_extractBuildInfo_GeneratedDataWithSourceImageName(t *testing.T) { func TestInterpolateBuildInfo_extractBuildInfo_GeneratedDataWithSourceImageName(t *testing.T) {
state := testState() state := testState()
state.Put("source_image", testImage()) state.Put("source_image", testImage())
extractBuildInfo("foo", state) generatedData := testGeneratedData(state)
extractBuildInfo("foo", state, &generatedData)
generatedData := state.Get("generated_data").(map[string]interface{}) generatedDataState := state.Get("generated_data").(map[string]interface{})
if generatedData["SourceAMIName"] != "ami_test_name" { if generatedDataState["SourceAMIName"] != "ami_test_name" {
t.Fatalf("Unexpected state SourceAMIName: expected %#v got %#v\n", "ami_test_name", generatedData["SourceAMIName"]) t.Fatalf("Unexpected state SourceAMIName: expected %#v got %#v\n", "ami_test_name", generatedDataState["SourceAMIName"])
} }
} }

View File

@ -3,10 +3,10 @@ package common
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/builder"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate" "github.com/hashicorp/packer/template/interpolate"
@ -20,6 +20,8 @@ type StepModifyAMIAttributes struct {
ProductCodes []string ProductCodes []string
Description string Description string
Ctx interpolate.Context Ctx interpolate.Context
GeneratedData *builder.GeneratedData
} }
func (s *StepModifyAMIAttributes) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepModifyAMIAttributes) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
@ -43,7 +45,7 @@ func (s *StepModifyAMIAttributes) Run(ctx context.Context, state multistep.State
} }
var err error var err error
s.Ctx.Data = extractBuildInfo(*ec2conn.Config.Region, state) s.Ctx.Data = extractBuildInfo(*ec2conn.Config.Region, state, s.GeneratedData)
s.Description, err = interpolate.Render(s.Description, &s.Ctx) s.Description, err = interpolate.Render(s.Description, &s.Ctx)
if err != nil { if err != nil {
err = fmt.Errorf("Error interpolating AMI description: %s", err) err = fmt.Errorf("Error interpolating AMI description: %s", err)

View File

@ -5,6 +5,7 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/builder"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate" "github.com/hashicorp/packer/template/interpolate"
@ -26,7 +27,8 @@ func (t TagMap) IsSet() bool {
func (t TagMap) EC2Tags(ictx interpolate.Context, region string, state multistep.StateBag) (EC2Tags, error) { func (t TagMap) EC2Tags(ictx interpolate.Context, region string, state multistep.StateBag) (EC2Tags, error) {
var ec2Tags []*ec2.Tag var ec2Tags []*ec2.Tag
ictx.Data = extractBuildInfo(region, state) generatedData := builder.GeneratedData{State: state}
ictx.Data = extractBuildInfo(region, state, &generatedData)
for key, value := range t { for key, value := range t {
interpolatedKey, err := interpolate.Render(key, &ictx) interpolatedKey, err := interpolate.Render(key, &ictx)

View File

@ -11,10 +11,10 @@ package ebs
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/iam" "github.com/aws/aws-sdk-go/service/iam"
"github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer/builder"
awscommon "github.com/hashicorp/packer/builder/amazon/common" awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/communicator"
@ -154,6 +154,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
state.Put("awsSession", session) state.Put("awsSession", session)
state.Put("hook", hook) state.Put("hook", hook)
state.Put("ui", ui) state.Put("ui", ui)
generatedData := &builder.GeneratedData{State: state}
var instanceStep multistep.Step var instanceStep multistep.Step
@ -301,6 +302,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
SnapshotUsers: b.config.SnapshotUsers, SnapshotUsers: b.config.SnapshotUsers,
SnapshotGroups: b.config.SnapshotGroups, SnapshotGroups: b.config.SnapshotGroups,
Ctx: b.config.ctx, Ctx: b.config.ctx,
GeneratedData: generatedData,
}, },
&awscommon.StepCreateTags{ &awscommon.StepCreateTags{
Tags: b.config.AMITags, Tags: b.config.AMITags,

View File

@ -9,10 +9,10 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/iam" "github.com/aws/aws-sdk-go/service/iam"
"github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer/builder"
awscommon "github.com/hashicorp/packer/builder/amazon/common" awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/communicator"
@ -177,6 +177,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
state.Put("awsSession", session) state.Put("awsSession", session)
state.Put("hook", hook) state.Put("hook", hook)
state.Put("ui", ui) state.Put("ui", ui)
generatedData := &builder.GeneratedData{State: state}
var instanceStep multistep.Step var instanceStep multistep.Step
@ -335,6 +336,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
SnapshotUsers: b.config.SnapshotUsers, SnapshotUsers: b.config.SnapshotUsers,
SnapshotGroups: b.config.SnapshotGroups, SnapshotGroups: b.config.SnapshotGroups,
Ctx: b.config.ctx, Ctx: b.config.ctx,
GeneratedData: generatedData,
}, },
&awscommon.StepCreateTags{ &awscommon.StepCreateTags{
Tags: b.config.AMITags, Tags: b.config.AMITags,

View File

@ -9,6 +9,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/hashicorp/packer/builder"
"os" "os"
"strings" "strings"
@ -248,6 +249,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
state.Put("awsSession", session) state.Put("awsSession", session)
state.Put("hook", hook) state.Put("hook", hook)
state.Put("ui", ui) state.Put("ui", ui)
generatedData := &builder.GeneratedData{State: state}
var instanceStep multistep.Step var instanceStep multistep.Step
@ -383,6 +385,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
SnapshotUsers: b.config.SnapshotUsers, SnapshotUsers: b.config.SnapshotUsers,
SnapshotGroups: b.config.SnapshotGroups, SnapshotGroups: b.config.SnapshotGroups,
Ctx: b.config.ctx, Ctx: b.config.ctx,
GeneratedData: generatedData,
}, },
&awscommon.StepCreateTags{ &awscommon.StepCreateTags{
Tags: b.config.AMITags, Tags: b.config.AMITags,

20
builder/generated_data.go Normal file
View File

@ -0,0 +1,20 @@
package builder
import "github.com/hashicorp/packer/helper/multistep"
// GeneratedData manages variables exported by a builder after
// it started. It uses the builder's multistep.StateBag internally, make sure it
// is not nil before calling any functions.
type GeneratedData struct {
// The builder's StateBag
State multistep.StateBag
}
func (gd *GeneratedData) Put(key string, data interface{}) {
genData := make(map[string]interface{})
if _, ok := gd.State.GetOk("generated_data"); ok {
genData = gd.State.Get("generated_data").(map[string]interface{})
}
genData[key] = data
gd.State.Put("generated_data", genData)
}

View File

@ -0,0 +1,30 @@
package builder
import (
"github.com/hashicorp/packer/helper/multistep"
"testing"
)
func TestGeneratedData_Put(t *testing.T) {
state := new(multistep.BasicStateBag)
generatedData := GeneratedData{
State: state,
}
expectedValue := "data value"
secondExpectedValue := "another data value"
generatedData.Put("data_key", expectedValue)
generatedData.Put("another_data_key", secondExpectedValue)
if _, ok := generatedData.State.GetOk("generated_data"); !ok {
t.Fatalf("BAD: StateBag should contain generated_data")
}
generatedDataState := generatedData.State.Get("generated_data").(map[string]interface{})
if generatedDataState["data_key"] != expectedValue {
t.Fatalf("Unexpected state for data_key: expected %#v got %#v\n", expectedValue, generatedDataState["data_key"])
}
if generatedDataState["another_data_key"] != secondExpectedValue {
t.Fatalf("Unexpected state for another_data_key: expected %#v got %#v\n", secondExpectedValue, generatedDataState["another_data_key"])
}
}

View File

@ -25,9 +25,12 @@ func (s *StepChrootProvision) Run(ctx context.Context, state multistep.StateBag)
CmdWrapper: wrappedCommand, CmdWrapper: wrappedCommand,
} }
// Loads hook data from builder's state, if it has been set.
hookData := common.PopulateProvisionHookData(state)
// Provision // Provision
log.Println("Running the provision hook") log.Println("Running the provision hook")
if err := hook.Run(ctx, packer.HookProvision, ui, comm, nil); err != nil { if err := hook.Run(ctx, packer.HookProvision, ui, comm, hookData); err != nil {
state.Put("error", err) state.Put("error", err)
return multistep.ActionHalt return multistep.ActionHalt
} }

View File

@ -52,7 +52,7 @@ func PopulateProvisionHookData(state multistep.StateBag) map[string]interface{}
// Read communicator data into hook data // Read communicator data into hook data
comm, ok := state.GetOk("communicator_config") comm, ok := state.GetOk("communicator_config")
if !ok { if !ok {
log.Printf("Unable to load config from state to populate provisionHookData") log.Printf("Unable to load communicator config from state to populate provisionHookData")
return hookData return hookData
} }
commConf := comm.(*communicator.Config) commConf := comm.(*communicator.Config)