func TestReconciler_FailedDeployment_CancelCanaries(t *testing.T) {
	// Create a job with two task groups
	job := mock.Job()
	job.TaskGroups[0].Update = canaryUpdate
	job.TaskGroups = append(job.TaskGroups, job.TaskGroups[0].Copy())
	job.TaskGroups[1].Name = "two"

	// Create an existing failed deployment that has promoted one task group
	d := structs.NewDeployment(job)
	d.Status = structs.DeploymentStatusFailed
	s0 := &structs.DeploymentState{
		Promoted:        true,
		DesiredTotal:    10,
		DesiredCanaries: 2,
		PlacedAllocs:    4,
	}
	s1 := &structs.DeploymentState{
		Promoted:        false,
		DesiredTotal:    10,
		DesiredCanaries: 2,
		PlacedAllocs:    2,
	}
	d.TaskGroups[job.TaskGroups[0].Name] = s0
	d.TaskGroups[job.TaskGroups[1].Name] = s1

	// Create 6 allocations from the old job
	var allocs []*structs.Allocation
	handled := make(map[string]allocUpdateType)
	for _, group := range []int{0, 1} {
		replacements := 4
		state := s0
		if group == 1 {
			replacements = 2
			state = s1
		}

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

			// Add the alloc to the canary list
			if i < 2 {
				state.PlacedCanaries = append(state.PlacedCanaries, new.ID)
			}
		}
		for i := replacements; 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[group].Name, uint(i))
			alloc.TaskGroup = job.TaskGroups[group].Name
			allocs = append(allocs, alloc)
		}
	}

	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,
		place:             0,
		inplace:           0,
		stop:              2,
		desiredTGUpdates: map[string]*structs.DesiredUpdates{
			job.TaskGroups[0].Name: {
				Ignore: 10,
			},
			job.TaskGroups[1].Name: {
				Stop:   2,
				Ignore: 8,
			},
		},
	})

	assertNamesHaveIndexes(t, intRange(0, 1), stopResultsToNames(r.stop))
}
// Test that a failed deployment cancels non-promoted canaries
Test that a failed deployment cancels non-promoted canaries
// Test that a failed deployment cancels non - promoted canaries
func TestReconciler_FailedDeployment_CancelCanaries(t *testing.T) {
	job := mock.Job()
	job.TaskGroups[0].Update = canaryUpdate
	job.TaskGroups = append(job.TaskGroups, job.TaskGroups[0].Copy())
	job.TaskGroups[1].Name = "two"
	d := structs.NewDeployment(job)
	d.Status = structs.DeploymentStatusFailed
	s0 := &structs.DeploymentState{
		Promoted:        true,
		DesiredTotal:    10,
		DesiredCanaries: 2,
		PlacedAllocs:    4,
	}
	s1 := &structs.DeploymentState{
		Promoted:        false,
		DesiredTotal:    10,
		DesiredCanaries: 2,
		PlacedAllocs:    2,
	}
	d.TaskGroups[job.TaskGroups[0].Name] = s0
	d.TaskGroups[job.TaskGroups[1].Name] = s1
	var allocs []*structs.Allocation
	handled := make(map[string]allocUpdateType)
	for _, group := range []int{0, 1} {
		replacements := 4
		state := s0
		if group == 1 {
			replacements = 2
			state = s1
		}
		for i := 0; i < replacements; i++ {
			new := mock.Alloc()
			new.Job = job
			new.JobID = job.ID
			new.NodeID = structs.GenerateUUID()
			new.Name = structs.AllocName(job.ID, job.TaskGroups[group].Name, uint(i))
			new.TaskGroup = job.TaskGroups[group].Name
			new.DeploymentID = d.ID
			new.DeploymentStatus = &structs.AllocDeploymentStatus{
				Healthy: helper.BoolToPtr(true),
			}
			allocs = append(allocs, new)
			handled[new.ID] = allocUpdateFnIgnore
			if i < 2 {
				state.PlacedCanaries = append(state.PlacedCanaries, new.ID)
			}
		}
		for i := replacements; 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[group].Name, uint(i))
			alloc.TaskGroup = job.TaskGroups[group].Name
			allocs = append(allocs, alloc)
		}
	}
	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,
		place:             0,
		inplace:           0,
		stop:              2,
		desiredTGUpdates: map[string]*structs.DesiredUpdates{
			job.TaskGroups[0].Name: {
				Ignore: 10,
			},
			job.TaskGroups[1].Name: {
				Stop:   2,
				Ignore: 8,
			},
		},
	})
	assertNamesHaveIndexes(t, intRange(0, 1), stopResultsToNames(r.stop))
}
func TestReconciler_FailedDeployment_CancelCanaries ( t * testing . T ) { job := mock . Job ( ) job . TaskGroups [ 0 ] . Update = canaryUpdate job . TaskGroups = append ( job . TaskGroups , job . TaskGroups [ 0 ] . Copy ( ) ) job . TaskGroups [ 1 ] . Name = " " d := structs . NewDeployment ( job ) d . Status = structs . DeploymentStatusFailed s0 := & structs . DeploymentState { Promoted : true , DesiredTotal : 10 , DesiredCanaries : 2 , PlacedAllocs : 4 , } s1 := & structs . DeploymentState { Promoted : false , DesiredTotal : 10 , DesiredCanaries : 2 , PlacedAllocs : 2 , } d . TaskGroups [ job . TaskGroups [ 0 ] . Name ] = s0 d . TaskGroups [ job . TaskGroups [ 1 ] . Name ] = s1 var allocs [ ] * structs . Allocation handled := make ( map [ string ] allocUpdateType ) for _ , group := range [ ] int { 0 , 1 } { replacements := 4 state := s0 if group == 1 { replacements = 2 state = s1 } for i := 0 ; i < replacements ; i ++ { new := mock . Alloc ( ) new . Job = job new . JobID = job . ID new . NodeID = structs . GenerateUUID ( ) new . Name = structs . AllocName ( job . ID , job . TaskGroups [ group ] . Name , uint ( i ) ) new . TaskGroup = job . TaskGroups [ group ] . Name new . DeploymentID = d . ID new . DeploymentStatus = & structs . AllocDeploymentStatus { Healthy : helper . BoolToPtr ( true ) , } allocs = append ( allocs , new ) handled [ new . ID ] = allocUpdateFnIgnore if i < 2 { state . PlacedCanaries = append ( state . PlacedCanaries , new . ID ) } } for i := replacements ; 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 [ group ] . Name , uint ( i ) ) alloc . TaskGroup = job . TaskGroups [ group ] . Name allocs = append ( allocs , alloc ) } } 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 , place : 0 , inplace : 0 , stop : 2 , desiredTGUpdates : map [ string ] * structs . DesiredUpdates { job . TaskGroups [ 0 ] . Name : { Ignore : 10 , } , job . TaskGroups [ 1 ] . Name : { Stop : 2 , Ignore : 8 , } , } , } ) assertNamesHaveIndexes ( t , intRange ( 0 , 1 ) , stopResultsToNames ( r . stop ) ) }
Test that a failed deployment cancels non-promoted canaries
Test that a failed deployment cancels non - promoted canaries
// Create a job with two task groups // Create an existing failed deployment that has promoted one task group // Create 6 allocations from the old job // Create the healthy replacements // Add the alloc to the canary list // Assert the correct results