From 20a074b4ee3cf348ab5303e8496547826eb59e04 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 13 Dec 2013 18:26:00 -0800 Subject: [PATCH] builder/googlecompute: StepCreateImage --- builder/googlecompute/builder.go | 2 +- builder/googlecompute/step_create_image.go | 28 +++--- .../googlecompute/step_create_image_test.go | 96 +++++++++++++++++++ 3 files changed, 114 insertions(+), 12 deletions(-) create mode 100644 builder/googlecompute/step_create_image_test.go diff --git a/builder/googlecompute/builder.go b/builder/googlecompute/builder.go index 83869de45..6f823a4e9 100644 --- a/builder/googlecompute/builder.go +++ b/builder/googlecompute/builder.go @@ -59,9 +59,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, new(common.StepProvision), new(StepUpdateGsutil), + new(StepCreateImage), } /* - new(stepCreateImage), new(stepUploadImage), new(stepRegisterImage), }*/ diff --git a/builder/googlecompute/step_create_image.go b/builder/googlecompute/step_create_image.go index 1b964a48e..249b59186 100644 --- a/builder/googlecompute/step_create_image.go +++ b/builder/googlecompute/step_create_image.go @@ -8,39 +8,45 @@ import ( "github.com/mitchellh/packer/packer" ) -// stepCreateImage represents a Packer build step that creates GCE machine +// StepCreateImage represents a Packer build step that creates GCE machine // images. -type stepCreateImage int +type StepCreateImage int // Run executes the Packer build step that creates a GCE machine image. // // Currently the only way to create a GCE image is to run the gcimagebundle // command on the running GCE instance. -func (s *stepCreateImage) Run(state multistep.StateBag) multistep.StepAction { - var ( - config = state.Get("config").(*Config) - comm = state.Get("communicator").(packer.Communicator) - sudoPrefix = "" - ui = state.Get("ui").(packer.Ui) - ) - ui.Say("Creating image...") +func (s *StepCreateImage) Run(state multistep.StateBag) multistep.StepAction { + config := state.Get("config").(*Config) + comm := state.Get("communicator").(packer.Communicator) + ui := state.Get("ui").(packer.Ui) + + sudoPrefix := "" if config.SSHUsername != "root" { sudoPrefix = "sudo " } + imageFilename := fmt.Sprintf("%s.tar.gz", config.ImageName) imageBundleCmd := "/usr/bin/gcimagebundle -d /dev/sda -o /tmp/" + + ui.Say("Creating image...") cmd := new(packer.RemoteCmd) cmd.Command = fmt.Sprintf("%s%s --output_file_name %s", sudoPrefix, imageBundleCmd, imageFilename) err := cmd.StartWithUi(comm, ui) + if err == nil && cmd.ExitStatus != 0 { + err = fmt.Errorf( + "gcimagebundle exited with non-zero exit status: %d", cmd.ExitStatus) + } if err != nil { err := fmt.Errorf("Error creating image: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } + state.Put("image_file_name", filepath.Join("/tmp", imageFilename)) return multistep.ActionContinue } -func (s *stepCreateImage) Cleanup(state multistep.StateBag) {} +func (s *StepCreateImage) Cleanup(state multistep.StateBag) {} diff --git a/builder/googlecompute/step_create_image_test.go b/builder/googlecompute/step_create_image_test.go new file mode 100644 index 000000000..0e9b1455d --- /dev/null +++ b/builder/googlecompute/step_create_image_test.go @@ -0,0 +1,96 @@ +package googlecompute + +import ( + "strings" + "testing" + + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" +) + +func TestStepCreateImage_impl(t *testing.T) { + var _ multistep.Step = new(StepCreateImage) +} + +func TestStepCreateImage(t *testing.T) { + state := testState(t) + step := new(StepCreateImage) + defer step.Cleanup(state) + + comm := new(packer.MockCommunicator) + state.Put("communicator", comm) + + // run the step + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + + // Verify + if !comm.StartCalled { + t.Fatal("start should be called") + } + if strings.HasPrefix(comm.StartCmd.Command, "sudo") { + t.Fatal("should not sudo") + } + if !strings.Contains(comm.StartCmd.Command, "gcimagebundle") { + t.Fatalf("bad command: %#v", comm.StartCmd.Command) + } + + if _, ok := state.GetOk("image_file_name"); !ok { + t.Fatal("should have image") + } +} + +func TestStepCreateImage_badExitStatus(t *testing.T) { + state := testState(t) + step := new(StepCreateImage) + defer step.Cleanup(state) + + comm := new(packer.MockCommunicator) + comm.StartExitStatus = 12 + state.Put("communicator", comm) + + // run the step + if action := step.Run(state); action != multistep.ActionHalt { + t.Fatalf("bad action: %#v", action) + } + + if _, ok := state.GetOk("error"); !ok { + t.Fatal("should have error") + } + if _, ok := state.GetOk("image_file_name"); ok { + t.Fatal("should NOT have image") + } +} + +func TestStepCreateImage_nonRoot(t *testing.T) { + state := testState(t) + step := new(StepCreateImage) + defer step.Cleanup(state) + + comm := new(packer.MockCommunicator) + state.Put("communicator", comm) + + config := state.Get("config").(*Config) + config.SSHUsername = "bob" + + // run the step + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + + // Verify + if !comm.StartCalled { + t.Fatal("start should be called") + } + if !strings.HasPrefix(comm.StartCmd.Command, "sudo") { + t.Fatal("should sudo") + } + if !strings.Contains(comm.StartCmd.Command, "gcimagebundle") { + t.Fatalf("bad command: %#v", comm.StartCmd.Command) + } + + if _, ok := state.GetOk("image_file_name"); !ok { + t.Fatal("should have image") + } +}