@@ -718,6 +718,179 @@ var _ = SIGDescribe("Pods Extended (pod generation)", feature.PodObservedGenerat
718718	})
719719})
720720
721+ var  _  =  SIGDescribe ("Pod Extended (container restart policy)" , feature .ContainerRestartRules , framework .WithFeatureGate (features .ContainerRestartRules ), func () {
722+ 	f  :=  framework .NewDefaultFramework ("pods" )
723+ 	f .NamespacePodSecurityLevel  =  admissionapi .LevelBaseline 
724+ 
725+ 	ginkgo .Describe ("Container Restart Rules" , func () {
726+ 		var  (
727+ 			containerRestartPolicyAlways  =  v1 .ContainerRestartPolicyAlways 
728+ 			containerRestartPolicyNever   =  v1 .ContainerRestartPolicyNever 
729+ 		)
730+ 
731+ 		ginkgo .It ("should restart container on rule match" , func (ctx  context.Context ) {
732+ 			podName  :=  "restart-rules-exit-code-"  +  string (uuid .NewUUID ())
733+ 			pod  :=  & v1.Pod {
734+ 				ObjectMeta : metav1.ObjectMeta {
735+ 					Name : podName ,
736+ 				},
737+ 				Spec : v1.PodSpec {
738+ 					RestartPolicy : v1 .RestartPolicyNever ,
739+ 					Containers : []v1.Container {
740+ 						{
741+ 							Name :          "main-container" ,
742+ 							Image :         imageutils .GetE2EImage (imageutils .BusyBox ),
743+ 							Command :       []string {"/bin/sh" , "-c" , "exit 42" },
744+ 							RestartPolicy : & containerRestartPolicyNever ,
745+ 							RestartPolicyRules : []v1.ContainerRestartRule {
746+ 								{
747+ 									Action : v1 .ContainerRestartRuleActionRestart ,
748+ 									ExitCodes : & v1.ContainerRestartRuleOnExitCodes {
749+ 										Operator : v1 .ContainerRestartRuleOnExitCodesOpIn ,
750+ 										Values :   []int32 {42 },
751+ 									},
752+ 								},
753+ 							},
754+ 						},
755+ 					},
756+ 				},
757+ 			}
758+ 
759+ 			createAndValidateRestartableContainer (ctx , f , pod , podName , "main-container" )
760+ 		})
761+ 
762+ 		ginkgo .It ("should not restart container on rule mismatch, container restart policy Never" , func (ctx  context.Context ) {
763+ 			podName  :=  "restart-rules-no-restart-"  +  string (uuid .NewUUID ())
764+ 			pod  :=  & v1.Pod {
765+ 				ObjectMeta : metav1.ObjectMeta {
766+ 					Name : podName ,
767+ 				},
768+ 				Spec : v1.PodSpec {
769+ 					RestartPolicy : v1 .RestartPolicyNever ,
770+ 					Containers : []v1.Container {
771+ 						{
772+ 							Name :          "main-container" ,
773+ 							Image :         imageutils .GetE2EImage (imageutils .BusyBox ),
774+ 							Command :       []string {"/bin/sh" , "-c" , "exit 1" },
775+ 							RestartPolicy : & containerRestartPolicyNever ,
776+ 							RestartPolicyRules : []v1.ContainerRestartRule {
777+ 								{
778+ 									Action : v1 .ContainerRestartRuleActionRestart ,
779+ 									ExitCodes : & v1.ContainerRestartRuleOnExitCodes {
780+ 										Operator : v1 .ContainerRestartRuleOnExitCodesOpIn ,
781+ 										Values :   []int32 {42 },
782+ 									},
783+ 								},
784+ 							},
785+ 						},
786+ 					},
787+ 				},
788+ 			}
789+ 
790+ 			createAndValidateNonRestartableContainer (ctx , f , pod , podName , "main-container" )
791+ 		})
792+ 
793+ 		ginkgo .It ("should restart container on container-level restart policy Never" , func (ctx  context.Context ) {
794+ 			podName  :=  "restart-rules-no-restart-"  +  string (uuid .NewUUID ())
795+ 			pod  :=  & v1.Pod {
796+ 				ObjectMeta : metav1.ObjectMeta {
797+ 					Name : podName ,
798+ 				},
799+ 				Spec : v1.PodSpec {
800+ 					RestartPolicy : v1 .RestartPolicyAlways ,
801+ 					Containers : []v1.Container {
802+ 						{
803+ 							Name :          "main-container" ,
804+ 							Image :         imageutils .GetE2EImage (imageutils .BusyBox ),
805+ 							Command :       []string {"/bin/sh" , "-c" , "exit 1" },
806+ 							RestartPolicy : & containerRestartPolicyNever ,
807+ 						},
808+ 					},
809+ 				},
810+ 			}
811+ 
812+ 			createAndValidateNonRestartableContainer (ctx , f , pod , podName , "main-container" )
813+ 		})
814+ 
815+ 		ginkgo .It ("should restart container on container-level restart policy Always" , func (ctx  context.Context ) {
816+ 			podName  :=  "restart-rules-no-restart-"  +  string (uuid .NewUUID ())
817+ 			pod  :=  & v1.Pod {
818+ 				ObjectMeta : metav1.ObjectMeta {
819+ 					Name : podName ,
820+ 				},
821+ 				Spec : v1.PodSpec {
822+ 					RestartPolicy : v1 .RestartPolicyNever ,
823+ 					Containers : []v1.Container {
824+ 						{
825+ 							Name :          "main-container" ,
826+ 							Image :         imageutils .GetE2EImage (imageutils .BusyBox ),
827+ 							Command :       []string {"/bin/sh" , "-c" , "exit 1" },
828+ 							RestartPolicy : & containerRestartPolicyAlways ,
829+ 						},
830+ 					},
831+ 				},
832+ 			}
833+ 
834+ 			createAndValidateRestartableContainer (ctx , f , pod , podName , "main-container" )
835+ 		})
836+ 
837+ 		ginkgo .It ("should restart container on pod-level restart policy Always when no container-level restart policy" , func (ctx  context.Context ) {
838+ 			podName  :=  "restart-rules-no-match-"  +  string (uuid .NewUUID ())
839+ 			pod  :=  & v1.Pod {
840+ 				ObjectMeta : metav1.ObjectMeta {
841+ 					Name : podName ,
842+ 				},
843+ 				Spec : v1.PodSpec {
844+ 					RestartPolicy : v1 .RestartPolicyAlways ,
845+ 					Containers : []v1.Container {
846+ 						{
847+ 							Name :    "main-container" ,
848+ 							Image :   imageutils .GetE2EImage (imageutils .BusyBox ),
849+ 							Command : []string {"/bin/sh" , "-c" , "exit 1" },
850+ 						},
851+ 					},
852+ 				},
853+ 			}
854+ 
855+ 			createAndValidateRestartableContainer (ctx , f , pod , podName , "main-container" )
856+ 		})
857+ 	})
858+ })
859+ 
860+ func  createAndValidateRestartableContainer (ctx  context.Context , f  * framework.Framework , pod  * v1.Pod , podName , containerName  string ) {
861+ 	ginkgo .By ("Creating the pod" )
862+ 	e2epod .NewPodClient (f ).Create (ctx , pod )
863+ 
864+ 	ginkgo .By ("Waiting for the container to restart" )
865+ 	err  :=  e2epod .WaitForPodCondition (ctx , f .ClientSet , f .Namespace .Name , podName , "container restarted" , 10 * time .Minute , func (pod  * v1.Pod ) (bool , error ) {
866+ 		for  _ , status  :=  range  pod .Status .ContainerStatuses  {
867+ 			if  status .Name  ==  containerName  &&  status .RestartCount  >  0  {
868+ 				return  true , nil 
869+ 			}
870+ 		}
871+ 		return  false , nil 
872+ 	})
873+ 	framework .ExpectNoError (err , "failed to see container restart" )
874+ }
875+ 
876+ func  createAndValidateNonRestartableContainer (ctx  context.Context , f  * framework.Framework , pod  * v1.Pod , podName , containerName  string ) {
877+ 	ginkgo .By ("Creating the pod" )
878+ 	e2epod .NewPodClient (f ).Create (ctx , pod )
879+ 
880+ 	ginkgo .By ("Waiting for the pod to terminate" )
881+ 	err  :=  e2epod .WaitTimeoutForPodNoLongerRunningInNamespace (ctx , f .ClientSet , podName , f .Namespace .Name , 10 * time .Minute )
882+ 	framework .ExpectNoError (err , "failed to wait for pod terminate" )
883+ 
884+ 	ginkgo .By ("Checking container restart count" )
885+ 	p , err  :=  e2epod .NewPodClient (f ).Get (ctx , podName , metav1.GetOptions {})
886+ 	framework .ExpectNoError (err , "failed to get pod" )
887+ 	for  _ , status  :=  range  p .Status .ContainerStatuses  {
888+ 		if  status .Name  ==  containerName  {
889+ 			gomega .Expect (status .RestartCount ).To (gomega .BeZero ())
890+ 		}
891+ 	}
892+ }
893+ 
721894func  createAndTestPodRepeatedly (ctx  context.Context , workers , iterations  int , scenario  podScenario , podClient  v1core.PodInterface ) {
722895	var  (
723896		lock  sync.Mutex 
0 commit comments