@@ -1573,3 +1573,132 @@ func TestAllocRunner_PersistState_Destroyed(t *testing.T) {
1573
1573
require .NoError (t , err )
1574
1574
require .Nil (t , ts )
1575
1575
}
1576
+
1577
+ // TestAllocRunner_Lifecycle_Shutdown_Order asserts that a service job with 3
1578
+ // lifecycle hooks (1 sidecar, 1 ephemeral, 1 poststop) starts all 4 tasks, and shuts down
1579
+ // the sidecar after main, but before poststop.
1580
+ func TestAllocRunner_Lifecycle_Shutdown_Order (t * testing.T ) {
1581
+ alloc := mock .LifecycleAllocWithPoststopDeploy ()
1582
+
1583
+ alloc .Job .Type = structs .JobTypeService
1584
+
1585
+ mainTask := alloc .Job .TaskGroups [0 ].Tasks [0 ]
1586
+ mainTask .Config ["run_for" ] = "100s"
1587
+
1588
+ sidecarTask := alloc .Job .TaskGroups [0 ].Tasks [1 ]
1589
+ sidecarTask .Lifecycle .Hook = structs .TaskLifecycleHookPoststart
1590
+ sidecarTask .Config ["run_for" ] = "100s"
1591
+
1592
+ poststopTask := alloc .Job .TaskGroups [0 ].Tasks [2 ]
1593
+ ephemeralTask := alloc .Job .TaskGroups [0 ].Tasks [3 ]
1594
+
1595
+ alloc .Job .TaskGroups [0 ].Tasks = []* structs.Task {mainTask , ephemeralTask , sidecarTask , poststopTask }
1596
+
1597
+ conf , cleanup := testAllocRunnerConfig (t , alloc )
1598
+ defer cleanup ()
1599
+ ar , err := NewAllocRunner (conf )
1600
+ require .NoError (t , err )
1601
+ defer destroy (ar )
1602
+ go ar .Run ()
1603
+
1604
+ upd := conf .StateUpdater .(* MockStateUpdater )
1605
+
1606
+ // Wait for main and sidecar tasks to be running, and that the
1607
+ // ephemeral task ran and exited.
1608
+ testutil .WaitForResult (func () (bool , error ) {
1609
+ last := upd .Last ()
1610
+ if last == nil {
1611
+ return false , fmt .Errorf ("No updates" )
1612
+ }
1613
+
1614
+ if last .ClientStatus != structs .AllocClientStatusRunning {
1615
+ return false , fmt .Errorf ("expected alloc to be running not %s" , last .ClientStatus )
1616
+ }
1617
+
1618
+ if s := last .TaskStates [mainTask .Name ].State ; s != structs .TaskStateRunning {
1619
+ return false , fmt .Errorf ("expected main task to be running not %s" , s )
1620
+ }
1621
+
1622
+ if s := last .TaskStates [sidecarTask .Name ].State ; s != structs .TaskStateRunning {
1623
+ return false , fmt .Errorf ("expected sidecar task to be running not %s" , s )
1624
+ }
1625
+
1626
+ if s := last .TaskStates [ephemeralTask .Name ].State ; s != structs .TaskStateDead {
1627
+ return false , fmt .Errorf ("expected ephemeral task to be dead not %s" , s )
1628
+ }
1629
+
1630
+ if last .TaskStates [ephemeralTask .Name ].Failed {
1631
+ return false , fmt .Errorf ("expected ephemeral task to be successful not failed" )
1632
+ }
1633
+
1634
+ return true , nil
1635
+ }, func (err error ) {
1636
+ t .Fatalf ("error waiting for initial state:\n %v" , err )
1637
+ })
1638
+
1639
+ // Tell the alloc to stop
1640
+ stopAlloc := alloc .Copy ()
1641
+ stopAlloc .DesiredStatus = structs .AllocDesiredStatusStop
1642
+ ar .Update (stopAlloc )
1643
+
1644
+ // Wait for tasks to stop.
1645
+ testutil .WaitForResult (func () (bool , error ) {
1646
+ last := upd .Last ()
1647
+
1648
+ if s := last .TaskStates [ephemeralTask .Name ].State ; s != structs .TaskStateDead {
1649
+ return false , fmt .Errorf ("expected ephemeral task to be dead not %s" , s )
1650
+ }
1651
+
1652
+ if last .TaskStates [ephemeralTask .Name ].Failed {
1653
+ return false , fmt .Errorf ("expected ephemeral task to be successful not failed" )
1654
+ }
1655
+
1656
+ if s := last .TaskStates [mainTask .Name ].State ; s != structs .TaskStateDead {
1657
+ return false , fmt .Errorf ("expected main task to be dead not %s" , s )
1658
+ }
1659
+
1660
+ if last .TaskStates [mainTask .Name ].Failed {
1661
+ return false , fmt .Errorf ("expected main task to be successful not failed" )
1662
+ }
1663
+
1664
+ if s := last .TaskStates [sidecarTask .Name ].State ; s != structs .TaskStateDead {
1665
+ return false , fmt .Errorf ("expected sidecar task to be dead not %s" , s )
1666
+ }
1667
+
1668
+ if last .TaskStates [sidecarTask .Name ].Failed {
1669
+ return false , fmt .Errorf ("expected sidecar task to be successful not failed" )
1670
+ }
1671
+
1672
+ if s := last .TaskStates [poststopTask .Name ].State ; s != structs .TaskStateRunning {
1673
+ return false , fmt .Errorf ("expected poststop task to be running not %s" , s )
1674
+ }
1675
+
1676
+ return true , nil
1677
+ }, func (err error ) {
1678
+ t .Fatalf ("error waiting for kill state:\n %v" , err )
1679
+ })
1680
+
1681
+ last := upd .Last ()
1682
+ require .True (t , last .TaskStates [ephemeralTask .Name ].FinishedAt .Before (last .TaskStates [mainTask .Name ].FinishedAt ))
1683
+ require .True (t , last .TaskStates [mainTask .Name ].FinishedAt .Before (last .TaskStates [sidecarTask .Name ].FinishedAt ))
1684
+
1685
+ // Wait for poststop task to stop.
1686
+ testutil .WaitForResult (func () (bool , error ) {
1687
+ last := upd .Last ()
1688
+
1689
+ if s := last .TaskStates [poststopTask .Name ].State ; s != structs .TaskStateDead {
1690
+ return false , fmt .Errorf ("expected poststop task to be dead not %s" , s )
1691
+ }
1692
+
1693
+ if last .TaskStates [poststopTask .Name ].Failed {
1694
+ return false , fmt .Errorf ("expected poststop task to be successful not failed" )
1695
+ }
1696
+
1697
+ return true , nil
1698
+ }, func (err error ) {
1699
+ t .Fatalf ("error waiting for poststop state:\n %v" , err )
1700
+ })
1701
+
1702
+ last = upd .Last ()
1703
+ require .True (t , last .TaskStates [sidecarTask .Name ].FinishedAt .Before (last .TaskStates [poststopTask .Name ].FinishedAt ))
1704
+ }
0 commit comments