diff --git a/builder/googlecompute/builder.go b/builder/googlecompute/builder.go index e314a8786..83869de45 100644 --- a/builder/googlecompute/builder.go +++ b/builder/googlecompute/builder.go @@ -58,9 +58,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SSHWaitTimeout: 5 * time.Minute, }, new(common.StepProvision), + new(StepUpdateGsutil), } /* - new(stepUpdateGsutil), new(stepCreateImage), new(stepUploadImage), new(stepRegisterImage), diff --git a/builder/googlecompute/step_update_gsutil.go b/builder/googlecompute/step_update_gsutil.go index d2e8be741..9062c500f 100644 --- a/builder/googlecompute/step_update_gsutil.go +++ b/builder/googlecompute/step_update_gsutil.go @@ -7,9 +7,9 @@ import ( "github.com/mitchellh/packer/packer" ) -// stepUpdateGsutil represents a Packer build step that updates the gsutil +// StepUpdateGsutil represents a Packer build step that updates the gsutil // utility to the latest version available. -type stepUpdateGsutil int +type StepUpdateGsutil int // Run executes the Packer build step that updates the gsutil utility to the // latest version available. @@ -17,29 +17,36 @@ type stepUpdateGsutil int // This step is required to prevent the image creation process from hanging; // the image creation process utilizes the gcimagebundle cli tool which will // prompt to update gsutil if a newer version is available. -func (s *stepUpdateGsutil) 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("Updating gsutil...") +func (s *StepUpdateGsutil) Run(state multistep.StateBag) multistep.StepAction { + comm := state.Get("communicator").(packer.Communicator) + config := state.Get("config").(*Config) + ui := state.Get("ui").(packer.Ui) + + sudoPrefix := "" + if config.SSHUsername != "root" { sudoPrefix = "sudo " } + gsutilUpdateCmd := "/usr/local/bin/gsutil update -n -f" cmd := new(packer.RemoteCmd) cmd.Command = fmt.Sprintf("%s%s", sudoPrefix, gsutilUpdateCmd) + + ui.Say("Updating gsutil...") err := cmd.StartWithUi(comm, ui) + if err == nil && cmd.ExitStatus != 0 { + err = fmt.Errorf( + "gsutil update exited with non-zero exit status: %d", cmd.ExitStatus) + } if err != nil { err := fmt.Errorf("Error updating gsutil: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } + return multistep.ActionContinue } // Cleanup. -func (s *stepUpdateGsutil) Cleanup(state multistep.StateBag) {} +func (s *StepUpdateGsutil) Cleanup(state multistep.StateBag) {} diff --git a/builder/googlecompute/step_update_gsutil_test.go b/builder/googlecompute/step_update_gsutil_test.go new file mode 100644 index 000000000..966857f93 --- /dev/null +++ b/builder/googlecompute/step_update_gsutil_test.go @@ -0,0 +1,85 @@ +package googlecompute + +import ( + "strings" + "testing" + + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" +) + +func TestStepUpdateGsutil_impl(t *testing.T) { + var _ multistep.Step = new(StepUpdateGsutil) +} + +func TestStepUpdateGsutil(t *testing.T) { + state := testState(t) + step := new(StepUpdateGsutil) + 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, "gsutil update") { + t.Fatalf("bad command: %#v", comm.StartCmd.Command) + } +} + +func TestStepUpdateGsutil_badExitStatus(t *testing.T) { + state := testState(t) + step := new(StepUpdateGsutil) + 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") + } +} + +func TestStepUpdateGsutil_nonRoot(t *testing.T) { + state := testState(t) + step := new(StepUpdateGsutil) + 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, "gsutil update") { + t.Fatalf("bad command: %#v", comm.StartCmd.Command) + } +}