diff --git a/nomad/resources_endpoint.go b/nomad/resources_endpoint.go index 5174c162ab5b21769bd93b490129986dcfcca1c5..bfdeafe8b32df413cf72b43e58631596ca14889a 100644 --- a/nomad/resources_endpoint.go +++ b/nomad/resources_endpoint.go @@ -74,6 +74,15 @@ func getResourceIter(context, prefix string, ws memdb.WatchSet, state *state.Sta } } +// If the length of a string is odd, return a subset of the string to the last +// even character (n-1) +func roundDownIfOdd(s string) string { + if len(s)%2 == 0 { + return s + } + return s[:len(s)-1] +} + // List is used to list the resouces registered in the system that matches the // given prefix. Resources are jobs, evaluations, allocations, and/or nodes. func (r *Resources) List(args *structs.ResourceListRequest, @@ -95,7 +104,7 @@ func (r *Resources) List(args *structs.ResourceListRequest, } for _, e := range contexts { - iter, err := getResourceIter(e, args.Prefix, ws, state) + iter, err := getResourceIter(e, roundDownIfOdd(args.Prefix), ws, state) if err != nil { return err } diff --git a/nomad/resources_endpoint_test.go b/nomad/resources_endpoint_test.go index 26d6046442c4a56b32aad863d0d58a1f4dbfa77f..ff7f56ed77c4b56bb1b94e127ffacd537a12b5a1 100644 --- a/nomad/resources_endpoint_test.go +++ b/nomad/resources_endpoint_test.go @@ -300,8 +300,8 @@ func TestResourcesEndpoint_List_NoPrefix(t *testing.T) { assert.Equal(uint64(jobIndex), resp.Index) } -//// Tests that the zero matches are returned when a prefix has no matching -//// results +// Tests that the zero matches are returned when a prefix has no matching +// results func TestResourcesEndpoint_List_NoMatches(t *testing.T) { assert := assert.New(t) @@ -329,3 +329,36 @@ func TestResourcesEndpoint_List_NoMatches(t *testing.T) { assert.Equal(0, len(resp.Matches["jobs"])) assert.Equal(uint64(0), resp.Index) } + +// Prefixes can only be looked up if their length is a power of two. For +// prefixes which are an odd length, use the length-1 characters. +func TestResourcesEndpoint_List_RoundDownToEven(t *testing.T) { + assert := assert.New(t) + id := "aaafaaaa-e8f7-fd38-c855-ab94ceb89" + prefix := "aaafe" + + t.Parallel() + s := testServer(t, func(c *Config) { + c.NumSchedulers = 0 + }) + + defer s.Shutdown() + codec := rpcClient(t, s) + testutil.WaitForLeader(t, s.RPC) + + jobID := registerAndVerifyJob(s, t, id, 0) + jobID := registerAndVerifyJob(s, t, "bbafaaaa-e8f7-fd38-c855-ab94ceb89", 50) + + req := &structs.ResourceListRequest{ + Prefix: prefix, + Context: "jobs", + } + + var resp structs.ResourceListResponse + if err := msgpackrpc.CallWithCodec(codec, "Resources.List", req, &resp); err != nil { + t.Fatalf("err: %v", err) + } + + assert.Equal(1, len(resp.Matches["jobs"])) + assert.Equal(jobID, resp.Matches["jobs"][0]) +}