@@ -1682,3 +1682,132 @@ func TestAllocRunner_Reconnect(t *testing.T) {
1682
1682
})
1683
1683
}
1684
1684
}
1685
+
1686
+ // TestAllocRunner_Lifecycle_Shutdown_Order asserts that a service job with 3
1687
+ // lifecycle hooks (1 sidecar, 1 ephemeral, 1 poststop) starts all 4 tasks, and shuts down
1688
+ // the sidecar after main, but before poststop.
1689
+ func TestAllocRunner_Lifecycle_Shutdown_Order (t * testing.T ) {
1690
+ alloc := mock .LifecycleAllocWithPoststopDeploy ()
1691
+
1692
+ alloc .Job .Type = structs .JobTypeService
1693
+
1694
+ mainTask := alloc .Job .TaskGroups [0 ].Tasks [0 ]
1695
+ mainTask .Config ["run_for" ] = "100s"
1696
+
1697
+ sidecarTask := alloc .Job .TaskGroups [0 ].Tasks [1 ]
1698
+ sidecarTask .Lifecycle .Hook = structs .TaskLifecycleHookPoststart
1699
+ sidecarTask .Config ["run_for" ] = "100s"
1700
+
1701
+ poststopTask := alloc .Job .TaskGroups [0 ].Tasks [2 ]
1702
+ ephemeralTask := alloc .Job .TaskGroups [0 ].Tasks [3 ]
1703
+
1704
+ alloc .Job .TaskGroups [0 ].Tasks = []* structs.Task {mainTask , ephemeralTask , sidecarTask , poststopTask }
1705
+
1706
+ conf , cleanup := testAllocRunnerConfig (t , alloc )
1707
+ defer cleanup ()
1708
+ ar , err := NewAllocRunner (conf )
1709
+ require .NoError (t , err )
1710
+ defer destroy (ar )
1711
+ go ar .Run ()
1712
+
1713
+ upd := conf .StateUpdater .(* MockStateUpdater )
1714
+
1715
+ // Wait for main and sidecar tasks to be running, and that the
1716
+ // ephemeral task ran and exited.
1717
+ testutil .WaitForResult (func () (bool , error ) {
1718
+ last := upd .Last ()
1719
+ if last == nil {
1720
+ return false , fmt .Errorf ("No updates" )
1721
+ }
1722
+
1723
+ if last .ClientStatus != structs .AllocClientStatusRunning {
1724
+ return false , fmt .Errorf ("expected alloc to be running not %s" , last .ClientStatus )
1725
+ }
1726
+
1727
+ if s := last .TaskStates [mainTask .Name ].State ; s != structs .TaskStateRunning {
1728
+ return false , fmt .Errorf ("expected main task to be running not %s" , s )
1729
+ }
1730
+
1731
+ if s := last .TaskStates [sidecarTask .Name ].State ; s != structs .TaskStateRunning {
1732
+ return false , fmt .Errorf ("expected sidecar task to be running not %s" , s )
1733
+ }
1734
+
1735
+ if s := last .TaskStates [ephemeralTask .Name ].State ; s != structs .TaskStateDead {
1736
+ return false , fmt .Errorf ("expected ephemeral task to be dead not %s" , s )
1737
+ }
1738
+
1739
+ if last .TaskStates [ephemeralTask .Name ].Failed {
1740
+ return false , fmt .Errorf ("expected ephemeral task to be successful not failed" )
1741
+ }
1742
+
1743
+ return true , nil
1744
+ }, func (err error ) {
1745
+ t .Fatalf ("error waiting for initial state:\n %v" , err )
1746
+ })
1747
+
1748
+ // Tell the alloc to stop
1749
+ stopAlloc := alloc .Copy ()
1750
+ stopAlloc .DesiredStatus = structs .AllocDesiredStatusStop
1751
+ ar .Update (stopAlloc )
1752
+
1753
+ // Wait for tasks to stop.
1754
+ testutil .WaitForResult (func () (bool , error ) {
1755
+ last := upd .Last ()
1756
+
1757
+ if s := last .TaskStates [ephemeralTask .Name ].State ; s != structs .TaskStateDead {
1758
+ return false , fmt .Errorf ("expected ephemeral task to be dead not %s" , s )
1759
+ }
1760
+
1761
+ if last .TaskStates [ephemeralTask .Name ].Failed {
1762
+ return false , fmt .Errorf ("expected ephemeral task to be successful not failed" )
1763
+ }
1764
+
1765
+ if s := last .TaskStates [mainTask .Name ].State ; s != structs .TaskStateDead {
1766
+ return false , fmt .Errorf ("expected main task to be dead not %s" , s )
1767
+ }
1768
+
1769
+ if last .TaskStates [mainTask .Name ].Failed {
1770
+ return false , fmt .Errorf ("expected main task to be successful not failed" )
1771
+ }
1772
+
1773
+ if s := last .TaskStates [sidecarTask .Name ].State ; s != structs .TaskStateDead {
1774
+ return false , fmt .Errorf ("expected sidecar task to be dead not %s" , s )
1775
+ }
1776
+
1777
+ if last .TaskStates [sidecarTask .Name ].Failed {
1778
+ return false , fmt .Errorf ("expected sidecar task to be successful not failed" )
1779
+ }
1780
+
1781
+ if s := last .TaskStates [poststopTask .Name ].State ; s != structs .TaskStateRunning {
1782
+ return false , fmt .Errorf ("expected poststop task to be running not %s" , s )
1783
+ }
1784
+
1785
+ return true , nil
1786
+ }, func (err error ) {
1787
+ t .Fatalf ("error waiting for kill state:\n %v" , err )
1788
+ })
1789
+
1790
+ last := upd .Last ()
1791
+ require .Less (t , last .TaskStates [ephemeralTask .Name ].FinishedAt , last .TaskStates [mainTask .Name ].FinishedAt )
1792
+ require .Less (t , last .TaskStates [mainTask .Name ].FinishedAt , last .TaskStates [sidecarTask .Name ].FinishedAt )
1793
+
1794
+ // Wait for poststop task to stop.
1795
+ testutil .WaitForResult (func () (bool , error ) {
1796
+ last := upd .Last ()
1797
+
1798
+ if s := last .TaskStates [poststopTask .Name ].State ; s != structs .TaskStateDead {
1799
+ return false , fmt .Errorf ("expected poststop task to be dead not %s" , s )
1800
+ }
1801
+
1802
+ if last .TaskStates [poststopTask .Name ].Failed {
1803
+ return false , fmt .Errorf ("expected poststop task to be successful not failed" )
1804
+ }
1805
+
1806
+ return true , nil
1807
+ }, func (err error ) {
1808
+ t .Fatalf ("error waiting for poststop state:\n %v" , err )
1809
+ })
1810
+
1811
+ last = upd .Last ()
1812
+ require .Less (t , last .TaskStates [sidecarTask .Name ].FinishedAt , last .TaskStates [poststopTask .Name ].FinishedAt )
1813
+ }
0 commit comments