github.com/ncodes/nomad@v0.5.7-0.20170403112158-97adf4a74fb3/scheduler/rank_test.go (about)

     1  package scheduler
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/ncodes/nomad/nomad/mock"
     7  	"github.com/ncodes/nomad/nomad/structs"
     8  )
     9  
    10  func TestFeasibleRankIterator(t *testing.T) {
    11  	_, ctx := testContext(t)
    12  	var nodes []*structs.Node
    13  	for i := 0; i < 10; i++ {
    14  		nodes = append(nodes, mock.Node())
    15  	}
    16  	static := NewStaticIterator(ctx, nodes)
    17  
    18  	feasible := NewFeasibleRankIterator(ctx, static)
    19  
    20  	out := collectRanked(feasible)
    21  	if len(out) != len(nodes) {
    22  		t.Fatalf("bad: %v", out)
    23  	}
    24  }
    25  
    26  func TestBinPackIterator_NoExistingAlloc(t *testing.T) {
    27  	_, ctx := testContext(t)
    28  	nodes := []*RankedNode{
    29  		&RankedNode{
    30  			Node: &structs.Node{
    31  				// Perfect fit
    32  				Resources: &structs.Resources{
    33  					CPU:      2048,
    34  					MemoryMB: 2048,
    35  				},
    36  				Reserved: &structs.Resources{
    37  					CPU:      1024,
    38  					MemoryMB: 1024,
    39  				},
    40  			},
    41  		},
    42  		&RankedNode{
    43  			Node: &structs.Node{
    44  				// Overloaded
    45  				Resources: &structs.Resources{
    46  					CPU:      1024,
    47  					MemoryMB: 1024,
    48  				},
    49  				Reserved: &structs.Resources{
    50  					CPU:      512,
    51  					MemoryMB: 512,
    52  				},
    53  			},
    54  		},
    55  		&RankedNode{
    56  			Node: &structs.Node{
    57  				// 50% fit
    58  				Resources: &structs.Resources{
    59  					CPU:      4096,
    60  					MemoryMB: 4096,
    61  				},
    62  				Reserved: &structs.Resources{
    63  					CPU:      1024,
    64  					MemoryMB: 1024,
    65  				},
    66  			},
    67  		},
    68  	}
    69  	static := NewStaticRankIterator(ctx, nodes)
    70  
    71  	taskGroup := &structs.TaskGroup{
    72  		EphemeralDisk: &structs.EphemeralDisk{},
    73  		Tasks: []*structs.Task{
    74  			{
    75  				Name: "web",
    76  				Resources: &structs.Resources{
    77  					CPU:      1024,
    78  					MemoryMB: 1024,
    79  				},
    80  			},
    81  		},
    82  	}
    83  	binp := NewBinPackIterator(ctx, static, false, 0)
    84  	binp.SetTaskGroup(taskGroup)
    85  
    86  	out := collectRanked(binp)
    87  	if len(out) != 2 {
    88  		t.Fatalf("Bad: %v", out)
    89  	}
    90  	if out[0] != nodes[0] || out[1] != nodes[2] {
    91  		t.Fatalf("Bad: %v", out)
    92  	}
    93  
    94  	if out[0].Score != 18 {
    95  		t.Fatalf("Bad: %v", out[0])
    96  	}
    97  	if out[1].Score < 10 || out[1].Score > 16 {
    98  		t.Fatalf("Bad: %v", out[1])
    99  	}
   100  }
   101  
   102  func TestBinPackIterator_PlannedAlloc(t *testing.T) {
   103  	_, ctx := testContext(t)
   104  	nodes := []*RankedNode{
   105  		&RankedNode{
   106  			Node: &structs.Node{
   107  				// Perfect fit
   108  				ID: structs.GenerateUUID(),
   109  				Resources: &structs.Resources{
   110  					CPU:      2048,
   111  					MemoryMB: 2048,
   112  				},
   113  			},
   114  		},
   115  		&RankedNode{
   116  			Node: &structs.Node{
   117  				// Perfect fit
   118  				ID: structs.GenerateUUID(),
   119  				Resources: &structs.Resources{
   120  					CPU:      2048,
   121  					MemoryMB: 2048,
   122  				},
   123  			},
   124  		},
   125  	}
   126  	static := NewStaticRankIterator(ctx, nodes)
   127  
   128  	// Add a planned alloc to node1 that fills it
   129  	plan := ctx.Plan()
   130  	plan.NodeAllocation[nodes[0].Node.ID] = []*structs.Allocation{
   131  		&structs.Allocation{
   132  			Resources: &structs.Resources{
   133  				CPU:      2048,
   134  				MemoryMB: 2048,
   135  			},
   136  		},
   137  	}
   138  
   139  	// Add a planned alloc to node2 that half fills it
   140  	plan.NodeAllocation[nodes[1].Node.ID] = []*structs.Allocation{
   141  		&structs.Allocation{
   142  			Resources: &structs.Resources{
   143  				CPU:      1024,
   144  				MemoryMB: 1024,
   145  			},
   146  		},
   147  	}
   148  
   149  	taskGroup := &structs.TaskGroup{
   150  		EphemeralDisk: &structs.EphemeralDisk{},
   151  		Tasks: []*structs.Task{
   152  			{
   153  				Name: "web",
   154  				Resources: &structs.Resources{
   155  					CPU:      1024,
   156  					MemoryMB: 1024,
   157  				},
   158  			},
   159  		},
   160  	}
   161  
   162  	binp := NewBinPackIterator(ctx, static, false, 0)
   163  	binp.SetTaskGroup(taskGroup)
   164  
   165  	out := collectRanked(binp)
   166  	if len(out) != 1 {
   167  		t.Fatalf("Bad: %#v", out)
   168  	}
   169  	if out[0] != nodes[1] {
   170  		t.Fatalf("Bad: %v", out)
   171  	}
   172  
   173  	if out[0].Score != 18 {
   174  		t.Fatalf("Bad: %v", out[0])
   175  	}
   176  }
   177  
   178  func TestBinPackIterator_ExistingAlloc(t *testing.T) {
   179  	state, ctx := testContext(t)
   180  	nodes := []*RankedNode{
   181  		&RankedNode{
   182  			Node: &structs.Node{
   183  				// Perfect fit
   184  				ID: structs.GenerateUUID(),
   185  				Resources: &structs.Resources{
   186  					CPU:      2048,
   187  					MemoryMB: 2048,
   188  				},
   189  			},
   190  		},
   191  		&RankedNode{
   192  			Node: &structs.Node{
   193  				// Perfect fit
   194  				ID: structs.GenerateUUID(),
   195  				Resources: &structs.Resources{
   196  					CPU:      2048,
   197  					MemoryMB: 2048,
   198  				},
   199  			},
   200  		},
   201  	}
   202  	static := NewStaticRankIterator(ctx, nodes)
   203  
   204  	// Add existing allocations
   205  	alloc1 := &structs.Allocation{
   206  		ID:     structs.GenerateUUID(),
   207  		EvalID: structs.GenerateUUID(),
   208  		NodeID: nodes[0].Node.ID,
   209  		JobID:  structs.GenerateUUID(),
   210  		Resources: &structs.Resources{
   211  			CPU:      2048,
   212  			MemoryMB: 2048,
   213  		},
   214  		DesiredStatus: structs.AllocDesiredStatusRun,
   215  		ClientStatus:  structs.AllocClientStatusPending,
   216  		TaskGroup:     "web",
   217  	}
   218  	alloc2 := &structs.Allocation{
   219  		ID:     structs.GenerateUUID(),
   220  		EvalID: structs.GenerateUUID(),
   221  		NodeID: nodes[1].Node.ID,
   222  		JobID:  structs.GenerateUUID(),
   223  		Resources: &structs.Resources{
   224  			CPU:      1024,
   225  			MemoryMB: 1024,
   226  		},
   227  		DesiredStatus: structs.AllocDesiredStatusRun,
   228  		ClientStatus:  structs.AllocClientStatusPending,
   229  		TaskGroup:     "web",
   230  	}
   231  	noErr(t, state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID)))
   232  	noErr(t, state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID)))
   233  	noErr(t, state.UpsertAllocs(1000, []*structs.Allocation{alloc1, alloc2}))
   234  
   235  	taskGroup := &structs.TaskGroup{
   236  		EphemeralDisk: &structs.EphemeralDisk{},
   237  		Tasks: []*structs.Task{
   238  			{
   239  				Name: "web",
   240  				Resources: &structs.Resources{
   241  					CPU:      1024,
   242  					MemoryMB: 1024,
   243  				},
   244  			},
   245  		},
   246  	}
   247  	binp := NewBinPackIterator(ctx, static, false, 0)
   248  	binp.SetTaskGroup(taskGroup)
   249  
   250  	out := collectRanked(binp)
   251  	if len(out) != 1 {
   252  		t.Fatalf("Bad: %#v", out)
   253  	}
   254  	if out[0] != nodes[1] {
   255  		t.Fatalf("Bad: %v", out)
   256  	}
   257  	if out[0].Score != 18 {
   258  		t.Fatalf("Bad: %v", out[0])
   259  	}
   260  }
   261  
   262  func TestBinPackIterator_ExistingAlloc_PlannedEvict(t *testing.T) {
   263  	state, ctx := testContext(t)
   264  	nodes := []*RankedNode{
   265  		&RankedNode{
   266  			Node: &structs.Node{
   267  				// Perfect fit
   268  				ID: structs.GenerateUUID(),
   269  				Resources: &structs.Resources{
   270  					CPU:      2048,
   271  					MemoryMB: 2048,
   272  				},
   273  			},
   274  		},
   275  		&RankedNode{
   276  			Node: &structs.Node{
   277  				// Perfect fit
   278  				ID: structs.GenerateUUID(),
   279  				Resources: &structs.Resources{
   280  					CPU:      2048,
   281  					MemoryMB: 2048,
   282  				},
   283  			},
   284  		},
   285  	}
   286  	static := NewStaticRankIterator(ctx, nodes)
   287  
   288  	// Add existing allocations
   289  	alloc1 := &structs.Allocation{
   290  		ID:     structs.GenerateUUID(),
   291  		EvalID: structs.GenerateUUID(),
   292  		NodeID: nodes[0].Node.ID,
   293  		JobID:  structs.GenerateUUID(),
   294  		Resources: &structs.Resources{
   295  			CPU:      2048,
   296  			MemoryMB: 2048,
   297  		},
   298  		DesiredStatus: structs.AllocDesiredStatusRun,
   299  		ClientStatus:  structs.AllocClientStatusPending,
   300  		TaskGroup:     "web",
   301  	}
   302  	alloc2 := &structs.Allocation{
   303  		ID:     structs.GenerateUUID(),
   304  		EvalID: structs.GenerateUUID(),
   305  		NodeID: nodes[1].Node.ID,
   306  		JobID:  structs.GenerateUUID(),
   307  		Resources: &structs.Resources{
   308  			CPU:      1024,
   309  			MemoryMB: 1024,
   310  		},
   311  		DesiredStatus: structs.AllocDesiredStatusRun,
   312  		ClientStatus:  structs.AllocClientStatusPending,
   313  		TaskGroup:     "web",
   314  	}
   315  	noErr(t, state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID)))
   316  	noErr(t, state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID)))
   317  	noErr(t, state.UpsertAllocs(1000, []*structs.Allocation{alloc1, alloc2}))
   318  
   319  	// Add a planned eviction to alloc1
   320  	plan := ctx.Plan()
   321  	plan.NodeUpdate[nodes[0].Node.ID] = []*structs.Allocation{alloc1}
   322  
   323  	taskGroup := &structs.TaskGroup{
   324  		EphemeralDisk: &structs.EphemeralDisk{},
   325  		Tasks: []*structs.Task{
   326  			{
   327  				Name: "web",
   328  				Resources: &structs.Resources{
   329  					CPU:      1024,
   330  					MemoryMB: 1024,
   331  				},
   332  			},
   333  		},
   334  	}
   335  
   336  	binp := NewBinPackIterator(ctx, static, false, 0)
   337  	binp.SetTaskGroup(taskGroup)
   338  
   339  	out := collectRanked(binp)
   340  	if len(out) != 2 {
   341  		t.Fatalf("Bad: %#v", out)
   342  	}
   343  	if out[0] != nodes[0] || out[1] != nodes[1] {
   344  		t.Fatalf("Bad: %v", out)
   345  	}
   346  	if out[0].Score < 10 || out[0].Score > 16 {
   347  		t.Fatalf("Bad: %v", out[0])
   348  	}
   349  	if out[1].Score != 18 {
   350  		t.Fatalf("Bad: %v", out[1])
   351  	}
   352  }
   353  
   354  func TestJobAntiAffinity_PlannedAlloc(t *testing.T) {
   355  	_, ctx := testContext(t)
   356  	nodes := []*RankedNode{
   357  		&RankedNode{
   358  			Node: &structs.Node{
   359  				ID: structs.GenerateUUID(),
   360  			},
   361  		},
   362  		&RankedNode{
   363  			Node: &structs.Node{
   364  				ID: structs.GenerateUUID(),
   365  			},
   366  		},
   367  	}
   368  	static := NewStaticRankIterator(ctx, nodes)
   369  
   370  	// Add a planned alloc to node1 that fills it
   371  	plan := ctx.Plan()
   372  	plan.NodeAllocation[nodes[0].Node.ID] = []*structs.Allocation{
   373  		&structs.Allocation{
   374  			ID:    structs.GenerateUUID(),
   375  			JobID: "foo",
   376  		},
   377  		&structs.Allocation{
   378  			ID:    structs.GenerateUUID(),
   379  			JobID: "foo",
   380  		},
   381  	}
   382  
   383  	// Add a planned alloc to node2 that half fills it
   384  	plan.NodeAllocation[nodes[1].Node.ID] = []*structs.Allocation{
   385  		&structs.Allocation{
   386  			JobID: "bar",
   387  		},
   388  	}
   389  
   390  	binp := NewJobAntiAffinityIterator(ctx, static, 5.0, "foo")
   391  
   392  	out := collectRanked(binp)
   393  	if len(out) != 2 {
   394  		t.Fatalf("Bad: %#v", out)
   395  	}
   396  	if out[0] != nodes[0] {
   397  		t.Fatalf("Bad: %v", out)
   398  	}
   399  	if out[0].Score != -10.0 {
   400  		t.Fatalf("Bad: %#v", out[0])
   401  	}
   402  
   403  	if out[1] != nodes[1] {
   404  		t.Fatalf("Bad: %v", out)
   405  	}
   406  	if out[1].Score != 0.0 {
   407  		t.Fatalf("Bad: %v", out[1])
   408  	}
   409  }
   410  
   411  func collectRanked(iter RankIterator) (out []*RankedNode) {
   412  	for {
   413  		next := iter.Next()
   414  		if next == nil {
   415  			break
   416  		}
   417  		out = append(out, next)
   418  	}
   419  	return
   420  }