package common import ( "context" "io/ioutil" "os" "path/filepath" "testing" "time" "github.com/hashicorp/packer/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer" ) func testLocalOutputDir(t *testing.T) *LocalOutputDir { td, err := ioutil.TempDir("", "packer") if err != nil { t.Fatalf("err: %s", err) } os.RemoveAll(td) result := new(LocalOutputDir) result.SetOutputDir(td) return result } func testStepShutdownState(t *testing.T) multistep.StateBag { dir := testLocalOutputDir(t) if err := dir.MkdirAll(); err != nil { t.Fatalf("err: %s", err) } state := testState(t) state.Put("communicator", new(packersdk.MockCommunicator)) state.Put("dir", dir) state.Put("vmx_path", "foo") return state } func TestStepShutdown_impl(t *testing.T) { var _ multistep.Step = new(StepShutdown) } func TestStepShutdown_command(t *testing.T) { state := testStepShutdownState(t) step := new(StepShutdown) step.Command = "foo" step.Timeout = 10 * time.Second step.Testing = true comm := state.Get("communicator").(*packersdk.MockCommunicator) driver := state.Get("driver").(*DriverMock) driver.IsRunningResult = true // Set not running after some time go func() { time.Sleep(100 * time.Millisecond) driver.Lock() defer driver.Unlock() driver.IsRunningResult = false }() resultCh := make(chan multistep.StepAction, 1) go func() { resultCh <- step.Run(context.Background(), state) }() select { case <-resultCh: t.Fatal("should not have returned so quickly") case <-time.After(50 * time.Millisecond): } var action multistep.StepAction select { case action = <-resultCh: case <-time.After(5 * time.Second): t.Fatal("should've returned by now") } // Test the run if 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.StopCalled { t.Fatal("stop should not be called") } if !comm.StartCalled { t.Fatal("start should be called") } if comm.StartCmd.Command != "foo" { t.Fatalf("bad: %#v", comm.StartCmd.Command) } // Clean up the created test output directory dir := state.Get("dir").(*LocalOutputDir) if err := dir.RemoveAll(); err != nil { t.Fatalf("Error cleaning up directory: %s", err) } } func TestStepShutdown_noCommand(t *testing.T) { state := testStepShutdownState(t) step := new(StepShutdown) comm := state.Get("communicator").(*packersdk.MockCommunicator) 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.StopCalled { t.Fatal("stop should be called") } if driver.StopPath != "foo" { t.Fatal("should call with right path") } if comm.StartCalled { t.Fatal("start should not be called") } // Clean up the created test output directory dir := state.Get("dir").(*LocalOutputDir) if err := dir.RemoveAll(); err != nil { t.Fatalf("Error cleaning up directory: %s", err) } } func TestStepShutdown_locks(t *testing.T) { if os.Getenv("PACKER_ACC") == "" { t.Skip("This test is only run with PACKER_ACC=1 due to the requirement of access to the VMware binaries.") } state := testStepShutdownState(t) step := new(StepShutdown) step.Testing = true dir := state.Get("dir").(*LocalOutputDir) comm := state.Get("communicator").(*packersdk.MockCommunicator) driver := state.Get("driver").(*DriverMock) // Create some lock files lockPath := filepath.Join(dir.dir, "nope.lck") err := ioutil.WriteFile(lockPath, []byte("foo"), 0644) if err != nil { t.Fatalf("err: %s", err) } // Remove the lock file after a certain time go func() { time.Sleep(100 * time.Millisecond) os.Remove(lockPath) }() resultCh := make(chan multistep.StepAction, 1) go func() { resultCh <- step.Run(context.Background(), state) }() select { case <-resultCh: t.Fatal("should not have returned so quickly") case <-time.After(50 * time.Millisecond): } var action multistep.StepAction select { case action = <-resultCh: case <-time.After(300 * time.Millisecond): t.Fatal("should've returned by now") } // Test the run if 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.StopCalled { t.Fatal("stop should be called") } if driver.StopPath != "foo" { t.Fatal("should call with right path") } if comm.StartCalled { t.Fatal("start should not be called") } }