@@ -21,11 +21,20 @@ import (
2121 "github.com/stacklok/go-microvm/hypervisor"
2222 "github.com/stacklok/go-microvm/image"
2323 "github.com/stacklok/go-microvm/internal/testutil"
24+ propnet "github.com/stacklok/go-microvm/net"
2425 "github.com/stacklok/go-microvm/net/firewall"
2526 "github.com/stacklok/go-microvm/preflight"
2627 "github.com/stacklok/go-microvm/state"
2728)
2829
30+ // sentinelProvider is a minimal net.Provider used by tests to assert that
31+ // a caller-supplied provider survives auto-wiring without being replaced.
32+ type sentinelProvider struct {}
33+
34+ func (* sentinelProvider ) Start (_ context.Context , _ propnet.Config ) error { return nil }
35+ func (* sentinelProvider ) SocketPath () string { return "" }
36+ func (* sentinelProvider ) Stop () {}
37+
2938// --- Pure function tests ---
3039
3140func TestBuildInitConfig_NilOCIConfig (t * testing.T ) {
@@ -660,6 +669,53 @@ func TestBuildNetConfig_Empty(t *testing.T) {
660669
661670// --- Egress validation tests ---
662671
672+ func TestWireDefaultProvider (t * testing.T ) {
673+ t .Parallel ()
674+
675+ t .Run ("no firewall config leaves provider nil" , func (t * testing.T ) {
676+ t .Parallel ()
677+ cfg := defaultConfig ()
678+ wireDefaultProvider (cfg )
679+ assert .Nil (t , cfg .netProvider )
680+ })
681+
682+ t .Run ("egress policy auto-wires provider" , func (t * testing.T ) {
683+ t .Parallel ()
684+ cfg := defaultConfig ()
685+ cfg .egressPolicy = & EgressPolicy {}
686+ wireDefaultProvider (cfg )
687+ assert .NotNil (t , cfg .netProvider )
688+ })
689+
690+ t .Run ("firewall rules alone auto-wire provider" , func (t * testing.T ) {
691+ t .Parallel ()
692+ cfg := defaultConfig ()
693+ cfg .firewallRules = []firewall.Rule {{Direction : firewall .Egress , Action : firewall .Allow }}
694+ wireDefaultProvider (cfg )
695+ assert .NotNil (t , cfg .netProvider ,
696+ "firewall-only config must auto-wire a provider; otherwise rules go unenforced" )
697+ })
698+
699+ t .Run ("deny default alone auto-wires provider" , func (t * testing.T ) {
700+ t .Parallel ()
701+ cfg := defaultConfig ()
702+ cfg .firewallDefaultAction = firewall .Deny
703+ wireDefaultProvider (cfg )
704+ assert .NotNil (t , cfg .netProvider ,
705+ "deny-default config must auto-wire a provider to actually deny" )
706+ })
707+
708+ t .Run ("explicit provider is not overwritten" , func (t * testing.T ) {
709+ t .Parallel ()
710+ existing := & sentinelProvider {}
711+ cfg := defaultConfig ()
712+ cfg .netProvider = existing
713+ cfg .firewallDefaultAction = firewall .Deny
714+ wireDefaultProvider (cfg )
715+ assert .Same (t , existing , cfg .netProvider )
716+ })
717+ }
718+
663719func TestRun_EgressPolicy_EmptyHosts_DenyAll (t * testing.T ) {
664720 t .Parallel ()
665721
@@ -862,6 +918,7 @@ func TestTerminateStaleRunner_AliveProcess_GracefulExit(t *testing.T) {
862918 // (after SIGTERM + first poll).
863919 return aliveCount <= 1
864920 }
921+ cfg .processIsExpected = func (_ int ) bool { return true }
865922
866923 terminateStaleRunner (cfg )
867924
@@ -899,6 +956,7 @@ func TestTerminateStaleRunner_AliveProcess_RequiresKill(t *testing.T) {
899956 }
900957 // Process never exits on its own.
901958 cfg .processAlive = func (_ int ) bool { return true }
959+ cfg .processIsExpected = func (_ int ) bool { return true }
902960
903961 terminateStaleRunner (cfg )
904962
@@ -941,6 +999,7 @@ func TestTerminateStaleRunner_SendsToProcessGroup(t *testing.T) {
941999 aliveCount ++
9421000 return aliveCount <= 1
9431001 }
1002+ cfg .processIsExpected = func (_ int ) bool { return true }
9441003
9451004 terminateStaleRunner (cfg )
9461005
@@ -950,6 +1009,38 @@ func TestTerminateStaleRunner_SendsToProcessGroup(t *testing.T) {
9501009 assert .Equal (t , - 55555 , receivedPIDs [0 ], "killProcess should receive negative PID for process group" )
9511010}
9521011
1012+ func TestTerminateStaleRunner_RecycledPID_Skipped (t * testing.T ) {
1013+ t .Parallel ()
1014+
1015+ // The state file points at a live PID, but processIsExpected says the
1016+ // binary at that PID is not the runner (as if the kernel had recycled
1017+ // the PID onto an unrelated process since state was written). The
1018+ // function must refuse to signal it.
1019+ dataDir := t .TempDir ()
1020+
1021+ mgr := state .NewManager (dataDir )
1022+ ls , err := mgr .LoadAndLock (context .Background ())
1023+ require .NoError (t , err )
1024+ ls .State .Active = true
1025+ ls .State .PID = 77777
1026+ require .NoError (t , ls .Save ())
1027+ ls .Release ()
1028+
1029+ cfg := defaultConfig ()
1030+ cfg .dataDir = dataDir
1031+
1032+ var killCalled bool
1033+ cfg .killProcess = func (_ int , _ syscall.Signal ) error {
1034+ killCalled = true
1035+ return nil
1036+ }
1037+ cfg .processAlive = func (_ int ) bool { return true }
1038+ cfg .processIsExpected = func (_ int ) bool { return false }
1039+
1040+ terminateStaleRunner (cfg )
1041+ assert .False (t , killCalled , "must not signal a recycled PID belonging to an unrelated binary" )
1042+ }
1043+
9531044func TestTerminateStaleRunner_PID1_Skipped (t * testing.T ) {
9541045 t .Parallel ()
9551046
@@ -973,6 +1064,7 @@ func TestTerminateStaleRunner_PID1_Skipped(t *testing.T) {
9731064 return nil
9741065 }
9751066 cfg .processAlive = func (_ int ) bool { return true }
1067+ cfg .processIsExpected = func (_ int ) bool { return true }
9761068
9771069 terminateStaleRunner (cfg )
9781070 assert .False (t , killCalled , "should not attempt to kill PID 1" )
0 commit comments