github.com/manicqin/nomad@v0.9.5/nomad/alloc_endpoint_test.go (about)

     1  package nomad
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  	"time"
     7  
     8  	msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc"
     9  	"github.com/hashicorp/nomad/acl"
    10  	"github.com/hashicorp/nomad/helper"
    11  	"github.com/hashicorp/nomad/helper/uuid"
    12  	"github.com/hashicorp/nomad/nomad/mock"
    13  	"github.com/hashicorp/nomad/nomad/structs"
    14  	"github.com/hashicorp/nomad/testutil"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  func TestAllocEndpoint_List(t *testing.T) {
    20  	t.Parallel()
    21  
    22  	s1, cleanupS1 := TestServer(t, nil)
    23  	defer cleanupS1()
    24  
    25  	codec := rpcClient(t, s1)
    26  	testutil.WaitForLeader(t, s1.RPC)
    27  
    28  	// Create the register request
    29  	alloc := mock.Alloc()
    30  	summary := mock.JobSummary(alloc.JobID)
    31  	state := s1.fsm.State()
    32  
    33  	if err := state.UpsertJobSummary(999, summary); err != nil {
    34  		t.Fatalf("err: %v", err)
    35  	}
    36  	if err := state.UpsertAllocs(1000, []*structs.Allocation{alloc}); err != nil {
    37  		t.Fatalf("err: %v", err)
    38  	}
    39  
    40  	// Lookup the allocations
    41  	get := &structs.AllocListRequest{
    42  		QueryOptions: structs.QueryOptions{
    43  			Region:    "global",
    44  			Namespace: structs.DefaultNamespace,
    45  		},
    46  	}
    47  	var resp structs.AllocListResponse
    48  	if err := msgpackrpc.CallWithCodec(codec, "Alloc.List", get, &resp); err != nil {
    49  		t.Fatalf("err: %v", err)
    50  	}
    51  	if resp.Index != 1000 {
    52  		t.Fatalf("Bad index: %d %d", resp.Index, 1000)
    53  	}
    54  
    55  	if len(resp.Allocations) != 1 {
    56  		t.Fatalf("bad: %#v", resp.Allocations)
    57  	}
    58  	if resp.Allocations[0].ID != alloc.ID {
    59  		t.Fatalf("bad: %#v", resp.Allocations[0])
    60  	}
    61  
    62  	// Lookup the allocations by prefix
    63  	get = &structs.AllocListRequest{
    64  		QueryOptions: structs.QueryOptions{
    65  			Region:    "global",
    66  			Namespace: structs.DefaultNamespace,
    67  			Prefix:    alloc.ID[:4],
    68  		},
    69  	}
    70  
    71  	var resp2 structs.AllocListResponse
    72  	if err := msgpackrpc.CallWithCodec(codec, "Alloc.List", get, &resp2); err != nil {
    73  		t.Fatalf("err: %v", err)
    74  	}
    75  	if resp2.Index != 1000 {
    76  		t.Fatalf("Bad index: %d %d", resp2.Index, 1000)
    77  	}
    78  
    79  	if len(resp2.Allocations) != 1 {
    80  		t.Fatalf("bad: %#v", resp2.Allocations)
    81  	}
    82  	if resp2.Allocations[0].ID != alloc.ID {
    83  		t.Fatalf("bad: %#v", resp2.Allocations[0])
    84  	}
    85  }
    86  
    87  func TestAllocEndpoint_List_ACL(t *testing.T) {
    88  	t.Parallel()
    89  
    90  	s1, root, cleanupS1 := TestACLServer(t, nil)
    91  	defer cleanupS1()
    92  	codec := rpcClient(t, s1)
    93  	testutil.WaitForLeader(t, s1.RPC)
    94  	assert := assert.New(t)
    95  
    96  	// Create the alloc
    97  	alloc := mock.Alloc()
    98  	allocs := []*structs.Allocation{alloc}
    99  	summary := mock.JobSummary(alloc.JobID)
   100  	state := s1.fsm.State()
   101  
   102  	assert.Nil(state.UpsertJobSummary(999, summary), "UpsertJobSummary")
   103  	assert.Nil(state.UpsertAllocs(1000, allocs), "UpsertAllocs")
   104  
   105  	stubAllocs := []*structs.AllocListStub{alloc.Stub()}
   106  	stubAllocs[0].CreateIndex = 1000
   107  	stubAllocs[0].ModifyIndex = 1000
   108  
   109  	// Create the namespace policy and tokens
   110  	validToken := mock.CreatePolicyAndToken(t, state, 1001, "test-valid",
   111  		mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob}))
   112  	invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid",
   113  		mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListJobs}))
   114  
   115  	// Lookup the allocs without a token and expect failure
   116  	get := &structs.AllocListRequest{
   117  		QueryOptions: structs.QueryOptions{
   118  			Region:    "global",
   119  			Namespace: structs.DefaultNamespace,
   120  		},
   121  	}
   122  	var resp structs.AllocListResponse
   123  	assert.NotNil(msgpackrpc.CallWithCodec(codec, "Alloc.List", get, &resp), "RPC")
   124  
   125  	// Try with a valid token
   126  	get.AuthToken = validToken.SecretID
   127  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Alloc.List", get, &resp), "RPC")
   128  	assert.EqualValues(resp.Index, 1000, "resp.Index")
   129  	assert.Equal(stubAllocs, resp.Allocations, "Returned alloc list not equal")
   130  
   131  	// Try with a invalid token
   132  	get.AuthToken = invalidToken.SecretID
   133  	err := msgpackrpc.CallWithCodec(codec, "Alloc.List", get, &resp)
   134  	assert.NotNil(err, "RPC")
   135  	assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
   136  
   137  	// Try with a root token
   138  	get.AuthToken = root.SecretID
   139  	assert.Nil(msgpackrpc.CallWithCodec(codec, "Alloc.List", get, &resp), "RPC")
   140  	assert.EqualValues(resp.Index, 1000, "resp.Index")
   141  	assert.Equal(stubAllocs, resp.Allocations, "Returned alloc list not equal")
   142  }
   143  
   144  func TestAllocEndpoint_List_Blocking(t *testing.T) {
   145  	t.Parallel()
   146  
   147  	s1, cleanupS1 := TestServer(t, nil)
   148  	defer cleanupS1()
   149  	state := s1.fsm.State()
   150  	codec := rpcClient(t, s1)
   151  	testutil.WaitForLeader(t, s1.RPC)
   152  
   153  	// Create the alloc
   154  	alloc := mock.Alloc()
   155  
   156  	summary := mock.JobSummary(alloc.JobID)
   157  	if err := state.UpsertJobSummary(1, summary); err != nil {
   158  		t.Fatalf("err: %v", err)
   159  	}
   160  	// Upsert alloc triggers watches
   161  	time.AfterFunc(100*time.Millisecond, func() {
   162  		if err := state.UpsertAllocs(2, []*structs.Allocation{alloc}); err != nil {
   163  			t.Fatalf("err: %v", err)
   164  		}
   165  	})
   166  
   167  	req := &structs.AllocListRequest{
   168  		QueryOptions: structs.QueryOptions{
   169  			Region:        "global",
   170  			Namespace:     structs.DefaultNamespace,
   171  			MinQueryIndex: 1,
   172  		},
   173  	}
   174  	start := time.Now()
   175  	var resp structs.AllocListResponse
   176  	if err := msgpackrpc.CallWithCodec(codec, "Alloc.List", req, &resp); err != nil {
   177  		t.Fatalf("err: %v", err)
   178  	}
   179  
   180  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
   181  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
   182  	}
   183  	if resp.Index != 2 {
   184  		t.Fatalf("Bad index: %d %d", resp.Index, 2)
   185  	}
   186  	if len(resp.Allocations) != 1 || resp.Allocations[0].ID != alloc.ID {
   187  		t.Fatalf("bad: %#v", resp.Allocations)
   188  	}
   189  
   190  	// Client updates trigger watches
   191  	alloc2 := mock.Alloc()
   192  	alloc2.ID = alloc.ID
   193  	alloc2.ClientStatus = structs.AllocClientStatusRunning
   194  	time.AfterFunc(100*time.Millisecond, func() {
   195  		state.UpsertJobSummary(3, mock.JobSummary(alloc2.JobID))
   196  		if err := state.UpdateAllocsFromClient(4, []*structs.Allocation{alloc2}); err != nil {
   197  			t.Fatalf("err: %v", err)
   198  		}
   199  	})
   200  
   201  	req.MinQueryIndex = 3
   202  	start = time.Now()
   203  	var resp2 structs.AllocListResponse
   204  	if err := msgpackrpc.CallWithCodec(codec, "Alloc.List", req, &resp2); err != nil {
   205  		t.Fatalf("err: %v", err)
   206  	}
   207  
   208  	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
   209  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp2)
   210  	}
   211  	if resp2.Index != 4 {
   212  		t.Fatalf("Bad index: %d %d", resp2.Index, 4)
   213  	}
   214  	if len(resp2.Allocations) != 1 || resp.Allocations[0].ID != alloc.ID ||
   215  		resp2.Allocations[0].ClientStatus != structs.AllocClientStatusRunning {
   216  		t.Fatalf("bad: %#v", resp2.Allocations)
   217  	}
   218  }
   219  
   220  func TestAllocEndpoint_GetAlloc(t *testing.T) {
   221  	t.Parallel()
   222  
   223  	s1, cleanupS1 := TestServer(t, nil)
   224  	defer cleanupS1()
   225  	codec := rpcClient(t, s1)
   226  	testutil.WaitForLeader(t, s1.RPC)
   227  
   228  	// Create the register request
   229  	prevAllocID := uuid.Generate()
   230  	alloc := mock.Alloc()
   231  	alloc.RescheduleTracker = &structs.RescheduleTracker{
   232  		Events: []*structs.RescheduleEvent{
   233  			{RescheduleTime: time.Now().UTC().UnixNano(), PrevNodeID: "boom", PrevAllocID: prevAllocID},
   234  		},
   235  	}
   236  	state := s1.fsm.State()
   237  	state.UpsertJobSummary(999, mock.JobSummary(alloc.JobID))
   238  	err := state.UpsertAllocs(1000, []*structs.Allocation{alloc})
   239  	if err != nil {
   240  		t.Fatalf("err: %v", err)
   241  	}
   242  
   243  	// Lookup the alloc
   244  	get := &structs.AllocSpecificRequest{
   245  		AllocID:      alloc.ID,
   246  		QueryOptions: structs.QueryOptions{Region: "global"},
   247  	}
   248  	var resp structs.SingleAllocResponse
   249  	if err := msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp); err != nil {
   250  		t.Fatalf("err: %v", err)
   251  	}
   252  	if resp.Index != 1000 {
   253  		t.Fatalf("Bad index: %d %d", resp.Index, 1000)
   254  	}
   255  
   256  	if !reflect.DeepEqual(alloc, resp.Alloc) {
   257  		t.Fatalf("bad: %#v", resp.Alloc)
   258  	}
   259  }
   260  
   261  func TestAllocEndpoint_GetAlloc_ACL(t *testing.T) {
   262  	t.Parallel()
   263  
   264  	s1, root, cleanupS1 := TestACLServer(t, nil)
   265  	defer cleanupS1()
   266  	codec := rpcClient(t, s1)
   267  	testutil.WaitForLeader(t, s1.RPC)
   268  	assert := assert.New(t)
   269  
   270  	// Create the alloc
   271  	alloc := mock.Alloc()
   272  	allocs := []*structs.Allocation{alloc}
   273  	summary := mock.JobSummary(alloc.JobID)
   274  	state := s1.fsm.State()
   275  
   276  	assert.Nil(state.UpsertJobSummary(999, summary), "UpsertJobSummary")
   277  	assert.Nil(state.UpsertAllocs(1000, allocs), "UpsertAllocs")
   278  
   279  	// Create the namespace policy and tokens
   280  	validToken := mock.CreatePolicyAndToken(t, state, 1001, "test-valid",
   281  		mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob}))
   282  	invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid",
   283  		mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListJobs}))
   284  
   285  	getReq := func() *structs.AllocSpecificRequest {
   286  		return &structs.AllocSpecificRequest{
   287  			AllocID: alloc.ID,
   288  			QueryOptions: structs.QueryOptions{
   289  				Region: "global",
   290  			},
   291  		}
   292  	}
   293  
   294  	cases := []struct {
   295  		Name string
   296  		F    func(t *testing.T)
   297  	}{
   298  		// Lookup the alloc without a token and expect failure
   299  		{
   300  			Name: "no-token",
   301  			F: func(t *testing.T) {
   302  				var resp structs.SingleAllocResponse
   303  				err := msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", getReq(), &resp)
   304  				require.True(t, structs.IsErrUnknownAllocation(err), "expected unknown alloc but found: %v", err)
   305  			},
   306  		},
   307  
   308  		// Try with a valid ACL token
   309  		{
   310  			Name: "valid-token",
   311  			F: func(t *testing.T) {
   312  				get := getReq()
   313  				get.AuthToken = validToken.SecretID
   314  				get.AllocID = alloc.ID
   315  				var resp structs.SingleAllocResponse
   316  				require.NoError(t, msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp), "RPC")
   317  				require.EqualValues(t, resp.Index, 1000, "resp.Index")
   318  				require.Equal(t, alloc, resp.Alloc, "Returned alloc not equal")
   319  			},
   320  		},
   321  
   322  		// Try with a valid Node.SecretID
   323  		{
   324  			Name: "valid-node-secret",
   325  			F: func(t *testing.T) {
   326  				node := mock.Node()
   327  				assert.Nil(state.UpsertNode(1005, node))
   328  				get := getReq()
   329  				get.AuthToken = node.SecretID
   330  				get.AllocID = alloc.ID
   331  				var resp structs.SingleAllocResponse
   332  				require.NoError(t, msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp), "RPC")
   333  				require.EqualValues(t, resp.Index, 1000, "resp.Index")
   334  				require.Equal(t, alloc, resp.Alloc, "Returned alloc not equal")
   335  			},
   336  		},
   337  
   338  		// Try with a invalid token
   339  		{
   340  			Name: "invalid-token",
   341  			F: func(t *testing.T) {
   342  				get := getReq()
   343  				get.AuthToken = invalidToken.SecretID
   344  				get.AllocID = alloc.ID
   345  				var resp structs.SingleAllocResponse
   346  				err := msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp)
   347  				require.NotNil(t, err, "RPC")
   348  				require.True(t, structs.IsErrUnknownAllocation(err), "expected unknown alloc but found: %v", err)
   349  			},
   350  		},
   351  
   352  		// Try with a root token
   353  		{
   354  			Name: "root-token",
   355  			F: func(t *testing.T) {
   356  				get := getReq()
   357  				get.AuthToken = root.SecretID
   358  				get.AllocID = alloc.ID
   359  				var resp structs.SingleAllocResponse
   360  				require.NoError(t, msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp), "RPC")
   361  				require.EqualValues(t, resp.Index, 1000, "resp.Index")
   362  				require.Equal(t, alloc, resp.Alloc, "Returned alloc not equal")
   363  			},
   364  		},
   365  	}
   366  
   367  	for _, tc := range cases {
   368  		t.Run(tc.Name, tc.F)
   369  	}
   370  }
   371  
   372  func TestAllocEndpoint_GetAlloc_Blocking(t *testing.T) {
   373  	t.Parallel()
   374  
   375  	s1, cleanupS1 := TestServer(t, nil)
   376  	defer cleanupS1()
   377  	state := s1.fsm.State()
   378  	codec := rpcClient(t, s1)
   379  	testutil.WaitForLeader(t, s1.RPC)
   380  
   381  	// Create the allocs
   382  	alloc1 := mock.Alloc()
   383  	alloc2 := mock.Alloc()
   384  
   385  	// First create an unrelated alloc
   386  	time.AfterFunc(100*time.Millisecond, func() {
   387  		state.UpsertJobSummary(99, mock.JobSummary(alloc1.JobID))
   388  		err := state.UpsertAllocs(100, []*structs.Allocation{alloc1})
   389  		if err != nil {
   390  			t.Fatalf("err: %v", err)
   391  		}
   392  	})
   393  
   394  	// Create the alloc we are watching later
   395  	time.AfterFunc(200*time.Millisecond, func() {
   396  		state.UpsertJobSummary(199, mock.JobSummary(alloc2.JobID))
   397  		err := state.UpsertAllocs(200, []*structs.Allocation{alloc2})
   398  		if err != nil {
   399  			t.Fatalf("err: %v", err)
   400  		}
   401  	})
   402  
   403  	// Lookup the allocs
   404  	get := &structs.AllocSpecificRequest{
   405  		AllocID: alloc2.ID,
   406  		QueryOptions: structs.QueryOptions{
   407  			Region:        "global",
   408  			MinQueryIndex: 150,
   409  		},
   410  	}
   411  	var resp structs.SingleAllocResponse
   412  	start := time.Now()
   413  	if err := msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp); err != nil {
   414  		t.Fatalf("err: %v", err)
   415  	}
   416  
   417  	if elapsed := time.Since(start); elapsed < 200*time.Millisecond {
   418  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
   419  	}
   420  	if resp.Index != 200 {
   421  		t.Fatalf("Bad index: %d %d", resp.Index, 200)
   422  	}
   423  	if resp.Alloc == nil || resp.Alloc.ID != alloc2.ID {
   424  		t.Fatalf("bad: %#v", resp.Alloc)
   425  	}
   426  }
   427  
   428  func TestAllocEndpoint_GetAllocs(t *testing.T) {
   429  	t.Parallel()
   430  
   431  	s1, cleanupS1 := TestServer(t, nil)
   432  	defer cleanupS1()
   433  	codec := rpcClient(t, s1)
   434  	testutil.WaitForLeader(t, s1.RPC)
   435  
   436  	// Create the register request
   437  	alloc := mock.Alloc()
   438  	alloc2 := mock.Alloc()
   439  	state := s1.fsm.State()
   440  	state.UpsertJobSummary(998, mock.JobSummary(alloc.JobID))
   441  	state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID))
   442  	err := state.UpsertAllocs(1000, []*structs.Allocation{alloc, alloc2})
   443  	if err != nil {
   444  		t.Fatalf("err: %v", err)
   445  	}
   446  
   447  	// Lookup the allocs
   448  	get := &structs.AllocsGetRequest{
   449  		AllocIDs: []string{alloc.ID, alloc2.ID},
   450  		QueryOptions: structs.QueryOptions{
   451  			Region: "global",
   452  		},
   453  	}
   454  	var resp structs.AllocsGetResponse
   455  	if err := msgpackrpc.CallWithCodec(codec, "Alloc.GetAllocs", get, &resp); err != nil {
   456  		t.Fatalf("err: %v", err)
   457  	}
   458  	if resp.Index != 1000 {
   459  		t.Fatalf("Bad index: %d %d", resp.Index, 1000)
   460  	}
   461  
   462  	if len(resp.Allocs) != 2 {
   463  		t.Fatalf("bad: %#v", resp.Allocs)
   464  	}
   465  
   466  	// Lookup nonexistent allocs.
   467  	get = &structs.AllocsGetRequest{
   468  		AllocIDs:     []string{"foo"},
   469  		QueryOptions: structs.QueryOptions{Region: "global"},
   470  	}
   471  	if err := msgpackrpc.CallWithCodec(codec, "Alloc.GetAllocs", get, &resp); err == nil {
   472  		t.Fatalf("expect error")
   473  	}
   474  }
   475  
   476  func TestAllocEndpoint_GetAllocs_Blocking(t *testing.T) {
   477  	t.Parallel()
   478  
   479  	s1, cleanupS1 := TestServer(t, nil)
   480  	defer cleanupS1()
   481  	state := s1.fsm.State()
   482  	codec := rpcClient(t, s1)
   483  	testutil.WaitForLeader(t, s1.RPC)
   484  
   485  	// Create the allocs
   486  	alloc1 := mock.Alloc()
   487  	alloc2 := mock.Alloc()
   488  
   489  	// First create an unrelated alloc
   490  	time.AfterFunc(100*time.Millisecond, func() {
   491  		state.UpsertJobSummary(99, mock.JobSummary(alloc1.JobID))
   492  		err := state.UpsertAllocs(100, []*structs.Allocation{alloc1})
   493  		if err != nil {
   494  			t.Fatalf("err: %v", err)
   495  		}
   496  	})
   497  
   498  	// Create the alloc we are watching later
   499  	time.AfterFunc(200*time.Millisecond, func() {
   500  		state.UpsertJobSummary(199, mock.JobSummary(alloc2.JobID))
   501  		err := state.UpsertAllocs(200, []*structs.Allocation{alloc2})
   502  		if err != nil {
   503  			t.Fatalf("err: %v", err)
   504  		}
   505  	})
   506  
   507  	// Lookup the allocs
   508  	get := &structs.AllocsGetRequest{
   509  		AllocIDs: []string{alloc1.ID, alloc2.ID},
   510  		QueryOptions: structs.QueryOptions{
   511  			Region:        "global",
   512  			MinQueryIndex: 150,
   513  		},
   514  	}
   515  	var resp structs.AllocsGetResponse
   516  	start := time.Now()
   517  	if err := msgpackrpc.CallWithCodec(codec, "Alloc.GetAllocs", get, &resp); err != nil {
   518  		t.Fatalf("err: %v", err)
   519  	}
   520  
   521  	if elapsed := time.Since(start); elapsed < 200*time.Millisecond {
   522  		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
   523  	}
   524  	if resp.Index != 200 {
   525  		t.Fatalf("Bad index: %d %d", resp.Index, 200)
   526  	}
   527  	if len(resp.Allocs) != 2 {
   528  		t.Fatalf("bad: %#v", resp.Allocs)
   529  	}
   530  }
   531  
   532  func TestAllocEndpoint_UpdateDesiredTransition(t *testing.T) {
   533  	t.Parallel()
   534  	require := require.New(t)
   535  
   536  	s1, _, cleanupS1 := TestACLServer(t, nil)
   537  	defer cleanupS1()
   538  	codec := rpcClient(t, s1)
   539  	testutil.WaitForLeader(t, s1.RPC)
   540  
   541  	// Create the register request
   542  	alloc := mock.Alloc()
   543  	alloc2 := mock.Alloc()
   544  	state := s1.fsm.State()
   545  	require.Nil(state.UpsertJobSummary(998, mock.JobSummary(alloc.JobID)))
   546  	require.Nil(state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID)))
   547  	require.Nil(state.UpsertAllocs(1000, []*structs.Allocation{alloc, alloc2}))
   548  
   549  	t1 := &structs.DesiredTransition{
   550  		Migrate: helper.BoolToPtr(true),
   551  	}
   552  
   553  	// Update the allocs desired status
   554  	get := &structs.AllocUpdateDesiredTransitionRequest{
   555  		Allocs: map[string]*structs.DesiredTransition{
   556  			alloc.ID:  t1,
   557  			alloc2.ID: t1,
   558  		},
   559  		Evals: []*structs.Evaluation{
   560  			{
   561  				ID:             uuid.Generate(),
   562  				Namespace:      alloc.Namespace,
   563  				Priority:       alloc.Job.Priority,
   564  				Type:           alloc.Job.Type,
   565  				TriggeredBy:    structs.EvalTriggerNodeDrain,
   566  				JobID:          alloc.Job.ID,
   567  				JobModifyIndex: alloc.Job.ModifyIndex,
   568  				Status:         structs.EvalStatusPending,
   569  			},
   570  			{
   571  				ID:             uuid.Generate(),
   572  				Namespace:      alloc2.Namespace,
   573  				Priority:       alloc2.Job.Priority,
   574  				Type:           alloc2.Job.Type,
   575  				TriggeredBy:    structs.EvalTriggerNodeDrain,
   576  				JobID:          alloc2.Job.ID,
   577  				JobModifyIndex: alloc2.Job.ModifyIndex,
   578  				Status:         structs.EvalStatusPending,
   579  			},
   580  		},
   581  		WriteRequest: structs.WriteRequest{
   582  			Region: "global",
   583  		},
   584  	}
   585  
   586  	// Try without permissions
   587  	var resp structs.GenericResponse
   588  	err := msgpackrpc.CallWithCodec(codec, "Alloc.UpdateDesiredTransition", get, &resp)
   589  	require.NotNil(err)
   590  	require.True(structs.IsErrPermissionDenied(err))
   591  
   592  	// Try with permissions
   593  	get.WriteRequest.AuthToken = s1.getLeaderAcl()
   594  	var resp2 structs.GenericResponse
   595  	require.Nil(msgpackrpc.CallWithCodec(codec, "Alloc.UpdateDesiredTransition", get, &resp2))
   596  	require.NotZero(resp2.Index)
   597  
   598  	// Look up the allocations
   599  	out1, err := state.AllocByID(nil, alloc.ID)
   600  	require.Nil(err)
   601  	out2, err := state.AllocByID(nil, alloc.ID)
   602  	require.Nil(err)
   603  	e1, err := state.EvalByID(nil, get.Evals[0].ID)
   604  	require.Nil(err)
   605  	e2, err := state.EvalByID(nil, get.Evals[1].ID)
   606  	require.Nil(err)
   607  
   608  	require.NotNil(out1.DesiredTransition.Migrate)
   609  	require.NotNil(out2.DesiredTransition.Migrate)
   610  	require.NotNil(e1)
   611  	require.NotNil(e2)
   612  	require.True(*out1.DesiredTransition.Migrate)
   613  	require.True(*out2.DesiredTransition.Migrate)
   614  }
   615  
   616  func TestAllocEndpoint_Stop_ACL(t *testing.T) {
   617  	t.Parallel()
   618  	require := require.New(t)
   619  
   620  	s1, _, cleanupS1 := TestACLServer(t, nil)
   621  	defer cleanupS1()
   622  	codec := rpcClient(t, s1)
   623  	testutil.WaitForLeader(t, s1.RPC)
   624  
   625  	// Create the register request
   626  	alloc := mock.Alloc()
   627  	alloc2 := mock.Alloc()
   628  	state := s1.fsm.State()
   629  	require.Nil(state.UpsertJobSummary(998, mock.JobSummary(alloc.JobID)))
   630  	require.Nil(state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID)))
   631  	require.Nil(state.UpsertAllocs(1000, []*structs.Allocation{alloc, alloc2}))
   632  
   633  	req := &structs.AllocStopRequest{
   634  		AllocID: alloc.ID,
   635  	}
   636  	req.Namespace = structs.DefaultNamespace
   637  	req.Region = alloc.Job.Region
   638  
   639  	// Try without permissions
   640  	var resp structs.AllocStopResponse
   641  	err := msgpackrpc.CallWithCodec(codec, "Alloc.Stop", req, &resp)
   642  	require.True(structs.IsErrPermissionDenied(err), "expected permissions error, got: %v", err)
   643  
   644  	// Try with management permissions
   645  	req.WriteRequest.AuthToken = s1.getLeaderAcl()
   646  	var resp2 structs.AllocStopResponse
   647  	require.Nil(msgpackrpc.CallWithCodec(codec, "Alloc.Stop", req, &resp2))
   648  	require.NotZero(resp2.Index)
   649  
   650  	// Try with alloc-lifecycle permissions
   651  	validToken := mock.CreatePolicyAndToken(t, state, 1002, "valid",
   652  		mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityAllocLifecycle}))
   653  	req.WriteRequest.AuthToken = validToken.SecretID
   654  	req.AllocID = alloc2.ID
   655  
   656  	var resp3 structs.AllocStopResponse
   657  	require.Nil(msgpackrpc.CallWithCodec(codec, "Alloc.Stop", req, &resp3))
   658  	require.NotZero(resp3.Index)
   659  
   660  	// Look up the allocations
   661  	out1, err := state.AllocByID(nil, alloc.ID)
   662  	require.Nil(err)
   663  	out2, err := state.AllocByID(nil, alloc2.ID)
   664  	require.Nil(err)
   665  	e1, err := state.EvalByID(nil, resp2.EvalID)
   666  	require.Nil(err)
   667  	e2, err := state.EvalByID(nil, resp3.EvalID)
   668  	require.Nil(err)
   669  
   670  	require.NotNil(out1.DesiredTransition.Migrate)
   671  	require.NotNil(out2.DesiredTransition.Migrate)
   672  	require.NotNil(e1)
   673  	require.NotNil(e2)
   674  	require.True(*out1.DesiredTransition.Migrate)
   675  	require.True(*out2.DesiredTransition.Migrate)
   676  }