github.com/ryanslade/nomad@v0.2.4-0.20160128061903-fc95782f2089/client/driver/spawn/spawn_test.go (about)

     1  package spawn
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"os/exec"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/hashicorp/nomad/helper/testtask"
    13  )
    14  
    15  func TestMain(m *testing.M) {
    16  	if !testtask.Run() {
    17  		os.Exit(m.Run())
    18  	}
    19  }
    20  
    21  func TestSpawn_NoCmd(t *testing.T) {
    22  	t.Parallel()
    23  	tempFile := tempFileName(t)
    24  	defer os.Remove(tempFile)
    25  
    26  	spawn := NewSpawner(tempFile)
    27  	if err := spawn.Spawn(nil); err == nil {
    28  		t.Fatalf("Spawn() with no user command should fail")
    29  	}
    30  }
    31  
    32  func TestSpawn_InvalidCmd(t *testing.T) {
    33  	t.Parallel()
    34  	tempFile := tempFileName(t)
    35  	defer os.Remove(tempFile)
    36  
    37  	spawn := NewSpawner(tempFile)
    38  	spawn.SetCommand(exec.Command("foo")) // non-existent command
    39  	if err := spawn.Spawn(nil); err == nil {
    40  		t.Fatalf("Spawn() with an invalid command should fail")
    41  	}
    42  }
    43  
    44  func TestSpawn_SetsLogs(t *testing.T) {
    45  	t.Parallel()
    46  	tempFile := tempFileName(t)
    47  	defer os.Remove(tempFile)
    48  
    49  	spawn := NewSpawner(tempFile)
    50  	exp := "foo"
    51  	spawn.SetCommand(testCommand("echo", exp))
    52  
    53  	// Create file for stdout.
    54  	stdout := tempFileName(t)
    55  	defer os.Remove(stdout)
    56  	spawn.SetLogs(&Logs{Stdout: stdout})
    57  
    58  	if err := spawn.Spawn(nil); err != nil {
    59  		t.Fatalf("Spawn() failed: %v", err)
    60  	}
    61  
    62  	if res := spawn.Wait(); res.ExitCode != 0 && res.Err != nil {
    63  		t.Fatalf("Wait() returned %v, %v; want 0, nil", res.ExitCode, res.Err)
    64  	}
    65  
    66  	stdout2, err := os.Open(stdout)
    67  	if err != nil {
    68  		t.Fatalf("Open() failed: %v", err)
    69  	}
    70  
    71  	data, err := ioutil.ReadAll(stdout2)
    72  	if err != nil {
    73  		t.Fatalf("ReadAll() failed: %v", err)
    74  	}
    75  
    76  	act := strings.TrimSpace(string(data))
    77  	if act != exp {
    78  		t.Fatalf("Unexpected data written to stdout; got %v; want %v", act, exp)
    79  	}
    80  }
    81  
    82  func TestSpawn_Callback(t *testing.T) {
    83  	t.Parallel()
    84  	tempFile := tempFileName(t)
    85  	defer os.Remove(tempFile)
    86  
    87  	spawn := NewSpawner(tempFile)
    88  	spawn.SetCommand(testCommand("sleep", "1s"))
    89  
    90  	called := false
    91  	cbErr := fmt.Errorf("ERROR CB")
    92  	cb := func(_ int) error {
    93  		called = true
    94  		return cbErr
    95  	}
    96  
    97  	if err := spawn.Spawn(cb); err == nil {
    98  		t.Fatalf("Spawn(%#v) should have errored; want %v", cb, cbErr)
    99  	}
   100  
   101  	if !called {
   102  		t.Fatalf("Spawn(%#v) didn't call callback", cb)
   103  	}
   104  }
   105  
   106  func TestSpawn_ParentWaitExited(t *testing.T) {
   107  	t.Parallel()
   108  	tempFile := tempFileName(t)
   109  	defer os.Remove(tempFile)
   110  
   111  	spawn := NewSpawner(tempFile)
   112  	spawn.SetCommand(testCommand("echo", "foo"))
   113  	if err := spawn.Spawn(nil); err != nil {
   114  		t.Fatalf("Spawn() failed %v", err)
   115  	}
   116  
   117  	time.Sleep(1 * time.Second)
   118  
   119  	if res := spawn.Wait(); res.ExitCode != 0 && res.Err != nil {
   120  		t.Fatalf("Wait() returned %v, %v; want 0, nil", res.ExitCode, res.Err)
   121  	}
   122  }
   123  
   124  func TestSpawn_ParentWait(t *testing.T) {
   125  	t.Parallel()
   126  	tempFile := tempFileName(t)
   127  	defer os.Remove(tempFile)
   128  
   129  	spawn := NewSpawner(tempFile)
   130  	spawn.SetCommand(testCommand("sleep", "2s"))
   131  	if err := spawn.Spawn(nil); err != nil {
   132  		t.Fatalf("Spawn() failed %v", err)
   133  	}
   134  
   135  	if res := spawn.Wait(); res.ExitCode != 0 && res.Err != nil {
   136  		t.Fatalf("Wait() returned %v, %v; want 0, nil", res.ExitCode, res.Err)
   137  	}
   138  }
   139  
   140  func TestSpawn_NonParentWaitExited(t *testing.T) {
   141  	t.Parallel()
   142  	tempFile := tempFileName(t)
   143  	defer os.Remove(tempFile)
   144  
   145  	spawn := NewSpawner(tempFile)
   146  	spawn.SetCommand(testCommand("echo", "foo"))
   147  	if err := spawn.Spawn(nil); err != nil {
   148  		t.Fatalf("Spawn() failed %v", err)
   149  	}
   150  
   151  	time.Sleep(1 * time.Second)
   152  
   153  	// Force the wait to assume non-parent.
   154  	spawn.SpawnPpid = 0
   155  	if res := spawn.Wait(); res.ExitCode != 0 && res.Err != nil {
   156  		t.Fatalf("Wait() returned %v, %v; want 0, nil", res.ExitCode, res.Err)
   157  	}
   158  }
   159  
   160  func TestSpawn_NonParentWait(t *testing.T) {
   161  	t.Parallel()
   162  	tempFile := tempFileName(t)
   163  	defer os.Remove(tempFile)
   164  
   165  	spawn := NewSpawner(tempFile)
   166  	spawn.SetCommand(testCommand("sleep", "2s"))
   167  	if err := spawn.Spawn(nil); err != nil {
   168  		t.Fatalf("Spawn() failed %v", err)
   169  	}
   170  
   171  	// Need to wait on the spawner, otherwise it becomes a zombie and the test
   172  	// only finishes after the init process cleans it. This speeds that up.
   173  	go func() {
   174  		time.Sleep(3 * time.Second)
   175  		if _, err := spawn.spawn.Wait(); err != nil {
   176  			t.FailNow()
   177  		}
   178  	}()
   179  
   180  	// Force the wait to assume non-parent.
   181  	spawn.SpawnPpid = 0
   182  	if res := spawn.Wait(); res.ExitCode != 0 && res.Err != nil {
   183  		t.Fatalf("Wait() returned %v, %v; want 0, nil", res.ExitCode, res.Err)
   184  	}
   185  }
   186  
   187  func TestSpawn_DeadSpawnDaemon_Parent(t *testing.T) {
   188  	t.Parallel()
   189  	tempFile := tempFileName(t)
   190  	defer os.Remove(tempFile)
   191  
   192  	var spawnPid int
   193  	cb := func(pid int) error {
   194  		spawnPid = pid
   195  		return nil
   196  	}
   197  
   198  	spawn := NewSpawner(tempFile)
   199  	spawn.SetCommand(testCommand("sleep", "5s"))
   200  	if err := spawn.Spawn(cb); err != nil {
   201  		t.Fatalf("Spawn() errored: %v", err)
   202  	}
   203  
   204  	proc, err := os.FindProcess(spawnPid)
   205  	if err != nil {
   206  		t.FailNow()
   207  	}
   208  
   209  	if err := proc.Kill(); err != nil {
   210  		t.FailNow()
   211  	}
   212  
   213  	if _, err := proc.Wait(); err != nil {
   214  		t.FailNow()
   215  	}
   216  
   217  	if res := spawn.Wait(); res.Err == nil {
   218  		t.Fatalf("Wait() should have failed: %v", res.Err)
   219  	}
   220  }
   221  
   222  func TestSpawn_DeadSpawnDaemon_NonParent(t *testing.T) {
   223  	t.Parallel()
   224  	tempFile := tempFileName(t)
   225  	defer os.Remove(tempFile)
   226  
   227  	var spawnPid int
   228  	cb := func(pid int) error {
   229  		spawnPid = pid
   230  		return nil
   231  	}
   232  
   233  	spawn := NewSpawner(tempFile)
   234  	spawn.SetCommand(testCommand("sleep", "2s"))
   235  	if err := spawn.Spawn(cb); err != nil {
   236  		t.Fatalf("Spawn() errored: %v", err)
   237  	}
   238  
   239  	proc, err := os.FindProcess(spawnPid)
   240  	if err != nil {
   241  		t.FailNow()
   242  	}
   243  
   244  	if err := proc.Kill(); err != nil {
   245  		t.FailNow()
   246  	}
   247  
   248  	if _, err := proc.Wait(); err != nil {
   249  		t.FailNow()
   250  	}
   251  
   252  	// Force the wait to assume non-parent.
   253  	spawn.SpawnPpid = 0
   254  	if res := spawn.Wait(); res.Err == nil {
   255  		t.Fatalf("Wait() should have failed: %v", res.Err)
   256  	}
   257  }
   258  
   259  func TestSpawn_Valid_TaskRunning(t *testing.T) {
   260  	t.Parallel()
   261  	tempFile := tempFileName(t)
   262  	defer os.Remove(tempFile)
   263  
   264  	spawn := NewSpawner(tempFile)
   265  	spawn.SetCommand(testCommand("sleep", "2s"))
   266  	if err := spawn.Spawn(nil); err != nil {
   267  		t.Fatalf("Spawn() failed %v", err)
   268  	}
   269  
   270  	if err := spawn.Valid(); err != nil {
   271  		t.Fatalf("Valid() failed: %v", err)
   272  	}
   273  
   274  	if res := spawn.Wait(); res.Err != nil {
   275  		t.Fatalf("Wait() failed: %v", res.Err)
   276  	}
   277  }
   278  
   279  func TestSpawn_Valid_TaskExit_ExitCode(t *testing.T) {
   280  	t.Parallel()
   281  	tempFile := tempFileName(t)
   282  	defer os.Remove(tempFile)
   283  
   284  	spawn := NewSpawner(tempFile)
   285  	spawn.SetCommand(testCommand("echo", "foo"))
   286  	if err := spawn.Spawn(nil); err != nil {
   287  		t.Fatalf("Spawn() failed %v", err)
   288  	}
   289  
   290  	if res := spawn.Wait(); res.Err != nil {
   291  		t.Fatalf("Wait() failed: %v", res.Err)
   292  	}
   293  
   294  	if err := spawn.Valid(); err != nil {
   295  		t.Fatalf("Valid() failed: %v", err)
   296  	}
   297  }
   298  
   299  func TestSpawn_Valid_TaskExit_NoExitCode(t *testing.T) {
   300  	t.Parallel()
   301  	tempFile := tempFileName(t)
   302  	defer os.Remove(tempFile)
   303  
   304  	spawn := NewSpawner(tempFile)
   305  	spawn.SetCommand(testCommand("echo", "foo"))
   306  	if err := spawn.Spawn(nil); err != nil {
   307  		t.Fatalf("Spawn() failed %v", err)
   308  	}
   309  
   310  	if res := spawn.Wait(); res.Err != nil {
   311  		t.Fatalf("Wait() failed: %v", res.Err)
   312  	}
   313  
   314  	// Delete the file so that it can't find the exit code.
   315  	os.Remove(tempFile)
   316  
   317  	if err := spawn.Valid(); err == nil {
   318  		t.Fatalf("Valid() should have failed")
   319  	}
   320  }
   321  
   322  func tempFileName(t *testing.T) string {
   323  	f, err := ioutil.TempFile("", "")
   324  	if err != nil {
   325  		t.Fatalf("TempFile() failed")
   326  	}
   327  	defer f.Close()
   328  	return f.Name()
   329  }
   330  
   331  func testCommand(args ...string) *exec.Cmd {
   332  	cmd := exec.Command(testtask.Path(), args...)
   333  	testtask.SetCmdEnv(cmd)
   334  	return cmd
   335  }