Skip to content

Commit

Permalink
pagination and filtering for Eval.List HTTP endpoint
Browse files Browse the repository at this point in the history
Parse the query parameters of the `Eval.List` API into the arguments
expected for filtering in the RPC call.
  • Loading branch information
tgross committed Dec 9, 2021
1 parent 3aa05db commit bf2ed52
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 30 deletions.
4 changes: 4 additions & 0 deletions command/agent/eval_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ func (s *HTTPServer) EvalsRequest(resp http.ResponseWriter, req *http.Request) (
return nil, nil
}

query := req.URL.Query()
args.FilterEvalStatus = query.Get("status")
args.FilterJobID = query.Get("job")

var out structs.EvalListResponse
if err := s.agent.RPC("Eval.List", &args, &out); err != nil {
return nil, err
Expand Down
62 changes: 34 additions & 28 deletions command/agent/eval_endpoint_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package agent

import (
"fmt"
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/require"

"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
)
Expand All @@ -17,39 +20,42 @@ func TestHTTP_EvalList(t *testing.T) {
eval1 := mock.Eval()
eval2 := mock.Eval()
err := state.UpsertEvals(structs.MsgTypeTestSetup, 1000, []*structs.Evaluation{eval1, eval2})
if err != nil {
t.Fatalf("err: %v", err)
}
require.NoError(t, err)

// Make the HTTP request
// simple list request
req, err := http.NewRequest("GET", "/v1/evaluations", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
require.NoError(t, err)
respW := httptest.NewRecorder()

// Make the request
obj, err := s.Server.EvalsRequest(respW, req)
if err != nil {
t.Fatalf("err: %v", err)
}

// Check for the index
if respW.HeaderMap.Get("X-Nomad-Index") == "" {
t.Fatalf("missing index")
}
if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" {
t.Fatalf("missing known leader")
}
if respW.HeaderMap.Get("X-Nomad-LastContact") == "" {
t.Fatalf("missing last contact")
}
require.NoError(t, err)

// check headers and response body
require.NotEqual(t, "", respW.HeaderMap.Get("X-Nomad-Index"), "missing index")
require.Equal(t, "true", respW.HeaderMap.Get("X-Nomad-KnownLeader"), "missing known leader")
require.NotEqual(t, "", respW.HeaderMap.Get("X-Nomad-LastContact"), "missing last contact")
require.Len(t, obj.([]*structs.Evaluation), 2, "expected 2 evals")

// paginated list request
req, err = http.NewRequest("GET", "/v1/evaluations?per_page=1", nil)
require.NoError(t, err)
respW = httptest.NewRecorder()
obj, err = s.Server.EvalsRequest(respW, req)
require.NoError(t, err)

// check response body
require.Len(t, obj.([]*structs.Evaluation), 1, "expected 1 eval")

// filtered list request
req, err = http.NewRequest("GET",
fmt.Sprintf("/v1/evaluations?per_page=10&job=%s", eval2.JobID), nil)
require.NoError(t, err)
respW = httptest.NewRecorder()
obj, err = s.Server.EvalsRequest(respW, req)
require.NoError(t, err)

// check response body
require.Len(t, obj.([]*structs.Evaluation), 1, "expected 1 eval")

// Check the eval
e := obj.([]*structs.Evaluation)
if len(e) != 2 {
t.Fatalf("bad: %#v", e)
}
})
}

Expand Down
11 changes: 9 additions & 2 deletions command/agent/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -608,11 +608,19 @@ func setLastContact(resp http.ResponseWriter, last time.Duration) {
resp.Header().Set("X-Nomad-LastContact", strconv.FormatUint(lastMsec, 10))
}

// setNextToken is used to set the next token header for pagination
func setNextToken(resp http.ResponseWriter, nextToken string) {
if nextToken != "" {
resp.Header().Set("X-Nomad-NextToken", nextToken)
}
}

// setMeta is used to set the query response meta data
func setMeta(resp http.ResponseWriter, m *structs.QueryMeta) {
setIndex(resp, m.Index)
setLastContact(resp, m.LastContact)
setKnownLeader(resp, m.KnownLeader)
setNextToken(resp, m.NextToken)
}

// setHeaders is used to set canonical response header fields
Expand Down Expand Up @@ -746,8 +754,7 @@ func parsePagination(req *http.Request, b *structs.QueryOptions) {
}
}

nextToken := query.Get("next_token")
b.NextToken = nextToken
b.NextToken = query.Get("next_token")
}

// parseWriteRequest is a convenience method for endpoints that need to parse a
Expand Down

0 comments on commit bf2ed52

Please sign in to comment.