@@ -5,10 +5,7 @@ import (
55 "testing"
66 "time"
77
8- "github.com/akitasoftware/akita-libs/akid"
9- "github.com/akitasoftware/akita-libs/tags"
108 "github.com/stretchr/testify/assert"
11- "github.com/stretchr/testify/require"
129)
1310
1411// TestPodArgs_StateTransitions tests basic valid state transitions
@@ -228,51 +225,6 @@ func TestPodArgs_ConcurrentAccess(t *testing.T) {
228225 assert .Equal (t , PodRunning , podArgs .PodTrafficMonitorState , "Final state should be PodRunning" )
229226 })
230227
231- t .Run ("Concurrent reads while state is being changed" , func (t * testing.T ) {
232- podArgs := NewPodArgs ("test-pod" )
233- podArgs .PodTrafficMonitorState = PodPending
234-
235- var wg sync.WaitGroup
236- readResults := make (chan PodTrafficMonitorState , 100 )
237-
238- // Start a goroutine that continuously changes state
239- wg .Add (1 )
240- go func () {
241- defer wg .Done ()
242- for i := 0 ; i < 10 ; i ++ {
243- podArgs .changePodTrafficMonitorState (PodRunning , PodPending )
244- time .Sleep (1 * time .Millisecond )
245- podArgs .changePodTrafficMonitorState (PodPending , PodRunning )
246- time .Sleep (1 * time .Millisecond )
247- }
248- }()
249-
250- // Start multiple goroutines reading state
251- for i := 0 ; i < 5 ; i ++ {
252- wg .Add (1 )
253- go func () {
254- defer wg .Done ()
255- for j := 0 ; j < 20 ; j ++ {
256- readResults <- podArgs .PodTrafficMonitorState
257- time .Sleep (1 * time .Millisecond )
258- }
259- }()
260- }
261-
262- wg .Wait ()
263- close (readResults )
264-
265- // Verify all read results are valid states
266- validStates := map [PodTrafficMonitorState ]bool {
267- PodPending : true ,
268- PodRunning : true ,
269- }
270-
271- for state := range readResults {
272- assert .True (t , validStates [state ], "Read state should be valid: %s" , state )
273- }
274- })
275-
276228 t .Run ("Race condition between state change and markAsPruneReady" , func (t * testing.T ) {
277229 podArgs := NewPodArgs ("test-pod" )
278230 podArgs .PodTrafficMonitorState = TrafficMonitoringEnded
@@ -446,166 +398,6 @@ func TestPodArgs_EdgeCases(t *testing.T) {
446398 assert .NoError (t , err )
447399 assert .Equal (t , PodRunning , podArgs .PodTrafficMonitorState )
448400 })
449-
450- t .Run ("State transition with nil allowed states" , func (t * testing.T ) {
451- podArgs := NewPodArgs ("test-pod" )
452- podArgs .PodTrafficMonitorState = PodPending
453-
454- // Nil allowed states should allow any transition
455- err := podArgs .changePodTrafficMonitorState (PodRunning )
456- assert .NoError (t , err )
457- assert .Equal (t , PodRunning , podArgs .PodTrafficMonitorState )
458- })
459-
460- t .Run ("NewPodArgs creates valid instance" , func (t * testing.T ) {
461- podArgs := NewPodArgs ("test-pod" )
462-
463- assert .Equal (t , "test-pod" , podArgs .PodName )
464- assert .NotNil (t , podArgs .TraceTags )
465- assert .NotNil (t , podArgs .StopChan )
466- assert .Equal (t , 2 , cap (podArgs .StopChan ))
467- assert .Equal (t , PodTrafficMonitorState ("" ), podArgs .PodTrafficMonitorState ) // Zero value
468- })
469-
470- t .Run ("PodArgs fields are properly initialized" , func (t * testing.T ) {
471- podArgs := NewPodArgs ("test-pod" )
472-
473- // Test that fields can be set and retrieved
474- podArgs .InsightsProjectID = akid.ServiceID {}
475- podArgs .ContainerUUID = "test-container-uuid"
476- podArgs .ReproMode = true
477- podArgs .DropNginxTraffic = true
478- podArgs .AgentRateLimit = 100.0
479- podArgs .PodCreds = PodCreds {
480- InsightsAPIKey : "test-key" ,
481- InsightsEnvironment : "test-env" ,
482- }
483- podArgs .TraceTags = tags.SingletonTags {"test" : "value" }
484-
485- assert .Equal (t , akid.ServiceID {}, podArgs .InsightsProjectID )
486- assert .Equal (t , "test-container-uuid" , podArgs .ContainerUUID )
487- assert .True (t , podArgs .ReproMode )
488- assert .True (t , podArgs .DropNginxTraffic )
489- assert .Equal (t , 100.0 , podArgs .AgentRateLimit )
490- assert .Equal (t , "test-key" , podArgs .PodCreds .InsightsAPIKey )
491- assert .Equal (t , "test-env" , podArgs .PodCreds .InsightsEnvironment )
492- assert .Equal (t , tags.SingletonTags {"test" : "value" }, podArgs .TraceTags )
493- })
494-
495- t .Run ("isTrafficMonitoringInFinalState function" , func (t * testing.T ) {
496- finalStates := []PodTrafficMonitorState {
497- TrafficMonitoringEnded ,
498- TrafficMonitoringFailed ,
499- RemovePodFromMap ,
500- }
501-
502- nonFinalStates := []PodTrafficMonitorState {
503- PodPending ,
504- PodRunning ,
505- PodSucceeded ,
506- PodFailed ,
507- PodTerminated ,
508- TrafficMonitoringRunning ,
509- DaemonSetShutdown ,
510- }
511-
512- for _ , state := range finalStates {
513- podArgs := NewPodArgs ("test-pod" )
514- podArgs .PodTrafficMonitorState = state
515- assert .True (t , isTrafficMonitoringInFinalState (podArgs ), "State %s should be final" , state )
516- }
517-
518- for _ , state := range nonFinalStates {
519- podArgs := NewPodArgs ("test-pod" )
520- podArgs .PodTrafficMonitorState = state
521- assert .False (t , isTrafficMonitoringInFinalState (podArgs ), "State %s should not be final" , state )
522- }
523- })
524- }
525-
526- // TestPodArgs_ConcurrencyStress tests performance under concurrent load
527- func TestPodArgs_ConcurrencyStress (t * testing.T ) {
528- t .Run ("High-frequency state transitions" , func (t * testing.T ) {
529- podArgs := NewPodArgs ("test-pod" )
530- podArgs .PodTrafficMonitorState = PodPending
531-
532- var wg sync.WaitGroup
533- successCount := 0
534- var mu sync.Mutex
535-
536- // Start 100 goroutines doing rapid state transitions
537- for i := 0 ; i < 100 ; i ++ {
538- wg .Add (1 )
539- go func () {
540- defer wg .Done ()
541- for j := 0 ; j < 10 ; j ++ {
542- err := podArgs .changePodTrafficMonitorState (PodRunning , PodPending )
543- if err == nil {
544- mu .Lock ()
545- successCount ++
546- mu .Unlock ()
547- // Change back to allow others to succeed
548- podArgs .changePodTrafficMonitorState (PodPending , PodRunning )
549- }
550- }
551- }()
552- }
553-
554- wg .Wait ()
555-
556- // Should have many successful transitions
557- assert .True (t , successCount > 0 , "Should have successful transitions" )
558- })
559-
560- t .Run ("Large number of concurrent PodArgs objects" , func (t * testing.T ) {
561- const numPods = 100
562- podArgsList := make ([]* PodArgs , numPods )
563-
564- // Create many PodArgs objects
565- for i := 0 ; i < numPods ; i ++ {
566- podArgsList [i ] = NewPodArgs ("test-pod-" + string (rune (i )))
567- podArgsList [i ].PodTrafficMonitorState = PodPending
568- }
569-
570- var wg sync.WaitGroup
571- successCount := 0
572- var mu sync.Mutex
573-
574- // Start goroutines for each PodArgs
575- for i := 0 ; i < numPods ; i ++ {
576- wg .Add (1 )
577- go func (podArgs * PodArgs ) {
578- defer wg .Done ()
579- err := podArgs .changePodTrafficMonitorState (PodRunning , PodPending )
580- if err == nil {
581- mu .Lock ()
582- successCount ++
583- mu .Unlock ()
584- }
585- }(podArgsList [i ])
586- }
587-
588- wg .Wait ()
589-
590- // All should succeed since they're different objects
591- assert .Equal (t , numPods , successCount , "All PodArgs should transition successfully" )
592- })
593-
594- t .Run ("Memory pressure scenarios" , func (t * testing.T ) {
595- // Test that we can create many PodArgs without memory issues
596- const numPods = 1000
597- podArgsList := make ([]* PodArgs , numPods )
598-
599- for i := 0 ; i < numPods ; i ++ {
600- podArgsList [i ] = NewPodArgs ("test-pod-" + string (rune (i )))
601- require .NotNil (t , podArgsList [i ])
602- }
603-
604- // Verify all are accessible
605- for i := 0 ; i < numPods ; i ++ {
606- assert .Equal (t , "test-pod-" + string (rune (i )), podArgsList [i ].PodName )
607- }
608- })
609401}
610402
611403// TestPodArgs_StateTransitionSequence tests complete state transition sequences
@@ -663,3 +455,100 @@ func TestPodArgs_StateTransitionSequence(t *testing.T) {
663455 assert .Equal (t , RemovePodFromMap , podArgs .PodTrafficMonitorState )
664456 })
665457}
458+
459+ // --- DaemonSet Shutdown and State Transition Tests ---
460+
461+ func TestPodArgs_DaemonShutdown (t * testing.T ) {
462+ tests := []struct {
463+ name string
464+ initialState PodTrafficMonitorState
465+ expectedError bool
466+ expectedState PodTrafficMonitorState
467+ }{
468+ {"TrafficMonitoringRunning to DaemonSetShutdown" , TrafficMonitoringRunning , false , DaemonSetShutdown },
469+ {"PodRunning to DaemonSetShutdown" , PodRunning , false , DaemonSetShutdown },
470+ {"PodPending to DaemonSetShutdown" , PodPending , false , DaemonSetShutdown },
471+ {"PodSucceeded to DaemonSetShutdown" , PodSucceeded , false , DaemonSetShutdown },
472+ {"PodFailed to DaemonSetShutdown" , PodFailed , false , DaemonSetShutdown },
473+ {"PodTerminated to DaemonSetShutdown" , PodTerminated , false , DaemonSetShutdown },
474+ {"TrafficMonitoringEnded to DaemonSetShutdown" , TrafficMonitoringEnded , true , TrafficMonitoringEnded },
475+ {"TrafficMonitoringFailed to DaemonSetShutdown" , TrafficMonitoringFailed , true , TrafficMonitoringFailed },
476+ {"RemovePodFromMap to DaemonSetShutdown" , RemovePodFromMap , true , RemovePodFromMap },
477+ }
478+ for _ , tt := range tests {
479+ t .Run (tt .name , func (t * testing.T ) {
480+ podArgs := NewPodArgs ("test-pod" )
481+ podArgs .PodTrafficMonitorState = tt .initialState
482+ err := podArgs .changePodTrafficMonitorState (DaemonSetShutdown )
483+ if tt .expectedError {
484+ assert .Error (t , err )
485+ assert .Contains (t , err .Error (), "already in final state" )
486+ } else {
487+ assert .NoError (t , err )
488+ assert .Equal (t , tt .expectedState , podArgs .PodTrafficMonitorState )
489+ }
490+ })
491+ }
492+ }
493+
494+ func TestPodArgs_DaemonShutdownConcurrency (t * testing.T ) {
495+ t .Run ("Multiple goroutines trying to change state during shutdown" , func (t * testing.T ) {
496+ podArgs := NewPodArgs ("test-pod" )
497+ podArgs .PodTrafficMonitorState = TrafficMonitoringRunning
498+ var wg sync.WaitGroup
499+ errors := make (chan error , 10 )
500+ successCount := 0
501+ var mu sync.Mutex
502+ for i := 0 ; i < 10 ; i ++ {
503+ wg .Add (1 )
504+ go func () {
505+ defer wg .Done ()
506+ err := podArgs .changePodTrafficMonitorState (DaemonSetShutdown )
507+ if err == nil {
508+ mu .Lock ()
509+ successCount ++
510+ mu .Unlock ()
511+ }
512+ errors <- err
513+ }()
514+ }
515+ wg .Wait ()
516+ close (errors )
517+ errorCount := 0
518+ for err := range errors {
519+ if err != nil {
520+ errorCount ++
521+ }
522+ }
523+ assert .Equal (t , 1 , successCount )
524+ assert .Equal (t , 9 , errorCount )
525+ assert .Equal (t , DaemonSetShutdown , podArgs .PodTrafficMonitorState )
526+ })
527+
528+ t .Run ("Race condition between daemon shutdown and normal state transitions" , func (t * testing.T ) {
529+ podArgs := NewPodArgs ("test-pod" )
530+ podArgs .PodTrafficMonitorState = TrafficMonitoringRunning
531+ var wg sync.WaitGroup
532+ errors := make (chan error , 2 )
533+ wg .Add (2 )
534+ go func () {
535+ defer wg .Done ()
536+ err := podArgs .changePodTrafficMonitorState (TrafficMonitoringEnded , TrafficMonitoringRunning )
537+ errors <- err
538+ }()
539+ go func () {
540+ defer wg .Done ()
541+ err := podArgs .changePodTrafficMonitorState (DaemonSetShutdown )
542+ errors <- err
543+ }()
544+ wg .Wait ()
545+ close (errors )
546+ errorCount := 0
547+ for err := range errors {
548+ if err != nil {
549+ errorCount ++
550+ }
551+ }
552+ assert .Equal (t , 1 , errorCount )
553+ })
554+ }
0 commit comments