6c788fdccd
* use msgtype in upsert node adds message type to signature for upsert node, update tests, remove placeholder method * UpsertAllocs msg type test setup * use upsertallocs with msg type in signature update test usage of delete node delete placeholder msgtype method * add msgtype to upsert evals signature, update test call sites with test setup msg type handle snapshot upsert eval outside of FSM and ignore eval event remove placeholder upsertevalsmsgtype handle job plan rpc and prevent event creation for plan msgtype cleanup upsertnodeevents updatenodedrain msgtype msg type 0 is a node registration event, so set the default to the ignore type * fix named import * fix signature ordering on upsertnode to match
826 lines
22 KiB
Go
826 lines
22 KiB
Go
package nomad
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
|
|
msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc"
|
|
"github.com/hashicorp/nomad/acl"
|
|
"github.com/hashicorp/nomad/helper/uuid"
|
|
"github.com/hashicorp/nomad/nomad/mock"
|
|
"github.com/hashicorp/nomad/nomad/state"
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
"github.com/hashicorp/nomad/testutil"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
const jobIndex = 1000
|
|
|
|
func registerAndVerifyJob(s *Server, t *testing.T, prefix string, counter int) *structs.Job {
|
|
job := mock.Job()
|
|
job.ID = prefix + strconv.Itoa(counter)
|
|
state := s.fsm.State()
|
|
if err := state.UpsertJob(structs.MsgTypeTestSetup, jobIndex, job); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
return job
|
|
}
|
|
|
|
func TestSearch_PrefixSearch_Job(t *testing.T) {
|
|
t.Parallel()
|
|
assert := assert.New(t)
|
|
prefix := "aaaaaaaa-e8f7-fd38-c855-ab94ceb8970"
|
|
|
|
s, cleanupS := TestServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
})
|
|
defer cleanupS()
|
|
codec := rpcClient(t, s)
|
|
testutil.WaitForLeader(t, s.RPC)
|
|
|
|
job := registerAndVerifyJob(s, t, prefix, 0)
|
|
|
|
req := &structs.SearchRequest{
|
|
Prefix: prefix,
|
|
Context: structs.Jobs,
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
Namespace: job.Namespace,
|
|
},
|
|
}
|
|
|
|
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(job.ID, resp.Matches[structs.Jobs][0])
|
|
assert.Equal(uint64(jobIndex), resp.Index)
|
|
}
|
|
|
|
func TestSearch_PrefixSearch_ACL(t *testing.T) {
|
|
t.Parallel()
|
|
assert := assert.New(t)
|
|
jobID := "aaaaaaaa-e8f7-fd38-c855-ab94ceb8970"
|
|
|
|
s, root, cleanupS := TestACLServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
})
|
|
defer cleanupS()
|
|
codec := rpcClient(t, s)
|
|
testutil.WaitForLeader(t, s.RPC)
|
|
state := s.fsm.State()
|
|
|
|
job := registerAndVerifyJob(s, t, jobID, 0)
|
|
assert.Nil(state.UpsertNode(structs.MsgTypeTestSetup, 1001, mock.Node()))
|
|
|
|
req := &structs.SearchRequest{
|
|
Prefix: "",
|
|
Context: structs.Jobs,
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
Namespace: job.Namespace,
|
|
},
|
|
}
|
|
|
|
// Try without a token and expect failure
|
|
{
|
|
var resp structs.SearchResponse
|
|
err := msgpackrpc.CallWithCodec(codec, "Search.PrefixSearch", req, &resp)
|
|
assert.NotNil(err)
|
|
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
|
|
}
|
|
|
|
// Try with an invalid token and expect failure
|
|
{
|
|
invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid",
|
|
mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListJobs}))
|
|
req.AuthToken = invalidToken.SecretID
|
|
var resp structs.SearchResponse
|
|
err := msgpackrpc.CallWithCodec(codec, "Search.PrefixSearch", req, &resp)
|
|
assert.NotNil(err)
|
|
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
|
|
}
|
|
|
|
// Try with a node:read token and expect failure due to Jobs being the context
|
|
{
|
|
validToken := mock.CreatePolicyAndToken(t, state, 1005, "test-invalid2", mock.NodePolicy(acl.PolicyRead))
|
|
req.AuthToken = validToken.SecretID
|
|
var resp structs.SearchResponse
|
|
err := msgpackrpc.CallWithCodec(codec, "Search.PrefixSearch", req, &resp)
|
|
assert.NotNil(err)
|
|
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
|
|
}
|
|
|
|
// Try with a node:read token and expect success due to All context
|
|
{
|
|
validToken := mock.CreatePolicyAndToken(t, state, 1007, "test-valid", mock.NodePolicy(acl.PolicyRead))
|
|
req.Context = structs.All
|
|
req.AuthToken = validToken.SecretID
|
|
var resp structs.SearchResponse
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Search.PrefixSearch", req, &resp))
|
|
assert.Equal(uint64(1001), resp.Index)
|
|
assert.Len(resp.Matches[structs.Nodes], 1)
|
|
|
|
// Jobs filtered out since token only has access to node:read
|
|
assert.Len(resp.Matches[structs.Jobs], 0)
|
|
}
|
|
|
|
// Try with a valid token for namespace:read-job
|
|
{
|
|
validToken := mock.CreatePolicyAndToken(t, state, 1009, "test-valid2",
|
|
mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob}))
|
|
req.AuthToken = validToken.SecretID
|
|
var resp structs.SearchResponse
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Search.PrefixSearch", req, &resp))
|
|
assert.Len(resp.Matches[structs.Jobs], 1)
|
|
assert.Equal(job.ID, resp.Matches[structs.Jobs][0])
|
|
|
|
// Index of job - not node - because node context is filtered out
|
|
assert.Equal(uint64(1000), resp.Index)
|
|
|
|
// Nodes filtered out since token only has access to namespace:read-job
|
|
assert.Len(resp.Matches[structs.Nodes], 0)
|
|
}
|
|
|
|
// Try with a valid token for node:read and namespace:read-job
|
|
{
|
|
validToken := mock.CreatePolicyAndToken(t, state, 1011, "test-valid3", strings.Join([]string{
|
|
mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob}),
|
|
mock.NodePolicy(acl.PolicyRead),
|
|
}, "\n"))
|
|
req.AuthToken = validToken.SecretID
|
|
var resp structs.SearchResponse
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Search.PrefixSearch", req, &resp))
|
|
assert.Len(resp.Matches[structs.Jobs], 1)
|
|
assert.Equal(job.ID, resp.Matches[structs.Jobs][0])
|
|
assert.Len(resp.Matches[structs.Nodes], 1)
|
|
assert.Equal(uint64(1001), resp.Index)
|
|
}
|
|
|
|
// Try with a management token
|
|
{
|
|
req.AuthToken = root.SecretID
|
|
var resp structs.SearchResponse
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Search.PrefixSearch", req, &resp))
|
|
assert.Equal(uint64(1001), resp.Index)
|
|
assert.Len(resp.Matches[structs.Jobs], 1)
|
|
assert.Equal(job.ID, resp.Matches[structs.Jobs][0])
|
|
assert.Len(resp.Matches[structs.Nodes], 1)
|
|
}
|
|
}
|
|
|
|
func TestSearch_PrefixSearch_All_JobWithHyphen(t *testing.T) {
|
|
t.Parallel()
|
|
assert := assert.New(t)
|
|
prefix := "example-test-------" // Assert that a job with more than 4 hyphens works
|
|
|
|
s, cleanupS := TestServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
})
|
|
defer cleanupS()
|
|
codec := rpcClient(t, s)
|
|
testutil.WaitForLeader(t, s.RPC)
|
|
|
|
// Register a job and an allocation
|
|
job := registerAndVerifyJob(s, t, prefix, 0)
|
|
alloc := mock.Alloc()
|
|
alloc.JobID = job.ID
|
|
alloc.Namespace = job.Namespace
|
|
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(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{alloc}); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
req := &structs.SearchRequest{
|
|
Context: structs.All,
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
Namespace: job.Namespace,
|
|
},
|
|
}
|
|
|
|
// req.Prefix = "example-te": 9
|
|
for i := 1; i < len(prefix); i++ {
|
|
req.Prefix = prefix[:i]
|
|
var resp structs.SearchResponse
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Search.PrefixSearch", req, &resp))
|
|
assert.Equal(1, len(resp.Matches[structs.Jobs]))
|
|
assert.Equal(job.ID, resp.Matches[structs.Jobs][0])
|
|
assert.EqualValues(jobIndex, resp.Index)
|
|
}
|
|
}
|
|
|
|
func TestSearch_PrefixSearch_All_LongJob(t *testing.T) {
|
|
t.Parallel()
|
|
assert := assert.New(t)
|
|
prefix := strings.Repeat("a", 100)
|
|
|
|
s, cleanupS := TestServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
})
|
|
defer cleanupS()
|
|
codec := rpcClient(t, s)
|
|
testutil.WaitForLeader(t, s.RPC)
|
|
|
|
// Register a job and an allocation
|
|
job := registerAndVerifyJob(s, t, prefix, 0)
|
|
alloc := mock.Alloc()
|
|
alloc.JobID = job.ID
|
|
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(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{alloc}); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
req := &structs.SearchRequest{
|
|
Prefix: prefix,
|
|
Context: structs.All,
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
Namespace: job.Namespace,
|
|
},
|
|
}
|
|
|
|
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(job.ID, resp.Matches[structs.Jobs][0])
|
|
assert.EqualValues(jobIndex, resp.Index)
|
|
}
|
|
|
|
// truncate should limit results to 20
|
|
func TestSearch_PrefixSearch_Truncate(t *testing.T) {
|
|
t.Parallel()
|
|
assert := assert.New(t)
|
|
prefix := "aaaaaaaa-e8f7-fd38-c855-ab94ceb8970"
|
|
|
|
s, cleanupS := TestServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
})
|
|
defer cleanupS()
|
|
codec := rpcClient(t, s)
|
|
testutil.WaitForLeader(t, s.RPC)
|
|
|
|
var job *structs.Job
|
|
for counter := 0; counter < 25; counter++ {
|
|
job = registerAndVerifyJob(s, t, prefix, counter)
|
|
}
|
|
|
|
req := &structs.SearchRequest{
|
|
Prefix: prefix,
|
|
Context: structs.Jobs,
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
Namespace: job.Namespace,
|
|
},
|
|
}
|
|
|
|
var resp structs.SearchResponse
|
|
if err := msgpackrpc.CallWithCodec(codec, "Search.PrefixSearch", req, &resp); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
assert.Equal(20, len(resp.Matches[structs.Jobs]))
|
|
assert.Equal(resp.Truncations[structs.Jobs], true)
|
|
assert.Equal(uint64(jobIndex), resp.Index)
|
|
}
|
|
|
|
func TestSearch_PrefixSearch_AllWithJob(t *testing.T) {
|
|
t.Parallel()
|
|
assert := assert.New(t)
|
|
prefix := "aaaaaaaa-e8f7-fd38-c855-ab94ceb8970"
|
|
|
|
s, cleanupS := TestServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
})
|
|
|
|
defer cleanupS()
|
|
codec := rpcClient(t, s)
|
|
testutil.WaitForLeader(t, s.RPC)
|
|
|
|
job := registerAndVerifyJob(s, t, prefix, 0)
|
|
|
|
eval1 := mock.Eval()
|
|
eval1.ID = job.ID
|
|
s.fsm.State().UpsertEvals(structs.MsgTypeTestSetup, 2000, []*structs.Evaluation{eval1})
|
|
|
|
req := &structs.SearchRequest{
|
|
Prefix: prefix,
|
|
Context: structs.All,
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
Namespace: job.Namespace,
|
|
},
|
|
}
|
|
|
|
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(job.ID, resp.Matches[structs.Jobs][0])
|
|
|
|
assert.Equal(1, len(resp.Matches[structs.Evals]))
|
|
assert.Equal(eval1.ID, resp.Matches[structs.Evals][0])
|
|
}
|
|
|
|
func TestSearch_PrefixSearch_Evals(t *testing.T) {
|
|
t.Parallel()
|
|
assert := assert.New(t)
|
|
|
|
s, cleanupS := TestServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
})
|
|
defer cleanupS()
|
|
codec := rpcClient(t, s)
|
|
testutil.WaitForLeader(t, s.RPC)
|
|
|
|
eval1 := mock.Eval()
|
|
s.fsm.State().UpsertEvals(structs.MsgTypeTestSetup, 2000, []*structs.Evaluation{eval1})
|
|
|
|
prefix := eval1.ID[:len(eval1.ID)-2]
|
|
|
|
req := &structs.SearchRequest{
|
|
Prefix: prefix,
|
|
Context: structs.Evals,
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
Namespace: eval1.Namespace,
|
|
},
|
|
}
|
|
|
|
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.Evals]))
|
|
assert.Equal(eval1.ID, resp.Matches[structs.Evals][0])
|
|
assert.Equal(resp.Truncations[structs.Evals], false)
|
|
|
|
assert.Equal(uint64(2000), resp.Index)
|
|
}
|
|
|
|
func TestSearch_PrefixSearch_Allocation(t *testing.T) {
|
|
t.Parallel()
|
|
assert := assert.New(t)
|
|
|
|
s, cleanupS := TestServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
})
|
|
defer cleanupS()
|
|
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(structs.MsgTypeTestSetup, 90, []*structs.Allocation{alloc}); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
prefix := alloc.ID[:len(alloc.ID)-2]
|
|
|
|
req := &structs.SearchRequest{
|
|
Prefix: prefix,
|
|
Context: structs.Allocs,
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
Namespace: alloc.Namespace,
|
|
},
|
|
}
|
|
|
|
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.Equal(uint64(90), resp.Index)
|
|
}
|
|
|
|
func TestSearch_PrefixSearch_All_UUID(t *testing.T) {
|
|
t.Parallel()
|
|
assert := assert.New(t)
|
|
|
|
s, cleanupS := TestServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
})
|
|
defer cleanupS()
|
|
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(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{alloc}); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
node := mock.Node()
|
|
if err := state.UpsertNode(structs.MsgTypeTestSetup, 1001, node); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
eval1 := mock.Eval()
|
|
eval1.ID = node.ID
|
|
if err := state.UpsertEvals(structs.MsgTypeTestSetup, 1002, []*structs.Evaluation{eval1}); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
req := &structs.SearchRequest{
|
|
Context: structs.All,
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
Namespace: eval1.Namespace,
|
|
},
|
|
}
|
|
|
|
for i := 1; i < len(alloc.ID); i++ {
|
|
req.Prefix = alloc.ID[:i]
|
|
var resp structs.SearchResponse
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Search.PrefixSearch", req, &resp))
|
|
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) {
|
|
t.Parallel()
|
|
assert := assert.New(t)
|
|
|
|
s, cleanupS := TestServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
})
|
|
defer cleanupS()
|
|
codec := rpcClient(t, s)
|
|
testutil.WaitForLeader(t, s.RPC)
|
|
|
|
state := s.fsm.State()
|
|
node := mock.Node()
|
|
|
|
if err := state.UpsertNode(structs.MsgTypeTestSetup, 100, node); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
prefix := node.ID[:len(node.ID)-2]
|
|
|
|
req := &structs.SearchRequest{
|
|
Prefix: prefix,
|
|
Context: structs.Nodes,
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
Namespace: structs.DefaultNamespace,
|
|
},
|
|
}
|
|
|
|
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.Nodes]))
|
|
assert.Equal(node.ID, resp.Matches[structs.Nodes][0])
|
|
assert.Equal(false, resp.Truncations[structs.Nodes])
|
|
|
|
assert.Equal(uint64(100), resp.Index)
|
|
}
|
|
|
|
func TestSearch_PrefixSearch_Deployment(t *testing.T) {
|
|
t.Parallel()
|
|
assert := assert.New(t)
|
|
|
|
s, cleanupS := TestServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
})
|
|
defer cleanupS()
|
|
codec := rpcClient(t, s)
|
|
testutil.WaitForLeader(t, s.RPC)
|
|
|
|
deployment := mock.Deployment()
|
|
s.fsm.State().UpsertDeployment(2000, deployment)
|
|
|
|
prefix := deployment.ID[:len(deployment.ID)-2]
|
|
|
|
req := &structs.SearchRequest{
|
|
Prefix: prefix,
|
|
Context: structs.Deployments,
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
Namespace: deployment.Namespace,
|
|
},
|
|
}
|
|
|
|
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.Deployments]))
|
|
assert.Equal(deployment.ID, resp.Matches[structs.Deployments][0])
|
|
assert.Equal(resp.Truncations[structs.Deployments], false)
|
|
|
|
assert.Equal(uint64(2000), resp.Index)
|
|
}
|
|
|
|
func TestSearch_PrefixSearch_AllContext(t *testing.T) {
|
|
t.Parallel()
|
|
assert := assert.New(t)
|
|
|
|
s, cleanupS := TestServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
})
|
|
|
|
defer cleanupS()
|
|
codec := rpcClient(t, s)
|
|
testutil.WaitForLeader(t, s.RPC)
|
|
|
|
state := s.fsm.State()
|
|
node := mock.Node()
|
|
|
|
if err := state.UpsertNode(structs.MsgTypeTestSetup, 100, node); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
eval1 := mock.Eval()
|
|
eval1.ID = node.ID
|
|
if err := state.UpsertEvals(structs.MsgTypeTestSetup, 1000, []*structs.Evaluation{eval1}); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
prefix := node.ID[:len(node.ID)-2]
|
|
|
|
req := &structs.SearchRequest{
|
|
Prefix: prefix,
|
|
Context: structs.All,
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
Namespace: eval1.Namespace,
|
|
},
|
|
}
|
|
|
|
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.Nodes]))
|
|
assert.Equal(1, len(resp.Matches[structs.Evals]))
|
|
|
|
assert.Equal(node.ID, resp.Matches[structs.Nodes][0])
|
|
assert.Equal(eval1.ID, resp.Matches[structs.Evals][0])
|
|
|
|
assert.Equal(uint64(1000), resp.Index)
|
|
}
|
|
|
|
// Tests that the top 20 matches are returned when no prefix is set
|
|
func TestSearch_PrefixSearch_NoPrefix(t *testing.T) {
|
|
t.Parallel()
|
|
assert := assert.New(t)
|
|
prefix := "aaaaaaaa-e8f7-fd38-c855-ab94ceb8970"
|
|
|
|
s, cleanupS := TestServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
})
|
|
defer cleanupS()
|
|
codec := rpcClient(t, s)
|
|
testutil.WaitForLeader(t, s.RPC)
|
|
|
|
job := registerAndVerifyJob(s, t, prefix, 0)
|
|
|
|
req := &structs.SearchRequest{
|
|
Prefix: "",
|
|
Context: structs.Jobs,
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
Namespace: job.Namespace,
|
|
},
|
|
}
|
|
|
|
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(job.ID, resp.Matches[structs.Jobs][0])
|
|
assert.Equal(uint64(jobIndex), resp.Index)
|
|
}
|
|
|
|
// Tests that the zero matches are returned when a prefix has no matching
|
|
// results
|
|
func TestSearch_PrefixSearch_NoMatches(t *testing.T) {
|
|
t.Parallel()
|
|
assert := assert.New(t)
|
|
prefix := "aaaaaaaa-e8f7-fd38-c855-ab94ceb8970"
|
|
|
|
s, cleanupS := TestServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
})
|
|
defer cleanupS()
|
|
codec := rpcClient(t, s)
|
|
testutil.WaitForLeader(t, s.RPC)
|
|
|
|
req := &structs.SearchRequest{
|
|
Prefix: prefix,
|
|
Context: structs.Jobs,
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
Namespace: structs.DefaultNamespace,
|
|
},
|
|
}
|
|
|
|
var resp structs.SearchResponse
|
|
if err := msgpackrpc.CallWithCodec(codec, "Search.PrefixSearch", req, &resp); err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
assert.Equal(0, len(resp.Matches[structs.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 TestSearch_PrefixSearch_RoundDownToEven(t *testing.T) {
|
|
t.Parallel()
|
|
assert := assert.New(t)
|
|
id1 := "aaafaaaa-e8f7-fd38-c855-ab94ceb89"
|
|
id2 := "aaafeaaa-e8f7-fd38-c855-ab94ceb89"
|
|
prefix := "aaafa"
|
|
|
|
s, cleanupS := TestServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
})
|
|
defer cleanupS()
|
|
codec := rpcClient(t, s)
|
|
testutil.WaitForLeader(t, s.RPC)
|
|
|
|
job := registerAndVerifyJob(s, t, id1, 0)
|
|
registerAndVerifyJob(s, t, id2, 50)
|
|
|
|
req := &structs.SearchRequest{
|
|
Prefix: prefix,
|
|
Context: structs.Jobs,
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
Namespace: job.Namespace,
|
|
},
|
|
}
|
|
|
|
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(job.ID, resp.Matches[structs.Jobs][0])
|
|
}
|
|
|
|
func TestSearch_PrefixSearch_MultiRegion(t *testing.T) {
|
|
t.Parallel()
|
|
assert := assert.New(t)
|
|
jobName := "exampleexample"
|
|
|
|
s1, cleanupS1 := TestServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
c.Region = "foo"
|
|
})
|
|
defer cleanupS1()
|
|
|
|
s2, cleanupS2 := TestServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
c.Region = "bar"
|
|
})
|
|
defer cleanupS2()
|
|
|
|
TestJoin(t, s1, s2)
|
|
testutil.WaitForLeader(t, s1.RPC)
|
|
|
|
job := registerAndVerifyJob(s1, t, jobName, 0)
|
|
|
|
req := &structs.SearchRequest{
|
|
Prefix: "",
|
|
Context: structs.Jobs,
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "foo",
|
|
Namespace: job.Namespace,
|
|
},
|
|
}
|
|
|
|
codec := rpcClient(t, s2)
|
|
|
|
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(job.ID, resp.Matches[structs.Jobs][0])
|
|
assert.Equal(uint64(jobIndex), resp.Index)
|
|
}
|
|
|
|
func TestSearch_PrefixSearch_CSIPlugin(t *testing.T) {
|
|
t.Parallel()
|
|
assert := assert.New(t)
|
|
|
|
s, cleanupS := TestServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
})
|
|
defer cleanupS()
|
|
codec := rpcClient(t, s)
|
|
testutil.WaitForLeader(t, s.RPC)
|
|
|
|
id := uuid.Generate()
|
|
state.CreateTestCSIPlugin(s.fsm.State(), id)
|
|
|
|
prefix := id[:len(id)-2]
|
|
|
|
req := &structs.SearchRequest{
|
|
Prefix: prefix,
|
|
Context: structs.Plugins,
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
},
|
|
}
|
|
|
|
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.Plugins]))
|
|
assert.Equal(id, resp.Matches[structs.Plugins][0])
|
|
assert.Equal(resp.Truncations[structs.Plugins], false)
|
|
}
|
|
|
|
func TestSearch_PrefixSearch_CSIVolume(t *testing.T) {
|
|
t.Parallel()
|
|
assert := assert.New(t)
|
|
|
|
s, cleanupS := TestServer(t, func(c *Config) {
|
|
c.NumSchedulers = 0
|
|
})
|
|
defer cleanupS()
|
|
codec := rpcClient(t, s)
|
|
testutil.WaitForLeader(t, s.RPC)
|
|
|
|
id := uuid.Generate()
|
|
err := s.fsm.State().CSIVolumeRegister(1000, []*structs.CSIVolume{{
|
|
ID: id,
|
|
Namespace: structs.DefaultNamespace,
|
|
PluginID: "glade",
|
|
}})
|
|
require.NoError(t, err)
|
|
|
|
prefix := id[:len(id)-2]
|
|
|
|
req := &structs.SearchRequest{
|
|
Prefix: prefix,
|
|
Context: structs.Volumes,
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
Namespace: structs.DefaultNamespace,
|
|
},
|
|
}
|
|
|
|
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.Volumes]))
|
|
assert.Equal(id, resp.Matches[structs.Volumes][0])
|
|
assert.Equal(resp.Truncations[structs.Volumes], false)
|
|
}
|