Repository: hashicorp/nomad
File: scheduler/generic_sched_test.go (Go)

Code before:
		planned = append(planned, allocList...)
	}

	assert.Len(t, planned, 3, "expected 3 planned allocations")

	// Lookup the allocations by JobID
	ws := memdb.NewWatchSet()
	out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
	require.NoError(t, err)

	assert.Len(t, out, 3, "expected 3 placed allocations")

	// Ensure the allocations continute to have the correct reserved cores
	for _, alloc := range out {
		if alloc.Name == "my-job.web[0]" {
			alloc.AllocatedResources.Tasks["web"].Cpu.ReservedCores = []uint16{uint16(1)}
		}
		if alloc.Name == "my-job.web[1]" {
			alloc.AllocatedResources.Tasks["web"].Cpu.ReservedCores = []uint16{uint16(2)}
		}
	}

	h.AssertEvalStatus(t, structs.EvalStatusComplete)
}

func TestServiceSched_ReservedCores_NewPlacements(t *testing.T) {
	ci.Parallel(t)

	h := NewHarness(t)

	// Create a node
	node := mock.Node()
	require.NoError(t, h.State.UpsertNode(structs.MsgTypeTestSetup, h.NextIndex(), node))

	// Create a job
	job := mock.Job()
	job.TaskGroups[0].Tasks[0].Resources.Cores = 1
	job.TaskGroups[0].Count = 3
	require.NoError(t, h.State.UpsertJob(structs.MsgTypeTestSetup, h.NextIndex(), nil, job))

	// Create a mock evaluation to register the job
	eval := &structs.Evaluation{
		Namespace:   structs.DefaultNamespace,
		ID:          uuid.Generate(),
		Priority:    job.Priority,
		TriggeredBy: structs.EvalTriggerJobRegister,
		JobID:       job.ID,
		Status:      structs.EvalStatusPending,
	}

	require.NoError(t, h.State.UpsertEvals(structs.MsgTypeTestSetup, h.NextIndex(), []*structs.Evaluation{eval}))

Review comment (suggestion):
Given the amount of setup these tests take, I wouldn't be opposed to merging these two tests. That way you could run the job v0 to make sure you have non-overlapping cores in the "fresh" state, modify it and run job v1 to make sure you still have non-overlapping cores afterwards.

Code after:
			default: // Ensure that the new planned alloc is not on core 1 or 2
				must.NotEq(t, []uint16{uint16(2)}, alloc.AllocatedResources.Tasks["web"].Cpu.ReservedCores)
				must.NotEq(t, []uint16{uint16(1)}, alloc.AllocatedResources.Tasks["web"].Cpu.ReservedCores)
			}
		}
		planned = append(planned, allocList...)
	}

	must.Len(t, 3, planned)

	// Lookup the allocations by JobID
	ws := memdb.NewWatchSet()
	out, err := h.State.AllocsByJob(ws, job.Namespace, job.ID, false)
	must.NoError(t, err)

	must.Len(t, 3, out)

	// Ensure the allocations continute to have the correct reserved cores
	for _, alloc := range out {
		switch alloc.Name {
		case "my-job.web[0]": // Ensure that the first alloc is still on core 1
			must.Eq(t, []uint16{uint16(1)}, alloc.AllocatedResources.Tasks["web"].Cpu.ReservedCores)
		case "my-job.web[1]": // Ensure that the second alloc is still on core 2
			must.Eq(t, []uint16{uint16(2)}, alloc.AllocatedResources.Tasks["web"].Cpu.ReservedCores)
		default: // Ensure that the new alloc is not on core 1 or 2
			must.NotEq(t, []uint16{uint16(2)}, alloc.AllocatedResources.Tasks["web"].Cpu.ReservedCores)
			must.NotEq(t, []uint16{uint16(1)}, alloc.AllocatedResources.Tasks["web"].Cpu.ReservedCores)
		}
	}

	h.AssertEvalStatus(t, structs.EvalStatusComplete)
}

func initNodeAndAllocs(t *testing.T, h *Harness, job *structs.Job,
	nodeStatus, clientStatus string) (*structs.Node, *structs.Job, []*structs.Allocation) {
	// Node, which is ready
	node := mock.Node()
	node.Status = nodeStatus
	require.NoError(t, h.State.UpsertNode(structs.MsgTypeTestSetup, h.NextIndex(), node))

	allocs := make([]*structs.Allocation, job.TaskGroups[0].Count)
	for i := 0; i < job.TaskGroups[0].Count; i++ {
		// Alloc for the running group
		alloc := mock.Alloc()
		alloc.Job = job
		alloc.JobID = job.ID
		alloc.NodeID = node.ID
		alloc.Name = fmt.Sprintf("my-job.web[%d]", i)
		alloc.DesiredStatus = structs.AllocDesiredStatusRun
		alloc.ClientStatus = clientStatus