github.com/bigcommerce/nomad@v0.9.3-bc/e2e/taskevents/taskevents.go (about)

     1  package taskevents
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"time"
     7  
     8  	"github.com/hashicorp/nomad/api"
     9  	"github.com/hashicorp/nomad/e2e/framework"
    10  	"github.com/hashicorp/nomad/testutil"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/hashicorp/nomad/e2e/e2eutil"
    14  	"github.com/hashicorp/nomad/helper/uuid"
    15  )
    16  
    17  type TaskEventsTest struct {
    18  	framework.TC
    19  	jobIds []string
    20  }
    21  
    22  func init() {
    23  	framework.AddSuites(&framework.TestSuite{
    24  		Component:   "TaskEvents",
    25  		CanRunLocal: true,
    26  		Cases: []framework.TestCase{
    27  			new(TaskEventsTest),
    28  		},
    29  	})
    30  }
    31  
    32  func (tc *TaskEventsTest) BeforeAll(f *framework.F) {
    33  	e2eutil.WaitForLeader(f.T(), tc.Nomad())
    34  	e2eutil.WaitForNodesReady(f.T(), tc.Nomad(), 1)
    35  }
    36  
    37  func (tc *TaskEventsTest) AfterEach(f *framework.F) {
    38  	nomadClient := tc.Nomad()
    39  	jobs := nomadClient.Jobs()
    40  	// Stop all jobs in test
    41  	for _, id := range tc.jobIds {
    42  		jobs.Deregister(id, true, nil)
    43  	}
    44  	// Garbage collect
    45  	nomadClient.System().GarbageCollect()
    46  }
    47  
    48  func formatEvents(events []*api.TaskEvent) string {
    49  	estrs := make([]string, len(events))
    50  	for i, e := range events {
    51  		estrs[i] = fmt.Sprintf("%2d %-20s fail=%t msg=> %s", i, e.Type, e.FailsTask, e.DisplayMessage)
    52  	}
    53  	return strings.Join(estrs, "\n")
    54  }
    55  
    56  // waitUntilEvents submits a job and then waits until the expected number of
    57  // events exist.
    58  //
    59  // The job name is used to load the job file from "input/${job}.nomad", and
    60  // events are only inspected for tasks named the same as the job. That task's
    61  // state is returned as well as the last allocation received.
    62  func (tc *TaskEventsTest) waitUntilEvents(f *framework.F, jobName string, numEvents int) (*api.Allocation, *api.TaskState) {
    63  	t := f.T()
    64  	nomadClient := tc.Nomad()
    65  	uuid := uuid.Generate()
    66  	uniqJobId := jobName + uuid[0:8]
    67  	tc.jobIds = append(tc.jobIds, uniqJobId)
    68  
    69  	jobFile := fmt.Sprintf("taskevents/input/%s.nomad", jobName)
    70  	allocs := e2eutil.RegisterAndWaitForAllocs(f.T(), nomadClient, jobFile, uniqJobId)
    71  
    72  	require.Len(t, allocs, 1)
    73  	allocID := allocs[0].ID
    74  	qo := &api.QueryOptions{
    75  		WaitTime: time.Second,
    76  	}
    77  
    78  	// Capture state outside of wait to ease assertions once expected
    79  	// number of events have been received.
    80  	var alloc *api.Allocation
    81  	var taskState *api.TaskState
    82  
    83  	testutil.WaitForResultRetries(10, func() (bool, error) {
    84  		a, meta, err := nomadClient.Allocations().Info(allocID, qo)
    85  		if err != nil {
    86  			return false, err
    87  		}
    88  
    89  		qo.WaitIndex = meta.LastIndex
    90  
    91  		// Capture alloc and task state
    92  		alloc = a
    93  		taskState = a.TaskStates[jobName]
    94  		if taskState == nil {
    95  			return false, fmt.Errorf("task state not found for %s", jobName)
    96  		}
    97  
    98  		// Assert expected number of task events
    99  		if len(taskState.Events) != numEvents {
   100  			return false, fmt.Errorf("expected %d task events but found %d\n%s",
   101  				numEvents, len(taskState.Events), formatEvents(taskState.Events),
   102  			)
   103  		}
   104  
   105  		return true, nil
   106  	}, func(err error) {
   107  		t.Fatalf("task events error: %v", err)
   108  	})
   109  
   110  	return alloc, taskState
   111  }
   112  
   113  func (tc *TaskEventsTest) TestTaskEvents_SimpleBatch(f *framework.F) {
   114  	t := f.T()
   115  	_, taskState := tc.waitUntilEvents(f, "simple_batch", 4)
   116  	events := taskState.Events
   117  
   118  	// Assert task did not fail
   119  	require.Falsef(t, taskState.Failed, "task unexpectedly failed after %d events\n%s",
   120  		len(events), formatEvents(events),
   121  	)
   122  
   123  	// Assert the expected type of events were emitted in a specific order
   124  	// (based on v0.8.6)
   125  	require.Equal(t, api.TaskReceived, events[0].Type)
   126  	require.Equal(t, api.TaskSetup, events[1].Type)
   127  	require.Equal(t, api.TaskStarted, events[2].Type)
   128  	require.Equal(t, api.TaskTerminated, events[3].Type)
   129  }
   130  
   131  func (tc *TaskEventsTest) TestTaskEvents_FailedBatch(f *framework.F) {
   132  	t := f.T()
   133  	_, taskState := tc.waitUntilEvents(f, "failed_batch", 4)
   134  	events := taskState.Events
   135  
   136  	// Assert task did fail
   137  	require.Truef(t, taskState.Failed, "task unexpectedly succeeded after %d events\n%s",
   138  		len(events), formatEvents(events),
   139  	)
   140  
   141  	// Assert the expected type of events were emitted in a specific order
   142  	// (based on v0.8.6)
   143  	require.Equal(t, api.TaskReceived, events[0].Type)
   144  	require.Equal(t, api.TaskSetup, events[1].Type)
   145  	require.Equal(t, api.TaskDriverFailure, events[2].Type)
   146  	require.Equal(t, api.TaskNotRestarting, events[3].Type)
   147  	require.True(t, events[3].FailsTask)
   148  }
   149  
   150  // TestTaskEvents_CompletedLeader asserts the proper events are emitted for a
   151  // non-leader task when its leader task completes.
   152  func (tc *TaskEventsTest) TestTaskEvents_CompletedLeader(f *framework.F) {
   153  	t := f.T()
   154  	_, taskState := tc.waitUntilEvents(f, "completed_leader", 7)
   155  	events := taskState.Events
   156  
   157  	// Assert task did not fail
   158  	require.Falsef(t, taskState.Failed, "task unexpectedly failed after %d events\n%s",
   159  		len(events), formatEvents(events),
   160  	)
   161  
   162  	// Assert the expected type of events were emitted in a specific order
   163  	require.Equal(t, api.TaskReceived, events[0].Type)
   164  	require.Equal(t, api.TaskSetup, events[1].Type)
   165  	require.Equal(t, api.TaskStarted, events[2].Type)
   166  	require.Equal(t, api.TaskLeaderDead, events[3].Type)
   167  	require.Equal(t, api.TaskKilling, events[4].Type)
   168  	require.Equal(t, api.TaskTerminated, events[5].Type)
   169  	require.Equal(t, api.TaskKilled, events[6].Type)
   170  }
   171  
   172  // TestTaskEvents_FailedSibling asserts the proper events are emitted for a
   173  // task when another task in its task group fails.
   174  func (tc *TaskEventsTest) TestTaskEvents_FailedSibling(f *framework.F) {
   175  	t := f.T()
   176  	alloc, taskState := tc.waitUntilEvents(f, "failed_sibling", 7)
   177  	events := taskState.Events
   178  
   179  	// Just because a sibling failed doesn't mean this task fails. It
   180  	// should exit cleanly. (same as in v0.8.6)
   181  	require.Falsef(t, taskState.Failed, "task unexpectedly failed after %d events\n%s",
   182  		len(events), formatEvents(events),
   183  	)
   184  
   185  	// The alloc should be faied
   186  	require.Equal(t, "failed", alloc.ClientStatus)
   187  
   188  	// Assert the expected type of events were emitted in a specific order
   189  	require.Equal(t, api.TaskReceived, events[0].Type)
   190  	require.Equal(t, api.TaskSetup, events[1].Type)
   191  	require.Equal(t, api.TaskStarted, events[2].Type)
   192  	require.Equal(t, api.TaskSiblingFailed, events[3].Type)
   193  	require.Equal(t, api.TaskKilling, events[4].Type)
   194  	require.Equal(t, api.TaskTerminated, events[5].Type)
   195  	require.Equal(t, api.TaskKilled, events[6].Type)
   196  }