@@ -14,6 +14,7 @@ import (
1414 "time"
1515
1616 "github.com/hashicorp/nomad/ci"
17+ "github.com/hashicorp/nomad/client/allocdir"
1718 regMock "github.com/hashicorp/nomad/client/serviceregistration/mock"
1819 "github.com/hashicorp/nomad/client/state"
1920 "github.com/hashicorp/nomad/client/taskenv"
@@ -108,6 +109,8 @@ func TestAllocRunner_Restore_RunningTerminal(t *testing.T) {
108109
109110 // Use original statedb to maintain hook state
110111 conf2 .StateDB = conf .StateDB
112+ conf2 .ClientConfig .AllocDir = conf .ClientConfig .AllocDir
113+ conf2 .ClientConfig .AllocMountsDir = conf .ClientConfig .AllocMountsDir
111114
112115 // Restore, start, and wait for task to be killed
113116 ar2Iface , err := NewAllocRunner (conf2 )
@@ -203,6 +206,8 @@ func TestAllocRunner_Restore_CompletedBatch(t *testing.T) {
203206
204207 // Use original statedb to maintain hook state
205208 conf2 .StateDB = conf .StateDB
209+ conf2 .ClientConfig .AllocDir = conf .ClientConfig .AllocDir
210+ conf2 .ClientConfig .AllocMountsDir = conf .ClientConfig .AllocMountsDir
206211
207212 // Restore, start, and wait for task to be killed
208213 ar2Iface , err := NewAllocRunner (conf2 )
@@ -307,3 +312,80 @@ func (*allocFailingPrestartHook) Name() string { return "failing_prestart" }
307312func (* allocFailingPrestartHook ) Prerun (_ * taskenv.TaskEnv ) error {
308313 return fmt .Errorf ("failing prestart hooks" )
309314}
315+
316+ // TestAllocRunner_Restore_MissingAllocDir asserts that restoring a running
317+ // system alloc fails if client.alloc_dir was wiped.
318+ func TestAllocRunner_Restore_MissingAllocDir (t * testing.T ) {
319+ ci .Parallel (t )
320+
321+ alloc := mock .Alloc ()
322+ alloc .Job .Type = structs .JobTypeSystem
323+ taskName := alloc .Job .TaskGroups [0 ].Tasks [0 ].Name
324+ alloc .Job .TaskGroups [0 ].Tasks [0 ].Driver = "mock_driver"
325+ alloc .Job .TaskGroups [0 ].Tasks [0 ].Config = map [string ]interface {}{
326+ "run_for" : "1h" ,
327+ }
328+
329+ conf , cleanup := testAllocRunnerConfig (t , alloc .Copy ())
330+ defer cleanup ()
331+
332+ // Persist task hook state across runner restarts to simulate data_dir.
333+ conf .StateDB = state .NewMemDB (conf .Logger )
334+
335+ arIface , err := NewAllocRunner (conf )
336+ must .NoError (t , err )
337+ ar := arIface .(* allocRunner )
338+
339+ go ar .Run ()
340+ defer destroy (ar )
341+
342+ testutil .WaitForResult (func () (bool , error ) {
343+ state := ar .AllocState ()
344+ ts , ok := state .TaskStates [taskName ]
345+ if ! ok || ts == nil {
346+ return false , fmt .Errorf ("expected task to be running" )
347+ }
348+ if ts .State != structs .TaskStateRunning {
349+ return false , fmt .Errorf ("expected task to be running" )
350+ }
351+ return true , nil
352+ }, func (err error ) {
353+ require .NoError (t , err )
354+ })
355+
356+ allocDirPath := ar .GetAllocDir ().AllocDirPath ()
357+
358+ ar .Shutdown ()
359+ select {
360+ case <- ar .ShutdownCh ():
361+ case <- time .After (30 * time .Second ):
362+ require .Fail (t , "AR took too long to exit" )
363+ }
364+
365+ // Unmount dangling mounts to allow folder deletion in this test process suite
366+ allocDirImpl , _ := ar .GetAllocDir ().(* allocdir.AllocDir )
367+ if allocDirImpl != nil {
368+ require .NoError (t , allocDirImpl .UnmountAll ())
369+ }
370+ require .NoError (t , os .RemoveAll (allocDirPath ))
371+
372+ conf2 , cleanup2 := testAllocRunnerConfig (t , alloc .Copy ())
373+ defer cleanup2 ()
374+
375+ // Reuse persistent state and the same alloc storage path after restart.
376+ conf2 .StateDB = conf .StateDB
377+ conf2 .ClientConfig .AllocDir = conf .ClientConfig .AllocDir
378+ conf2 .ClientConfig .AllocMountsDir = conf .ClientConfig .AllocMountsDir
379+
380+ ar2Iface , err := NewAllocRunner (conf2 )
381+ must .NoError (t , err )
382+ ar2 := ar2Iface .(* allocRunner )
383+ defer destroy (ar2 )
384+
385+ err = ar2 .Restore ()
386+ must .ErrorContains (t , err , "allocation directory is inacessible" )
387+
388+ // Usually set by client.go restoreState handle code
389+ ar2 .SetClientStatus (structs .AllocClientStatusFailed )
390+ must .Eq (t , structs .AllocClientStatusFailed , ar2 .AllocState ().ClientStatus )
391+ }
0 commit comments