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 | 199 ++++++++++++++------------- builder/azure/chroot/builder_test.go | 103 ++++++++++++++ 2 files changed, 206 insertions(+), 96 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,101 +338,8 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack state.Put("instance", info) - // Build the steps - var steps []multistep.Step - - if b.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, - Location: info.Location, - }) - } else { - switch b.config.sourceType { - case sourcePlatformImage: - - if pi, err := client.ParsePlatformImageURN(b.config.Source); err == nil { - if strings.EqualFold(pi.Version, "latest") { - steps = append(steps, &StepResolvePlatformImageVersion{ - PlatformImage: pi, - Location: info.Location, - }) - } - 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, - Location: info.Location, - PlatformImage: pi, - - SkipCleanup: b.config.OSDiskSkipCleanup, - }) - } else { - panic("Unknown image source: " + b.config.Source) - } - case sourceDisk: - steps = append(steps, - &StepVerifySourceDisk{ - SourceDiskResourceID: b.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, - Location: info.Location, - - SkipCleanup: b.config.OSDiskSkipCleanup, - }) - default: - panic(fmt.Errorf("Unknown source type: %+q", b.config.sourceType)) - } - } - - steps = append(steps, - &StepAttachDisk{}, // uses os_disk_resource_id and sets 'device' in stateBag - &chroot.StepPreMountCommands{ - Commands: b.config.PreMountCommands, - }, - &StepMountDevice{ - MountOptions: b.config.MountOptions, - MountPartition: b.config.MountPartition, - MountPath: b.config.MountPath, - }, - &chroot.StepPostMountCommands{ - Commands: b.config.PostMountCommands, - }, - &chroot.StepMountExtra{ - ChrootMounts: b.config.ChrootMounts, - }, - &chroot.StepCopyFiles{ - Files: b.config.CopyFiles, - }, - &chroot.StepChrootProvision{}, - &chroot.StepEarlyCleanup{}, - &StepCreateImage{ - ImageResourceID: b.config.ImageResourceID, - ImageOSState: string(compute.Generalized), - OSDiskCacheType: b.config.OSDiskCacheType, - OSDiskStorageAccountType: b.config.OSDiskStorageAccountType, - Location: info.Location, - }, - ) + // Build the step array from the config + steps := buildsteps(b.config, info) // Run! b.runner = common.NewRunner(steps, b.config.PackerConfig, ui) @@ -450,4 +360,101 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack return artifact, nil } -var _ packer.Builder = &Builder{} +func buildsteps(config Config, info *client.ComputeInfo) []multistep.Step { + // Build the steps + var steps []multistep.Step + + if config.FromScratch { + steps = append(steps, + &StepCreateNewDisk{ + SubscriptionID: info.SubscriptionID, + ResourceGroup: info.ResourceGroupName, + DiskName: config.TemporaryOSDiskName, + DiskSizeGB: config.OSDiskSizeGB, + DiskStorageAccountType: config.OSDiskStorageAccountType, + HyperVGeneration: config.ImageHyperVGeneration, + Location: info.Location, + }) + } else { + switch config.sourceType { + case sourcePlatformImage: + + if pi, err := client.ParsePlatformImageURN(config.Source); err == nil { + if strings.EqualFold(pi.Version, "latest") { + steps = append(steps, &StepResolvePlatformImageVersion{ + PlatformImage: pi, + Location: info.Location, + }) + } + steps = append(steps, + &StepCreateNewDisk{ + SubscriptionID: info.SubscriptionID, + ResourceGroup: info.ResourceGroupName, + DiskName: config.TemporaryOSDiskName, + DiskSizeGB: config.OSDiskSizeGB, + DiskStorageAccountType: config.OSDiskStorageAccountType, + HyperVGeneration: config.ImageHyperVGeneration, + Location: info.Location, + PlatformImage: pi, + + SkipCleanup: config.OSDiskSkipCleanup, + }) + } else { + panic("Unknown image source: " + config.Source + " err: " + err.Error()) + } + case sourceDisk: + steps = append(steps, + &StepVerifySourceDisk{ + SourceDiskResourceID: config.Source, + SubscriptionID: info.SubscriptionID, + Location: info.Location, + }, + &StepCreateNewDisk{ + SubscriptionID: info.SubscriptionID, + ResourceGroup: info.ResourceGroupName, + DiskName: config.TemporaryOSDiskName, + DiskSizeGB: config.OSDiskSizeGB, + DiskStorageAccountType: config.OSDiskStorageAccountType, + HyperVGeneration: config.ImageHyperVGeneration, + SourceDiskResourceID: config.Source, + Location: info.Location, + + SkipCleanup: config.OSDiskSkipCleanup, + }) + default: + 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: config.PreMountCommands, + }, + &StepMountDevice{ + MountOptions: config.MountOptions, + MountPartition: config.MountPartition, + MountPath: config.MountPath, + }, + &chroot.StepPostMountCommands{ + Commands: config.PostMountCommands, + }, + &chroot.StepMountExtra{ + ChrootMounts: config.ChrootMounts, + }, + &chroot.StepCopyFiles{ + Files: config.CopyFiles, + }, + &chroot.StepChrootProvision{}, + &chroot.StepEarlyCleanup{}, + &StepCreateImage{ + ImageResourceID: config.ImageResourceID, + ImageOSState: string(compute.Generalized), + OSDiskCacheType: config.OSDiskCacheType, + OSDiskStorageAccountType: config.OSDiskStorageAccountType, + Location: info.Location, + }, + ) + + return steps +} 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) + }) + }) + } +}