mirror of
https://github.com/kemko/nomad.git
synced 2026-01-07 10:55:42 +03:00
Merge pull request #3120 from hashicorp/b-multi-match
Fix exact job match when prefix of other job
This commit is contained in:
@@ -103,14 +103,18 @@ func (c *StatusCommand) Run(args []string) int {
|
||||
var match contexts.Context
|
||||
matchCount := 0
|
||||
for ctx, vers := range res.Matches {
|
||||
if len(vers) == 1 {
|
||||
if l := len(vers); l == 1 {
|
||||
match = ctx
|
||||
matchCount++
|
||||
} else if l > 0 && vers[0] == id {
|
||||
// Exact match
|
||||
match = ctx
|
||||
break
|
||||
}
|
||||
|
||||
// Only a single result should return, as this is a match against a full id
|
||||
if matchCount > 1 || len(vers) > 1 {
|
||||
c.Ui.Error(fmt.Sprintf("Multiple matches found for id %q", id))
|
||||
c.logMultiMatchError(id, res.Matches)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
@@ -134,3 +138,17 @@ func (c *StatusCommand) Run(args []string) int {
|
||||
|
||||
return cmd.Run(argsCopy)
|
||||
}
|
||||
|
||||
// logMultiMatchError is used to log an error message when multiple matches are
|
||||
// found. The error message logged displays the matched IDs per context.
|
||||
func (c *StatusCommand) logMultiMatchError(id string, matches map[contexts.Context][]string) {
|
||||
c.Ui.Error(fmt.Sprintf("Multiple matches found for id %q", id))
|
||||
for ctx, vers := range matches {
|
||||
if len(vers) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
c.Ui.Error(fmt.Sprintf("\n%s:", strings.Title(string(ctx))))
|
||||
c.Ui.Error(fmt.Sprintf("%s", strings.Join(vers, ", ")))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,35 @@ func TestStatusCommand_Run_JobStatus(t *testing.T) {
|
||||
ui.OutputWriter.Reset()
|
||||
}
|
||||
|
||||
func TestStatusCommand_Run_JobStatus_MultiMatch(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
t.Parallel()
|
||||
|
||||
srv, _, url := testServer(t, true, nil)
|
||||
defer srv.Shutdown()
|
||||
|
||||
ui := new(cli.MockUi)
|
||||
cmd := &StatusCommand{Meta: Meta{Ui: ui, flagAddress: url}}
|
||||
|
||||
// Create two fake jobs sharing a prefix
|
||||
state := srv.Agent.Server().State()
|
||||
j := mock.Job()
|
||||
j2 := mock.Job()
|
||||
j2.ID = fmt.Sprintf("%s-more", j.ID)
|
||||
assert.Nil(state.UpsertJob(1000, j))
|
||||
assert.Nil(state.UpsertJob(1001, j2))
|
||||
|
||||
// Query to check the job status
|
||||
if code := cmd.Run([]string{"-address=" + url, j.ID}); code != 0 {
|
||||
t.Fatalf("expected exit 0, got: %d", code)
|
||||
}
|
||||
|
||||
out := ui.OutputWriter.String()
|
||||
assert.Contains(out, j.ID)
|
||||
|
||||
ui.OutputWriter.Reset()
|
||||
}
|
||||
|
||||
func TestStatusCommand_Run_EvalStatus(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
t.Parallel()
|
||||
|
||||
@@ -91,7 +91,10 @@ func roundUUIDDownIfOdd(prefix string, context structs.Context) string {
|
||||
return prefix
|
||||
}
|
||||
|
||||
l := len(prefix)
|
||||
// We ignore the count of hyphens when calculating if the prefix is even:
|
||||
// E.g "e3671fa4-21"
|
||||
numHyphens := strings.Count(prefix, "-")
|
||||
l := len(prefix) - numHyphens
|
||||
if l%2 == 0 {
|
||||
return prefix
|
||||
}
|
||||
|
||||
@@ -55,6 +55,48 @@ func TestSearch_PrefixSearch_Job(t *testing.T) {
|
||||
assert.Equal(uint64(jobIndex), resp.Index)
|
||||
}
|
||||
|
||||
func TestSearch_PrefixSearch_All_JobWithHyphen(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
prefix := "example-test"
|
||||
|
||||
t.Parallel()
|
||||
s := testServer(t, func(c *Config) {
|
||||
c.NumSchedulers = 0
|
||||
})
|
||||
|
||||
defer s.Shutdown()
|
||||
codec := rpcClient(t, s)
|
||||
testutil.WaitForLeader(t, s.RPC)
|
||||
|
||||
// Register a job and an allocation
|
||||
jobID := registerAndVerifyJob(s, t, prefix, 0)
|
||||
alloc := mock.Alloc()
|
||||
alloc.JobID = jobID
|
||||
summary := mock.JobSummary(alloc.JobID)
|
||||
state := s.fsm.State()
|
||||
|
||||
if err := state.UpsertJobSummary(999, summary); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if err := state.UpsertAllocs(1000, []*structs.Allocation{alloc}); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
req := &structs.SearchRequest{
|
||||
Prefix: "example-",
|
||||
Context: structs.All,
|
||||
}
|
||||
|
||||
var resp structs.SearchResponse
|
||||
if err := msgpackrpc.CallWithCodec(codec, "Search.PrefixSearch", req, &resp); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
assert.Equal(1, len(resp.Matches[structs.Jobs]))
|
||||
assert.Equal(jobID, resp.Matches[structs.Jobs][0])
|
||||
assert.EqualValues(jobIndex, resp.Index)
|
||||
}
|
||||
|
||||
// truncate should limit results to 20
|
||||
func TestSearch_PrefixSearch_Truncate(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
@@ -198,6 +240,59 @@ func TestSearch_PrefixSearch_Allocation(t *testing.T) {
|
||||
assert.Equal(uint64(90), resp.Index)
|
||||
}
|
||||
|
||||
func TestSearch_PrefixSearch_All_UUID_EvenPrefix(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
t.Parallel()
|
||||
s := testServer(t, func(c *Config) {
|
||||
c.NumSchedulers = 0
|
||||
})
|
||||
|
||||
defer s.Shutdown()
|
||||
codec := rpcClient(t, s)
|
||||
testutil.WaitForLeader(t, s.RPC)
|
||||
|
||||
alloc := mock.Alloc()
|
||||
summary := mock.JobSummary(alloc.JobID)
|
||||
state := s.fsm.State()
|
||||
|
||||
if err := state.UpsertJobSummary(999, summary); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if err := state.UpsertAllocs(1000, []*structs.Allocation{alloc}); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
node := mock.Node()
|
||||
if err := state.UpsertNode(1001, node); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
eval1 := mock.Eval()
|
||||
eval1.ID = node.ID
|
||||
if err := state.UpsertEvals(1002, []*structs.Evaluation{eval1}); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
prefix := alloc.ID[:13]
|
||||
t.Log(prefix)
|
||||
|
||||
req := &structs.SearchRequest{
|
||||
Prefix: prefix,
|
||||
Context: structs.All,
|
||||
}
|
||||
|
||||
var resp structs.SearchResponse
|
||||
if err := msgpackrpc.CallWithCodec(codec, "Search.PrefixSearch", req, &resp); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
assert.Equal(1, len(resp.Matches[structs.Allocs]))
|
||||
assert.Equal(alloc.ID, resp.Matches[structs.Allocs][0])
|
||||
assert.Equal(resp.Truncations[structs.Allocs], false)
|
||||
|
||||
assert.EqualValues(1002, resp.Index)
|
||||
}
|
||||
|
||||
func TestSearch_PrefixSearch_Node(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
t.Parallel()
|
||||
|
||||
Reference in New Issue
Block a user