@@ -546,6 +546,12 @@ func TestNormal_NetworkPolicy(t *testing.T) {
546546 }
547547}
548548
549+ // hostPortTestState stores state for HostPort test configuration and restoration
550+ type hostPortTestState struct {
551+ backup * ENIConfigBackup
552+ configured bool
553+ }
554+
549555func TestNormal_HostPort (t * testing.T ) {
550556 // Check terway daemonset name is terway-eniip
551557 if terway != "terway-eniip" {
@@ -554,6 +560,7 @@ func TestNormal_HostPort(t *testing.T) {
554560 }
555561
556562 var feats []features.Feature
563+ state := & hostPortTestState {}
557564
558565 mutateConfig := generatePodConfigs ("HostPort" )
559566 if mutateConfig == nil {
@@ -592,6 +599,45 @@ func TestNormal_HostPort(t *testing.T) {
592599 t .Skipf ("TestNormal_HostPort requires k8s version >= v1.34.0, current version: %s" , k8sVersion )
593600 }
594601
602+ // Check if nodes have no-kube-proxy label (kube-proxy replacement / Datapath V2)
603+ hasNoKubeProxy , err := CheckNoKubeProxyLabel (ctx , config .Client ())
604+ if err != nil {
605+ t .Fatalf ("failed to check no-kube-proxy label: %v" , err )
606+ }
607+
608+ if hasNoKubeProxy {
609+ t .Log ("Node has k8s.aliyun.com/no-kube-proxy=true label, HostPort can be tested directly" )
610+ } else {
611+ t .Log ("Node does not have k8s.aliyun.com/no-kube-proxy=true label, configuring CNI chain with portmap plugin" )
612+
613+ // Backup current eni-config
614+ state .backup , err = BackupENIConfig (ctx , config )
615+ if err != nil {
616+ t .Fatalf ("failed to backup eni-config: %v" , err )
617+ }
618+
619+ // Check if Datapath V2 is enabled
620+ isDatapathV2 , err := IsDatapathV2Enabled (ctx , config )
621+ if err != nil {
622+ t .Fatalf ("failed to check datapath v2: %v" , err )
623+ }
624+
625+ // Configure CNI chain with portmap plugin
626+ err = ConfigureHostPortCNIChain (ctx , config , isDatapathV2 )
627+ if err != nil {
628+ t .Fatalf ("failed to configure CNI chain for HostPort: %v" , err )
629+ }
630+
631+ // Restart terway to apply new configuration
632+ err = restartTerway (ctx , config )
633+ if err != nil {
634+ t .Fatalf ("failed to restart terway: %v" , err )
635+ }
636+
637+ state .configured = true
638+ t .Log ("CNI chain configured with portmap plugin and terway restarted" )
639+ }
640+
595641 var objs []k8s.Object
596642
597643 // Create server pod with hostPort
@@ -701,6 +747,76 @@ func TestNormal_HostPort(t *testing.T) {
701747 t .Skipf ("TestNormal_HostPort requires k8s version >= v1.34.0, current version: %s" , k8sVersion )
702748 }
703749
750+ // Check if any node has external IP before running the test
751+ nodes := & corev1.NodeList {}
752+ err = config .Client ().Resources ().List (ctx , nodes )
753+ if err != nil {
754+ t .Fatalf ("failed to list nodes: %v" , err )
755+ }
756+
757+ hasExternalIP := false
758+ for _ , node := range nodes .Items {
759+ for _ , stack := range getStack () {
760+ isIPv6 := stack == "ipv6"
761+ _ , externalIP := getNodeIPs (& node , isIPv6 )
762+ if externalIP != "" {
763+ hasExternalIP = true
764+ break
765+ }
766+ }
767+ if hasExternalIP {
768+ break
769+ }
770+ }
771+
772+ if ! hasExternalIP {
773+ t .Skipf ("No node has external IP, skipping HostPort/ExternalIP test" )
774+ }
775+
776+ // Check if nodes have no-kube-proxy label (kube-proxy replacement / Datapath V2)
777+ hasNoKubeProxy , err := CheckNoKubeProxyLabel (ctx , config .Client ())
778+ if err != nil {
779+ t .Fatalf ("failed to check no-kube-proxy label: %v" , err )
780+ }
781+
782+ if hasNoKubeProxy {
783+ t .Log ("Node has k8s.aliyun.com/no-kube-proxy=true label, HostPort can be tested directly" )
784+ } else {
785+ // Only configure if not already configured by the first test case
786+ if ! state .configured {
787+ t .Log ("Node does not have k8s.aliyun.com/no-kube-proxy=true label, configuring CNI chain with portmap plugin" )
788+
789+ // Backup current eni-config
790+ state .backup , err = BackupENIConfig (ctx , config )
791+ if err != nil {
792+ t .Fatalf ("failed to backup eni-config: %v" , err )
793+ }
794+
795+ // Check if Datapath V2 is enabled
796+ isDatapathV2 , err := IsDatapathV2Enabled (ctx , config )
797+ if err != nil {
798+ t .Fatalf ("failed to check datapath v2: %v" , err )
799+ }
800+
801+ // Configure CNI chain with portmap plugin
802+ err = ConfigureHostPortCNIChain (ctx , config , isDatapathV2 )
803+ if err != nil {
804+ t .Fatalf ("failed to configure CNI chain for HostPort: %v" , err )
805+ }
806+
807+ // Restart terway to apply new configuration
808+ err = restartTerway (ctx , config )
809+ if err != nil {
810+ t .Fatalf ("failed to restart terway: %v" , err )
811+ }
812+
813+ state .configured = true
814+ t .Log ("CNI chain configured with portmap plugin and terway restarted" )
815+ } else {
816+ t .Log ("CNI chain already configured with portmap plugin from previous test case" )
817+ }
818+ }
819+
704820 var objs []k8s.Object
705821
706822 // Create server pod with hostPort on different port
@@ -783,12 +899,34 @@ func TestNormal_HostPort(t *testing.T) {
783899
784900 return MarkTestSuccess (ctx )
785901 }).
902+ Teardown (func (ctx context.Context , t * testing.T , config * envconf.Config ) context.Context {
903+ // Restore eni-config if we configured it (do this in the last feature's teardown)
904+ if state .configured && state .backup != nil {
905+ err := RestoreENIConfig (ctx , config , state .backup )
906+ if err != nil {
907+ t .Logf ("Warning: failed to restore eni-config: %v" , err )
908+ } else {
909+ t .Log ("Restored original eni-config" )
910+ // Restart terway to apply restored configuration
911+ err = restartTerway (ctx , config )
912+ if err != nil {
913+ t .Logf ("Warning: failed to restart terway after restoration: %v" , err )
914+ } else {
915+ t .Log ("Terway restarted with restored configuration" )
916+ }
917+ }
918+ state .configured = false
919+ state .backup = nil
920+ }
921+ return ctx
922+ }).
786923 Feature ()
787924
788925 feats = append (feats , hostPortNodeIP , hostPortExternalIP )
789926 }
790927
791928 testenv .Test (t , feats ... )
929+
792930 if t .Failed () {
793931 isFailed .Store (true )
794932 }
0 commit comments