@@ -741,6 +741,78 @@ func TestModuleReloading(t *testing.T) {
741741 err = mod .process .Stop ()
742742 test .That (t , err , test .ShouldBeNil )
743743 })
744+ t .Run ("do not restart module if pexec context is cancelled" , func (t * testing.T ) {
745+ // Lower restart interval so the test runs faster
746+ originalInterval := oueRestartInterval
747+ t .Cleanup (func () {
748+ oueRestartInterval = originalInterval
749+ })
750+ oueRestartInterval = 50 * time .Millisecond
751+
752+ logger , logs := logging .NewObservedTestLogger (t )
753+
754+ // Precompile module to avoid timeout issues when building takes too long.
755+ modCfg .ExePath = rtestutils .BuildTempModule (t , "module/testmodule" )
756+
757+ // This test neither uses a resource manager nor asserts anything about
758+ // the existence of resources in the graph. Use a dummy
759+ // HandleOrphanedResources function so orphaned resource logic does not
760+ // panic.
761+ var dummyHandleOrphanedResourcesCallCount atomic.Uint64
762+ dummyHandleOrphanedResources := func (context.Context , []resource.Name ) {
763+ dummyHandleOrphanedResourcesCallCount .Add (1 )
764+ }
765+ mgr := setupModManager (t , ctx , parentAddr , logger , modmanageroptions.Options {
766+ UntrustedEnv : false ,
767+ HandleOrphanedResources : dummyHandleOrphanedResources ,
768+ })
769+ err = mgr .Add (ctx , modCfg )
770+ test .That (t , err , test .ShouldBeNil )
771+
772+ // Add helper resource and ensure "echo" works correctly.
773+ h , err := mgr .AddResource (ctx , cfgMyHelper , nil )
774+ test .That (t , err , test .ShouldBeNil )
775+ ok := mgr .IsModularResource (rNameMyHelper )
776+ test .That (t , ok , test .ShouldBeTrue )
777+
778+ // Remove testmodule binary, so process cannot be successfully restarted
779+ // after crash.
780+ err = os .Remove (modCfg .ExePath )
781+ test .That (t , err , test .ShouldBeNil )
782+
783+ // Grab a copy of the original managed process before the modmanager
784+ // overwrites it during restart attempts.
785+ mod , ok := mgr .modules .Load (modCfg .Name )
786+ test .That (t , ok , test .ShouldBeTrue )
787+ modProc := mod .process
788+
789+ // Run 'kill_module' command through helper resource to cause module to
790+ // exit with error. Assert that we do not restart the module if context is cancelled.
791+ _ , err = h .DoCommand (ctx , map [string ]interface {}{"command" : "kill_module" })
792+ test .That (t , err , test .ShouldNotBeNil )
793+ test .That (t , err .Error (), test .ShouldContainSubstring , "rpc error" )
794+
795+ testutils .WaitForAssertion (t , func (tb testing.TB ) {
796+ tb .Helper ()
797+ test .That (tb , logs .FilterMessageSnippet ("Module has unexpectedly exited." ).Len (),
798+ test .ShouldEqual , 1 )
799+ })
800+
801+ ok = mgr .IsModularResource (rNameMyHelper )
802+ test .That (t , ok , test .ShouldBeTrue )
803+ _ , err = h .DoCommand (ctx , map [string ]interface {}{"command" : "echo" })
804+ test .That (t , err , test .ShouldNotBeNil )
805+
806+ // Stop the module managed process manually and make sure the OUE handler
807+ // stops trying to restart it.
808+ err = modProc .Stop ()
809+ test .That (t , err , test .ShouldBeNil )
810+ testutils .WaitForAssertion (t , func (tb testing.TB ) {
811+ tb .Helper ()
812+ matching := logs .FilterMessageSnippet ("pexec context canceled, abandoning restart attempt" ).Len ()
813+ test .That (tb , matching , test .ShouldEqual , 1 )
814+ })
815+ })
744816 t .Run ("timed out module process is stopped" , func (t * testing.T ) {
745817 logger , logs := logging .NewObservedTestLogger (t )
746818
0 commit comments