func TestReconciler_DeploymentLimit_HealthAccounting(t *testing.T) {
	job := mock.Job()
	job.TaskGroups[0].Update = noCanaryUpdate

	cases := []struct {
		healthy int
	}{
		{
			healthy: 0,
		},
		{
			healthy: 1,
		},
		{
			healthy: 2,
		},
		{
			healthy: 3,
		},
		{
			healthy: 4,
		},
	}

	for _, c := range cases {
		t.Run(fmt.Sprintf("%d healthy", c.healthy), func(t *testing.T) {
			// Create an existing deployment that has placed some canaries and mark them
			// promoted
			d := structs.NewDeployment(job)
			d.TaskGroups[job.TaskGroups[0].Name] = &structs.DeploymentState{
				Promoted:     true,
				DesiredTotal: 10,
				PlacedAllocs: 4,
			}

			// Create 6 allocations from the old job
			var allocs []*structs.Allocation
			for i := 4; i < 10; i++ {
				alloc := mock.Alloc()
				alloc.Job = job
				alloc.JobID = job.ID
				alloc.NodeID = structs.GenerateUUID()
				alloc.Name = structs.AllocName(job.ID, job.TaskGroups[0].Name, uint(i))
				alloc.TaskGroup = job.TaskGroups[0].Name
				allocs = append(allocs, alloc)
			}

			// Create the new allocs
			handled := make(map[string]allocUpdateType)
			for i := 0; i < 4; i++ {
				new := mock.Alloc()
				new.Job = job
				new.JobID = job.ID
				new.NodeID = structs.GenerateUUID()
				new.Name = structs.AllocName(job.ID, job.TaskGroups[0].Name, uint(i))
				new.TaskGroup = job.TaskGroups[0].Name
				new.DeploymentID = d.ID
				if i < c.healthy {
					new.DeploymentStatus = &structs.AllocDeploymentStatus{
						Healthy: helper.BoolToPtr(true),
					}
				}
				allocs = append(allocs, new)
				handled[new.ID] = allocUpdateFnIgnore
			}

			mockUpdateFn := allocUpdateFnMock(handled, allocUpdateFnDestructive)
			reconciler := NewAllocReconciler(testLogger(), mockUpdateFn, false, job.ID, job, d, allocs, nil)
			r := reconciler.Compute()

			// Assert the correct results
			assertResults(t, r, &resultExpectation{
				createDeployment:  nil,
				deploymentUpdates: nil,
				destructive:       c.healthy,
				desiredTGUpdates: map[string]*structs.DesiredUpdates{
					job.TaskGroups[0].Name: {
						DestructiveUpdate: uint64(c.healthy),
						Ignore:            uint64(10 - c.healthy),
					},
				},
			})

			if c.healthy != 0 {
				assertNamesHaveIndexes(t, intRange(4, 3+c.healthy), destructiveResultsToNames(r.destructiveUpdate))
			}
		})
	}
}
// Tests the reconciler checks the health of placed allocs to determine the
// limit
Tests the reconciler checks the health of placed allocs to determine the
limit
// Tests the reconciler checks the health of placed allocs to determine the // limit
func TestReconciler_DeploymentLimit_HealthAccounting(t *testing.T) {
	job := mock.Job()
	job.TaskGroups[0].Update = noCanaryUpdate
	cases := []struct {
		healthy int
	}{
		{
			healthy: 0,
		},
		{
			healthy: 1,
		},
		{
			healthy: 2,
		},
		{
			healthy: 3,
		},
		{
			healthy: 4,
		},
	}
	for _, c := range cases {
		t.Run(fmt.Sprintf("%d healthy", c.healthy), func(t *testing.T) {
			d := structs.NewDeployment(job)
			d.TaskGroups[job.TaskGroups[0].Name] = &structs.DeploymentState{
				Promoted:     true,
				DesiredTotal: 10,
				PlacedAllocs: 4,
			}
			var allocs []*structs.Allocation
			for i := 4; i < 10; i++ {
				alloc := mock.Alloc()
				alloc.Job = job
				alloc.JobID = job.ID
				alloc.NodeID = structs.GenerateUUID()
				alloc.Name = structs.AllocName(job.ID, job.TaskGroups[0].Name, uint(i))
				alloc.TaskGroup = job.TaskGroups[0].Name
				allocs = append(allocs, alloc)
			}
			handled := make(map[string]allocUpdateType)
			for i := 0; i < 4; i++ {
				new := mock.Alloc()
				new.Job = job
				new.JobID = job.ID
				new.NodeID = structs.GenerateUUID()
				new.Name = structs.AllocName(job.ID, job.TaskGroups[0].Name, uint(i))
				new.TaskGroup = job.TaskGroups[0].Name
				new.DeploymentID = d.ID
				if i < c.healthy {
					new.DeploymentStatus = &structs.AllocDeploymentStatus{
						Healthy: helper.BoolToPtr(true),
					}
				}
				allocs = append(allocs, new)
				handled[new.ID] = allocUpdateFnIgnore
			}
			mockUpdateFn := allocUpdateFnMock(handled, allocUpdateFnDestructive)
			reconciler := NewAllocReconciler(testLogger(), mockUpdateFn, false, job.ID, job, d, allocs, nil)
			r := reconciler.Compute()
			assertResults(t, r, &resultExpectation{
				createDeployment:  nil,
				deploymentUpdates: nil,
				destructive:       c.healthy,
				desiredTGUpdates: map[string]*structs.DesiredUpdates{
					job.TaskGroups[0].Name: {
						DestructiveUpdate: uint64(c.healthy),
						Ignore:            uint64(10 - c.healthy),
					},
				},
			})
			if c.healthy != 0 {
				assertNamesHaveIndexes(t, intRange(4, 3+c.healthy), destructiveResultsToNames(r.destructiveUpdate))
			}
		})
	}
}
func TestReconciler_DeploymentLimit_HealthAccounting ( t * testing . T ) { job := mock . Job ( ) job . TaskGroups [ 0 ] . Update = noCanaryUpdate cases := [ ] struct { healthy int } { { healthy : 0 , } , { healthy : 1 , } , { healthy : 2 , } , { healthy : 3 , } , { healthy : 4 , } , } for _ , c := range cases { t . Run ( fmt . Sprintf ( " " , c . healthy ) , func ( t * testing . T ) { d := structs . NewDeployment ( job ) d . TaskGroups [ job . TaskGroups [ 0 ] . Name ] = & structs . DeploymentState { Promoted : true , DesiredTotal : 10 , PlacedAllocs : 4 , } var allocs [ ] * structs . Allocation for i := 4 ; i < 10 ; i ++ { alloc := mock . Alloc ( ) alloc . Job = job alloc . JobID = job . ID alloc . NodeID = structs . GenerateUUID ( ) alloc . Name = structs . AllocName ( job . ID , job . TaskGroups [ 0 ] . Name , uint ( i ) ) alloc . TaskGroup = job . TaskGroups [ 0 ] . Name allocs = append ( allocs , alloc ) } handled := make ( map [ string ] allocUpdateType ) for i := 0 ; i < 4 ; i ++ { new := mock . Alloc ( ) new . Job = job new . JobID = job . ID new . NodeID = structs . GenerateUUID ( ) new . Name = structs . AllocName ( job . ID , job . TaskGroups [ 0 ] . Name , uint ( i ) ) new . TaskGroup = job . TaskGroups [ 0 ] . Name new . DeploymentID = d . ID if i < c . healthy { new . DeploymentStatus = & structs . AllocDeploymentStatus { Healthy : helper . BoolToPtr ( true ) , } } allocs = append ( allocs , new ) handled [ new . ID ] = allocUpdateFnIgnore } mockUpdateFn := allocUpdateFnMock ( handled , allocUpdateFnDestructive ) reconciler := NewAllocReconciler ( testLogger ( ) , mockUpdateFn , false , job . ID , job , d , allocs , nil ) r := reconciler . Compute ( ) assertResults ( t , r , & resultExpectation { createDeployment : nil , deploymentUpdates : nil , destructive : c . healthy , desiredTGUpdates : map [ string ] * structs . DesiredUpdates { job . TaskGroups [ 0 ] . Name : { DestructiveUpdate : uint64 ( c . healthy ) , Ignore : uint64 ( 10 - c . healthy ) , } , } , } ) if c . healthy != 0 { assertNamesHaveIndexes ( t , intRange ( 4 , 3 + c . healthy ) , destructiveResultsToNames ( r . destructiveUpdate ) ) } } ) } }
Tests the reconciler checks the health of placed allocs to determine the
limit
Tests the reconciler checks the health of placed allocs to determine the limit
// Create an existing deployment that has placed some canaries and mark them // promoted // Create 6 allocations from the old job // Create the new allocs // Assert the correct results