github.com/bigcommerce/nomad@v0.9.3-bc/nomad/structs/funcs_test.go (about)

     1  package structs
     2  
     3  import (
     4  	"encoding/base64"
     5  	"fmt"
     6  	"testing"
     7  
     8  	lru "github.com/hashicorp/golang-lru"
     9  	"github.com/hashicorp/nomad/helper/uuid"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  func TestRemoveAllocs(t *testing.T) {
    15  	l := []*Allocation{
    16  		{ID: "foo"},
    17  		{ID: "bar"},
    18  		{ID: "baz"},
    19  		{ID: "zip"},
    20  	}
    21  
    22  	out := RemoveAllocs(l, []*Allocation{l[1], l[3]})
    23  	if len(out) != 2 {
    24  		t.Fatalf("bad: %#v", out)
    25  	}
    26  	if out[0].ID != "foo" && out[1].ID != "baz" {
    27  		t.Fatalf("bad: %#v", out)
    28  	}
    29  }
    30  
    31  func TestFilterTerminalAllocs(t *testing.T) {
    32  	l := []*Allocation{
    33  		{
    34  			ID:            "bar",
    35  			Name:          "myname1",
    36  			DesiredStatus: AllocDesiredStatusEvict,
    37  		},
    38  		{ID: "baz", DesiredStatus: AllocDesiredStatusStop},
    39  		{
    40  			ID:            "foo",
    41  			DesiredStatus: AllocDesiredStatusRun,
    42  			ClientStatus:  AllocClientStatusPending,
    43  		},
    44  		{
    45  			ID:            "bam",
    46  			Name:          "myname",
    47  			DesiredStatus: AllocDesiredStatusRun,
    48  			ClientStatus:  AllocClientStatusComplete,
    49  			CreateIndex:   5,
    50  		},
    51  		{
    52  			ID:            "lol",
    53  			Name:          "myname",
    54  			DesiredStatus: AllocDesiredStatusRun,
    55  			ClientStatus:  AllocClientStatusComplete,
    56  			CreateIndex:   2,
    57  		},
    58  	}
    59  
    60  	out, terminalAllocs := FilterTerminalAllocs(l)
    61  	if len(out) != 1 {
    62  		t.Fatalf("bad: %#v", out)
    63  	}
    64  	if out[0].ID != "foo" {
    65  		t.Fatalf("bad: %#v", out)
    66  	}
    67  
    68  	if len(terminalAllocs) != 3 {
    69  		for _, o := range terminalAllocs {
    70  			fmt.Printf("%#v \n", o)
    71  		}
    72  
    73  		t.Fatalf("bad: %#v", terminalAllocs)
    74  	}
    75  
    76  	if terminalAllocs["myname"].ID != "bam" {
    77  		t.Fatalf("bad: %#v", terminalAllocs["myname"])
    78  	}
    79  }
    80  
    81  // COMPAT(0.11): Remove in 0.11
    82  func TestAllocsFit_PortsOvercommitted_Old(t *testing.T) {
    83  	n := &Node{
    84  		Resources: &Resources{
    85  			Networks: []*NetworkResource{
    86  				{
    87  					Device: "eth0",
    88  					CIDR:   "10.0.0.0/8",
    89  					MBits:  100,
    90  				},
    91  			},
    92  		},
    93  	}
    94  
    95  	a1 := &Allocation{
    96  		Job: &Job{
    97  			TaskGroups: []*TaskGroup{
    98  				{
    99  					Name:          "web",
   100  					EphemeralDisk: DefaultEphemeralDisk(),
   101  				},
   102  			},
   103  		},
   104  		TaskResources: map[string]*Resources{
   105  			"web": {
   106  				Networks: []*NetworkResource{
   107  					{
   108  						Device:        "eth0",
   109  						IP:            "10.0.0.1",
   110  						MBits:         50,
   111  						ReservedPorts: []Port{{"main", 8000}},
   112  					},
   113  				},
   114  			},
   115  		},
   116  	}
   117  
   118  	// Should fit one allocation
   119  	fit, dim, _, err := AllocsFit(n, []*Allocation{a1}, nil, false)
   120  	if err != nil {
   121  		t.Fatalf("err: %v", err)
   122  	}
   123  	if !fit {
   124  		t.Fatalf("Bad: %s", dim)
   125  	}
   126  
   127  	// Should not fit second allocation
   128  	fit, _, _, err = AllocsFit(n, []*Allocation{a1, a1}, nil, false)
   129  	if err != nil {
   130  		t.Fatalf("err: %v", err)
   131  	}
   132  	if fit {
   133  		t.Fatalf("Bad")
   134  	}
   135  }
   136  
   137  // COMPAT(0.11): Remove in 0.11
   138  func TestAllocsFit_Old(t *testing.T) {
   139  	require := require.New(t)
   140  
   141  	n := &Node{
   142  		Resources: &Resources{
   143  			CPU:      2000,
   144  			MemoryMB: 2048,
   145  			DiskMB:   10000,
   146  			Networks: []*NetworkResource{
   147  				{
   148  					Device: "eth0",
   149  					CIDR:   "10.0.0.0/8",
   150  					MBits:  100,
   151  				},
   152  			},
   153  		},
   154  		Reserved: &Resources{
   155  			CPU:      1000,
   156  			MemoryMB: 1024,
   157  			DiskMB:   5000,
   158  			Networks: []*NetworkResource{
   159  				{
   160  					Device:        "eth0",
   161  					IP:            "10.0.0.1",
   162  					MBits:         50,
   163  					ReservedPorts: []Port{{"main", 80}},
   164  				},
   165  			},
   166  		},
   167  	}
   168  
   169  	a1 := &Allocation{
   170  		Resources: &Resources{
   171  			CPU:      1000,
   172  			MemoryMB: 1024,
   173  			DiskMB:   5000,
   174  			Networks: []*NetworkResource{
   175  				{
   176  					Device:        "eth0",
   177  					IP:            "10.0.0.1",
   178  					MBits:         50,
   179  					ReservedPorts: []Port{{"main", 8000}},
   180  				},
   181  			},
   182  		},
   183  	}
   184  
   185  	// Should fit one allocation
   186  	fit, _, used, err := AllocsFit(n, []*Allocation{a1}, nil, false)
   187  	require.NoError(err)
   188  	require.True(fit)
   189  
   190  	// Sanity check the used resources
   191  	require.EqualValues(2000, used.Flattened.Cpu.CpuShares)
   192  	require.EqualValues(2048, used.Flattened.Memory.MemoryMB)
   193  
   194  	// Should not fit second allocation
   195  	fit, _, used, err = AllocsFit(n, []*Allocation{a1, a1}, nil, false)
   196  	require.NoError(err)
   197  	require.False(fit)
   198  
   199  	// Sanity check the used resources
   200  	require.EqualValues(3000, used.Flattened.Cpu.CpuShares)
   201  	require.EqualValues(3072, used.Flattened.Memory.MemoryMB)
   202  }
   203  
   204  // COMPAT(0.11): Remove in 0.11
   205  func TestAllocsFit_TerminalAlloc_Old(t *testing.T) {
   206  	require := require.New(t)
   207  
   208  	n := &Node{
   209  		Resources: &Resources{
   210  			CPU:      2000,
   211  			MemoryMB: 2048,
   212  			DiskMB:   10000,
   213  			Networks: []*NetworkResource{
   214  				{
   215  					Device: "eth0",
   216  					CIDR:   "10.0.0.0/8",
   217  					MBits:  100,
   218  				},
   219  			},
   220  		},
   221  		Reserved: &Resources{
   222  			CPU:      1000,
   223  			MemoryMB: 1024,
   224  			DiskMB:   5000,
   225  			Networks: []*NetworkResource{
   226  				{
   227  					Device:        "eth0",
   228  					IP:            "10.0.0.1",
   229  					MBits:         50,
   230  					ReservedPorts: []Port{{"main", 80}},
   231  				},
   232  			},
   233  		},
   234  	}
   235  
   236  	a1 := &Allocation{
   237  		Resources: &Resources{
   238  			CPU:      1000,
   239  			MemoryMB: 1024,
   240  			DiskMB:   5000,
   241  			Networks: []*NetworkResource{
   242  				{
   243  					Device:        "eth0",
   244  					IP:            "10.0.0.1",
   245  					MBits:         50,
   246  					ReservedPorts: []Port{{"main", 8000}},
   247  				},
   248  			},
   249  		},
   250  	}
   251  
   252  	// Should fit one allocation
   253  	fit, _, used, err := AllocsFit(n, []*Allocation{a1}, nil, false)
   254  	require.NoError(err)
   255  	require.True(fit)
   256  
   257  	// Sanity check the used resources
   258  	require.EqualValues(2000, used.Flattened.Cpu.CpuShares)
   259  	require.EqualValues(2048, used.Flattened.Memory.MemoryMB)
   260  
   261  	// Should fit second allocation since it is terminal
   262  	a2 := a1.Copy()
   263  	a2.DesiredStatus = AllocDesiredStatusStop
   264  	fit, _, used, err = AllocsFit(n, []*Allocation{a1, a2}, nil, false)
   265  	require.NoError(err)
   266  	require.True(fit)
   267  
   268  	// Sanity check the used resources
   269  	require.EqualValues(2000, used.Flattened.Cpu.CpuShares)
   270  	require.EqualValues(2048, used.Flattened.Memory.MemoryMB)
   271  }
   272  
   273  func TestAllocsFit(t *testing.T) {
   274  	require := require.New(t)
   275  
   276  	n := &Node{
   277  		NodeResources: &NodeResources{
   278  			Cpu: NodeCpuResources{
   279  				CpuShares: 2000,
   280  			},
   281  			Memory: NodeMemoryResources{
   282  				MemoryMB: 2048,
   283  			},
   284  			Disk: NodeDiskResources{
   285  				DiskMB: 10000,
   286  			},
   287  			Networks: []*NetworkResource{
   288  				{
   289  					Device: "eth0",
   290  					CIDR:   "10.0.0.0/8",
   291  					MBits:  100,
   292  				},
   293  			},
   294  		},
   295  		ReservedResources: &NodeReservedResources{
   296  			Cpu: NodeReservedCpuResources{
   297  				CpuShares: 1000,
   298  			},
   299  			Memory: NodeReservedMemoryResources{
   300  				MemoryMB: 1024,
   301  			},
   302  			Disk: NodeReservedDiskResources{
   303  				DiskMB: 5000,
   304  			},
   305  			Networks: NodeReservedNetworkResources{
   306  				ReservedHostPorts: "80",
   307  			},
   308  		},
   309  	}
   310  
   311  	a1 := &Allocation{
   312  		AllocatedResources: &AllocatedResources{
   313  			Tasks: map[string]*AllocatedTaskResources{
   314  				"web": {
   315  					Cpu: AllocatedCpuResources{
   316  						CpuShares: 1000,
   317  					},
   318  					Memory: AllocatedMemoryResources{
   319  						MemoryMB: 1024,
   320  					},
   321  					Networks: []*NetworkResource{
   322  						{
   323  							Device:        "eth0",
   324  							IP:            "10.0.0.1",
   325  							MBits:         50,
   326  							ReservedPorts: []Port{{"main", 8000}},
   327  						},
   328  					},
   329  				},
   330  			},
   331  			Shared: AllocatedSharedResources{
   332  				DiskMB: 5000,
   333  			},
   334  		},
   335  	}
   336  
   337  	// Should fit one allocation
   338  	fit, _, used, err := AllocsFit(n, []*Allocation{a1}, nil, false)
   339  	require.NoError(err)
   340  	require.True(fit)
   341  
   342  	// Sanity check the used resources
   343  	require.EqualValues(2000, used.Flattened.Cpu.CpuShares)
   344  	require.EqualValues(2048, used.Flattened.Memory.MemoryMB)
   345  
   346  	// Should not fit second allocation
   347  	fit, _, used, err = AllocsFit(n, []*Allocation{a1, a1}, nil, false)
   348  	require.NoError(err)
   349  	require.False(fit)
   350  
   351  	// Sanity check the used resources
   352  	require.EqualValues(3000, used.Flattened.Cpu.CpuShares)
   353  	require.EqualValues(3072, used.Flattened.Memory.MemoryMB)
   354  }
   355  
   356  func TestAllocsFit_TerminalAlloc(t *testing.T) {
   357  	require := require.New(t)
   358  
   359  	n := &Node{
   360  		NodeResources: &NodeResources{
   361  			Cpu: NodeCpuResources{
   362  				CpuShares: 2000,
   363  			},
   364  			Memory: NodeMemoryResources{
   365  				MemoryMB: 2048,
   366  			},
   367  			Disk: NodeDiskResources{
   368  				DiskMB: 10000,
   369  			},
   370  			Networks: []*NetworkResource{
   371  				{
   372  					Device: "eth0",
   373  					CIDR:   "10.0.0.0/8",
   374  					IP:     "10.0.0.1",
   375  					MBits:  100,
   376  				},
   377  			},
   378  		},
   379  		ReservedResources: &NodeReservedResources{
   380  			Cpu: NodeReservedCpuResources{
   381  				CpuShares: 1000,
   382  			},
   383  			Memory: NodeReservedMemoryResources{
   384  				MemoryMB: 1024,
   385  			},
   386  			Disk: NodeReservedDiskResources{
   387  				DiskMB: 5000,
   388  			},
   389  			Networks: NodeReservedNetworkResources{
   390  				ReservedHostPorts: "80",
   391  			},
   392  		},
   393  	}
   394  
   395  	a1 := &Allocation{
   396  		AllocatedResources: &AllocatedResources{
   397  			Tasks: map[string]*AllocatedTaskResources{
   398  				"web": {
   399  					Cpu: AllocatedCpuResources{
   400  						CpuShares: 1000,
   401  					},
   402  					Memory: AllocatedMemoryResources{
   403  						MemoryMB: 1024,
   404  					},
   405  					Networks: []*NetworkResource{
   406  						{
   407  							Device:        "eth0",
   408  							IP:            "10.0.0.1",
   409  							MBits:         50,
   410  							ReservedPorts: []Port{{"main", 8000}},
   411  						},
   412  					},
   413  				},
   414  			},
   415  			Shared: AllocatedSharedResources{
   416  				DiskMB: 5000,
   417  			},
   418  		},
   419  	}
   420  
   421  	// Should fit one allocation
   422  	fit, _, used, err := AllocsFit(n, []*Allocation{a1}, nil, false)
   423  	require.NoError(err)
   424  	require.True(fit)
   425  
   426  	// Sanity check the used resources
   427  	require.EqualValues(2000, used.Flattened.Cpu.CpuShares)
   428  	require.EqualValues(2048, used.Flattened.Memory.MemoryMB)
   429  
   430  	// Should fit second allocation since it is terminal
   431  	a2 := a1.Copy()
   432  	a2.DesiredStatus = AllocDesiredStatusStop
   433  	fit, dim, used, err := AllocsFit(n, []*Allocation{a1, a2}, nil, false)
   434  	require.NoError(err)
   435  	require.True(fit, dim)
   436  
   437  	// Sanity check the used resources
   438  	require.EqualValues(2000, used.Flattened.Cpu.CpuShares)
   439  	require.EqualValues(2048, used.Flattened.Memory.MemoryMB)
   440  }
   441  
   442  // Tests that AllocsFit detects device collisions
   443  func TestAllocsFit_Devices(t *testing.T) {
   444  	require := require.New(t)
   445  
   446  	n := MockNvidiaNode()
   447  	a1 := &Allocation{
   448  		AllocatedResources: &AllocatedResources{
   449  			Tasks: map[string]*AllocatedTaskResources{
   450  				"web": {
   451  					Cpu: AllocatedCpuResources{
   452  						CpuShares: 1000,
   453  					},
   454  					Memory: AllocatedMemoryResources{
   455  						MemoryMB: 1024,
   456  					},
   457  					Devices: []*AllocatedDeviceResource{
   458  						{
   459  							Type:      "gpu",
   460  							Vendor:    "nvidia",
   461  							Name:      "1080ti",
   462  							DeviceIDs: []string{n.NodeResources.Devices[0].Instances[0].ID},
   463  						},
   464  					},
   465  				},
   466  			},
   467  			Shared: AllocatedSharedResources{
   468  				DiskMB: 5000,
   469  			},
   470  		},
   471  	}
   472  	a2 := a1.Copy()
   473  	a2.AllocatedResources.Tasks["web"] = &AllocatedTaskResources{
   474  		Cpu: AllocatedCpuResources{
   475  			CpuShares: 1000,
   476  		},
   477  		Memory: AllocatedMemoryResources{
   478  			MemoryMB: 1024,
   479  		},
   480  		Devices: []*AllocatedDeviceResource{
   481  			{
   482  				Type:      "gpu",
   483  				Vendor:    "nvidia",
   484  				Name:      "1080ti",
   485  				DeviceIDs: []string{n.NodeResources.Devices[0].Instances[0].ID}, // Use the same ID
   486  			},
   487  		},
   488  	}
   489  
   490  	// Should fit one allocation
   491  	fit, _, _, err := AllocsFit(n, []*Allocation{a1}, nil, true)
   492  	require.NoError(err)
   493  	require.True(fit)
   494  
   495  	// Should not fit second allocation
   496  	fit, msg, _, err := AllocsFit(n, []*Allocation{a1, a2}, nil, true)
   497  	require.NoError(err)
   498  	require.False(fit)
   499  	require.Equal("device oversubscribed", msg)
   500  
   501  	// Should not fit second allocation but won't detect since we disabled
   502  	// devices
   503  	fit, _, _, err = AllocsFit(n, []*Allocation{a1, a2}, nil, false)
   504  	require.NoError(err)
   505  	require.True(fit)
   506  }
   507  
   508  // COMPAT(0.11): Remove in 0.11
   509  func TestScoreFit_Old(t *testing.T) {
   510  	node := &Node{}
   511  	node.Resources = &Resources{
   512  		CPU:      4096,
   513  		MemoryMB: 8192,
   514  	}
   515  	node.Reserved = &Resources{
   516  		CPU:      2048,
   517  		MemoryMB: 4096,
   518  	}
   519  
   520  	// Test a perfect fit
   521  	util := &ComparableResources{
   522  		Flattened: AllocatedTaskResources{
   523  			Cpu: AllocatedCpuResources{
   524  				CpuShares: 2048,
   525  			},
   526  			Memory: AllocatedMemoryResources{
   527  				MemoryMB: 4096,
   528  			},
   529  		},
   530  	}
   531  	score := ScoreFit(node, util)
   532  	if score != 18.0 {
   533  		t.Fatalf("bad: %v", score)
   534  	}
   535  
   536  	// Test the worst fit
   537  	util = &ComparableResources{
   538  		Flattened: AllocatedTaskResources{
   539  			Cpu: AllocatedCpuResources{
   540  				CpuShares: 0,
   541  			},
   542  			Memory: AllocatedMemoryResources{
   543  				MemoryMB: 0,
   544  			},
   545  		},
   546  	}
   547  	score = ScoreFit(node, util)
   548  	if score != 0.0 {
   549  		t.Fatalf("bad: %v", score)
   550  	}
   551  
   552  	// Test a mid-case scenario
   553  	util = &ComparableResources{
   554  		Flattened: AllocatedTaskResources{
   555  			Cpu: AllocatedCpuResources{
   556  				CpuShares: 1024,
   557  			},
   558  			Memory: AllocatedMemoryResources{
   559  				MemoryMB: 2048,
   560  			},
   561  		},
   562  	}
   563  	score = ScoreFit(node, util)
   564  	if score < 10.0 || score > 16.0 {
   565  		t.Fatalf("bad: %v", score)
   566  	}
   567  }
   568  
   569  func TestScoreFit(t *testing.T) {
   570  	node := &Node{}
   571  	node.NodeResources = &NodeResources{
   572  		Cpu: NodeCpuResources{
   573  			CpuShares: 4096,
   574  		},
   575  		Memory: NodeMemoryResources{
   576  			MemoryMB: 8192,
   577  		},
   578  	}
   579  	node.ReservedResources = &NodeReservedResources{
   580  		Cpu: NodeReservedCpuResources{
   581  			CpuShares: 2048,
   582  		},
   583  		Memory: NodeReservedMemoryResources{
   584  			MemoryMB: 4096,
   585  		},
   586  	}
   587  
   588  	// Test a perfect fit
   589  	util := &ComparableResources{
   590  		Flattened: AllocatedTaskResources{
   591  			Cpu: AllocatedCpuResources{
   592  				CpuShares: 2048,
   593  			},
   594  			Memory: AllocatedMemoryResources{
   595  				MemoryMB: 4096,
   596  			},
   597  		},
   598  	}
   599  	score := ScoreFit(node, util)
   600  	if score != 18.0 {
   601  		t.Fatalf("bad: %v", score)
   602  	}
   603  
   604  	// Test the worst fit
   605  	util = &ComparableResources{
   606  		Flattened: AllocatedTaskResources{
   607  			Cpu: AllocatedCpuResources{
   608  				CpuShares: 0,
   609  			},
   610  			Memory: AllocatedMemoryResources{
   611  				MemoryMB: 0,
   612  			},
   613  		},
   614  	}
   615  	score = ScoreFit(node, util)
   616  	if score != 0.0 {
   617  		t.Fatalf("bad: %v", score)
   618  	}
   619  
   620  	// Test a mid-case scenario
   621  	util = &ComparableResources{
   622  		Flattened: AllocatedTaskResources{
   623  			Cpu: AllocatedCpuResources{
   624  				CpuShares: 1024,
   625  			},
   626  			Memory: AllocatedMemoryResources{
   627  				MemoryMB: 2048,
   628  			},
   629  		},
   630  	}
   631  	score = ScoreFit(node, util)
   632  	if score < 10.0 || score > 16.0 {
   633  		t.Fatalf("bad: %v", score)
   634  	}
   635  }
   636  
   637  func TestACLPolicyListHash(t *testing.T) {
   638  	h1 := ACLPolicyListHash(nil)
   639  	assert.NotEqual(t, "", h1)
   640  
   641  	p1 := &ACLPolicy{
   642  		Name:        fmt.Sprintf("policy-%s", uuid.Generate()),
   643  		Description: "Super cool policy!",
   644  		Rules: `
   645  		namespace "default" {
   646  			policy = "write"
   647  		}
   648  		node {
   649  			policy = "read"
   650  		}
   651  		agent {
   652  			policy = "read"
   653  		}
   654  		`,
   655  		CreateIndex: 10,
   656  		ModifyIndex: 20,
   657  	}
   658  
   659  	h2 := ACLPolicyListHash([]*ACLPolicy{p1})
   660  	assert.NotEqual(t, "", h2)
   661  	assert.NotEqual(t, h1, h2)
   662  
   663  	// Create P2 as copy of P1 with new name
   664  	p2 := &ACLPolicy{}
   665  	*p2 = *p1
   666  	p2.Name = fmt.Sprintf("policy-%s", uuid.Generate())
   667  
   668  	h3 := ACLPolicyListHash([]*ACLPolicy{p1, p2})
   669  	assert.NotEqual(t, "", h3)
   670  	assert.NotEqual(t, h2, h3)
   671  
   672  	h4 := ACLPolicyListHash([]*ACLPolicy{p2})
   673  	assert.NotEqual(t, "", h4)
   674  	assert.NotEqual(t, h3, h4)
   675  
   676  	// ModifyIndex should change the hash
   677  	p2.ModifyIndex++
   678  	h5 := ACLPolicyListHash([]*ACLPolicy{p2})
   679  	assert.NotEqual(t, "", h5)
   680  	assert.NotEqual(t, h4, h5)
   681  }
   682  
   683  func TestCompileACLObject(t *testing.T) {
   684  	p1 := &ACLPolicy{
   685  		Name:        fmt.Sprintf("policy-%s", uuid.Generate()),
   686  		Description: "Super cool policy!",
   687  		Rules: `
   688  		namespace "default" {
   689  			policy = "write"
   690  		}
   691  		node {
   692  			policy = "read"
   693  		}
   694  		agent {
   695  			policy = "read"
   696  		}
   697  		`,
   698  		CreateIndex: 10,
   699  		ModifyIndex: 20,
   700  	}
   701  
   702  	// Create P2 as copy of P1 with new name
   703  	p2 := &ACLPolicy{}
   704  	*p2 = *p1
   705  	p2.Name = fmt.Sprintf("policy-%s", uuid.Generate())
   706  
   707  	// Create a small cache
   708  	cache, err := lru.New2Q(16)
   709  	assert.Nil(t, err)
   710  
   711  	// Test compilation
   712  	aclObj, err := CompileACLObject(cache, []*ACLPolicy{p1})
   713  	assert.Nil(t, err)
   714  	assert.NotNil(t, aclObj)
   715  
   716  	// Should get the same object
   717  	aclObj2, err := CompileACLObject(cache, []*ACLPolicy{p1})
   718  	assert.Nil(t, err)
   719  	if aclObj != aclObj2 {
   720  		t.Fatalf("expected the same object")
   721  	}
   722  
   723  	// Should get another object
   724  	aclObj3, err := CompileACLObject(cache, []*ACLPolicy{p1, p2})
   725  	assert.Nil(t, err)
   726  	assert.NotNil(t, aclObj3)
   727  	if aclObj == aclObj3 {
   728  		t.Fatalf("unexpected same object")
   729  	}
   730  
   731  	// Should be order independent
   732  	aclObj4, err := CompileACLObject(cache, []*ACLPolicy{p2, p1})
   733  	assert.Nil(t, err)
   734  	assert.NotNil(t, aclObj4)
   735  	if aclObj3 != aclObj4 {
   736  		t.Fatalf("expected same object")
   737  	}
   738  }
   739  
   740  // TestGenerateMigrateToken asserts the migrate token is valid for use in HTTP
   741  // headers and CompareMigrateToken works as expected.
   742  func TestGenerateMigrateToken(t *testing.T) {
   743  	assert := assert.New(t)
   744  	allocID := uuid.Generate()
   745  	nodeSecret := uuid.Generate()
   746  	token, err := GenerateMigrateToken(allocID, nodeSecret)
   747  	assert.Nil(err)
   748  	_, err = base64.URLEncoding.DecodeString(token)
   749  	assert.Nil(err)
   750  
   751  	assert.True(CompareMigrateToken(allocID, nodeSecret, token))
   752  	assert.False(CompareMigrateToken("x", nodeSecret, token))
   753  	assert.False(CompareMigrateToken(allocID, "x", token))
   754  	assert.False(CompareMigrateToken(allocID, nodeSecret, "x"))
   755  
   756  	token2, err := GenerateMigrateToken("x", nodeSecret)
   757  	assert.Nil(err)
   758  	assert.False(CompareMigrateToken(allocID, nodeSecret, token2))
   759  	assert.True(CompareMigrateToken("x", nodeSecret, token2))
   760  }