github.com/smithx10/nomad@v0.9.1-rc1/client/allocrunner/taskrunner/logmon_hook_unix_test.go (about)

     1  // +build !windows
     2  
     3  package taskrunner
     4  
     5  import (
     6  	"context"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"syscall"
    12  	"testing"
    13  
    14  	"github.com/hashicorp/nomad/client/allocrunner/interfaces"
    15  	"github.com/hashicorp/nomad/helper/testlog"
    16  	"github.com/hashicorp/nomad/nomad/mock"
    17  	"github.com/hashicorp/nomad/testutil"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  // TestTaskRunner_LogmonHook_StartCrashStop simulates logmon crashing while the
    22  // Nomad client is restarting and asserts failing to reattach to logmon causes
    23  // nomad to spawn a new logmon.
    24  func TestTaskRunner_LogmonHook_StartCrashStop(t *testing.T) {
    25  	t.Parallel()
    26  
    27  	alloc := mock.BatchAlloc()
    28  	task := alloc.Job.TaskGroups[0].Tasks[0]
    29  
    30  	dir, err := ioutil.TempDir("", "nomadtest")
    31  	require.NoError(t, err)
    32  	defer func() {
    33  		require.NoError(t, os.RemoveAll(dir))
    34  	}()
    35  
    36  	hookConf := newLogMonHookConfig(task.Name, dir)
    37  	hook := newLogMonHook(hookConf, testlog.HCLogger(t))
    38  
    39  	req := interfaces.TaskPrestartRequest{
    40  		Task: task,
    41  	}
    42  	resp := interfaces.TaskPrestartResponse{}
    43  
    44  	// First start
    45  	require.NoError(t, hook.Prestart(context.Background(), &req, &resp))
    46  	defer hook.Stop(context.Background(), nil, nil)
    47  
    48  	origState := resp.State
    49  	origHookData := resp.State[logmonReattachKey]
    50  	require.NotEmpty(t, origHookData)
    51  
    52  	// Pluck PID out of reattach synthesize a crash
    53  	reattach := struct {
    54  		Pid int
    55  	}{}
    56  	require.NoError(t, json.Unmarshal([]byte(origHookData), &reattach))
    57  	pid := reattach.Pid
    58  	require.NotZero(t, pid)
    59  
    60  	proc, _ := os.FindProcess(pid)
    61  
    62  	// Assert logmon is running
    63  	require.NoError(t, proc.Signal(syscall.Signal(0)))
    64  
    65  	// Kill it
    66  	require.NoError(t, proc.Signal(os.Kill))
    67  
    68  	// Since signals are asynchronous wait for the process to die
    69  	testutil.WaitForResult(func() (bool, error) {
    70  		err := proc.Signal(syscall.Signal(0))
    71  		return err != nil, fmt.Errorf("pid %d still running", pid)
    72  	}, func(err error) {
    73  		require.NoError(t, err)
    74  	})
    75  
    76  	// Running prestart again should return a recoverable error with no
    77  	// reattach config to cause the task to be restarted with a new logmon.
    78  	req.PreviousState = map[string]string{
    79  		logmonReattachKey: origHookData,
    80  	}
    81  	resp = interfaces.TaskPrestartResponse{}
    82  	err = hook.Prestart(context.Background(), &req, &resp)
    83  	require.NoError(t, err)
    84  	require.NotEqual(t, origState, resp.State)
    85  
    86  	// Running stop should shutdown logmon
    87  	require.NoError(t, hook.Stop(context.Background(), nil, nil))
    88  }