diff --git a/command/agent/alloc_endpoint.go b/command/agent/alloc_endpoint.go index 65bcbd011..03d8bf8c1 100644 --- a/command/agent/alloc_endpoint.go +++ b/command/agent/alloc_endpoint.go @@ -34,6 +34,9 @@ func (s *HTTPServer) AllocsRequest(resp http.ResponseWriter, req *http.Request) if out.Allocations == nil { out.Allocations = make([]*structs.AllocListStub, 0) } + for _, alloc := range out.Allocations { + alloc.SetEventDisplayMessages() + } return out.Allocations, nil } @@ -70,6 +73,7 @@ func (s *HTTPServer) AllocSpecificRequest(resp http.ResponseWriter, req *http.Re alloc = alloc.Copy() alloc.Job.Payload = decoded } + alloc.SetEventDisplayMessages() return alloc, nil } diff --git a/command/agent/alloc_endpoint_test.go b/command/agent/alloc_endpoint_test.go index 80d4616d8..e81c897c9 100644 --- a/command/agent/alloc_endpoint_test.go +++ b/command/agent/alloc_endpoint_test.go @@ -22,7 +22,17 @@ func TestHTTP_AllocsList(t *testing.T) { // Directly manipulate the state state := s.Agent.server.State() alloc1 := mock.Alloc() + testEvent := structs.NewTaskEvent(structs.TaskSiblingFailed) + var events1 []*structs.TaskEvent + events1 = append(events1, testEvent) + taskState := &structs.TaskState{Events: events1} + alloc1.TaskStates = make(map[string]*structs.TaskState) + alloc1.TaskStates["test"] = taskState + alloc2 := mock.Alloc() + alloc2.TaskStates = make(map[string]*structs.TaskState) + alloc2.TaskStates["test"] = taskState + state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID)) state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID)) err := state.UpsertAllocs(1000, @@ -56,10 +66,15 @@ func TestHTTP_AllocsList(t *testing.T) { } // Check the alloc - n := obj.([]*structs.AllocListStub) - if len(n) != 2 { - t.Fatalf("bad: %#v", n) + allocs := obj.([]*structs.AllocListStub) + if len(allocs) != 2 { + t.Fatalf("bad: %#v", allocs) } + expectedMsg := "Task's sibling failed" + displayMsg1 := allocs[0].TaskStates["test"].Events[0].DisplayMessage + assert.Equal(t, expectedMsg, displayMsg1, "DisplayMessage should be set") + displayMsg2 := allocs[0].TaskStates["test"].Events[0].DisplayMessage + assert.Equal(t, expectedMsg, displayMsg2, "DisplayMessage should be set") }) } @@ -73,6 +88,14 @@ func TestHTTP_AllocsPrefixList(t *testing.T) { alloc1.ID = "aaaaaaaa-e8f7-fd38-c855-ab94ceb89706" alloc2 := mock.Alloc() alloc2.ID = "aaabbbbb-e8f7-fd38-c855-ab94ceb89706" + + testEvent := structs.NewTaskEvent(structs.TaskSiblingFailed) + var events1 []*structs.TaskEvent + events1 = append(events1, testEvent) + taskState := &structs.TaskState{Events: events1} + alloc2.TaskStates = make(map[string]*structs.TaskState) + alloc2.TaskStates["test"] = taskState + summary1 := mock.JobSummary(alloc1.JobID) summary2 := mock.JobSummary(alloc2.JobID) if err := state.UpsertJobSummary(998, summary1); err != nil { @@ -120,6 +143,10 @@ func TestHTTP_AllocsPrefixList(t *testing.T) { if n[0].ID != alloc2.ID { t.Fatalf("expected alloc ID: %v, Actual: %v", alloc2.ID, n[0].ID) } + expectedMsg := "Task's sibling failed" + displayMsg1 := n[0].TaskStates["test"].Events[0].DisplayMessage + assert.Equal(t, expectedMsg, displayMsg1, "DisplayMessage should be set") + }) } diff --git a/command/agent/deployment_endpoint.go b/command/agent/deployment_endpoint.go index 4ae3cb18f..dd18a6564 100644 --- a/command/agent/deployment_endpoint.go +++ b/command/agent/deployment_endpoint.go @@ -166,6 +166,9 @@ func (s *HTTPServer) deploymentAllocations(resp http.ResponseWriter, req *http.R if out.Allocations == nil { out.Allocations = make([]*structs.AllocListStub, 0) } + for _, alloc := range out.Allocations { + alloc.SetEventDisplayMessages() + } return out.Allocations, nil } diff --git a/command/agent/deployment_endpoint_test.go b/command/agent/deployment_endpoint_test.go index 99dad7a8a..329a7b15a 100644 --- a/command/agent/deployment_endpoint_test.go +++ b/command/agent/deployment_endpoint_test.go @@ -87,9 +87,26 @@ func TestHTTP_DeploymentAllocations(t *testing.T) { a1 := mock.Alloc() a1.JobID = j.ID a1.DeploymentID = d.ID + + testEvent := structs.NewTaskEvent(structs.TaskSiblingFailed) + var events1 []*structs.TaskEvent + events1 = append(events1, testEvent) + taskState := &structs.TaskState{Events: events1} + a1.TaskStates = make(map[string]*structs.TaskState) + a1.TaskStates["test"] = taskState + a2 := mock.Alloc() a2.JobID = j.ID a2.DeploymentID = d.ID + + // Create a test event + testEvent2 := structs.NewTaskEvent(structs.TaskSiblingFailed) + var events2 []*structs.TaskEvent + events2 = append(events2, testEvent2) + taskState2 := &structs.TaskState{Events: events2} + a2.TaskStates = make(map[string]*structs.TaskState) + a2.TaskStates["test"] = taskState2 + assert.Nil(state.UpsertJob(998, j), "UpsertJob") assert.Nil(state.UpsertDeployment(999, d), "UpsertDeployment") assert.Nil(state.UpsertAllocs(1000, []*structs.Allocation{a1, a2}), "UpsertAllocs") @@ -108,9 +125,14 @@ func TestHTTP_DeploymentAllocations(t *testing.T) { assert.Equal("true", respW.HeaderMap.Get("X-Nomad-KnownLeader"), "missing known leader") assert.NotZero(respW.HeaderMap.Get("X-Nomad-LastContact"), "missing last contact") - // Check the ouptput + // Check the output allocs := obj.([]*structs.AllocListStub) assert.Len(allocs, 2, "Deployment Allocs") + expectedMsg := "Task's sibling failed" + displayMsg1 := allocs[0].TaskStates["test"].Events[0].DisplayMessage + assert.Equal(expectedMsg, displayMsg1, "DisplayMessage should be set") + displayMsg2 := allocs[0].TaskStates["test"].Events[0].DisplayMessage + assert.Equal(expectedMsg, displayMsg2, "DisplayMessage should be set") }) } diff --git a/command/agent/job_endpoint.go b/command/agent/job_endpoint.go index 448041074..bcddd800a 100644 --- a/command/agent/job_endpoint.go +++ b/command/agent/job_endpoint.go @@ -217,6 +217,9 @@ func (s *HTTPServer) jobAllocations(resp http.ResponseWriter, req *http.Request, if out.Allocations == nil { out.Allocations = make([]*structs.AllocListStub, 0) } + for _, alloc := range out.Allocations { + alloc.SetEventDisplayMessages() + } return out.Allocations, nil } diff --git a/command/agent/job_endpoint_test.go b/command/agent/job_endpoint_test.go index 779463970..8ad407603 100644 --- a/command/agent/job_endpoint_test.go +++ b/command/agent/job_endpoint_test.go @@ -646,6 +646,13 @@ func TestHTTP_JobAllocations(t *testing.T) { } // Directly manipulate the state + expectedDisplayMsg := "test message" + testEvent := structs.NewTaskEvent("test event").SetMessage(expectedDisplayMsg) + var events []*structs.TaskEvent + events = append(events, testEvent) + taskState := &structs.TaskState{Events: events} + alloc1.TaskStates = make(map[string]*structs.TaskState) + alloc1.TaskStates["test"] = taskState state := s.Agent.server.State() err := state.UpsertAllocs(1000, []*structs.Allocation{alloc1}) if err != nil { @@ -670,6 +677,8 @@ func TestHTTP_JobAllocations(t *testing.T) { if len(allocs) != 1 && allocs[0].ID != alloc1.ID { t.Fatalf("bad: %v", allocs) } + displayMsg := allocs[0].TaskStates["test"].Events[0].DisplayMessage + assert.Equal(t, expectedDisplayMsg, displayMsg) // Check for the index if respW.HeaderMap.Get("X-Nomad-Index") == "" { diff --git a/command/agent/node_endpoint.go b/command/agent/node_endpoint.go index 1cb3c881c..fd396a67c 100644 --- a/command/agent/node_endpoint.go +++ b/command/agent/node_endpoint.go @@ -89,6 +89,9 @@ func (s *HTTPServer) nodeAllocations(resp http.ResponseWriter, req *http.Request if out.Allocs == nil { out.Allocs = make([]*structs.Allocation, 0) } + for _, alloc := range out.Allocs { + alloc.SetEventDisplayMessages() + } return out.Allocs, nil } diff --git a/command/agent/node_endpoint_test.go b/command/agent/node_endpoint_test.go index 9f8a1edad..8bf56e328 100644 --- a/command/agent/node_endpoint_test.go +++ b/command/agent/node_endpoint_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/nomad/nomad/mock" "github.com/hashicorp/nomad/nomad/structs" + "github.com/stretchr/testify/assert" ) func TestHTTP_NodesList(t *testing.T) { @@ -187,6 +188,14 @@ func TestHTTP_NodeAllocations(t *testing.T) { if err := state.UpsertJobSummary(999, mock.JobSummary(alloc1.JobID)); err != nil { t.Fatal(err) } + // Create a test event for the allocation + testEvent := structs.NewTaskEvent(structs.TaskStarted) + var events []*structs.TaskEvent + events = append(events, testEvent) + taskState := &structs.TaskState{Events: events} + alloc1.TaskStates = make(map[string]*structs.TaskState) + alloc1.TaskStates["test"] = taskState + err := state.UpsertAllocs(1000, []*structs.Allocation{alloc1}) if err != nil { t.Fatalf("err: %v", err) @@ -221,6 +230,9 @@ func TestHTTP_NodeAllocations(t *testing.T) { if len(allocs) != 1 || allocs[0].ID != alloc1.ID { t.Fatalf("bad: %#v", allocs) } + expectedDisplayMsg := "Task started by client" + displayMsg := allocs[0].TaskStates["test"].Events[0].DisplayMessage + assert.Equal(t, expectedDisplayMsg, displayMsg) }) } diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index c07ccf9ba..beae3efc2 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -4919,6 +4919,13 @@ func (a *Allocation) ShouldMigrate() bool { return true } +// SetEventDisplayMessage populates the display message if its not already set, +// a temporary fix to handle old allocations that don't have it. +// This method will be removed in a future release. +func (a *Allocation) SetEventDisplayMessages() { + setDisplayMsg(a.TaskStates) +} + // Stub returns a list stub for the allocation func (a *Allocation) Stub() *AllocListStub { return &AllocListStub{ @@ -4963,6 +4970,23 @@ type AllocListStub struct { ModifyTime int64 } +// SetEventDisplayMessage populates the display message if its not already set, +// a temporary fix to handle old allocations that don't have it. +// This method will be removed in a future release. +func (a *AllocListStub) SetEventDisplayMessages() { + setDisplayMsg(a.TaskStates) +} + +func setDisplayMsg(taskStates map[string]*TaskState) { + if taskStates != nil { + for _, taskState := range taskStates { + for _, event := range taskState.Events { + event.PopulateEventDisplayMessage() + } + } + } +} + // AllocMetric is used to track various metrics while attempting // to make an allocation. These are used to debug a job, or to better // understand the pressure within the system.