github.com/dkerwin/nomad@v0.3.3-0.20160525181927-74554135514b/scheduler/rank_test.go (about)

     1  package scheduler
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/hashicorp/nomad/nomad/mock"
     7  	"github.com/hashicorp/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  	task := &structs.Task{
    72  		Name: "web",
    73  		Resources: &structs.Resources{
    74  			CPU:      1024,
    75  			MemoryMB: 1024,
    76  		},
    77  	}
    78  
    79  	binp := NewBinPackIterator(ctx, static, false, 0)
    80  	binp.SetTasks([]*structs.Task{task})
    81  
    82  	out := collectRanked(binp)
    83  	if len(out) != 2 {
    84  		t.Fatalf("Bad: %v", out)
    85  	}
    86  	if out[0] != nodes[0] || out[1] != nodes[2] {
    87  		t.Fatalf("Bad: %v", out)
    88  	}
    89  
    90  	if out[0].Score != 18 {
    91  		t.Fatalf("Bad: %v", out[0])
    92  	}
    93  	if out[1].Score < 10 || out[1].Score > 16 {
    94  		t.Fatalf("Bad: %v", out[1])
    95  	}
    96  }
    97  
    98  func TestBinPackIterator_PlannedAlloc(t *testing.T) {
    99  	_, ctx := testContext(t)
   100  	nodes := []*RankedNode{
   101  		&RankedNode{
   102  			Node: &structs.Node{
   103  				// Perfect fit
   104  				ID: structs.GenerateUUID(),
   105  				Resources: &structs.Resources{
   106  					CPU:      2048,
   107  					MemoryMB: 2048,
   108  				},
   109  			},
   110  		},
   111  		&RankedNode{
   112  			Node: &structs.Node{
   113  				// Perfect fit
   114  				ID: structs.GenerateUUID(),
   115  				Resources: &structs.Resources{
   116  					CPU:      2048,
   117  					MemoryMB: 2048,
   118  				},
   119  			},
   120  		},
   121  	}
   122  	static := NewStaticRankIterator(ctx, nodes)
   123  
   124  	// Add a planned alloc to node1 that fills it
   125  	plan := ctx.Plan()
   126  	plan.NodeAllocation[nodes[0].Node.ID] = []*structs.Allocation{
   127  		&structs.Allocation{
   128  			Resources: &structs.Resources{
   129  				CPU:      2048,
   130  				MemoryMB: 2048,
   131  			},
   132  		},
   133  	}
   134  
   135  	// Add a planned alloc to node2 that half fills it
   136  	plan.NodeAllocation[nodes[1].Node.ID] = []*structs.Allocation{
   137  		&structs.Allocation{
   138  			Resources: &structs.Resources{
   139  				CPU:      1024,
   140  				MemoryMB: 1024,
   141  			},
   142  		},
   143  	}
   144  
   145  	task := &structs.Task{
   146  		Name: "web",
   147  		Resources: &structs.Resources{
   148  			CPU:      1024,
   149  			MemoryMB: 1024,
   150  		},
   151  	}
   152  
   153  	binp := NewBinPackIterator(ctx, static, false, 0)
   154  	binp.SetTasks([]*structs.Task{task})
   155  
   156  	out := collectRanked(binp)
   157  	if len(out) != 1 {
   158  		t.Fatalf("Bad: %#v", out)
   159  	}
   160  	if out[0] != nodes[1] {
   161  		t.Fatalf("Bad: %v", out)
   162  	}
   163  
   164  	if out[0].Score != 18 {
   165  		t.Fatalf("Bad: %v", out[0])
   166  	}
   167  }
   168  
   169  func TestBinPackIterator_ExistingAlloc(t *testing.T) {
   170  	state, ctx := testContext(t)
   171  	nodes := []*RankedNode{
   172  		&RankedNode{
   173  			Node: &structs.Node{
   174  				// Perfect fit
   175  				ID: structs.GenerateUUID(),
   176  				Resources: &structs.Resources{
   177  					CPU:      2048,
   178  					MemoryMB: 2048,
   179  				},
   180  			},
   181  		},
   182  		&RankedNode{
   183  			Node: &structs.Node{
   184  				// Perfect fit
   185  				ID: structs.GenerateUUID(),
   186  				Resources: &structs.Resources{
   187  					CPU:      2048,
   188  					MemoryMB: 2048,
   189  				},
   190  			},
   191  		},
   192  	}
   193  	static := NewStaticRankIterator(ctx, nodes)
   194  
   195  	// Add existing allocations
   196  	alloc1 := &structs.Allocation{
   197  		ID:     structs.GenerateUUID(),
   198  		EvalID: structs.GenerateUUID(),
   199  		NodeID: nodes[0].Node.ID,
   200  		JobID:  structs.GenerateUUID(),
   201  		Resources: &structs.Resources{
   202  			CPU:      2048,
   203  			MemoryMB: 2048,
   204  		},
   205  		DesiredStatus: structs.AllocDesiredStatusRun,
   206  		ClientStatus:  structs.AllocClientStatusPending,
   207  	}
   208  	alloc2 := &structs.Allocation{
   209  		ID:     structs.GenerateUUID(),
   210  		EvalID: structs.GenerateUUID(),
   211  		NodeID: nodes[1].Node.ID,
   212  		JobID:  structs.GenerateUUID(),
   213  		Resources: &structs.Resources{
   214  			CPU:      1024,
   215  			MemoryMB: 1024,
   216  		},
   217  		DesiredStatus: structs.AllocDesiredStatusRun,
   218  		ClientStatus:  structs.AllocClientStatusPending,
   219  	}
   220  	noErr(t, state.UpsertAllocs(1000, []*structs.Allocation{alloc1, alloc2}))
   221  
   222  	task := &structs.Task{
   223  		Name: "web",
   224  		Resources: &structs.Resources{
   225  			CPU:      1024,
   226  			MemoryMB: 1024,
   227  		},
   228  	}
   229  
   230  	binp := NewBinPackIterator(ctx, static, false, 0)
   231  	binp.SetTasks([]*structs.Task{task})
   232  
   233  	out := collectRanked(binp)
   234  	if len(out) != 1 {
   235  		t.Fatalf("Bad: %#v", out)
   236  	}
   237  	if out[0] != nodes[1] {
   238  		t.Fatalf("Bad: %v", out)
   239  	}
   240  	if out[0].Score != 18 {
   241  		t.Fatalf("Bad: %v", out[0])
   242  	}
   243  }
   244  
   245  func TestBinPackIterator_ExistingAlloc_PlannedEvict(t *testing.T) {
   246  	state, ctx := testContext(t)
   247  	nodes := []*RankedNode{
   248  		&RankedNode{
   249  			Node: &structs.Node{
   250  				// Perfect fit
   251  				ID: structs.GenerateUUID(),
   252  				Resources: &structs.Resources{
   253  					CPU:      2048,
   254  					MemoryMB: 2048,
   255  				},
   256  			},
   257  		},
   258  		&RankedNode{
   259  			Node: &structs.Node{
   260  				// Perfect fit
   261  				ID: structs.GenerateUUID(),
   262  				Resources: &structs.Resources{
   263  					CPU:      2048,
   264  					MemoryMB: 2048,
   265  				},
   266  			},
   267  		},
   268  	}
   269  	static := NewStaticRankIterator(ctx, nodes)
   270  
   271  	// Add existing allocations
   272  	alloc1 := &structs.Allocation{
   273  		ID:     structs.GenerateUUID(),
   274  		EvalID: structs.GenerateUUID(),
   275  		NodeID: nodes[0].Node.ID,
   276  		JobID:  structs.GenerateUUID(),
   277  		Resources: &structs.Resources{
   278  			CPU:      2048,
   279  			MemoryMB: 2048,
   280  		},
   281  		DesiredStatus: structs.AllocDesiredStatusRun,
   282  		ClientStatus:  structs.AllocClientStatusPending,
   283  	}
   284  	alloc2 := &structs.Allocation{
   285  		ID:     structs.GenerateUUID(),
   286  		EvalID: structs.GenerateUUID(),
   287  		NodeID: nodes[1].Node.ID,
   288  		JobID:  structs.GenerateUUID(),
   289  		Resources: &structs.Resources{
   290  			CPU:      1024,
   291  			MemoryMB: 1024,
   292  		},
   293  		DesiredStatus: structs.AllocDesiredStatusRun,
   294  		ClientStatus:  structs.AllocClientStatusPending,
   295  	}
   296  	noErr(t, state.UpsertAllocs(1000, []*structs.Allocation{alloc1, alloc2}))
   297  
   298  	// Add a planned eviction to alloc1
   299  	plan := ctx.Plan()
   300  	plan.NodeUpdate[nodes[0].Node.ID] = []*structs.Allocation{alloc1}
   301  
   302  	task := &structs.Task{
   303  		Name: "web",
   304  		Resources: &structs.Resources{
   305  			CPU:      1024,
   306  			MemoryMB: 1024,
   307  		},
   308  	}
   309  
   310  	binp := NewBinPackIterator(ctx, static, false, 0)
   311  	binp.SetTasks([]*structs.Task{task})
   312  
   313  	out := collectRanked(binp)
   314  	if len(out) != 2 {
   315  		t.Fatalf("Bad: %#v", out)
   316  	}
   317  	if out[0] != nodes[0] || out[1] != nodes[1] {
   318  		t.Fatalf("Bad: %v", out)
   319  	}
   320  	if out[0].Score < 10 || out[0].Score > 16 {
   321  		t.Fatalf("Bad: %v", out[0])
   322  	}
   323  	if out[1].Score != 18 {
   324  		t.Fatalf("Bad: %v", out[1])
   325  	}
   326  }
   327  
   328  func TestJobAntiAffinity_PlannedAlloc(t *testing.T) {
   329  	_, ctx := testContext(t)
   330  	nodes := []*RankedNode{
   331  		&RankedNode{
   332  			Node: &structs.Node{
   333  				ID: structs.GenerateUUID(),
   334  			},
   335  		},
   336  		&RankedNode{
   337  			Node: &structs.Node{
   338  				ID: structs.GenerateUUID(),
   339  			},
   340  		},
   341  	}
   342  	static := NewStaticRankIterator(ctx, nodes)
   343  
   344  	// Add a planned alloc to node1 that fills it
   345  	plan := ctx.Plan()
   346  	plan.NodeAllocation[nodes[0].Node.ID] = []*structs.Allocation{
   347  		&structs.Allocation{
   348  			ID:    structs.GenerateUUID(),
   349  			JobID: "foo",
   350  		},
   351  		&structs.Allocation{
   352  			ID:    structs.GenerateUUID(),
   353  			JobID: "foo",
   354  		},
   355  	}
   356  
   357  	// Add a planned alloc to node2 that half fills it
   358  	plan.NodeAllocation[nodes[1].Node.ID] = []*structs.Allocation{
   359  		&structs.Allocation{
   360  			JobID: "bar",
   361  		},
   362  	}
   363  
   364  	binp := NewJobAntiAffinityIterator(ctx, static, 5.0, "foo")
   365  
   366  	out := collectRanked(binp)
   367  	if len(out) != 2 {
   368  		t.Fatalf("Bad: %#v", out)
   369  	}
   370  	if out[0] != nodes[0] {
   371  		t.Fatalf("Bad: %v", out)
   372  	}
   373  	if out[0].Score != -10.0 {
   374  		t.Fatalf("Bad: %#v", out[0])
   375  	}
   376  
   377  	if out[1] != nodes[1] {
   378  		t.Fatalf("Bad: %v", out)
   379  	}
   380  	if out[1].Score != 0.0 {
   381  		t.Fatalf("Bad: %v", out[1])
   382  	}
   383  }
   384  
   385  func collectRanked(iter RankIterator) (out []*RankedNode) {
   386  	for {
   387  		next := iter.Next()
   388  		if next == nil {
   389  			break
   390  		}
   391  		out = append(out, next)
   392  	}
   393  	return
   394  }