@@ -5,6 +5,7 @@ package e2e
55
66import (
77 "bytes"
8+ "context"
89 "fmt"
910 "math/rand"
1011 "os"
@@ -19,8 +20,11 @@ import (
1920 "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/test/utils"
2021 v1 "k8s.io/api/core/v1"
2122 "k8s.io/apimachinery/pkg/util/intstr"
23+ "sigs.k8s.io/e2e-framework/klient/wait"
24+ "sigs.k8s.io/e2e-framework/klient/wait/conditions"
2225 "sigs.k8s.io/e2e-framework/pkg/env"
2326 "sigs.k8s.io/e2e-framework/pkg/envconf"
27+ "sigs.k8s.io/e2e-framework/pkg/features"
2428)
2529
2630var E2eNamespace = envconf .RandomName ("coco-pp-e2e-test" , 25 )
@@ -810,3 +814,181 @@ func DoTestPodVMWithAnnotationMemory(t *testing.T, e env.Environment, assert Clo
810814 pod := NewPod (E2eNamespace , podName , containerName , imageName , WithCommand ([]string {"/bin/sh" , "-c" , "sleep 3600" }), WithAnnotations (annotationData ))
811815 NewTestCase (t , e , "PodVMwithAnnotationMemory" , assert , "PodVM with Annotation Memory is created" ).WithPod (pod ).WithExpectedInstanceType (expectedType ).Run ()
812816}
817+
818+ // DoTestSandboxPersistence verifies that a peer pod survives a CAA restart.
819+ // It creates a counter pod, deletes the CAA daemonset pod, waits for recovery,
820+ // then checks the counter is still incrementing and the pod has zero restarts.
821+ func DoTestSandboxPersistence (t * testing.T , e env.Environment , assert CloudAssert ) {
822+ podName := "sandbox-persistence"
823+ containerName := "busybox"
824+ imageName := getBusyboxTestImage (t )
825+ pod := NewPod (E2eNamespace , podName , containerName , imageName ,
826+ WithCommand ([]string {"/bin/sh" , "-c" , "i=0; while true; do echo counter=$i; i=$((i+1)); sleep 1; done" }),
827+ WithRestartPolicy (v1 .RestartPolicyAlways ),
828+ )
829+
830+ feature := features .New ("Sandbox persistence across CAA restart" ).
831+ WithSetup ("Create counter pod" , func (ctx context.Context , t * testing.T , cfg * envconf.Config ) context.Context {
832+ client , err := cfg .NewClient ()
833+ if err != nil {
834+ t .Fatal (err )
835+ }
836+ if err = client .Resources ().Create (ctx , pod ); err != nil {
837+ t .Fatal (err )
838+ }
839+ if err = wait .For (conditions .New (client .Resources ()).PodPhaseMatch (pod , v1 .PodRunning ), wait .WithTimeout (WaitPodRunningTimeout )); err != nil {
840+ t .Fatal (err )
841+ }
842+ t .Log ("Waiting for containers to be ready" )
843+ if err = wait .For (conditions .New (client .Resources ()).ContainersReady (pod ), wait .WithTimeout (WaitPodRunningTimeout )); err != nil {
844+ t .Fatal (err )
845+ }
846+ return ctx
847+ }).
848+ Assess ("Pod survives CAA restart" , func (ctx context.Context , t * testing.T , cfg * envconf.Config ) context.Context {
849+ client := cfg .Client ()
850+
851+ // // Capture initial counter value
852+ // var stdout, stderr bytes.Buffer
853+ // if err := client.Resources(pod.Namespace).ExecInPod(ctx, pod.Namespace, pod.Name, containerName,
854+ // []string{"cat", "/proc/1/fd/1"}, &stdout, &stderr); err != nil {
855+ // // Fallback: read from pod logs
856+ // stdout.Reset()
857+ // logStr, logErr := GetPodLog(ctx, client, pod)
858+ // if logErr != nil {
859+ // t.Fatalf("Failed to get counter value: exec=%v, logs=%v", err, logErr)
860+ // }
861+ // stdout.WriteString(logStr)
862+ // }
863+ // lines := strings.Split(strings.TrimSpace(stdout.String()), "\n")
864+ // if len(lines) == 0 {
865+ // t.Fatal("No counter output found")
866+ // }
867+ // lastLine := lines[len(lines)-1]
868+ // t.Logf("Counter before CAA restart: %s", lastLine)
869+ //
870+ // var preRestartCounter int
871+ // if _, err := fmt.Sscanf(lastLine, "counter=%d", &preRestartCounter); err != nil {
872+ // t.Fatalf("Failed to parse counter from %q: %v", lastLine, err)
873+ // }
874+ // if preRestartCounter == 0 {
875+ // t.Fatal("Counter should be > 0 before CAA restart")
876+ // }
877+
878+ // Find the CAA pod on the same node as our test pod
879+ nodeName , err := GetNodeNameFromPod (ctx , client , pod )
880+ if err != nil {
881+ t .Fatalf ("GetNodeNameFromPod: %v" , err )
882+ }
883+ t .Logf ("Test pod is on node %s" , nodeName )
884+
885+ caaPod , err := getCaaPod (ctx , client , t , nodeName )
886+ if err != nil {
887+ t .Fatalf ("getCaaPod: %v" , err )
888+ }
889+ oldCaaPodName := caaPod .Name
890+ t .Logf ("Deleting CAA pod %s" , oldCaaPodName )
891+
892+ // Delete the CAA pod — the daemonset controller will recreate it
893+ if err := client .Resources ().Delete (ctx , caaPod ); err != nil {
894+ t .Fatalf ("Failed to delete CAA pod: %v" , err )
895+ }
896+
897+ // Wait for a new CAA pod to appear and become ready
898+ caaNamespace := pv .GetCAANamespace ()
899+ t .Log ("Waiting for new CAA pod to become ready" )
900+ if err := wait .For (func (ctx context.Context ) (bool , error ) {
901+ pods , listErr := GetPodNamesByLabel (ctx , client , t , caaNamespace , "app" , "cloud-api-adaptor" , nodeName )
902+ if listErr != nil {
903+ return false , nil
904+ }
905+ if len (pods .Items ) == 0 {
906+ return false , nil
907+ }
908+ newPod := pods .Items [0 ]
909+ if newPod .Name == oldCaaPodName {
910+ return false , nil
911+ }
912+ for _ , cond := range newPod .Status .Conditions {
913+ if cond .Type == v1 .PodReady && cond .Status == v1 .ConditionTrue {
914+ t .Logf ("New CAA pod %s is ready" , newPod .Name )
915+ return true , nil
916+ }
917+ }
918+ return false , nil
919+ }, wait .WithTimeout (5 * time .Minute ), wait .WithInterval (5 * time .Second )); err != nil {
920+ t .Fatalf ("New CAA pod did not become ready: %v" , err )
921+ }
922+
923+ // Give CAA time to recover existing sandboxes
924+ t .Log ("Waiting for sandbox recovery" )
925+ time .Sleep (30 * time .Second )
926+
927+ // // Exec into the pod again — verify counter is still incrementing
928+ // stdout.Reset()
929+ // stderr.Reset()
930+ // if err := client.Resources(pod.Namespace).ExecInPod(ctx, pod.Namespace, pod.Name, containerName,
931+ // []string{"cat", "/proc/1/fd/1"}, &stdout, &stderr); err != nil {
932+ // // Fallback: read from pod logs
933+ // stdout.Reset()
934+ // logStr, logErr := GetPodLog(ctx, client, pod)
935+ // if logErr != nil {
936+ // t.Fatalf("Failed to get counter after recovery: exec=%v, logs=%v", err, logErr)
937+ // }
938+ // stdout.WriteString(logStr)
939+ // }
940+ // lines = strings.Split(strings.TrimSpace(stdout.String()), "\n")
941+ // if len(lines) == 0 {
942+ // t.Fatal("No counter output after CAA restart")
943+ // }
944+ // lastLine = lines[len(lines)-1]
945+ // t.Logf("Counter after CAA restart: %s", lastLine)
946+ //
947+ // var postRestartCounter int
948+ // if _, err := fmt.Sscanf(lastLine, "counter=%d", &postRestartCounter); err != nil {
949+ // t.Fatalf("Failed to parse counter from %q: %v", lastLine, err)
950+ // }
951+ // if postRestartCounter <= preRestartCounter {
952+ // t.Fatalf("Counter did not advance: before=%d, after=%d", preRestartCounter, postRestartCounter)
953+ // }
954+ // t.Logf("Counter advanced from %d to %d across CAA restart", preRestartCounter, postRestartCounter)
955+
956+ // Verify the pod was never restarted
957+ refreshedPod , err := findPod (ctx , client , pod )
958+ if err != nil {
959+ t .Fatalf ("findPod: %v" , err )
960+ }
961+ for _ , cs := range refreshedPod .Status .ContainerStatuses {
962+ if cs .RestartCount != 0 {
963+ t .Fatalf ("Container %s has RestartCount=%d, expected 0" , cs .Name , cs .RestartCount )
964+ }
965+ }
966+ t .Log ("Pod has zero restarts — sandbox persistence verified" )
967+
968+ return ctx
969+ }).
970+ Teardown (func (ctx context.Context , t * testing.T , cfg * envconf.Config ) context.Context {
971+ client , err := cfg .NewClient ()
972+ if err != nil {
973+ t .Fatal (err )
974+ }
975+ if t .Failed () {
976+ LogPodDebugInfo (ctx , t , client , pod )
977+ }
978+ if err = client .Resources ().Delete (ctx , pod ); err != nil {
979+ t .Error (err )
980+ }
981+ t .Logf ("Deleting pod %s..." , pod .Name )
982+ deletionTimeout := assert .DefaultTimeout ()
983+ if err = wait .For (conditions .New (
984+ client .Resources ()).ResourceDeleted (pod ),
985+ wait .WithInterval (5 * time .Second ),
986+ wait .WithTimeout (deletionTimeout )); err != nil {
987+ t .Error (err )
988+ }
989+ t .Logf ("Pod %s deleted" , pod .Name )
990+ return ctx
991+ }).Feature ()
992+
993+ e .Test (t , feature )
994+ }
0 commit comments