From f73a673b991e144d0cd4ea615779de456424af8e Mon Sep 17 00:00:00 2001 From: Paul Meyer Date: Wed, 25 Mar 2020 18:23:10 +0000 Subject: [PATCH] Extract build steps generation Add tests for build step generation --- builder/azure/chroot/builder.go | 109 ++++++++++++++------------- builder/azure/chroot/builder_test.go | 103 +++++++++++++++++++++++++ 2 files changed, 161 insertions(+), 51 deletions(-) diff --git a/builder/azure/chroot/builder.go b/builder/azure/chroot/builder.go index 4995a860e..f6da3622f 100644 --- a/builder/azure/chroot/builder.go +++ b/builder/azure/chroot/builder.go @@ -117,6 +117,9 @@ type Builder struct { runner multistep.Runner } +// verify interface implementation +var _ packer.Builder = &Builder{} + func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { @@ -335,25 +338,48 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack state.Put("instance", info) + // Build the step array from the config + steps := buildsteps(b.config, info) + + // Run! + b.runner = common.NewRunner(steps, b.config.PackerConfig, ui) + b.runner.Run(ctx, state) + + // If there was an error, return that + if rawErr, ok := state.GetOk("error"); ok { + return nil, rawErr.(error) + } + + // Build the artifact and return it + artifact := &azcommon.Artifact{ + Resources: []string{b.config.ImageResourceID}, + BuilderIdValue: BuilderId, + StateData: map[string]interface{}{"generated_data": state.Get("generated_data")}, + } + + return artifact, nil +} + +func buildsteps(config Config, info *client.ComputeInfo) []multistep.Step { // Build the steps var steps []multistep.Step - if b.config.FromScratch { + if config.FromScratch { steps = append(steps, &StepCreateNewDisk{ SubscriptionID: info.SubscriptionID, ResourceGroup: info.ResourceGroupName, - DiskName: b.config.TemporaryOSDiskName, - DiskSizeGB: b.config.OSDiskSizeGB, - DiskStorageAccountType: b.config.OSDiskStorageAccountType, - HyperVGeneration: b.config.ImageHyperVGeneration, + DiskName: config.TemporaryOSDiskName, + DiskSizeGB: config.OSDiskSizeGB, + DiskStorageAccountType: config.OSDiskStorageAccountType, + HyperVGeneration: config.ImageHyperVGeneration, Location: info.Location, }) } else { - switch b.config.sourceType { + switch config.sourceType { case sourcePlatformImage: - if pi, err := client.ParsePlatformImageURN(b.config.Source); err == nil { + if pi, err := client.ParsePlatformImageURN(config.Source); err == nil { if strings.EqualFold(pi.Version, "latest") { steps = append(steps, &StepResolvePlatformImageVersion{ PlatformImage: pi, @@ -361,93 +387,74 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack }) } steps = append(steps, - &StepCreateNewDisk{ SubscriptionID: info.SubscriptionID, ResourceGroup: info.ResourceGroupName, - DiskName: b.config.TemporaryOSDiskName, - DiskSizeGB: b.config.OSDiskSizeGB, - DiskStorageAccountType: b.config.OSDiskStorageAccountType, - HyperVGeneration: b.config.ImageHyperVGeneration, + DiskName: config.TemporaryOSDiskName, + DiskSizeGB: config.OSDiskSizeGB, + DiskStorageAccountType: config.OSDiskStorageAccountType, + HyperVGeneration: config.ImageHyperVGeneration, Location: info.Location, PlatformImage: pi, - SkipCleanup: b.config.OSDiskSkipCleanup, + SkipCleanup: config.OSDiskSkipCleanup, }) } else { - panic("Unknown image source: " + b.config.Source) + panic("Unknown image source: " + config.Source + " err: " + err.Error()) } case sourceDisk: steps = append(steps, &StepVerifySourceDisk{ - SourceDiskResourceID: b.config.Source, + SourceDiskResourceID: config.Source, SubscriptionID: info.SubscriptionID, Location: info.Location, }, &StepCreateNewDisk{ SubscriptionID: info.SubscriptionID, ResourceGroup: info.ResourceGroupName, - DiskName: b.config.TemporaryOSDiskName, - DiskSizeGB: b.config.OSDiskSizeGB, - DiskStorageAccountType: b.config.OSDiskStorageAccountType, - HyperVGeneration: b.config.ImageHyperVGeneration, - SourceDiskResourceID: b.config.Source, + DiskName: config.TemporaryOSDiskName, + DiskSizeGB: config.OSDiskSizeGB, + DiskStorageAccountType: config.OSDiskStorageAccountType, + HyperVGeneration: config.ImageHyperVGeneration, + SourceDiskResourceID: config.Source, Location: info.Location, - SkipCleanup: b.config.OSDiskSkipCleanup, + SkipCleanup: config.OSDiskSkipCleanup, }) default: - panic(fmt.Errorf("Unknown source type: %+q", b.config.sourceType)) + panic(fmt.Errorf("Unknown source type: %+q", config.sourceType)) } } steps = append(steps, &StepAttachDisk{}, // uses os_disk_resource_id and sets 'device' in stateBag &chroot.StepPreMountCommands{ - Commands: b.config.PreMountCommands, + Commands: config.PreMountCommands, }, &StepMountDevice{ - MountOptions: b.config.MountOptions, - MountPartition: b.config.MountPartition, - MountPath: b.config.MountPath, + MountOptions: config.MountOptions, + MountPartition: config.MountPartition, + MountPath: config.MountPath, }, &chroot.StepPostMountCommands{ - Commands: b.config.PostMountCommands, + Commands: config.PostMountCommands, }, &chroot.StepMountExtra{ - ChrootMounts: b.config.ChrootMounts, + ChrootMounts: config.ChrootMounts, }, &chroot.StepCopyFiles{ - Files: b.config.CopyFiles, + Files: config.CopyFiles, }, &chroot.StepChrootProvision{}, &chroot.StepEarlyCleanup{}, &StepCreateImage{ - ImageResourceID: b.config.ImageResourceID, + ImageResourceID: config.ImageResourceID, ImageOSState: string(compute.Generalized), - OSDiskCacheType: b.config.OSDiskCacheType, - OSDiskStorageAccountType: b.config.OSDiskStorageAccountType, + OSDiskCacheType: config.OSDiskCacheType, + OSDiskStorageAccountType: config.OSDiskStorageAccountType, Location: info.Location, }, ) - // Run! - b.runner = common.NewRunner(steps, b.config.PackerConfig, ui) - b.runner.Run(ctx, state) - - // If there was an error, return that - if rawErr, ok := state.GetOk("error"); ok { - return nil, rawErr.(error) - } - - // Build the artifact and return it - artifact := &azcommon.Artifact{ - Resources: []string{b.config.ImageResourceID}, - BuilderIdValue: BuilderId, - StateData: map[string]interface{}{"generated_data": state.Get("generated_data")}, - } - - return artifact, nil + return steps } - -var _ packer.Builder = &Builder{} diff --git a/builder/azure/chroot/builder_test.go b/builder/azure/chroot/builder_test.go index 71cde7ed3..89781e822 100644 --- a/builder/azure/chroot/builder_test.go +++ b/builder/azure/chroot/builder_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute" + "github.com/hashicorp/packer/builder/azure/common/client" + "github.com/hashicorp/packer/helper/multistep" ) func TestBuilder_Prepare(t *testing.T) { @@ -70,3 +72,104 @@ func TestBuilder_Prepare(t *testing.T) { }) } } + +func Test_buildsteps(t *testing.T) { + info := &client.ComputeInfo{ + Location: "northpole", + Name: "unittestVM", + ResourceGroupName: "unittestResourceGroup", + SubscriptionID: "96854241-60c7-426d-9a27-3fdeec8957f4", + } + + tests := []struct { + name string + config Config + verify func([]multistep.Step, *testing.T) + }{ + { + name: "Source FromScrath creates empty disk", + config: Config{FromScratch: true}, + verify: func(steps []multistep.Step, _ *testing.T) { + for _, s := range steps { + if s, ok := s.(*StepCreateNewDisk); ok { + if s.SourceDiskResourceID == "" && + s.PlatformImage == nil { + return + } + t.Errorf("found misconfigured StepCreateNewDisk: %+v", s) + } + } + t.Error("did not find a StepCreateNewDisk") + }}, + { + name: "Source Platform image disk creation", + config: Config{Source: "publisher:offer:sku:version", sourceType: sourcePlatformImage}, + verify: func(steps []multistep.Step, _ *testing.T) { + for _, s := range steps { + if s, ok := s.(*StepCreateNewDisk); ok { + if s.SourceDiskResourceID == "" && + s.PlatformImage != nil && + s.PlatformImage.Publisher == "publisher" { + return + } + t.Errorf("found misconfigured StepCreateNewDisk: %+v", s) + } + } + t.Error("did not find a StepCreateNewDisk") + }}, + { + name: "Source Platform image with version latest adds StepResolvePlatformImageVersion", + config: Config{Source: "publisher:offer:sku:latest", sourceType: sourcePlatformImage}, + verify: func(steps []multistep.Step, _ *testing.T) { + for _, s := range steps { + if s, ok := s.(*StepResolvePlatformImageVersion); ok { + if s.PlatformImage != nil && + s.Location == info.Location { + return + } + t.Errorf("found misconfigured StepResolvePlatformImageVersion: %+v", s) + } + } + t.Error("did not find a StepResolvePlatformImageVersion") + }}, + { + name: "Source Disk adds correct disk creation", + config: Config{Source: "diskresourceid", sourceType: sourceDisk}, + verify: func(steps []multistep.Step, _ *testing.T) { + for _, s := range steps { + if s, ok := s.(*StepCreateNewDisk); ok { + if s.SourceDiskResourceID == "diskresourceid" && + s.PlatformImage == nil { + return + } + t.Errorf("found misconfigured StepCreateNewDisk: %+v", s) + } + } + t.Error("did not find a StepCreateNewDisk") + }}, + { + name: "Source disk adds StepVerifySourceDisk", + config: Config{Source: "diskresourceid", sourceType: sourceDisk}, + verify: func(steps []multistep.Step, _ *testing.T) { + for _, s := range steps { + if s, ok := s.(*StepVerifySourceDisk); ok { + if s.SourceDiskResourceID == "diskresourceid" && + s.Location == info.Location && + s.SubscriptionID == info.SubscriptionID { + return + } + t.Errorf("found misconfigured StepVerifySourceDisk: %+v", s) + } + } + t.Error("did not find a StepVerifySourceDisk") + }}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + withMetadataStub(func() { // ensure that values are taken from info, instead of retrieved again + got := buildsteps(tt.config, info) + tt.verify(got, t) + }) + }) + } +}