github.com/hernad/nomad@v1.6.112/nomad/eval_endpoint_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package nomad
     5  
     6  import (
     7  	"fmt"
     8  	"reflect"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	memdb "github.com/hashicorp/go-memdb"
    14  	"github.com/hashicorp/go-set"
    15  	msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc"
    16  	"github.com/hernad/nomad/acl"
    17  	"github.com/hernad/nomad/ci"
    18  	"github.com/hernad/nomad/helper/uuid"
    19  	"github.com/hernad/nomad/nomad/mock"
    20  	"github.com/hernad/nomad/nomad/structs"
    21  	"github.com/hernad/nomad/scheduler"
    22  	"github.com/hernad/nomad/testutil"
    23  	"github.com/shoenig/test/must"
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/require"
    26  )
    27  
    28  func TestEvalEndpoint_GetEval(t *testing.T) {
    29  	ci.Parallel(t)
    30  
    31  	s1, cleanupS1 := TestServer(t, nil)
    32  	defer cleanupS1()
    33  	codec := rpcClient(t, s1)
    34  	testutil.WaitForLeader(t, s1.RPC)
    35  
    36  	// Create the register request
    37  	eval1 := mock.Eval()
    38  	eval2 := mock.Eval()
    39  
    40  	// Link the evals
    41  	eval1.NextEval = eval2.ID
    42  	eval2.PreviousEval = eval1.ID
    43  
    44  	err := s1.fsm.State().UpsertEvals(structs.MsgTypeTestSetup, 1000, []*structs.Evaluation{eval1, eval2})
    45  	require.NoError(t, err)
    46  
    47  	t.Run("lookup eval", func(t *testing.T) {
    48  		get := &structs.EvalSpecificRequest{
    49  			EvalID:       eval1.ID,
    50  			QueryOptions: structs.QueryOptions{Region: "global"},
    51  		}
    52  		var resp structs.SingleEvalResponse
    53  		err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", get, &resp)
    54  		require.NoError(t, err)
    55  		require.EqualValues(t, 1000, resp.Index, "bad index")
    56  		require.Equal(t, eval1, resp.Eval)
    57  	})
    58  
    59  	t.Run("lookup non-existing eval", func(t *testing.T) {
    60  		get := &structs.EvalSpecificRequest{
    61  			EvalID:       uuid.Generate(),
    62  			QueryOptions: structs.QueryOptions{Region: "global"},
    63  		}
    64  		var resp structs.SingleEvalResponse
    65  		err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", get, &resp)
    66  		require.NoError(t, err)
    67  		require.EqualValues(t, 1000, resp.Index, "bad index")
    68  		require.Nil(t, resp.Eval, "unexpected eval")
    69  	})
    70  
    71  	t.Run("lookup related evals", func(t *testing.T) {
    72  		get := &structs.EvalSpecificRequest{
    73  			EvalID:         eval1.ID,
    74  			QueryOptions:   structs.QueryOptions{Region: "global"},
    75  			IncludeRelated: true,
    76  		}
    77  		var resp structs.SingleEvalResponse
    78  		err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", get, &resp)
    79  		require.NoError(t, err)
    80  		require.EqualValues(t, 1000, resp.Index, "bad index")
    81  		require.Equal(t, eval1.ID, resp.Eval.ID)
    82  
    83  		// Make sure we didn't modify the eval on a read request.
    84  		require.Nil(t, eval1.RelatedEvals)
    85  
    86  		// Check for the related evals
    87  		expected := []*structs.EvaluationStub{
    88  			eval2.Stub(),
    89  		}
    90  		require.Equal(t, expected, resp.Eval.RelatedEvals)
    91  	})
    92  }
    93  
    94  func TestEvalEndpoint_GetEval_ACL(t *testing.T) {
    95  	ci.Parallel(t)
    96  
    97  	s1, root, cleanupS1 := TestACLServer(t, nil)
    98  	defer cleanupS1()
    99  	codec := rpcClient(t, s1)
   100  	testutil.WaitForLeader(t, s1.RPC)
   101  	assert := assert.New(t)
   102  
   103  	// Create the register request
   104  	eval1 := mock.Eval()
   105  	state := s1.fsm.State()
   106  	state.UpsertEvals(structs.MsgTypeTestSetup, 1000, []*structs.Evaluation{eval1})
   107  
   108  	// Create ACL tokens
   109  	validToken := mock.CreatePolicyAndToken(t, state, 1003, "test-valid",
   110  		mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob}))
   111  	invalidToken := mock.CreatePolicyAndToken(t, state, 1001, "test-invalid",
   112  		mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListJobs}))
   113  
   114  	get := &structs.EvalSpecificRequest{
   115  		EvalID:       eval1.ID,
   116  		QueryOptions: structs.QueryOptions{Region: "global"},
   117  	}
   118  
   119  	// Try with no token and expect permission denied
   120  	{
   121  		var resp structs.SingleEvalResponse
   122  		err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", get, &resp)
   123  		assert.NotNil(err)
   124  		assert.Contains(err.Error(), structs.ErrPermissionDenied.Error())
   125  	}
   126  
   127  	// Try with an invalid token and expect permission denied
   128  	{
   129  		get.AuthToken = invalidToken.SecretID
   130  		var resp structs.SingleEvalResponse
   131  		err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", get, &resp)
   132  		assert.NotNil(err)
   133  		assert.Contains(err.Error(), structs.ErrPermissionDenied.Error())
   134  	}
   135  
   136  	// Lookup the eval using a valid token
   137  	{
   138  		get.AuthToken = validToken.SecretID
   139  		var resp structs.SingleEvalResponse
   140  		assert.Nil(msgpackrpc.CallWithCodec(codec, "Eval.GetEval", get, &resp))
   141  		assert.Equal(uint64(1000), resp.Index, "Bad index: %d %d", resp.Index, 1000)
   142  		assert.Equal(eval1, resp.Eval)
   143  	}
   144  
   145  	// Lookup the eval using a root token
   146  	{
   147  		get.AuthToken = root.SecretID
   148  		var resp structs.SingleEvalResponse
   149  		assert.Nil(msgpackrpc.CallWithCodec(codec, "Eval.GetEval", get, &resp))
   150  		assert.Equal(uint64(1000), resp.Index, "Bad index: %d %d", resp.Index, 1000)
   151  		assert.Equal(eval1, resp.Eval)
   152  	}
   153  }
   154  
   155  func TestEvalEndpoint_GetEval_Blocking(t *testing.T) {
   156  	ci.Parallel(t)
   157  
   158  	s1, cleanupS1 := TestServer(t, nil)
   159  	defer cleanupS1()
   160  	state := s1.fsm.State()
   161  	codec := rpcClient(t, s1)
   162  	testutil.WaitForLeader(t, s1.RPC)
   163  
   164  	// Create the evals
   165  	eval1 := mock.Eval()
   166  	eval2 := mock.Eval()
   167  
   168  	// First create an unrelated eval
   169  	time.AfterFunc(100*time.Millisecond, func() {
   170  		err := state.UpsertEvals(structs.MsgTypeTestSetup, 100, []*structs.Evaluation{eval1})
   171  		if err != nil {
   172  			t.Fatalf("err: %v", err)
   173  		}
   174  	})
   175  
   176  	// Upsert the eval we are watching later
   177  	time.AfterFunc(200*time.Millisecond, func() {
   178  		err := state.UpsertEvals(structs.MsgTypeTestSetup, 200, []*structs.Evaluation{eval2})
   179  		if err != nil {
   180  			t.Fatalf("err: %v", err)
   181  		}
   182  	})
   183  
   184  	// Lookup the eval
   185  	req := &structs.EvalSpecificRequest{
   186  		EvalID: eval2.ID,
   187  		QueryOptions: structs.QueryOptions{
   188  			Region:        "global",
   189  			MinQueryIndex: 150,
   190  		},
   191  	}
   192  	var resp structs.SingleEvalResponse
   193  	start := time.Now()
   194  	if err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", req, &resp); err != nil {
   195  		t.Fatalf("err: %v", err)
   196  	}
   197  
   198  	if elapsed := time.Since(start); elapsed < 200*time.Millisecond {
   199  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
   200  	}
   201  	if resp.Index != 200 {
   202  		t.Fatalf("Bad index: %d %d", resp.Index, 200)
   203  	}
   204  	if resp.Eval == nil || resp.Eval.ID != eval2.ID {
   205  		t.Fatalf("bad: %#v", resp.Eval)
   206  	}
   207  
   208  	// Eval delete triggers watches
   209  	time.AfterFunc(100*time.Millisecond, func() {
   210  		err := state.DeleteEval(300, []string{eval2.ID}, []string{}, false)
   211  		if err != nil {
   212  			t.Fatalf("err: %v", err)
   213  		}
   214  	})
   215  
   216  	req.QueryOptions.MinQueryIndex = 250
   217  	var resp2 structs.SingleEvalResponse
   218  	start = time.Now()
   219  	if err := msgpackrpc.CallWithCodec(codec, "Eval.GetEval", req, &resp2); err != nil {
   220  		t.Fatalf("err: %v", err)
   221  	}
   222  
   223  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
   224  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp2)
   225  	}
   226  	if resp2.Index != 300 {
   227  		t.Fatalf("Bad index: %d %d", resp2.Index, 300)
   228  	}
   229  	if resp2.Eval != nil {
   230  		t.Fatalf("bad: %#v", resp2.Eval)
   231  	}
   232  }
   233  
   234  func TestEvalEndpoint_Dequeue(t *testing.T) {
   235  	ci.Parallel(t)
   236  
   237  	s1, cleanupS1 := TestServer(t, func(c *Config) {
   238  		c.NumSchedulers = 0 // Prevent automatic dequeue
   239  	})
   240  	defer cleanupS1()
   241  	codec := rpcClient(t, s1)
   242  	testutil.WaitForLeader(t, s1.RPC)
   243  
   244  	// Create the register request
   245  	eval1 := mock.Eval()
   246  	s1.evalBroker.Enqueue(eval1)
   247  
   248  	// Dequeue the eval
   249  	get := &structs.EvalDequeueRequest{
   250  		Schedulers:       defaultSched,
   251  		SchedulerVersion: scheduler.SchedulerVersion,
   252  		WriteRequest:     structs.WriteRequest{Region: "global"},
   253  	}
   254  	var resp structs.EvalDequeueResponse
   255  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Dequeue", get, &resp); err != nil {
   256  		t.Fatalf("err: %v", err)
   257  	}
   258  
   259  	if !reflect.DeepEqual(eval1, resp.Eval) {
   260  		t.Fatalf("bad: %v %v", eval1, resp.Eval)
   261  	}
   262  
   263  	// Ensure outstanding
   264  	token, ok := s1.evalBroker.Outstanding(eval1.ID)
   265  	if !ok {
   266  		t.Fatalf("should be outstanding")
   267  	}
   268  	if token != resp.Token {
   269  		t.Fatalf("bad token: %#v %#v", token, resp.Token)
   270  	}
   271  
   272  	if resp.WaitIndex != eval1.ModifyIndex {
   273  		t.Fatalf("bad wait index; got %d; want %d", resp.WaitIndex, eval1.ModifyIndex)
   274  	}
   275  }
   276  
   277  // TestEvalEndpoint_Dequeue_WaitIndex_Snapshot asserts that an eval's wait
   278  // index will be equal to the highest eval modify index in the state store.
   279  func TestEvalEndpoint_Dequeue_WaitIndex_Snapshot(t *testing.T) {
   280  	ci.Parallel(t)
   281  
   282  	s1, cleanupS1 := TestServer(t, func(c *Config) {
   283  		c.NumSchedulers = 0 // Prevent automatic dequeue
   284  	})
   285  	defer cleanupS1()
   286  	codec := rpcClient(t, s1)
   287  	testutil.WaitForLeader(t, s1.RPC)
   288  
   289  	// Create the register request
   290  	eval1 := mock.Eval()
   291  	eval2 := mock.Eval()
   292  	eval2.JobID = eval1.JobID
   293  	s1.fsm.State().UpsertEvals(structs.MsgTypeTestSetup, 1000, []*structs.Evaluation{eval1})
   294  	s1.evalBroker.Enqueue(eval1)
   295  	s1.fsm.State().UpsertEvals(structs.MsgTypeTestSetup, 1001, []*structs.Evaluation{eval2})
   296  
   297  	// Dequeue the eval
   298  	get := &structs.EvalDequeueRequest{
   299  		Schedulers:       defaultSched,
   300  		SchedulerVersion: scheduler.SchedulerVersion,
   301  		WriteRequest:     structs.WriteRequest{Region: "global"},
   302  	}
   303  	var resp structs.EvalDequeueResponse
   304  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Dequeue", get, &resp); err != nil {
   305  		t.Fatalf("err: %v", err)
   306  	}
   307  
   308  	if !reflect.DeepEqual(eval1, resp.Eval) {
   309  		t.Fatalf("bad: %v %v", eval1, resp.Eval)
   310  	}
   311  
   312  	// Ensure outstanding
   313  	token, ok := s1.evalBroker.Outstanding(eval1.ID)
   314  	if !ok {
   315  		t.Fatalf("should be outstanding")
   316  	}
   317  	if token != resp.Token {
   318  		t.Fatalf("bad token: %#v %#v", token, resp.Token)
   319  	}
   320  
   321  	if resp.WaitIndex != 1001 {
   322  		t.Fatalf("bad wait index; got %d; want %d", resp.WaitIndex, 1001)
   323  	}
   324  }
   325  
   326  // TestEvalEndpoint_Dequeue_WaitIndex_Eval asserts that an eval's wait index
   327  // will be its own modify index if its modify index is greater than all of the
   328  // indexes in the state store. This can happen if Dequeue receives an eval that
   329  // has not yet been applied from the Raft log to the local node's state store.
   330  func TestEvalEndpoint_Dequeue_WaitIndex_Eval(t *testing.T) {
   331  	ci.Parallel(t)
   332  	s1, cleanupS1 := TestServer(t, func(c *Config) {
   333  		c.NumSchedulers = 0 // Prevent automatic dequeue
   334  	})
   335  	defer cleanupS1()
   336  	codec := rpcClient(t, s1)
   337  	testutil.WaitForLeader(t, s1.RPC)
   338  
   339  	// Create the register request but only upsert 1 into the state store
   340  	eval1 := mock.Eval()
   341  	eval2 := mock.Eval()
   342  	eval2.JobID = eval1.JobID
   343  	s1.fsm.State().UpsertEvals(structs.MsgTypeTestSetup, 1000, []*structs.Evaluation{eval1})
   344  	eval2.ModifyIndex = 1001
   345  	s1.evalBroker.Enqueue(eval2)
   346  
   347  	// Dequeue the eval
   348  	get := &structs.EvalDequeueRequest{
   349  		Schedulers:       defaultSched,
   350  		SchedulerVersion: scheduler.SchedulerVersion,
   351  		WriteRequest:     structs.WriteRequest{Region: "global"},
   352  	}
   353  	var resp structs.EvalDequeueResponse
   354  	require.NoError(t, msgpackrpc.CallWithCodec(codec, "Eval.Dequeue", get, &resp))
   355  	require.Equal(t, eval2, resp.Eval)
   356  
   357  	// Ensure outstanding
   358  	token, ok := s1.evalBroker.Outstanding(eval2.ID)
   359  	require.True(t, ok)
   360  	require.Equal(t, resp.Token, token)
   361  
   362  	// WaitIndex should be equal to the max ModifyIndex - even when that
   363  	// modify index is of the dequeued eval which has yet to be applied to
   364  	// the state store.
   365  	require.Equal(t, eval2.ModifyIndex, resp.WaitIndex)
   366  }
   367  
   368  func TestEvalEndpoint_Dequeue_UpdateWaitIndex(t *testing.T) {
   369  	// test enqueuing an eval, updating a plan result for the same eval and de-queueing the eval
   370  	ci.Parallel(t)
   371  	s1, cleanupS1 := TestServer(t, func(c *Config) {
   372  		c.NumSchedulers = 0 // Prevent automatic dequeue
   373  	})
   374  	defer cleanupS1()
   375  	codec := rpcClient(t, s1)
   376  	testutil.WaitForLeader(t, s1.RPC)
   377  
   378  	alloc := mock.Alloc()
   379  	job := alloc.Job
   380  	alloc.Job = nil
   381  
   382  	state := s1.fsm.State()
   383  
   384  	if err := state.UpsertJob(structs.MsgTypeTestSetup, 999, nil, job); err != nil {
   385  		t.Fatalf("err: %v", err)
   386  	}
   387  
   388  	eval := mock.Eval()
   389  	eval.JobID = job.ID
   390  
   391  	// Create an eval
   392  	if err := state.UpsertEvals(structs.MsgTypeTestSetup, 1, []*structs.Evaluation{eval}); err != nil {
   393  		t.Fatalf("err: %v", err)
   394  	}
   395  
   396  	s1.evalBroker.Enqueue(eval)
   397  
   398  	// Create a plan result and apply it with a later index
   399  	res := structs.ApplyPlanResultsRequest{
   400  		AllocUpdateRequest: structs.AllocUpdateRequest{
   401  			Alloc: []*structs.Allocation{alloc},
   402  			Job:   job,
   403  		},
   404  		EvalID: eval.ID,
   405  	}
   406  	assert := assert.New(t)
   407  	err := state.UpsertPlanResults(structs.MsgTypeTestSetup, 1000, &res)
   408  	assert.Nil(err)
   409  
   410  	// Dequeue the eval
   411  	get := &structs.EvalDequeueRequest{
   412  		Schedulers:       defaultSched,
   413  		SchedulerVersion: scheduler.SchedulerVersion,
   414  		WriteRequest:     structs.WriteRequest{Region: "global"},
   415  	}
   416  	var resp structs.EvalDequeueResponse
   417  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Dequeue", get, &resp); err != nil {
   418  		t.Fatalf("err: %v", err)
   419  	}
   420  
   421  	// Ensure outstanding
   422  	token, ok := s1.evalBroker.Outstanding(eval.ID)
   423  	if !ok {
   424  		t.Fatalf("should be outstanding")
   425  	}
   426  	if token != resp.Token {
   427  		t.Fatalf("bad token: %#v %#v", token, resp.Token)
   428  	}
   429  
   430  	if resp.WaitIndex != 1000 {
   431  		t.Fatalf("bad wait index; got %d; want %d", resp.WaitIndex, 1000)
   432  	}
   433  }
   434  
   435  func TestEvalEndpoint_Dequeue_Version_Mismatch(t *testing.T) {
   436  	ci.Parallel(t)
   437  
   438  	s1, cleanupS1 := TestServer(t, func(c *Config) {
   439  		c.NumSchedulers = 0 // Prevent automatic dequeue
   440  	})
   441  	defer cleanupS1()
   442  	codec := rpcClient(t, s1)
   443  	testutil.WaitForLeader(t, s1.RPC)
   444  
   445  	// Create the register request
   446  	eval1 := mock.Eval()
   447  	s1.evalBroker.Enqueue(eval1)
   448  
   449  	// Dequeue the eval
   450  	get := &structs.EvalDequeueRequest{
   451  		Schedulers:       defaultSched,
   452  		SchedulerVersion: 0,
   453  		WriteRequest:     structs.WriteRequest{Region: "global"},
   454  	}
   455  	var resp structs.EvalDequeueResponse
   456  	err := msgpackrpc.CallWithCodec(codec, "Eval.Dequeue", get, &resp)
   457  	if err == nil || !strings.Contains(err.Error(), "scheduler version is 0") {
   458  		t.Fatalf("err: %v", err)
   459  	}
   460  }
   461  
   462  func TestEvalEndpoint_Dequeue_BrokerDisabled(t *testing.T) {
   463  	ci.Parallel(t)
   464  
   465  	s1, cleanupS1 := TestServer(t, func(c *Config) {
   466  		c.NumSchedulers = 0 // Prevent automatic dequeue.
   467  	})
   468  	defer cleanupS1()
   469  	codec := rpcClient(t, s1)
   470  	testutil.WaitForLeader(t, s1.RPC)
   471  
   472  	// Create the register a request.
   473  	eval1 := mock.Eval()
   474  	s1.evalBroker.Enqueue(eval1)
   475  
   476  	// Disable the eval broker and try to dequeue.
   477  	s1.evalBroker.SetEnabled(false)
   478  
   479  	get := &structs.EvalDequeueRequest{
   480  		Schedulers:       defaultSched,
   481  		SchedulerVersion: scheduler.SchedulerVersion,
   482  		WriteRequest:     structs.WriteRequest{Region: "global"},
   483  	}
   484  	var resp structs.EvalDequeueResponse
   485  	require.NoError(t, msgpackrpc.CallWithCodec(codec, "Eval.Dequeue", get, &resp))
   486  	require.Empty(t, resp.Eval)
   487  }
   488  
   489  func TestEvalEndpoint_Ack(t *testing.T) {
   490  	ci.Parallel(t)
   491  
   492  	s1, cleanupS1 := TestServer(t, nil)
   493  	defer cleanupS1()
   494  	codec := rpcClient(t, s1)
   495  
   496  	testutil.WaitForResult(func() (bool, error) {
   497  		return s1.evalBroker.Enabled(), nil
   498  	}, func(err error) {
   499  		t.Fatalf("should enable eval broker")
   500  	})
   501  
   502  	// Create the register request
   503  	eval1 := mock.Eval()
   504  	s1.evalBroker.Enqueue(eval1)
   505  	out, token, err := s1.evalBroker.Dequeue(defaultSched, time.Second)
   506  	if err != nil {
   507  		t.Fatalf("err: %v", err)
   508  	}
   509  	if out == nil {
   510  		t.Fatalf("missing eval")
   511  	}
   512  
   513  	// Ack the eval
   514  	get := &structs.EvalAckRequest{
   515  		EvalID:       out.ID,
   516  		Token:        token,
   517  		WriteRequest: structs.WriteRequest{Region: "global"},
   518  	}
   519  	var resp structs.GenericResponse
   520  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Ack", get, &resp); err != nil {
   521  		t.Fatalf("err: %v", err)
   522  	}
   523  
   524  	// Ensure outstanding
   525  	if _, ok := s1.evalBroker.Outstanding(eval1.ID); ok {
   526  		t.Fatalf("should not be outstanding")
   527  	}
   528  }
   529  
   530  func TestEvalEndpoint_Nack(t *testing.T) {
   531  	ci.Parallel(t)
   532  
   533  	s1, cleanupS1 := TestServer(t, func(c *Config) {
   534  		// Disable all of the schedulers so we can manually dequeue
   535  		// evals and check the queue status
   536  		c.NumSchedulers = 0
   537  	})
   538  	defer cleanupS1()
   539  	codec := rpcClient(t, s1)
   540  
   541  	testutil.WaitForResult(func() (bool, error) {
   542  		return s1.evalBroker.Enabled(), nil
   543  	}, func(err error) {
   544  		t.Fatalf("should enable eval broker")
   545  	})
   546  
   547  	// Create the register request
   548  	eval1 := mock.Eval()
   549  	s1.evalBroker.Enqueue(eval1)
   550  	out, token, _ := s1.evalBroker.Dequeue(defaultSched, time.Second)
   551  	if out == nil {
   552  		t.Fatalf("missing eval")
   553  	}
   554  
   555  	// Nack the eval
   556  	get := &structs.EvalAckRequest{
   557  		EvalID:       out.ID,
   558  		Token:        token,
   559  		WriteRequest: structs.WriteRequest{Region: "global"},
   560  	}
   561  	var resp structs.GenericResponse
   562  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Nack", get, &resp); err != nil {
   563  		t.Fatalf("err: %v", err)
   564  	}
   565  
   566  	// Ensure outstanding
   567  	if _, ok := s1.evalBroker.Outstanding(eval1.ID); ok {
   568  		t.Fatalf("should not be outstanding")
   569  	}
   570  
   571  	// Should get it back
   572  	testutil.WaitForResult(func() (bool, error) {
   573  		out2, _, _ := s1.evalBroker.Dequeue(defaultSched, time.Second)
   574  		if out2 != out {
   575  			return false, fmt.Errorf("nack failed")
   576  		}
   577  
   578  		return true, nil
   579  	}, func(err error) {
   580  		t.Fatal(err)
   581  	})
   582  }
   583  
   584  func TestEvalEndpoint_Update(t *testing.T) {
   585  	ci.Parallel(t)
   586  
   587  	s1, cleanupS1 := TestServer(t, nil)
   588  	defer cleanupS1()
   589  	codec := rpcClient(t, s1)
   590  
   591  	testutil.WaitForResult(func() (bool, error) {
   592  		return s1.evalBroker.Enabled(), nil
   593  	}, func(err error) {
   594  		t.Fatalf("should enable eval broker")
   595  	})
   596  
   597  	// Create the register request
   598  	eval1 := mock.Eval()
   599  	s1.evalBroker.Enqueue(eval1)
   600  	out, token, err := s1.evalBroker.Dequeue(defaultSched, time.Second)
   601  	if err != nil {
   602  		t.Fatalf("err: %v", err)
   603  	}
   604  	if out == nil {
   605  		t.Fatalf("missing eval")
   606  	}
   607  
   608  	// Update the eval
   609  	eval2 := eval1.Copy()
   610  	eval2.Status = structs.EvalStatusComplete
   611  
   612  	get := &structs.EvalUpdateRequest{
   613  		Evals:        []*structs.Evaluation{eval2},
   614  		EvalToken:    token,
   615  		WriteRequest: structs.WriteRequest{Region: "global"},
   616  	}
   617  	var resp structs.GenericResponse
   618  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Update", get, &resp); err != nil {
   619  		t.Fatalf("err: %v", err)
   620  	}
   621  
   622  	// Ensure updated
   623  	ws := memdb.NewWatchSet()
   624  	outE, err := s1.fsm.State().EvalByID(ws, eval2.ID)
   625  	if err != nil {
   626  		t.Fatalf("err: %v", err)
   627  	}
   628  	if outE.Status != structs.EvalStatusComplete {
   629  		t.Fatalf("Bad: %#v", out)
   630  	}
   631  }
   632  
   633  func TestEvalEndpoint_Create(t *testing.T) {
   634  	ci.Parallel(t)
   635  
   636  	s1, cleanupS1 := TestServer(t, func(c *Config) {
   637  		c.NumSchedulers = 0 // Prevent automatic dequeue
   638  	})
   639  	defer cleanupS1()
   640  	codec := rpcClient(t, s1)
   641  
   642  	testutil.WaitForResult(func() (bool, error) {
   643  		return s1.evalBroker.Enabled(), nil
   644  	}, func(err error) {
   645  		t.Fatalf("should enable eval broker")
   646  	})
   647  
   648  	// Create the register request
   649  	prev := mock.Eval()
   650  	s1.evalBroker.Enqueue(prev)
   651  	out, token, err := s1.evalBroker.Dequeue(defaultSched, time.Second)
   652  	if err != nil {
   653  		t.Fatalf("err: %v", err)
   654  	}
   655  	if out == nil {
   656  		t.Fatalf("missing eval")
   657  	}
   658  
   659  	// Create the register request
   660  	eval1 := mock.Eval()
   661  	eval1.PreviousEval = prev.ID
   662  	get := &structs.EvalUpdateRequest{
   663  		Evals:        []*structs.Evaluation{eval1},
   664  		EvalToken:    token,
   665  		WriteRequest: structs.WriteRequest{Region: "global"},
   666  	}
   667  	var resp structs.GenericResponse
   668  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Create", get, &resp); err != nil {
   669  		t.Fatalf("err: %v", err)
   670  	}
   671  
   672  	// Ensure created
   673  	ws := memdb.NewWatchSet()
   674  	outE, err := s1.fsm.State().EvalByID(ws, eval1.ID)
   675  	if err != nil {
   676  		t.Fatalf("err: %v", err)
   677  	}
   678  
   679  	eval1.CreateIndex = resp.Index
   680  	eval1.ModifyIndex = resp.Index
   681  	if !reflect.DeepEqual(eval1, outE) {
   682  		t.Fatalf("Bad: %#v %#v", outE, eval1)
   683  	}
   684  }
   685  
   686  func TestEvalEndpoint_Reap(t *testing.T) {
   687  	ci.Parallel(t)
   688  
   689  	s1, cleanupS1 := TestServer(t, nil)
   690  	defer cleanupS1()
   691  	codec := rpcClient(t, s1)
   692  	testutil.WaitForLeader(t, s1.RPC)
   693  
   694  	// Create the register request
   695  	eval1 := mock.Eval()
   696  	s1.fsm.State().UpsertEvals(structs.MsgTypeTestSetup, 1000, []*structs.Evaluation{eval1})
   697  
   698  	// Reap the eval
   699  	get := &structs.EvalReapRequest{
   700  		Evals:        []string{eval1.ID},
   701  		WriteRequest: structs.WriteRequest{Region: "global"},
   702  	}
   703  	var resp structs.GenericResponse
   704  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Reap", get, &resp); err != nil {
   705  		t.Fatalf("err: %v", err)
   706  	}
   707  	if resp.Index == 0 {
   708  		t.Fatalf("Bad index: %d", resp.Index)
   709  	}
   710  
   711  	// Ensure deleted
   712  	ws := memdb.NewWatchSet()
   713  	outE, err := s1.fsm.State().EvalByID(ws, eval1.ID)
   714  	if err != nil {
   715  		t.Fatalf("err: %v", err)
   716  	}
   717  	if outE != nil {
   718  		t.Fatalf("Bad: %#v", outE)
   719  	}
   720  }
   721  
   722  func TestEvalEndpoint_Delete(t *testing.T) {
   723  	ci.Parallel(t)
   724  
   725  	setup := func(t *testing.T) (*Server, *structs.ACLToken, func()) {
   726  		t.Helper()
   727  		testServer, rootToken, cleanupFn := TestACLServer(t, func(c *Config) {
   728  			c.NumSchedulers = 0
   729  		})
   730  		testutil.WaitForLeader(t, testServer.RPC)
   731  		return testServer, rootToken, cleanupFn
   732  	}
   733  
   734  	// Set the expected eval broker state and scheduler config
   735  	setBrokerEnabled := func(t *testing.T, testServer *Server, enabled bool) {
   736  		t.Helper()
   737  		testServer.evalBroker.SetEnabled(enabled)
   738  
   739  		_, schedulerConfig, err := testServer.fsm.State().SchedulerConfig()
   740  		must.NoError(t, err)
   741  		must.NotNil(t, schedulerConfig)
   742  
   743  		schedulerConfig.PauseEvalBroker = !enabled
   744  		must.NoError(t, testServer.fsm.State().SchedulerSetConfig(10, schedulerConfig))
   745  	}
   746  
   747  	t.Run("unsuccessful delete broker enabled", func(t *testing.T) {
   748  
   749  		testServer, rootToken, cleanup := setup(t)
   750  		defer cleanup()
   751  		codec := rpcClient(t, testServer)
   752  
   753  		// Ensure broker is enabled
   754  		setBrokerEnabled(t, testServer, true)
   755  
   756  		// Create and upsert an evaluation.
   757  		mockEval := mock.Eval()
   758  		must.NoError(t, testServer.fsm.State().UpsertEvals(
   759  			structs.MsgTypeTestSetup, 20, []*structs.Evaluation{mockEval}))
   760  
   761  		// Attempt to delete the eval, which should fail because the
   762  		// eval broker is not paused.
   763  		get := &structs.EvalDeleteRequest{
   764  			EvalIDs: []string{mockEval.ID},
   765  			WriteRequest: structs.WriteRequest{
   766  				Region:    "global",
   767  				AuthToken: rootToken.SecretID},
   768  		}
   769  		var resp structs.EvalDeleteResponse
   770  		err := msgpackrpc.CallWithCodec(codec, structs.EvalDeleteRPCMethod, get, &resp)
   771  		must.EqError(t, err, "eval broker is enabled; eval broker must be paused to delete evals")
   772  	})
   773  
   774  	t.Run("successful delete without ACLs", func(t *testing.T) {
   775  		testServer, testServerCleanup := TestServer(t, func(c *Config) {
   776  			c.NumSchedulers = 0
   777  		})
   778  		defer testServerCleanup()
   779  
   780  		codec := rpcClient(t, testServer)
   781  		testutil.WaitForLeader(t, testServer.RPC)
   782  
   783  		// Ensure broker is disabled
   784  		setBrokerEnabled(t, testServer, false)
   785  
   786  		// Create and upsert an evaluation.
   787  		mockEval := mock.Eval()
   788  		must.NoError(t, testServer.fsm.State().UpsertEvals(
   789  			structs.MsgTypeTestSetup, 10, []*structs.Evaluation{mockEval}))
   790  
   791  		// Attempt to delete the eval, which should succeed as the eval
   792  		// broker is disabled.
   793  		get := &structs.EvalDeleteRequest{
   794  			EvalIDs:      []string{mockEval.ID},
   795  			WriteRequest: structs.WriteRequest{Region: "global"},
   796  		}
   797  		var resp structs.EvalDeleteResponse
   798  		must.NoError(t, msgpackrpc.CallWithCodec(codec, structs.EvalDeleteRPCMethod, get, &resp))
   799  
   800  		// Attempt to read the eval from state; this should not be found.
   801  		ws := memdb.NewWatchSet()
   802  		respEval, err := testServer.fsm.State().EvalByID(ws, mockEval.ID)
   803  		must.Nil(t, err)
   804  		must.Nil(t, respEval)
   805  	})
   806  
   807  	t.Run("successful delete with ACLs", func(t *testing.T) {
   808  
   809  		testServer, rootToken, cleanup := setup(t)
   810  		defer cleanup()
   811  		codec := rpcClient(t, testServer)
   812  
   813  		// Ensure broker is disabled
   814  		setBrokerEnabled(t, testServer, false)
   815  
   816  		// Create and upsert an evaluation.
   817  		mockEval := mock.Eval()
   818  		must.NoError(t, testServer.fsm.State().UpsertEvals(
   819  			structs.MsgTypeTestSetup, 20, []*structs.Evaluation{mockEval}))
   820  
   821  		// Attempt to delete the eval, which should succeed as the eval
   822  		// broker is disabled, and we are using a management token.
   823  		get := &structs.EvalDeleteRequest{
   824  			EvalIDs: []string{mockEval.ID},
   825  			WriteRequest: structs.WriteRequest{
   826  				AuthToken: rootToken.SecretID,
   827  				Region:    "global",
   828  			},
   829  		}
   830  		var resp structs.EvalDeleteResponse
   831  		must.NoError(t, msgpackrpc.CallWithCodec(codec, structs.EvalDeleteRPCMethod, get, &resp))
   832  
   833  		// Attempt to read the eval from state; this should not be found.
   834  		ws := memdb.NewWatchSet()
   835  		respEval, err := testServer.fsm.State().EvalByID(ws, mockEval.ID)
   836  		must.Nil(t, err)
   837  		must.Nil(t, respEval)
   838  	})
   839  
   840  	t.Run("unsuccessful delete with ACLs incorrect token permissions", func(t *testing.T) {
   841  
   842  		testServer, _, cleanup := setup(t)
   843  		defer cleanup()
   844  		codec := rpcClient(t, testServer)
   845  
   846  		// Ensure broker is disabled
   847  		setBrokerEnabled(t, testServer, false)
   848  
   849  		// Create and upsert an evaluation.
   850  		mockEval := mock.Eval()
   851  		must.NoError(t, testServer.fsm.State().UpsertEvals(
   852  			structs.MsgTypeTestSetup, 10, []*structs.Evaluation{mockEval}))
   853  
   854  		nonMgntToken := mock.CreatePolicyAndToken(t, testServer.State(), 20, "test-valid",
   855  			mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilitySubmitJob}))
   856  
   857  		// Attempt to delete the eval, which should not succeed as we
   858  		// are using a non-management token.
   859  		get := &structs.EvalDeleteRequest{
   860  			EvalIDs: []string{mockEval.ID},
   861  			WriteRequest: structs.WriteRequest{
   862  				AuthToken: nonMgntToken.SecretID,
   863  				Region:    "global",
   864  			},
   865  		}
   866  		var resp structs.EvalDeleteResponse
   867  		err := msgpackrpc.CallWithCodec(codec, structs.EvalDeleteRPCMethod, get, &resp)
   868  		must.EqError(t, err, structs.ErrPermissionDenied.Error())
   869  	})
   870  
   871  	t.Run("successful delete by filter", func(t *testing.T) {
   872  
   873  		testServer, rootToken, cleanup := setup(t)
   874  		defer cleanup()
   875  		codec := rpcClient(t, testServer)
   876  
   877  		// Ensure broker is disabled
   878  		setBrokerEnabled(t, testServer, false)
   879  
   880  		evalCount := 10000
   881  		index := uint64(100)
   882  
   883  		store := testServer.fsm.State()
   884  
   885  		// Create a large set of pending evaluations
   886  
   887  		evals := []*structs.Evaluation{}
   888  		for i := 0; i < evalCount; i++ {
   889  			mockEval := mock.Eval()
   890  			evals = append(evals, mockEval)
   891  		}
   892  		must.NoError(t, store.UpsertEvals(
   893  			structs.MsgTypeTestSetup, index, evals))
   894  
   895  		// Create some evaluations we don't want to delete
   896  
   897  		evalsToKeep := []*structs.Evaluation{}
   898  		for i := 0; i < 3; i++ {
   899  			mockEval := mock.Eval()
   900  			mockEval.JobID = "keepme"
   901  			evalsToKeep = append(evalsToKeep, mockEval)
   902  		}
   903  		index++
   904  		must.NoError(t, store.UpsertEvals(
   905  			structs.MsgTypeTestSetup, index, evalsToKeep))
   906  
   907  		// Create a job with running allocs and evaluations those allocs reference
   908  
   909  		job := mock.Job()
   910  		job.ID = "notsafetodelete"
   911  		job.Status = structs.JobStatusRunning
   912  		index++
   913  		must.NoError(t, store.UpsertJob(structs.MsgTypeTestSetup, index, nil, job))
   914  
   915  		evalsNotSafeToDelete := []*structs.Evaluation{}
   916  		for i := 0; i < 3; i++ {
   917  			mockEval := mock.Eval()
   918  			mockEval.JobID = job.ID
   919  			evalsNotSafeToDelete = append(evalsNotSafeToDelete, mockEval)
   920  		}
   921  		index++
   922  		must.NoError(t, store.UpsertEvals(
   923  			structs.MsgTypeTestSetup, index, evalsNotSafeToDelete))
   924  
   925  		allocs := []*structs.Allocation{}
   926  		for i := 0; i < 3; i++ {
   927  			alloc := mock.Alloc()
   928  			alloc.ClientStatus = structs.AllocClientStatusRunning
   929  			alloc.EvalID = evalsNotSafeToDelete[i].ID
   930  			allocs = append(allocs, alloc)
   931  		}
   932  		index++
   933  		must.NoError(t, store.UpsertAllocs(structs.MsgTypeTestSetup, index, allocs))
   934  
   935  		// Delete all the unwanted evals
   936  
   937  		get := &structs.EvalDeleteRequest{
   938  			Filter:       "JobID != \"keepme\"",
   939  			WriteRequest: structs.WriteRequest{AuthToken: rootToken.SecretID, Region: "global"},
   940  		}
   941  		var resp structs.EvalDeleteResponse
   942  		must.NoError(t, msgpackrpc.CallWithCodec(codec, structs.EvalDeleteRPCMethod, get, &resp))
   943  		must.Eq(t, resp.Count, evalCount)
   944  
   945  		// Assert we didn't delete the filtered evals
   946  		gotKeptEvals, err := store.EvalsByJob(nil, job.Namespace, "keepme")
   947  		must.NoError(t, err)
   948  		must.Len(t, 3, gotKeptEvals)
   949  		must.Eq(t, set.From(evalsToKeep), set.From(gotKeptEvals))
   950  
   951  		// Assert we didn't delete the evals that were not safe to delete
   952  		gotNotSafeEvals, err := store.EvalsByJob(nil, job.Namespace, "notsafetodelete")
   953  		must.NoError(t, err)
   954  		must.Len(t, 3, gotNotSafeEvals)
   955  		must.Eq(t, set.From(evalsNotSafeToDelete), set.From(gotNotSafeEvals))
   956  
   957  	})
   958  
   959  }
   960  
   961  func TestEvalEndpoint_List(t *testing.T) {
   962  	ci.Parallel(t)
   963  
   964  	s1, cleanupS1 := TestServer(t, nil)
   965  	defer cleanupS1()
   966  	codec := rpcClient(t, s1)
   967  	testutil.WaitForLeader(t, s1.RPC)
   968  
   969  	// Create the register request
   970  	eval1 := mock.Eval()
   971  	eval1.ID = "aaaaaaaa-3350-4b4b-d185-0e1992ed43e9"
   972  	eval2 := mock.Eval()
   973  	eval2.ID = "aaaabbbb-3350-4b4b-d185-0e1992ed43e9"
   974  	s1.fsm.State().UpsertEvals(structs.MsgTypeTestSetup, 1000, []*structs.Evaluation{eval1, eval2})
   975  
   976  	// Lookup the eval
   977  	get := &structs.EvalListRequest{
   978  		QueryOptions: structs.QueryOptions{
   979  			Region:    "global",
   980  			Namespace: structs.DefaultNamespace,
   981  		},
   982  	}
   983  	var resp structs.EvalListResponse
   984  	if err := msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp); err != nil {
   985  		t.Fatalf("err: %v", err)
   986  	}
   987  	if resp.Index != 1000 {
   988  		t.Fatalf("Bad index: %d %d", resp.Index, 1000)
   989  	}
   990  
   991  	if len(resp.Evaluations) != 2 {
   992  		t.Fatalf("bad: %#v", resp.Evaluations)
   993  	}
   994  
   995  	// Lookup the eval by prefix
   996  	get = &structs.EvalListRequest{
   997  		QueryOptions: structs.QueryOptions{
   998  			Region:    "global",
   999  			Namespace: structs.DefaultNamespace,
  1000  			Prefix:    "aaaabb",
  1001  		},
  1002  	}
  1003  	var resp2 structs.EvalListResponse
  1004  	if err := msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp2); err != nil {
  1005  		t.Fatalf("err: %v", err)
  1006  	}
  1007  	if resp2.Index != 1000 {
  1008  		t.Fatalf("Bad index: %d %d", resp2.Index, 1000)
  1009  	}
  1010  
  1011  	if len(resp2.Evaluations) != 1 {
  1012  		t.Fatalf("bad: %#v", resp2.Evaluations)
  1013  	}
  1014  }
  1015  
  1016  func TestEvalEndpoint_List_order(t *testing.T) {
  1017  	ci.Parallel(t)
  1018  
  1019  	s1, cleanupS1 := TestServer(t, nil)
  1020  	defer cleanupS1()
  1021  	codec := rpcClient(t, s1)
  1022  	testutil.WaitForLeader(t, s1.RPC)
  1023  
  1024  	// Create register requests
  1025  	uuid1 := uuid.Generate()
  1026  	eval1 := mock.Eval()
  1027  	eval1.ID = uuid1
  1028  
  1029  	uuid2 := uuid.Generate()
  1030  	eval2 := mock.Eval()
  1031  	eval2.ID = uuid2
  1032  
  1033  	uuid3 := uuid.Generate()
  1034  	eval3 := mock.Eval()
  1035  	eval3.ID = uuid3
  1036  
  1037  	err := s1.fsm.State().UpsertEvals(structs.MsgTypeTestSetup, 1000, []*structs.Evaluation{eval1})
  1038  	require.NoError(t, err)
  1039  
  1040  	err = s1.fsm.State().UpsertEvals(structs.MsgTypeTestSetup, 1001, []*structs.Evaluation{eval2})
  1041  	require.NoError(t, err)
  1042  
  1043  	err = s1.fsm.State().UpsertEvals(structs.MsgTypeTestSetup, 1002, []*structs.Evaluation{eval3})
  1044  	require.NoError(t, err)
  1045  
  1046  	// update eval2 again so we can later assert create index order did not change
  1047  	err = s1.fsm.State().UpsertEvals(structs.MsgTypeTestSetup, 1003, []*structs.Evaluation{eval2})
  1048  	require.NoError(t, err)
  1049  
  1050  	t.Run("default", func(t *testing.T) {
  1051  		// Lookup the evaluations in the default order (oldest first)
  1052  		get := &structs.EvalListRequest{
  1053  			QueryOptions: structs.QueryOptions{
  1054  				Region:    "global",
  1055  				Namespace: "*",
  1056  			},
  1057  		}
  1058  
  1059  		var resp structs.EvalListResponse
  1060  		err = msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp)
  1061  		require.NoError(t, err)
  1062  		require.Equal(t, uint64(1003), resp.Index)
  1063  		require.Len(t, resp.Evaluations, 3)
  1064  
  1065  		// Assert returned order is by CreateIndex (ascending)
  1066  		require.Equal(t, uint64(1000), resp.Evaluations[0].CreateIndex)
  1067  		require.Equal(t, uuid1, resp.Evaluations[0].ID)
  1068  
  1069  		require.Equal(t, uint64(1001), resp.Evaluations[1].CreateIndex)
  1070  		require.Equal(t, uuid2, resp.Evaluations[1].ID)
  1071  
  1072  		require.Equal(t, uint64(1002), resp.Evaluations[2].CreateIndex)
  1073  		require.Equal(t, uuid3, resp.Evaluations[2].ID)
  1074  	})
  1075  
  1076  	t.Run("reverse", func(t *testing.T) {
  1077  		// Lookup the evaluations in reverse order (newest first)
  1078  		get := &structs.EvalListRequest{
  1079  			QueryOptions: structs.QueryOptions{
  1080  				Region:    "global",
  1081  				Namespace: "*",
  1082  				Reverse:   true,
  1083  			},
  1084  		}
  1085  
  1086  		var resp structs.EvalListResponse
  1087  		err = msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp)
  1088  		require.NoError(t, err)
  1089  		require.Equal(t, uint64(1003), resp.Index)
  1090  		require.Len(t, resp.Evaluations, 3)
  1091  
  1092  		// Assert returned order is by CreateIndex (descending)
  1093  		require.Equal(t, uint64(1002), resp.Evaluations[0].CreateIndex)
  1094  		require.Equal(t, uuid3, resp.Evaluations[0].ID)
  1095  
  1096  		require.Equal(t, uint64(1001), resp.Evaluations[1].CreateIndex)
  1097  		require.Equal(t, uuid2, resp.Evaluations[1].ID)
  1098  
  1099  		require.Equal(t, uint64(1000), resp.Evaluations[2].CreateIndex)
  1100  		require.Equal(t, uuid1, resp.Evaluations[2].ID)
  1101  	})
  1102  }
  1103  
  1104  func TestEvalEndpoint_ListAllNamespaces(t *testing.T) {
  1105  	ci.Parallel(t)
  1106  
  1107  	s1, cleanupS1 := TestServer(t, nil)
  1108  	defer cleanupS1()
  1109  	codec := rpcClient(t, s1)
  1110  	testutil.WaitForLeader(t, s1.RPC)
  1111  
  1112  	// Create the register request
  1113  	eval1 := mock.Eval()
  1114  	eval1.ID = "aaaaaaaa-3350-4b4b-d185-0e1992ed43e9"
  1115  	eval2 := mock.Eval()
  1116  	eval2.ID = "aaaabbbb-3350-4b4b-d185-0e1992ed43e9"
  1117  	s1.fsm.State().UpsertEvals(structs.MsgTypeTestSetup, 1000, []*structs.Evaluation{eval1, eval2})
  1118  
  1119  	// Lookup the eval
  1120  	get := &structs.EvalListRequest{
  1121  		QueryOptions: structs.QueryOptions{
  1122  			Region:    "global",
  1123  			Namespace: "*",
  1124  		},
  1125  	}
  1126  	var resp structs.EvalListResponse
  1127  	if err := msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp); err != nil {
  1128  		t.Fatalf("err: %v", err)
  1129  	}
  1130  	if resp.Index != 1000 {
  1131  		t.Fatalf("Bad index: %d %d", resp.Index, 1000)
  1132  	}
  1133  
  1134  	if len(resp.Evaluations) != 2 {
  1135  		t.Fatalf("bad: %#v", resp.Evaluations)
  1136  	}
  1137  }
  1138  
  1139  func TestEvalEndpoint_List_ACL(t *testing.T) {
  1140  	ci.Parallel(t)
  1141  
  1142  	s1, root, cleanupS1 := TestACLServer(t, nil)
  1143  	defer cleanupS1()
  1144  	codec := rpcClient(t, s1)
  1145  	testutil.WaitForLeader(t, s1.RPC)
  1146  
  1147  	// Create dev namespace
  1148  	devNS := mock.Namespace()
  1149  	devNS.Name = "dev"
  1150  	err := s1.fsm.State().UpsertNamespaces(999, []*structs.Namespace{devNS})
  1151  	require.NoError(t, err)
  1152  
  1153  	// Create the register request
  1154  	eval1 := mock.Eval()
  1155  	eval1.ID = "aaaaaaaa-3350-4b4b-d185-0e1992ed43e9"
  1156  	eval2 := mock.Eval()
  1157  	eval2.ID = "aaaabbbb-3350-4b4b-d185-0e1992ed43e9"
  1158  	eval3 := mock.Eval()
  1159  	eval3.ID = "aaaacccc-3350-4b4b-d185-0e1992ed43e9"
  1160  	eval3.Namespace = devNS.Name
  1161  	state := s1.fsm.State()
  1162  	err = state.UpsertEvals(structs.MsgTypeTestSetup, 1000, []*structs.Evaluation{eval1, eval2, eval3})
  1163  	require.NoError(t, err)
  1164  
  1165  	// Create ACL tokens
  1166  	validToken := mock.CreatePolicyAndToken(t, state, 1003, "test-valid",
  1167  		mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob}))
  1168  	invalidToken := mock.CreatePolicyAndToken(t, state, 1001, "test-invalid",
  1169  		mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListJobs}))
  1170  	devToken := mock.CreatePolicyAndToken(t, state, 1005, "test-dev",
  1171  		mock.NamespacePolicy("dev", "", []string{acl.NamespaceCapabilityReadJob}))
  1172  
  1173  	testCases := []struct {
  1174  		name          string
  1175  		namespace     string
  1176  		token         string
  1177  		expectedEvals []string
  1178  		expectedError string
  1179  	}{
  1180  		{
  1181  			name:          "no token",
  1182  			token:         "",
  1183  			namespace:     structs.DefaultNamespace,
  1184  			expectedError: structs.ErrPermissionDenied.Error(),
  1185  		},
  1186  		{
  1187  			name:          "invalid token",
  1188  			token:         invalidToken.SecretID,
  1189  			namespace:     structs.DefaultNamespace,
  1190  			expectedError: structs.ErrPermissionDenied.Error(),
  1191  		},
  1192  		{
  1193  			name:          "valid token",
  1194  			token:         validToken.SecretID,
  1195  			namespace:     structs.DefaultNamespace,
  1196  			expectedEvals: []string{eval1.ID, eval2.ID},
  1197  		},
  1198  		{
  1199  			name:          "root token default namespace",
  1200  			token:         root.SecretID,
  1201  			namespace:     structs.DefaultNamespace,
  1202  			expectedEvals: []string{eval1.ID, eval2.ID},
  1203  		},
  1204  		{
  1205  			name:          "root token all namespaces",
  1206  			token:         root.SecretID,
  1207  			namespace:     structs.AllNamespacesSentinel,
  1208  			expectedEvals: []string{eval1.ID, eval2.ID, eval3.ID},
  1209  		},
  1210  		{
  1211  			name:          "dev token all namespaces",
  1212  			token:         devToken.SecretID,
  1213  			namespace:     structs.AllNamespacesSentinel,
  1214  			expectedEvals: []string{eval3.ID},
  1215  		},
  1216  	}
  1217  
  1218  	for _, tc := range testCases {
  1219  		t.Run(tc.name, func(t *testing.T) {
  1220  			get := &structs.EvalListRequest{
  1221  				QueryOptions: structs.QueryOptions{
  1222  					AuthToken: tc.token,
  1223  					Region:    "global",
  1224  					Namespace: tc.namespace,
  1225  				},
  1226  			}
  1227  
  1228  			var resp structs.EvalListResponse
  1229  			err := msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp)
  1230  
  1231  			if tc.expectedError != "" {
  1232  				require.Contains(t, err.Error(), tc.expectedError)
  1233  			} else {
  1234  				require.NoError(t, err)
  1235  				require.Equal(t, uint64(1000), resp.Index, "Bad index: %d %d", resp.Index, 1000)
  1236  
  1237  				got := make([]string, len(resp.Evaluations))
  1238  				for i, eval := range resp.Evaluations {
  1239  					got[i] = eval.ID
  1240  				}
  1241  				require.ElementsMatch(t, got, tc.expectedEvals)
  1242  			}
  1243  		})
  1244  	}
  1245  }
  1246  
  1247  func TestEvalEndpoint_List_Blocking(t *testing.T) {
  1248  	ci.Parallel(t)
  1249  
  1250  	s1, cleanupS1 := TestServer(t, nil)
  1251  	defer cleanupS1()
  1252  	state := s1.fsm.State()
  1253  	codec := rpcClient(t, s1)
  1254  	testutil.WaitForLeader(t, s1.RPC)
  1255  
  1256  	// Create the ieval
  1257  	eval := mock.Eval()
  1258  
  1259  	// Upsert eval triggers watches
  1260  	time.AfterFunc(100*time.Millisecond, func() {
  1261  		if err := state.UpsertEvals(structs.MsgTypeTestSetup, 2, []*structs.Evaluation{eval}); err != nil {
  1262  			t.Fatalf("err: %v", err)
  1263  		}
  1264  	})
  1265  
  1266  	req := &structs.EvalListRequest{
  1267  		QueryOptions: structs.QueryOptions{
  1268  			Region:        "global",
  1269  			Namespace:     structs.DefaultNamespace,
  1270  			MinQueryIndex: 1,
  1271  		},
  1272  	}
  1273  	start := time.Now()
  1274  	var resp structs.EvalListResponse
  1275  	if err := msgpackrpc.CallWithCodec(codec, "Eval.List", req, &resp); err != nil {
  1276  		t.Fatalf("err: %v", err)
  1277  	}
  1278  
  1279  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
  1280  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
  1281  	}
  1282  	if resp.Index != 2 {
  1283  		t.Fatalf("Bad index: %d %d", resp.Index, 2)
  1284  	}
  1285  	if len(resp.Evaluations) != 1 || resp.Evaluations[0].ID != eval.ID {
  1286  		t.Fatalf("bad: %#v", resp.Evaluations)
  1287  	}
  1288  
  1289  	// Eval deletion triggers watches
  1290  	time.AfterFunc(100*time.Millisecond, func() {
  1291  		if err := state.DeleteEval(3, []string{eval.ID}, nil, false); err != nil {
  1292  			t.Fatalf("err: %v", err)
  1293  		}
  1294  	})
  1295  
  1296  	req.MinQueryIndex = 2
  1297  	start = time.Now()
  1298  	var resp2 structs.EvalListResponse
  1299  	if err := msgpackrpc.CallWithCodec(codec, "Eval.List", req, &resp2); err != nil {
  1300  		t.Fatalf("err: %v", err)
  1301  	}
  1302  
  1303  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
  1304  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp2)
  1305  	}
  1306  	if resp2.Index != 3 {
  1307  		t.Fatalf("Bad index: %d %d", resp2.Index, 3)
  1308  	}
  1309  	if len(resp2.Evaluations) != 0 {
  1310  		t.Fatalf("bad: %#v", resp2.Evaluations)
  1311  	}
  1312  }
  1313  
  1314  func TestEvalEndpoint_List_PaginationFiltering(t *testing.T) {
  1315  	ci.Parallel(t)
  1316  	s1, _, cleanupS1 := TestACLServer(t, nil)
  1317  	defer cleanupS1()
  1318  	codec := rpcClient(t, s1)
  1319  	testutil.WaitForLeader(t, s1.RPC)
  1320  
  1321  	// Create non-default namespace
  1322  	nondefaultNS := mock.Namespace()
  1323  	nondefaultNS.Name = "non-default"
  1324  	err := s1.fsm.State().UpsertNamespaces(999, []*structs.Namespace{nondefaultNS})
  1325  	require.NoError(t, err)
  1326  
  1327  	// create a set of evals and field values to filter on. these are
  1328  	// in the order that the state store will return them from the
  1329  	// iterator (sorted by create index), for ease of writing tests
  1330  	mocks := []struct {
  1331  		ids       []string
  1332  		namespace string
  1333  		jobID     string
  1334  		status    string
  1335  	}{
  1336  		{ids: []string{"aaaa1111-3350-4b4b-d185-0e1992ed43e9"}, jobID: "example"},                    // 0
  1337  		{ids: []string{"aaaaaa22-3350-4b4b-d185-0e1992ed43e9"}, jobID: "example"},                    // 1
  1338  		{ids: []string{"aaaaaa33-3350-4b4b-d185-0e1992ed43e9"}, namespace: nondefaultNS.Name},        // 2
  1339  		{ids: []string{"aaaaaaaa-3350-4b4b-d185-0e1992ed43e9"}, jobID: "example", status: "blocked"}, // 3
  1340  		{ids: []string{"aaaaaabb-3350-4b4b-d185-0e1992ed43e9"}},                                      // 4
  1341  		{ids: []string{"aaaaaacc-3350-4b4b-d185-0e1992ed43e9"}},                                      // 5
  1342  		{ids: []string{"aaaaaadd-3350-4b4b-d185-0e1992ed43e9"}, jobID: "example"},                    // 6
  1343  		{ids: []string{"aaaaaaee-3350-4b4b-d185-0e1992ed43e9"}, jobID: "example"},                    // 7
  1344  		{ids: []string{"aaaaaaff-3350-4b4b-d185-0e1992ed43e9"}},                                      // 8
  1345  		{ids: []string{"00000111-3350-4b4b-d185-0e1992ed43e9"}},                                      // 9
  1346  		{ids: []string{ // 10
  1347  			"00000222-3350-4b4b-d185-0e1992ed43e9",
  1348  			"00000333-3350-4b4b-d185-0e1992ed43e9",
  1349  		}},
  1350  		{}, // 11, index missing
  1351  		{ids: []string{"bbbb1111-3350-4b4b-d185-0e1992ed43e9"}}, // 12
  1352  	}
  1353  
  1354  	state := s1.fsm.State()
  1355  
  1356  	var evals []*structs.Evaluation
  1357  	for i, m := range mocks {
  1358  		evalsInTx := []*structs.Evaluation{}
  1359  		for _, id := range m.ids {
  1360  			eval := mock.Eval()
  1361  			eval.ID = id
  1362  			if m.namespace != "" { // defaults to "default"
  1363  				eval.Namespace = m.namespace
  1364  			}
  1365  			if m.jobID != "" { // defaults to some random UUID
  1366  				eval.JobID = m.jobID
  1367  			}
  1368  			if m.status != "" { // defaults to "pending"
  1369  				eval.Status = m.status
  1370  			}
  1371  			evals = append(evals, eval)
  1372  			evalsInTx = append(evalsInTx, eval)
  1373  		}
  1374  		index := 1000 + uint64(i)
  1375  		require.NoError(t, state.UpsertEvals(structs.MsgTypeTestSetup, index, evalsInTx))
  1376  	}
  1377  
  1378  	aclToken := mock.CreatePolicyAndToken(t, state, 1100, "test-valid-read",
  1379  		mock.NamespacePolicy("*", "read", nil)).
  1380  		SecretID
  1381  
  1382  	cases := []struct {
  1383  		name              string
  1384  		namespace         string
  1385  		prefix            string
  1386  		nextToken         string
  1387  		filterJobID       string
  1388  		filterStatus      string
  1389  		filter            string
  1390  		pageSize          int32
  1391  		expectedNextToken string
  1392  		expectedIDs       []string
  1393  		expectedError     string
  1394  	}{
  1395  		{
  1396  			name:     "test01 size-2 page-1 default NS",
  1397  			pageSize: 2,
  1398  			expectedIDs: []string{ // first two items
  1399  				"aaaa1111-3350-4b4b-d185-0e1992ed43e9",
  1400  				"aaaaaa22-3350-4b4b-d185-0e1992ed43e9",
  1401  			},
  1402  			expectedNextToken: "1003.aaaaaaaa-3350-4b4b-d185-0e1992ed43e9", // next one in default namespace
  1403  		},
  1404  		{
  1405  			name:              "test02 size-2 page-1 default NS with prefix",
  1406  			prefix:            "aaaa",
  1407  			pageSize:          2,
  1408  			expectedNextToken: "aaaaaaaa-3350-4b4b-d185-0e1992ed43e9", // prefix results are not sorted by create index
  1409  			expectedIDs: []string{
  1410  				"aaaa1111-3350-4b4b-d185-0e1992ed43e9",
  1411  				"aaaaaa22-3350-4b4b-d185-0e1992ed43e9",
  1412  			},
  1413  		},
  1414  		{
  1415  			name:              "test03 size-2 page-2 default NS",
  1416  			pageSize:          2,
  1417  			nextToken:         "1003.aaaaaaaa-3350-4b4b-d185-0e1992ed43e9",
  1418  			expectedNextToken: "1005.aaaaaacc-3350-4b4b-d185-0e1992ed43e9",
  1419  			expectedIDs: []string{
  1420  				"aaaaaaaa-3350-4b4b-d185-0e1992ed43e9",
  1421  				"aaaaaabb-3350-4b4b-d185-0e1992ed43e9",
  1422  			},
  1423  		},
  1424  		{
  1425  			name:              "test04 size-2 page-2 default NS with prefix",
  1426  			prefix:            "aaaa",
  1427  			pageSize:          2,
  1428  			nextToken:         "aaaaaabb-3350-4b4b-d185-0e1992ed43e9",
  1429  			expectedNextToken: "aaaaaadd-3350-4b4b-d185-0e1992ed43e9",
  1430  			expectedIDs: []string{
  1431  				"aaaaaabb-3350-4b4b-d185-0e1992ed43e9",
  1432  				"aaaaaacc-3350-4b4b-d185-0e1992ed43e9",
  1433  			},
  1434  		},
  1435  		{
  1436  			name:         "test05 size-2 page-1 with filters default NS",
  1437  			pageSize:     2,
  1438  			filterJobID:  "example",
  1439  			filterStatus: "pending",
  1440  			// aaaaaaaa, bb, and cc are filtered by status
  1441  			expectedNextToken: "1006.aaaaaadd-3350-4b4b-d185-0e1992ed43e9",
  1442  			expectedIDs: []string{
  1443  				"aaaa1111-3350-4b4b-d185-0e1992ed43e9",
  1444  				"aaaaaa22-3350-4b4b-d185-0e1992ed43e9",
  1445  			},
  1446  		},
  1447  		{
  1448  			name:         "test06 size-2 page-1 with filters default NS with short prefix",
  1449  			prefix:       "aaaa",
  1450  			pageSize:     2,
  1451  			filterJobID:  "example",
  1452  			filterStatus: "pending",
  1453  			// aaaaaaaa, bb, and cc are filtered by status
  1454  			expectedNextToken: "aaaaaadd-3350-4b4b-d185-0e1992ed43e9",
  1455  			expectedIDs: []string{
  1456  				"aaaa1111-3350-4b4b-d185-0e1992ed43e9",
  1457  				"aaaaaa22-3350-4b4b-d185-0e1992ed43e9",
  1458  			},
  1459  		},
  1460  		{
  1461  			name:              "test07 size-2 page-1 with filters default NS with longer prefix",
  1462  			prefix:            "aaaaaa",
  1463  			pageSize:          2,
  1464  			filterJobID:       "example",
  1465  			filterStatus:      "pending",
  1466  			expectedNextToken: "aaaaaaee-3350-4b4b-d185-0e1992ed43e9",
  1467  			expectedIDs: []string{
  1468  				"aaaaaa22-3350-4b4b-d185-0e1992ed43e9",
  1469  				"aaaaaadd-3350-4b4b-d185-0e1992ed43e9",
  1470  			},
  1471  		},
  1472  		{
  1473  			name:              "test08 size-2 page-2 filter skip nextToken", //
  1474  			pageSize:          3,                                            // reads off the end
  1475  			filterJobID:       "example",
  1476  			filterStatus:      "pending",
  1477  			nextToken:         "1003.aaaaaaaa-3350-4b4b-d185-0e1992ed43e9",
  1478  			expectedNextToken: "",
  1479  			expectedIDs: []string{
  1480  				"aaaaaadd-3350-4b4b-d185-0e1992ed43e9",
  1481  				"aaaaaaee-3350-4b4b-d185-0e1992ed43e9",
  1482  			},
  1483  		},
  1484  		{
  1485  			name:              "test09 size-2 page-2 filters skip nextToken with prefix",
  1486  			prefix:            "aaaaaa",
  1487  			pageSize:          3, // reads off the end
  1488  			filterJobID:       "example",
  1489  			filterStatus:      "pending",
  1490  			nextToken:         "aaaaaaaa-3350-4b4b-d185-0e1992ed43e9",
  1491  			expectedNextToken: "",
  1492  			expectedIDs: []string{
  1493  				"aaaaaadd-3350-4b4b-d185-0e1992ed43e9",
  1494  				"aaaaaaee-3350-4b4b-d185-0e1992ed43e9",
  1495  			},
  1496  		},
  1497  		{
  1498  			name:              "test10 size-2 page-2 all namespaces",
  1499  			namespace:         "*",
  1500  			pageSize:          2,
  1501  			nextToken:         "1002.aaaaaa33-3350-4b4b-d185-0e1992ed43e9",
  1502  			expectedNextToken: "1004.aaaaaabb-3350-4b4b-d185-0e1992ed43e9",
  1503  			expectedIDs: []string{
  1504  				"aaaaaa33-3350-4b4b-d185-0e1992ed43e9",
  1505  				"aaaaaaaa-3350-4b4b-d185-0e1992ed43e9",
  1506  			},
  1507  		},
  1508  		{
  1509  			name:        "test11 no valid results with filters",
  1510  			pageSize:    2,
  1511  			filterJobID: "whatever",
  1512  			nextToken:   "",
  1513  			expectedIDs: []string{},
  1514  		},
  1515  		{
  1516  			name:        "test12 no valid results with filters and prefix",
  1517  			prefix:      "aaaa",
  1518  			pageSize:    2,
  1519  			filterJobID: "whatever",
  1520  			nextToken:   "",
  1521  			expectedIDs: []string{},
  1522  		},
  1523  		{
  1524  			name:        "test13 no valid results with filters page-2",
  1525  			filterJobID: "whatever",
  1526  			nextToken:   "aaaaaa11-3350-4b4b-d185-0e1992ed43e9",
  1527  			expectedIDs: []string{},
  1528  		},
  1529  		{
  1530  			name:        "test14 no valid results with filters page-2 with prefix",
  1531  			prefix:      "aaaa",
  1532  			filterJobID: "whatever",
  1533  			nextToken:   "aaaaaa11-3350-4b4b-d185-0e1992ed43e9",
  1534  			expectedIDs: []string{},
  1535  		},
  1536  		{
  1537  			name:        "test15 go-bexpr filter",
  1538  			filter:      `Status == "blocked"`,
  1539  			nextToken:   "",
  1540  			expectedIDs: []string{"aaaaaaaa-3350-4b4b-d185-0e1992ed43e9"},
  1541  		},
  1542  		{
  1543  			name:              "test16 go-bexpr filter with pagination",
  1544  			filter:            `JobID == "example"`,
  1545  			pageSize:          2,
  1546  			expectedNextToken: "1003.aaaaaaaa-3350-4b4b-d185-0e1992ed43e9",
  1547  			expectedIDs: []string{
  1548  				"aaaa1111-3350-4b4b-d185-0e1992ed43e9",
  1549  				"aaaaaa22-3350-4b4b-d185-0e1992ed43e9",
  1550  			},
  1551  		},
  1552  		{
  1553  			name:      "test17 go-bexpr filter namespace",
  1554  			namespace: "non-default",
  1555  			filter:    `ID contains "aaa"`,
  1556  			expectedIDs: []string{
  1557  				"aaaaaa33-3350-4b4b-d185-0e1992ed43e9",
  1558  			},
  1559  		},
  1560  		{
  1561  			name:        "test18 go-bexpr wrong namespace",
  1562  			namespace:   "default",
  1563  			filter:      `Namespace == "non-default"`,
  1564  			expectedIDs: []string{},
  1565  		},
  1566  		{
  1567  			name:          "test19 incompatible filtering",
  1568  			filter:        `JobID == "example"`,
  1569  			filterStatus:  "complete",
  1570  			expectedError: structs.ErrIncompatibleFiltering.Error(),
  1571  		},
  1572  		{
  1573  			name:          "test20 go-bexpr invalid expression",
  1574  			filter:        `NotValid`,
  1575  			expectedError: "failed to read filter expression",
  1576  		},
  1577  		{
  1578  			name:          "test21 go-bexpr invalid field",
  1579  			filter:        `InvalidField == "value"`,
  1580  			expectedError: "error finding value in datum",
  1581  		},
  1582  		{
  1583  			name:              "test22 non-lexicographic order",
  1584  			pageSize:          1,
  1585  			nextToken:         "1009.00000111-3350-4b4b-d185-0e1992ed43e9",
  1586  			expectedNextToken: "1010.00000222-3350-4b4b-d185-0e1992ed43e9",
  1587  			expectedIDs: []string{
  1588  				"00000111-3350-4b4b-d185-0e1992ed43e9",
  1589  			},
  1590  		},
  1591  		{
  1592  			name:              "test23 same index",
  1593  			pageSize:          1,
  1594  			nextToken:         "1010.00000222-3350-4b4b-d185-0e1992ed43e9",
  1595  			expectedNextToken: "1010.00000333-3350-4b4b-d185-0e1992ed43e9",
  1596  			expectedIDs: []string{
  1597  				"00000222-3350-4b4b-d185-0e1992ed43e9",
  1598  			},
  1599  		},
  1600  		{
  1601  			name:      "test24 missing index",
  1602  			pageSize:  1,
  1603  			nextToken: "1011.e9522802-0cd8-4b1d-9c9e-ab3d97938371",
  1604  			expectedIDs: []string{
  1605  				"bbbb1111-3350-4b4b-d185-0e1992ed43e9",
  1606  			},
  1607  		},
  1608  	}
  1609  
  1610  	for _, tc := range cases {
  1611  		t.Run(tc.name, func(t *testing.T) {
  1612  			req := &structs.EvalListRequest{
  1613  				FilterJobID:      tc.filterJobID,
  1614  				FilterEvalStatus: tc.filterStatus,
  1615  				QueryOptions: structs.QueryOptions{
  1616  					Region:    "global",
  1617  					Namespace: tc.namespace,
  1618  					Prefix:    tc.prefix,
  1619  					PerPage:   tc.pageSize,
  1620  					NextToken: tc.nextToken,
  1621  					Filter:    tc.filter,
  1622  				},
  1623  			}
  1624  			req.AuthToken = aclToken
  1625  			var resp structs.EvalListResponse
  1626  			err := msgpackrpc.CallWithCodec(codec, "Eval.List", req, &resp)
  1627  			if tc.expectedError == "" {
  1628  				require.NoError(t, err)
  1629  			} else {
  1630  				require.Error(t, err)
  1631  				require.Contains(t, err.Error(), tc.expectedError)
  1632  				return
  1633  			}
  1634  
  1635  			gotIDs := []string{}
  1636  			for _, eval := range resp.Evaluations {
  1637  				gotIDs = append(gotIDs, eval.ID)
  1638  			}
  1639  			require.Equal(t, tc.expectedIDs, gotIDs, "unexpected page of evals")
  1640  			require.Equal(t, tc.expectedNextToken, resp.QueryMeta.NextToken, "unexpected NextToken")
  1641  		})
  1642  	}
  1643  
  1644  }
  1645  
  1646  func TestEvalEndpoint_Count(t *testing.T) {
  1647  	ci.Parallel(t)
  1648  	s1, _, cleanupS1 := TestACLServer(t, nil)
  1649  	defer cleanupS1()
  1650  	codec := rpcClient(t, s1)
  1651  	index := uint64(100)
  1652  	testutil.WaitForLeader(t, s1.RPC)
  1653  	store := s1.fsm.State()
  1654  
  1655  	// Create non-default namespace
  1656  	nondefaultNS := mock.Namespace()
  1657  	nondefaultNS.Name = "non-default"
  1658  	err := store.UpsertNamespaces(index, []*structs.Namespace{nondefaultNS})
  1659  	must.NoError(t, err)
  1660  
  1661  	// create a set of evals and field values to filter on.
  1662  	mocks := []struct {
  1663  		namespace string
  1664  		status    string
  1665  	}{
  1666  		{namespace: structs.DefaultNamespace, status: structs.EvalStatusPending},
  1667  		{namespace: structs.DefaultNamespace, status: structs.EvalStatusPending},
  1668  		{namespace: structs.DefaultNamespace, status: structs.EvalStatusPending},
  1669  		{namespace: nondefaultNS.Name, status: structs.EvalStatusPending},
  1670  		{namespace: structs.DefaultNamespace, status: structs.EvalStatusComplete},
  1671  		{namespace: nondefaultNS.Name, status: structs.EvalStatusComplete},
  1672  	}
  1673  
  1674  	evals := []*structs.Evaluation{}
  1675  	for i, m := range mocks {
  1676  		eval := mock.Eval()
  1677  		eval.ID = fmt.Sprintf("%d", i) + uuid.Generate()[1:] // sorted for prefix count tests
  1678  		eval.Namespace = m.namespace
  1679  		eval.Status = m.status
  1680  		evals = append(evals, eval)
  1681  	}
  1682  
  1683  	index++
  1684  	require.NoError(t, store.UpsertEvals(structs.MsgTypeTestSetup, index, evals))
  1685  
  1686  	index++
  1687  	aclToken := mock.CreatePolicyAndToken(t, store, index, "test-read-any",
  1688  		mock.NamespacePolicy("*", "read", nil)).SecretID
  1689  
  1690  	limitedACLToken := mock.CreatePolicyAndToken(t, store, index, "test-read-limited",
  1691  		mock.NamespacePolicy("default", "read", nil)).SecretID
  1692  
  1693  	cases := []struct {
  1694  		name          string
  1695  		namespace     string
  1696  		prefix        string
  1697  		filter        string
  1698  		token         string
  1699  		expectedCount int
  1700  	}{
  1701  		{
  1702  			name:          "count wildcard namespace with read-any ACL",
  1703  			namespace:     "*",
  1704  			token:         aclToken,
  1705  			expectedCount: 6,
  1706  		},
  1707  		{
  1708  			name:          "count wildcard namespace with limited-read ACL",
  1709  			namespace:     "*",
  1710  			token:         limitedACLToken,
  1711  			expectedCount: 4,
  1712  		},
  1713  		{
  1714  			name:          "count wildcard namespace with prefix",
  1715  			namespace:     "*",
  1716  			prefix:        evals[2].ID[:2],
  1717  			token:         aclToken,
  1718  			expectedCount: 1,
  1719  		},
  1720  		{
  1721  			name:          "count default namespace with filter",
  1722  			namespace:     structs.DefaultNamespace,
  1723  			filter:        "Status == \"pending\"",
  1724  			token:         aclToken,
  1725  			expectedCount: 3,
  1726  		},
  1727  		{
  1728  			name:          "count nondefault namespace with filter",
  1729  			namespace:     "non-default",
  1730  			filter:        "Status == \"complete\"",
  1731  			token:         aclToken,
  1732  			expectedCount: 1,
  1733  		},
  1734  		{
  1735  			name:          "count no results",
  1736  			namespace:     "non-default",
  1737  			filter:        "Status == \"never\"",
  1738  			token:         aclToken,
  1739  			expectedCount: 0,
  1740  		},
  1741  	}
  1742  
  1743  	for _, tc := range cases {
  1744  		t.Run(tc.name, func(t *testing.T) {
  1745  			req := &structs.EvalCountRequest{
  1746  				QueryOptions: structs.QueryOptions{
  1747  					Region:    "global",
  1748  					Namespace: tc.namespace,
  1749  					Prefix:    tc.prefix,
  1750  					Filter:    tc.filter,
  1751  				},
  1752  			}
  1753  			req.AuthToken = tc.token
  1754  			var resp structs.EvalCountResponse
  1755  			err := msgpackrpc.CallWithCodec(codec, "Eval.Count", req, &resp)
  1756  			must.NoError(t, err)
  1757  			must.Eq(t, tc.expectedCount, resp.Count)
  1758  		})
  1759  	}
  1760  
  1761  }
  1762  
  1763  func TestEvalEndpoint_Allocations(t *testing.T) {
  1764  	ci.Parallel(t)
  1765  
  1766  	s1, cleanupS1 := TestServer(t, nil)
  1767  	defer cleanupS1()
  1768  	codec := rpcClient(t, s1)
  1769  	testutil.WaitForLeader(t, s1.RPC)
  1770  
  1771  	// Create the register request
  1772  	alloc1 := mock.Alloc()
  1773  	alloc2 := mock.Alloc()
  1774  	alloc2.EvalID = alloc1.EvalID
  1775  	state := s1.fsm.State()
  1776  	state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID))
  1777  	state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID))
  1778  	err := state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{alloc1, alloc2})
  1779  	if err != nil {
  1780  		t.Fatalf("err: %v", err)
  1781  	}
  1782  
  1783  	// Lookup the eval
  1784  	get := &structs.EvalSpecificRequest{
  1785  		EvalID:       alloc1.EvalID,
  1786  		QueryOptions: structs.QueryOptions{Region: "global"},
  1787  	}
  1788  	var resp structs.EvalAllocationsResponse
  1789  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Allocations", get, &resp); err != nil {
  1790  		t.Fatalf("err: %v", err)
  1791  	}
  1792  	if resp.Index != 1000 {
  1793  		t.Fatalf("Bad index: %d %d", resp.Index, 1000)
  1794  	}
  1795  
  1796  	if len(resp.Allocations) != 2 {
  1797  		t.Fatalf("bad: %#v", resp.Allocations)
  1798  	}
  1799  }
  1800  
  1801  func TestEvalEndpoint_Allocations_ACL(t *testing.T) {
  1802  	ci.Parallel(t)
  1803  
  1804  	s1, root, cleanupS1 := TestACLServer(t, nil)
  1805  	defer cleanupS1()
  1806  	codec := rpcClient(t, s1)
  1807  	testutil.WaitForLeader(t, s1.RPC)
  1808  	assert := assert.New(t)
  1809  
  1810  	// Create the register request
  1811  	alloc1 := mock.Alloc()
  1812  	alloc2 := mock.Alloc()
  1813  	alloc2.EvalID = alloc1.EvalID
  1814  	state := s1.fsm.State()
  1815  	assert.Nil(state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID)))
  1816  	assert.Nil(state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID)))
  1817  	assert.Nil(state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{alloc1, alloc2}))
  1818  
  1819  	// Create ACL tokens
  1820  	validToken := mock.CreatePolicyAndToken(t, state, 1003, "test-valid",
  1821  		mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob}))
  1822  	invalidToken := mock.CreatePolicyAndToken(t, state, 1001, "test-invalid",
  1823  		mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListJobs}))
  1824  
  1825  	get := &structs.EvalSpecificRequest{
  1826  		EvalID:       alloc1.EvalID,
  1827  		QueryOptions: structs.QueryOptions{Region: "global"},
  1828  	}
  1829  
  1830  	// Try with no token and expect permission denied
  1831  	{
  1832  		var resp structs.EvalAllocationsResponse
  1833  		err := msgpackrpc.CallWithCodec(codec, "Eval.Allocations", get, &resp)
  1834  		assert.NotNil(err)
  1835  		assert.Contains(err.Error(), structs.ErrPermissionDenied.Error())
  1836  	}
  1837  
  1838  	// Try with an invalid token and expect permission denied
  1839  	{
  1840  		get.AuthToken = invalidToken.SecretID
  1841  		var resp structs.EvalAllocationsResponse
  1842  		err := msgpackrpc.CallWithCodec(codec, "Eval.Allocations", get, &resp)
  1843  		assert.NotNil(err)
  1844  		assert.Contains(err.Error(), structs.ErrPermissionDenied.Error())
  1845  	}
  1846  
  1847  	// Lookup the eval with a valid token
  1848  	{
  1849  		get.AuthToken = validToken.SecretID
  1850  		var resp structs.EvalAllocationsResponse
  1851  		assert.Nil(msgpackrpc.CallWithCodec(codec, "Eval.Allocations", get, &resp))
  1852  		assert.Equal(uint64(1000), resp.Index, "Bad index: %d %d", resp.Index, 1000)
  1853  		assert.Lenf(resp.Allocations, 2, "bad: %#v", resp.Allocations)
  1854  	}
  1855  
  1856  	// Lookup the eval with a root token
  1857  	{
  1858  		get.AuthToken = root.SecretID
  1859  		var resp structs.EvalAllocationsResponse
  1860  		assert.Nil(msgpackrpc.CallWithCodec(codec, "Eval.Allocations", get, &resp))
  1861  		assert.Equal(uint64(1000), resp.Index, "Bad index: %d %d", resp.Index, 1000)
  1862  		assert.Lenf(resp.Allocations, 2, "bad: %#v", resp.Allocations)
  1863  	}
  1864  }
  1865  
  1866  func TestEvalEndpoint_Allocations_Blocking(t *testing.T) {
  1867  	ci.Parallel(t)
  1868  
  1869  	s1, cleanupS1 := TestServer(t, nil)
  1870  	defer cleanupS1()
  1871  	state := s1.fsm.State()
  1872  	codec := rpcClient(t, s1)
  1873  	testutil.WaitForLeader(t, s1.RPC)
  1874  
  1875  	// Create the allocs
  1876  	alloc1 := mock.Alloc()
  1877  	alloc2 := mock.Alloc()
  1878  
  1879  	// Upsert an unrelated alloc first
  1880  	time.AfterFunc(100*time.Millisecond, func() {
  1881  		state.UpsertJobSummary(99, mock.JobSummary(alloc1.JobID))
  1882  		err := state.UpsertAllocs(structs.MsgTypeTestSetup, 100, []*structs.Allocation{alloc1})
  1883  		if err != nil {
  1884  			t.Fatalf("err: %v", err)
  1885  		}
  1886  	})
  1887  
  1888  	// Upsert an alloc which will trigger the watch later
  1889  	time.AfterFunc(200*time.Millisecond, func() {
  1890  		state.UpsertJobSummary(199, mock.JobSummary(alloc2.JobID))
  1891  		err := state.UpsertAllocs(structs.MsgTypeTestSetup, 200, []*structs.Allocation{alloc2})
  1892  		if err != nil {
  1893  			t.Fatalf("err: %v", err)
  1894  		}
  1895  	})
  1896  
  1897  	// Lookup the eval
  1898  	get := &structs.EvalSpecificRequest{
  1899  		EvalID: alloc2.EvalID,
  1900  		QueryOptions: structs.QueryOptions{
  1901  			Region:        "global",
  1902  			MinQueryIndex: 150,
  1903  		},
  1904  	}
  1905  	var resp structs.EvalAllocationsResponse
  1906  	start := time.Now()
  1907  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Allocations", get, &resp); err != nil {
  1908  		t.Fatalf("err: %v", err)
  1909  	}
  1910  
  1911  	if elapsed := time.Since(start); elapsed < 200*time.Millisecond {
  1912  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
  1913  	}
  1914  	if resp.Index != 200 {
  1915  		t.Fatalf("Bad index: %d %d", resp.Index, 200)
  1916  	}
  1917  	if len(resp.Allocations) != 1 || resp.Allocations[0].ID != alloc2.ID {
  1918  		t.Fatalf("bad: %#v", resp.Allocations)
  1919  	}
  1920  }
  1921  
  1922  func TestEvalEndpoint_Reblock_Nonexistent(t *testing.T) {
  1923  	ci.Parallel(t)
  1924  
  1925  	s1, cleanupS1 := TestServer(t, func(c *Config) {
  1926  		c.NumSchedulers = 0 // Prevent automatic dequeue
  1927  	})
  1928  	defer cleanupS1()
  1929  	codec := rpcClient(t, s1)
  1930  
  1931  	testutil.WaitForResult(func() (bool, error) {
  1932  		return s1.evalBroker.Enabled(), nil
  1933  	}, func(err error) {
  1934  		t.Fatalf("should enable eval broker")
  1935  	})
  1936  
  1937  	// Create the register request
  1938  	eval1 := mock.Eval()
  1939  	s1.evalBroker.Enqueue(eval1)
  1940  	out, token, err := s1.evalBroker.Dequeue(defaultSched, time.Second)
  1941  	if err != nil {
  1942  		t.Fatalf("err: %v", err)
  1943  	}
  1944  	if out == nil {
  1945  		t.Fatalf("missing eval")
  1946  	}
  1947  
  1948  	get := &structs.EvalUpdateRequest{
  1949  		Evals:        []*structs.Evaluation{eval1},
  1950  		EvalToken:    token,
  1951  		WriteRequest: structs.WriteRequest{Region: "global"},
  1952  	}
  1953  	var resp structs.GenericResponse
  1954  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Reblock", get, &resp); err == nil {
  1955  		t.Fatalf("expect error since eval does not exist")
  1956  	}
  1957  }
  1958  
  1959  func TestEvalEndpoint_Reblock_NonBlocked(t *testing.T) {
  1960  	ci.Parallel(t)
  1961  
  1962  	s1, cleanupS1 := TestServer(t, func(c *Config) {
  1963  		c.NumSchedulers = 0 // Prevent automatic dequeue
  1964  	})
  1965  	defer cleanupS1()
  1966  	codec := rpcClient(t, s1)
  1967  
  1968  	testutil.WaitForResult(func() (bool, error) {
  1969  		return s1.evalBroker.Enabled(), nil
  1970  	}, func(err error) {
  1971  		t.Fatalf("should enable eval broker")
  1972  	})
  1973  
  1974  	// Create the eval
  1975  	eval1 := mock.Eval()
  1976  	s1.evalBroker.Enqueue(eval1)
  1977  
  1978  	// Insert it into the state store
  1979  	if err := s1.fsm.State().UpsertEvals(structs.MsgTypeTestSetup, 1000, []*structs.Evaluation{eval1}); err != nil {
  1980  		t.Fatal(err)
  1981  	}
  1982  
  1983  	out, token, err := s1.evalBroker.Dequeue(defaultSched, 2*time.Second)
  1984  	if err != nil {
  1985  		t.Fatalf("err: %v", err)
  1986  	}
  1987  	if out == nil {
  1988  		t.Fatalf("missing eval")
  1989  	}
  1990  
  1991  	get := &structs.EvalUpdateRequest{
  1992  		Evals:        []*structs.Evaluation{eval1},
  1993  		EvalToken:    token,
  1994  		WriteRequest: structs.WriteRequest{Region: "global"},
  1995  	}
  1996  	var resp structs.GenericResponse
  1997  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Reblock", get, &resp); err == nil {
  1998  		t.Fatalf("should error since eval was not in blocked state: %v", err)
  1999  	}
  2000  }
  2001  
  2002  func TestEvalEndpoint_Reblock(t *testing.T) {
  2003  	ci.Parallel(t)
  2004  
  2005  	s1, cleanupS1 := TestServer(t, func(c *Config) {
  2006  		c.NumSchedulers = 0 // Prevent automatic dequeue
  2007  	})
  2008  	defer cleanupS1()
  2009  	codec := rpcClient(t, s1)
  2010  
  2011  	testutil.WaitForResult(func() (bool, error) {
  2012  		return s1.evalBroker.Enabled(), nil
  2013  	}, func(err error) {
  2014  		t.Fatalf("should enable eval broker")
  2015  	})
  2016  
  2017  	// Create the eval
  2018  	eval1 := mock.Eval()
  2019  	eval1.Status = structs.EvalStatusBlocked
  2020  	s1.evalBroker.Enqueue(eval1)
  2021  
  2022  	// Insert it into the state store
  2023  	if err := s1.fsm.State().UpsertEvals(structs.MsgTypeTestSetup, 1000, []*structs.Evaluation{eval1}); err != nil {
  2024  		t.Fatal(err)
  2025  	}
  2026  
  2027  	out, token, err := s1.evalBroker.Dequeue(defaultSched, 7*time.Second)
  2028  	if err != nil {
  2029  		t.Fatalf("err: %v", err)
  2030  	}
  2031  	if out == nil {
  2032  		t.Fatalf("bad: %v", out)
  2033  	}
  2034  
  2035  	get := &structs.EvalUpdateRequest{
  2036  		Evals:        []*structs.Evaluation{eval1},
  2037  		EvalToken:    token,
  2038  		WriteRequest: structs.WriteRequest{Region: "global"},
  2039  	}
  2040  	var resp structs.GenericResponse
  2041  	if err := msgpackrpc.CallWithCodec(codec, "Eval.Reblock", get, &resp); err != nil {
  2042  		t.Fatalf("err: %v", err)
  2043  	}
  2044  
  2045  	// Check that it is blocked
  2046  	bStats := s1.blockedEvals.Stats()
  2047  	if bStats.TotalBlocked+bStats.TotalEscaped == 0 {
  2048  		t.Fatalf("ReblockEval didn't insert eval into the blocked eval tracker")
  2049  	}
  2050  }