diff --git a/api/tasks.go b/api/tasks.go index 853df741826..c956aac307c 100644 --- a/api/tasks.go +++ b/api/tasks.go @@ -3,7 +3,6 @@ package api import ( "fmt" "path" - "path/filepath" "strings" "time" @@ -591,8 +590,11 @@ const ( // TaskEvent is an event that effects the state of a task and contains meta-data // appropriate to the events type. type TaskEvent struct { - Type string - Time int64 + Type string + Time int64 + DisplayMessage string + Details map[string]string + // DEPRECATION NOTICE: The following fields are all deprecated. see TaskEvent struct in structs.go for details. FailsTask bool RestartReason string SetupError string diff --git a/client/alloc_runner_test.go b/client/alloc_runner_test.go index c374aa81c71..f53b01ef7ad 100644 --- a/client/alloc_runner_test.go +++ b/client/alloc_runner_test.go @@ -152,7 +152,7 @@ func TestAllocRunner_DeploymentHealth_Unhealthy_BadStart(t *testing.T) { assert.NotNil(state) assert.NotEmpty(state.Events) last := state.Events[len(state.Events)-1] - assert.Equal(allocHealthEventSource, last.GenericSource) + assert.Equal(allocHealthEventSource, last.Type) assert.Contains(last.Message, "failed task") } @@ -202,7 +202,7 @@ func TestAllocRunner_DeploymentHealth_Unhealthy_Deadline(t *testing.T) { assert.NotNil(state) assert.NotEmpty(state.Events) last := state.Events[len(state.Events)-1] - assert.Equal(allocHealthEventSource, last.GenericSource) + assert.Equal(allocHealthEventSource, last.Type) assert.Contains(last.Message, "not running by deadline") } @@ -412,7 +412,7 @@ func TestAllocRunner_DeploymentHealth_Unhealthy_Checks(t *testing.T) { assert.NotNil(state) assert.NotEmpty(state.Events) last := state.Events[len(state.Events)-1] - assert.Equal(allocHealthEventSource, last.GenericSource) + assert.Equal(allocHealthEventSource, last.Type) assert.Contains(last.Message, "Services not healthy by deadline") } diff --git a/client/task_runner.go b/client/task_runner.go index 4ab4b4f1f7f..f7f2c043b07 100644 --- a/client/task_runner.go +++ b/client/task_runner.go @@ -542,6 +542,8 @@ func (r *TaskRunner) DestroyState() error { // setState is used to update the state of the task runner func (r *TaskRunner) setState(state string, event *structs.TaskEvent, lazySync bool) { + event.PopulateEventDisplayMessage() + // Persist our state to disk. if err := r.SaveState(); err != nil { r.logger.Printf("[ERR] client: failed to save state of Task Runner for task %q: %v", r.task.Name, err) @@ -1751,8 +1753,8 @@ func (r *TaskRunner) Kill(source, reason string, fail bool) { } func (r *TaskRunner) EmitEvent(source, message string) { - event := structs.NewTaskEvent(structs.TaskGenericMessage). - SetGenericSource(source).SetMessage(message) + event := structs.NewTaskEvent(source). + SetMessage(message) r.setState("", event, false) r.logger.Printf("[DEBUG] client: event from %q for task %q in alloc %q: %v", source, r.task.Name, r.alloc.ID, message) diff --git a/client/task_runner_test.go b/client/task_runner_test.go index d12369307bd..5b99a1ca07b 100644 --- a/client/task_runner_test.go +++ b/client/task_runner_test.go @@ -210,21 +210,43 @@ func TestTaskRunner_SimpleRun(t *testing.T) { t.Fatalf("TaskState %v; want %v", ctx.upd.state, structs.TaskStateDead) } - if ctx.upd.events[0].Type != structs.TaskReceived { + event := ctx.upd.events[0] + + if event.Type != structs.TaskReceived { t.Fatalf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) } - if ctx.upd.events[1].Type != structs.TaskSetup { + event = ctx.upd.events[1] + if event.Type != structs.TaskSetup { t.Fatalf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) } + displayMsg := event.DisplayMessage - if ctx.upd.events[2].Type != structs.TaskStarted { + if displayMsg != "Building Task Directory" { + t.Fatalf("Bad display message:%v", displayMsg) + } + + event = ctx.upd.events[2] + if event.Type != structs.TaskStarted { t.Fatalf("Second Event was %v; want %v", ctx.upd.events[2].Type, structs.TaskStarted) } + displayMsg = event.DisplayMessage + if displayMsg != "Task started by client" { + t.Fatalf("Bad display message:%v", displayMsg) + } - if ctx.upd.events[3].Type != structs.TaskTerminated { - t.Fatalf("Third Event was %v; want %v", ctx.upd.events[3].Type, structs.TaskTerminated) + event = ctx.upd.events[3] + if event.Type != structs.TaskTerminated { + t.Fatalf("Third Event was %v; want %v", event.Type, structs.TaskTerminated) + } + displayMsg = event.DisplayMessage + if displayMsg != "Exit Code: 0" { + t.Fatalf("Bad display message:%v", displayMsg) } + if event.Details["exit_code"] != "0" { + t.Fatalf("Bad details map :%v", event.Details) + } + } func TestTaskRunner_Run_RecoverableStartError(t *testing.T) { diff --git a/command/alloc_status.go b/command/alloc_status.go index d1e75932e31..8b6af6d6632 100644 --- a/command/alloc_status.go +++ b/command/alloc_status.go @@ -325,120 +325,127 @@ func (c *AllocStatusCommand) outputTaskStatus(state *api.TaskState) { size := len(state.Events) for i, event := range state.Events { - formatedTime := formatUnixNanoTime(event.Time) - - // Build up the description based on the event type. - var desc string - switch event.Type { - case api.TaskSetup: - desc = event.Message - case api.TaskStarted: - desc = "Task started by client" - case api.TaskReceived: - desc = "Task received by client" - case api.TaskFailedValidation: - if event.ValidationError != "" { - desc = event.ValidationError - } else { - desc = "Validation of task failed" - } - case api.TaskSetupFailure: - if event.SetupError != "" { - desc = event.SetupError - } else { - desc = "Task setup failed" - } - case api.TaskDriverFailure: - if event.DriverError != "" { - desc = event.DriverError - } else { - desc = "Failed to start task" - } - case api.TaskDownloadingArtifacts: - desc = "Client is downloading artifacts" - case api.TaskArtifactDownloadFailed: - if event.DownloadError != "" { - desc = event.DownloadError - } else { - desc = "Failed to download artifacts" - } - case api.TaskKilling: - if event.KillReason != "" { - desc = fmt.Sprintf("Killing task: %v", event.KillReason) - } else if event.KillTimeout != 0 { - desc = fmt.Sprintf("Sent interrupt. Waiting %v before force killing", event.KillTimeout) - } else { - desc = "Sent interrupt" - } - case api.TaskKilled: - if event.KillError != "" { - desc = event.KillError - } else { - desc = "Task successfully killed" - } - case api.TaskTerminated: - var parts []string - parts = append(parts, fmt.Sprintf("Exit Code: %d", event.ExitCode)) + msg := event.DisplayMessage + if msg == "" { + msg = buildDisplayMessage(event) + } + formattedTime := formatUnixNanoTime(event.Time) + events[size-i] = fmt.Sprintf("%s|%s|%s", formattedTime, event.Type, msg) + // Reverse order so we are sorted by time + } + c.Ui.Output(formatList(events)) +} - if event.Signal != 0 { - parts = append(parts, fmt.Sprintf("Signal: %d", event.Signal)) - } +func buildDisplayMessage(event *api.TaskEvent) string { + // Build up the description based on the event type. + var desc string + switch event.Type { + case api.TaskSetup: + desc = event.Message + case api.TaskStarted: + desc = "Task started by client" + case api.TaskReceived: + desc = "Task received by client" + case api.TaskFailedValidation: + if event.ValidationError != "" { + desc = event.ValidationError + } else { + desc = "Validation of task failed" + } + case api.TaskSetupFailure: + if event.SetupError != "" { + desc = event.SetupError + } else { + desc = "Task setup failed" + } + case api.TaskDriverFailure: + if event.DriverError != "" { + desc = event.DriverError + } else { + desc = "Failed to start task" + } + case api.TaskDownloadingArtifacts: + desc = "Client is downloading artifacts" + case api.TaskArtifactDownloadFailed: + if event.DownloadError != "" { + desc = event.DownloadError + } else { + desc = "Failed to download artifacts" + } + case api.TaskKilling: + if event.KillReason != "" { + desc = fmt.Sprintf("Killing task: %v", event.KillReason) + } else if event.KillTimeout != 0 { + desc = fmt.Sprintf("Sent interrupt. Waiting %v before force killing", event.KillTimeout) + } else { + desc = "Sent interrupt" + } + case api.TaskKilled: + if event.KillError != "" { + desc = event.KillError + } else { + desc = "Task successfully killed" + } + case api.TaskTerminated: + var parts []string + parts = append(parts, fmt.Sprintf("Exit Code: %d", event.ExitCode)) - if event.Message != "" { - parts = append(parts, fmt.Sprintf("Exit Message: %q", event.Message)) - } - desc = strings.Join(parts, ", ") - case api.TaskRestarting: - in := fmt.Sprintf("Task restarting in %v", time.Duration(event.StartDelay)) - if event.RestartReason != "" && event.RestartReason != client.ReasonWithinPolicy { - desc = fmt.Sprintf("%s - %s", event.RestartReason, in) - } else { - desc = in - } - case api.TaskNotRestarting: - if event.RestartReason != "" { - desc = event.RestartReason - } else { - desc = "Task exceeded restart policy" - } - case api.TaskSiblingFailed: - if event.FailedSibling != "" { - desc = fmt.Sprintf("Task's sibling %q failed", event.FailedSibling) - } else { - desc = "Task's sibling failed" - } - case api.TaskSignaling: - sig := event.TaskSignal - reason := event.TaskSignalReason - - if sig == "" && reason == "" { - desc = "Task being sent a signal" - } else if sig == "" { - desc = reason - } else if reason == "" { - desc = fmt.Sprintf("Task being sent signal %v", sig) - } else { - desc = fmt.Sprintf("Task being sent signal %v: %v", sig, reason) - } - case api.TaskRestartSignal: - if event.RestartReason != "" { - desc = event.RestartReason - } else { - desc = "Task signaled to restart" - } - case api.TaskDriverMessage: - desc = event.DriverMessage - case api.TaskLeaderDead: - desc = "Leader Task in Group dead" - case api.TaskGenericMessage: - event.Type = event.GenericSource - desc = event.Message + if event.Signal != 0 { + parts = append(parts, fmt.Sprintf("Signal: %d", event.Signal)) } - // Reverse order so we are sorted by time - events[size-i] = fmt.Sprintf("%s|%s|%s", formatedTime, event.Type, desc) + if event.Message != "" { + parts = append(parts, fmt.Sprintf("Exit Message: %q", event.Message)) + } + desc = strings.Join(parts, ", ") + case api.TaskRestarting: + in := fmt.Sprintf("Task restarting in %v", time.Duration(event.StartDelay)) + if event.RestartReason != "" && event.RestartReason != client.ReasonWithinPolicy { + desc = fmt.Sprintf("%s - %s", event.RestartReason, in) + } else { + desc = in + } + case api.TaskNotRestarting: + if event.RestartReason != "" { + desc = event.RestartReason + } else { + desc = "Task exceeded restart policy" + } + case api.TaskSiblingFailed: + if event.FailedSibling != "" { + desc = fmt.Sprintf("Task's sibling %q failed", event.FailedSibling) + } else { + desc = "Task's sibling failed" + } + case api.TaskSignaling: + sig := event.TaskSignal + reason := event.TaskSignalReason + + if sig == "" && reason == "" { + desc = "Task being sent a signal" + } else if sig == "" { + desc = reason + } else if reason == "" { + desc = fmt.Sprintf("Task being sent signal %v", sig) + } else { + desc = fmt.Sprintf("Task being sent signal %v: %v", sig, reason) + } + case api.TaskRestartSignal: + if event.RestartReason != "" { + desc = event.RestartReason + } else { + desc = "Task signaled to restart" + } + case api.TaskDriverMessage: + desc = event.DriverMessage + case api.TaskLeaderDead: + desc = "Leader Task in Group dead" + case api.TaskGenericMessage: + event.Type = event.GenericSource + desc = event.Message } - c.Ui.Output(formatList(events)) + + return desc } // outputTaskResources prints the task resources for the passed task and if diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index fbe93de855b..b2cd3cc1d66 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -2484,6 +2484,9 @@ const ( // RestartPolicyMinInterval is the minimum interval that is accepted for a // restart policy. RestartPolicyMinInterval = 5 * time.Second + + // ReasonWithinPolicy describes restart events that are within policy + ReasonWithinPolicy = "Restart within policy" ) // RestartPolicy configures how Tasks are restarted when they crash or fail. @@ -3750,9 +3753,6 @@ const ( // TaskLeaderDead indicates that the leader task within the has finished. TaskLeaderDead = "Leader Task Dead" - - // TaskGenericMessage is used by various subsystems to emit a message. - TaskGenericMessage = "Generic" ) // TaskEvent is an event that effects the state of a task and contains meta-data @@ -3761,64 +3761,214 @@ type TaskEvent struct { Type string Time int64 // Unix Nanosecond timestamp - // FailsTask marks whether this event fails the task + Message string // A possible message explaining the termination of the task. + + // DisplayMessage is a human friendly message about the event + DisplayMessage string + + // Details is a map with annotated info about the event + Details map[string]string + + // DEPRECATION NOTICE: The following fields are deprecated and will be removed + // in a future release. Field values are available in the Details map. + + // FailsTask marks whether this event fails the task. + // Deprecated, use Details["fails_task"] to access this. FailsTask bool // Restart fields. + // Deprecated, use Details["restart_reason"] to access this. RestartReason string // Setup Failure fields. + // Deprecated, use Details["setup_error"] to access this. SetupError string // Driver Failure fields. + // Deprecated, use Details["driver_error"] to access this. DriverError string // A driver error occurred while starting the task. // Task Terminated Fields. - ExitCode int // The exit code of the task. - Signal int // The signal that terminated the task. - Message string // A possible message explaining the termination of the task. + + // Deprecated, use Details["exit_code"] to access this. + ExitCode int // The exit code of the task. + + // Deprecated, use Details["signal"] to access this. + Signal int // The signal that terminated the task. // Killing fields + // Deprecated, use Details["kill_timeout"] to access this. KillTimeout time.Duration // Task Killed Fields. + // Deprecated, use Details["kill_error"] to access this. KillError string // Error killing the task. // KillReason is the reason the task was killed + // Deprecated, use Details["kill_reason"] to access this. KillReason string // TaskRestarting fields. + // Deprecated, use Details["start_delay"] to access this. StartDelay int64 // The sleep period before restarting the task in unix nanoseconds. // Artifact Download fields + // Deprecated, use Details["download_error"] to access this. DownloadError string // Error downloading artifacts // Validation fields + // Deprecated, use Details["validation_error"] to access this. ValidationError string // Validation error // The maximum allowed task disk size. + // Deprecated, use Details["disk_limit"] to access this. DiskLimit int64 // Name of the sibling task that caused termination of the task that // the TaskEvent refers to. + // Deprecated, use Details["failed_sibling"] to access this. FailedSibling string // VaultError is the error from token renewal + // Deprecated, use Details["vault_renewal_error"] to access this. VaultError string // TaskSignalReason indicates the reason the task is being signalled. + // Deprecated, use Details["task_signal_reason"] to access this. TaskSignalReason string // TaskSignal is the signal that was sent to the task + // Deprecated, use Details["task_signal"] to access this. TaskSignal string // DriverMessage indicates a driver action being taken. + // Deprecated, use Details["driver_message"] to access this. DriverMessage string // GenericSource is the source of a message. + // Deprecated, is redundant with event type. GenericSource string } +func (event *TaskEvent) PopulateEventDisplayMessage() { + // Build up the description based on the event type. + if event == nil { //TODO(preetha) needs investigation alloc_runner's Run method sends a nil event when sigterming nomad. Why? + return + } + + if event.DisplayMessage != "" { + return + } + + var desc string + switch event.Type { + case TaskSetup: + desc = event.Message + case TaskStarted: + desc = "Task started by client" + case TaskReceived: + desc = "Task received by client" + case TaskFailedValidation: + if event.ValidationError != "" { + desc = event.ValidationError + } else { + desc = "Validation of task failed" + } + case TaskSetupFailure: + if event.SetupError != "" { + desc = event.SetupError + } else { + desc = "Task setup failed" + } + case TaskDriverFailure: + if event.DriverError != "" { + desc = event.DriverError + } else { + desc = "Failed to start task" + } + case TaskDownloadingArtifacts: + desc = "Client is downloading artifacts" + case TaskArtifactDownloadFailed: + if event.DownloadError != "" { + desc = event.DownloadError + } else { + desc = "Failed to download artifacts" + } + case TaskKilling: + if event.KillReason != "" { + desc = fmt.Sprintf("Killing task: %v", event.KillReason) + } else if event.KillTimeout != 0 { + desc = fmt.Sprintf("Sent interrupt. Waiting %v before force killing", event.KillTimeout) + } else { + desc = "Sent interrupt" + } + case TaskKilled: + if event.KillError != "" { + desc = event.KillError + } else { + desc = "Task successfully killed" + } + case TaskTerminated: + var parts []string + parts = append(parts, fmt.Sprintf("Exit Code: %d", event.ExitCode)) + + if event.Signal != 0 { + parts = append(parts, fmt.Sprintf("Signal: %d", event.Signal)) + } + + if event.Message != "" { + parts = append(parts, fmt.Sprintf("Exit Message: %q", event.Message)) + } + desc = strings.Join(parts, ", ") + case TaskRestarting: + in := fmt.Sprintf("Task restarting in %v", time.Duration(event.StartDelay)) + if event.RestartReason != "" && event.RestartReason != ReasonWithinPolicy { + desc = fmt.Sprintf("%s - %s", event.RestartReason, in) + } else { + desc = in + } + case TaskNotRestarting: + if event.RestartReason != "" { + desc = event.RestartReason + } else { + desc = "Task exceeded restart policy" + } + case TaskSiblingFailed: + if event.FailedSibling != "" { + desc = fmt.Sprintf("Task's sibling %q failed", event.FailedSibling) + } else { + desc = "Task's sibling failed" + } + case TaskSignaling: + sig := event.TaskSignal + reason := event.TaskSignalReason + + if sig == "" && reason == "" { + desc = "Task being sent a signal" + } else if sig == "" { + desc = reason + } else if reason == "" { + desc = fmt.Sprintf("Task being sent signal %v", sig) + } else { + desc = fmt.Sprintf("Task being sent signal %v: %v", sig, reason) + } + case TaskRestartSignal: + if event.RestartReason != "" { + desc = event.RestartReason + } else { + desc = "Task signaled to restart" + } + case TaskDriverMessage: + desc = event.DriverMessage + case TaskLeaderDead: + desc = "Leader Task in Group dead" + default: + desc = "" + } + + event.DisplayMessage = desc +} + func (te *TaskEvent) GoString() string { return fmt.Sprintf("%v - %v", te.Time, te.Type) } @@ -3826,6 +3976,7 @@ func (te *TaskEvent) GoString() string { // SetMessage sets the message of TaskEvent func (te *TaskEvent) SetMessage(msg string) *TaskEvent { te.Message = msg + te.Details["message"] = msg return te } @@ -3840,8 +3991,9 @@ func (te *TaskEvent) Copy() *TaskEvent { func NewTaskEvent(event string) *TaskEvent { return &TaskEvent{ - Type: event, - Time: time.Now().UnixNano(), + Type: event, + Time: time.Now().UnixNano(), + Details: make(map[string]string), } } @@ -3850,35 +4002,41 @@ func NewTaskEvent(event string) *TaskEvent { func (e *TaskEvent) SetSetupError(err error) *TaskEvent { if err != nil { e.SetupError = err.Error() + e.Details["setup_error"] = err.Error() } return e } func (e *TaskEvent) SetFailsTask() *TaskEvent { e.FailsTask = true + e.Details["fails_task"] = "true" return e } func (e *TaskEvent) SetDriverError(err error) *TaskEvent { if err != nil { e.DriverError = err.Error() + e.Details["driver_error"] = err.Error() } return e } func (e *TaskEvent) SetExitCode(c int) *TaskEvent { e.ExitCode = c + e.Details["exit_code"] = fmt.Sprintf("%d", c) return e } func (e *TaskEvent) SetSignal(s int) *TaskEvent { e.Signal = s + e.Details["signal"] = fmt.Sprintf("%d", s) return e } func (e *TaskEvent) SetExitMessage(err error) *TaskEvent { if err != nil { e.Message = err.Error() + e.Details["exit_message"] = err.Error() } return e } @@ -3886,38 +4044,45 @@ func (e *TaskEvent) SetExitMessage(err error) *TaskEvent { func (e *TaskEvent) SetKillError(err error) *TaskEvent { if err != nil { e.KillError = err.Error() + e.Details["kill_error"] = err.Error() } return e } func (e *TaskEvent) SetKillReason(r string) *TaskEvent { e.KillReason = r + e.Details["kill_reason"] = r return e } func (e *TaskEvent) SetRestartDelay(delay time.Duration) *TaskEvent { e.StartDelay = int64(delay) + e.Details["start_delay"] = fmt.Sprintf("%d", delay) return e } func (e *TaskEvent) SetRestartReason(reason string) *TaskEvent { e.RestartReason = reason + e.Details["restart_reason"] = reason return e } func (e *TaskEvent) SetTaskSignalReason(r string) *TaskEvent { e.TaskSignalReason = r + e.Details["task_signal_reason"] = r return e } func (e *TaskEvent) SetTaskSignal(s os.Signal) *TaskEvent { e.TaskSignal = s.String() + e.Details["task_signal"] = s.String() return e } func (e *TaskEvent) SetDownloadError(err error) *TaskEvent { if err != nil { e.DownloadError = err.Error() + e.Details["download_error"] = err.Error() } return e } @@ -3925,39 +4090,40 @@ func (e *TaskEvent) SetDownloadError(err error) *TaskEvent { func (e *TaskEvent) SetValidationError(err error) *TaskEvent { if err != nil { e.ValidationError = err.Error() + e.Details["validation_error"] = err.Error() } return e } func (e *TaskEvent) SetKillTimeout(timeout time.Duration) *TaskEvent { e.KillTimeout = timeout + e.Details["kill_timeout"] = timeout.String() return e } func (e *TaskEvent) SetDiskLimit(limit int64) *TaskEvent { e.DiskLimit = limit + e.Details["disk_limit"] = fmt.Sprintf("%d", limit) return e } func (e *TaskEvent) SetFailedSibling(sibling string) *TaskEvent { e.FailedSibling = sibling + e.Details["failed_sibling"] = sibling return e } func (e *TaskEvent) SetVaultRenewalError(err error) *TaskEvent { if err != nil { e.VaultError = err.Error() + e.Details["vault_renewal_error"] = err.Error() } return e } func (e *TaskEvent) SetDriverMessage(m string) *TaskEvent { e.DriverMessage = m - return e -} - -func (e *TaskEvent) SetGenericSource(s string) *TaskEvent { - e.GenericSource = s + e.Details["driver_message"] = m return e } diff --git a/nomad/structs/structs_test.go b/nomad/structs/structs_test.go index d5b4ff4976c..08de5218680 100644 --- a/nomad/structs/structs_test.go +++ b/nomad/structs/structs_test.go @@ -2431,3 +2431,32 @@ func TestACLPolicySetHash(t *testing.T) { assert.Equal(t, out2, ap.Hash) assert.NotEqual(t, out1, out2) } + +func TestTaskEventPopulate(t *testing.T) { + testcases := []struct { + event *TaskEvent + expectedMsg string + }{ + {nil, ""}, + {NewTaskEvent(TaskStarted), "Task started by client"}, + {NewTaskEvent(TaskReceived), "Task received by client"}, + {NewTaskEvent(TaskFailedValidation).SetValidationError(fmt.Errorf("task failed validation")), "task failed validation"}, + {NewTaskEvent(TaskSetupFailure).SetSetupError(fmt.Errorf("task failed setup")), "task failed setup"}, + {NewTaskEvent(TaskDriverFailure), "Failed to start task"}, + {NewTaskEvent(TaskDownloadingArtifacts), "Client is downloading artifacts"}, + {NewTaskEvent(TaskArtifactDownloadFailed), "Failed to download artifacts"}, + {NewTaskEvent(TaskArtifactDownloadFailed).SetDownloadError(fmt.Errorf("connection reset by peer")), "connection reset by peer"}, + {NewTaskEvent(TaskKilling).SetKillReason("Its time for you to die"), "Killing task: Its time for you to die"}, + {NewTaskEvent(TaskKilling).SetKillTimeout(1 * time.Second), "Sent interrupt. Waiting 1s before force killing"}, + {NewTaskEvent(TaskTerminated).SetExitCode(-1).SetSignal(3), "Exit Code: -1, Signal: 3"}, + {NewTaskEvent(TaskLeaderDead), "Leader Task in Group dead"}, + {NewTaskEvent(TaskSiblingFailed).SetFailedSibling("patient zero"), "Task's sibling \"patient zero\" failed"}, + } + + for _, tc := range testcases { + tc.event.PopulateEventDisplayMessage() + if tc.event != nil && tc.event.DisplayMessage != tc.expectedMsg { + t.Fatalf("Expected %v but got %v", tc.expectedMsg, tc.event.DisplayMessage) + } + } +}