From 32148168bd3bc1e559c078c6262bbf0ad7da32dd Mon Sep 17 00:00:00 2001 From: DanHam Date: Sun, 8 Jul 2018 14:12:32 +0100 Subject: [PATCH] Introduce a new step to collate build artifact at the end of the build The new step collects together all the required build artifacts and places them in the output directory. * Reintroduce/add the code removed from step export to preserve the legacy export directory structure when skip_export is unset/false * Add a place holder for a future function that will move just the VHD files from the build directory to the output directory when skip_export is true * Add tests for current functionality and placeholder tests for future functions --- .../hyperv/common/step_collate_artifacts.go | 57 +++++++++++++ .../common/step_collate_artifacts_test.go | 82 +++++++++++++++++++ builder/hyperv/iso/builder.go | 4 + builder/hyperv/vmcx/builder.go | 4 + 4 files changed, 147 insertions(+) create mode 100644 builder/hyperv/common/step_collate_artifacts.go create mode 100644 builder/hyperv/common/step_collate_artifacts_test.go diff --git a/builder/hyperv/common/step_collate_artifacts.go b/builder/hyperv/common/step_collate_artifacts.go new file mode 100644 index 000000000..4f506218b --- /dev/null +++ b/builder/hyperv/common/step_collate_artifacts.go @@ -0,0 +1,57 @@ +package common + +import ( + "context" + "fmt" + + "github.com/hashicorp/packer/helper/multistep" + "github.com/hashicorp/packer/packer" +) + +type StepCollateArtifacts struct { + OutputDir string + SkipExport bool +} + +// Runs the step required to collate all build artifacts under the +// specified output directory +func (s *StepCollateArtifacts) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + driver := state.Get("driver").(Driver) + ui := state.Get("ui").(packer.Ui) + + ui.Say("Collating build artifacts...") + + if s.SkipExport { + // If the user has chosen to skip a full export of the VM the only + // artifacts that they are interested in will be the VHDs + + // TODO: Grab the disks from the build directory and place them in + // a folder named 'Virtual Hard Disks' under the output directory + } else { + // Get the full path to the export directory from the statebag + var exportPath string + if v, ok := state.GetOk("export_path"); ok { + exportPath = v.(string) + } + // The export process exports the VM into a folder named 'vm name' + // under the output directory. However, to maintain backwards + // compatibility, we now need to shuffle around the exported folders + // so the 'Snapshots', 'Virtual Hard Disks' and 'Virtual Machines' + // directories appear *directly* under . + // The empty '/' directory is removed + // when complete. + // The 'Snapshots' folder will not be moved into the output + // directory if it is empty. + err := driver.PreserveLegacyExportBehaviour(exportPath, s.OutputDir) + if err != nil { + // No need to halt here; Just warn the user instead + err = fmt.Errorf("WARNING: Error restoring legacy export dir structure: %s", err) + ui.Error(err.Error()) + } + } + + return multistep.ActionContinue +} + +// Cleanup does nothing +func (s *StepCollateArtifacts) Cleanup(state multistep.StateBag) {} diff --git a/builder/hyperv/common/step_collate_artifacts_test.go b/builder/hyperv/common/step_collate_artifacts_test.go new file mode 100644 index 000000000..d138fb87d --- /dev/null +++ b/builder/hyperv/common/step_collate_artifacts_test.go @@ -0,0 +1,82 @@ +package common + +import ( + "context" + "path/filepath" + "testing" + + "github.com/hashicorp/packer/helper/multistep" +) + +func TestStepCollateArtifacts_impl(t *testing.T) { + var _ multistep.Step = new(StepCollateArtifacts) +} + +func TestStepCollateArtifacts_exportedArtifacts(t *testing.T) { + state := testState(t) + step := new(StepCollateArtifacts) + + step.OutputDir = "foopath" + vmName := "foo" + + // Uses export path from the state bag + exportPath := filepath.Join(step.OutputDir, vmName) + state.Put("export_path", exportPath) + + driver := state.Get("driver").(*DriverMock) + + // Test the run + if action := step.Run(context.Background(), state); action != multistep.ActionContinue { + t.Fatalf("Bad action: %v", action) + } + if _, ok := state.GetOk("error"); ok { + t.Fatal("Should NOT have error") + } + + // Test the driver + if !driver.PreserveLegacyExportBehaviour_Called { + t.Fatal("Should have called PreserveLegacyExportBehaviour") + } + if driver.PreserveLegacyExportBehaviour_SrcPath != exportPath { + t.Fatalf("Should call with correct srcPath. Got: %s Wanted: %s", + driver.PreserveLegacyExportBehaviour_SrcPath, exportPath) + } + if driver.PreserveLegacyExportBehaviour_DstPath != step.OutputDir { + t.Fatalf("Should call with correct dstPath. Got: %s Wanted: %s", + driver.PreserveLegacyExportBehaviour_DstPath, step.OutputDir) + } + + // TODO: Create MoveCreatedVHDsToOutput func etc + // if driver.MoveCreatedVHDsToOutput_Called { + // t.Fatal("Should NOT have called MoveCreatedVHDsToOutput") + // } +} + +func TestStepCollateArtifacts_skipExportedArtifacts(t *testing.T) { + state := testState(t) + step := new(StepCollateArtifacts) + + // TODO: Needs the path to the main output directory + // outputDir := "foopath" + // Export has been skipped + step.SkipExport = true + + driver := state.Get("driver").(*DriverMock) + + // Test the run + if action := step.Run(context.Background(), state); action != multistep.ActionContinue { + t.Fatalf("Bad action: %v", action) + } + if _, ok := state.GetOk("error"); ok { + t.Fatal("Should NOT have error") + } + + // TODO: Create MoveCreatedVHDsToOutput func etc + // if !driver.MoveCreatedVHDsToOutput_Called { + // t.Fatal("Should have called MoveCreatedVHDsToOutput") + // } + + if driver.PreserveLegacyExportBehaviour_Called { + t.Fatal("Should NOT have called PreserveLegacyExportBehaviour") + } +} diff --git a/builder/hyperv/iso/builder.go b/builder/hyperv/iso/builder.go index e9ed68216..f6627b8fe 100644 --- a/builder/hyperv/iso/builder.go +++ b/builder/hyperv/iso/builder.go @@ -465,6 +465,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe OutputDir: b.config.OutputDir, SkipExport: b.config.SkipExport, }, + &hypervcommon.StepCollateArtifacts{ + OutputDir: b.config.OutputDir, + SkipExport: b.config.SkipExport, + }, // the clean up actions for each step will be executed reverse order } diff --git a/builder/hyperv/vmcx/builder.go b/builder/hyperv/vmcx/builder.go index 4a95a80dc..ada64faf0 100644 --- a/builder/hyperv/vmcx/builder.go +++ b/builder/hyperv/vmcx/builder.go @@ -482,6 +482,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe OutputDir: b.config.OutputDir, SkipExport: b.config.SkipExport, }, + &hypervcommon.StepCollateArtifacts{ + OutputDir: b.config.OutputDir, + SkipExport: b.config.SkipExport, + }, // the clean up actions for each step will be executed reverse order )