@@ -25,6 +25,7 @@ import (
25
25
"fmt"
26
26
"io"
27
27
"net"
28
+ "net/http"
28
29
"os/exec"
29
30
"regexp"
30
31
"strconv"
@@ -123,6 +124,36 @@ func pfPod(expectedClientData, chunks, chunkSize, chunkIntervalMillis string, bi
123
124
}
124
125
}
125
126
127
+ func testWebServerPod () * v1.Pod {
128
+ return & v1.Pod {
129
+ ObjectMeta : metav1.ObjectMeta {
130
+ Name : podName ,
131
+ Labels : map [string ]string {"name" : podName },
132
+ },
133
+ Spec : v1.PodSpec {
134
+ Containers : []v1.Container {
135
+ {
136
+ Name : "testwebserver" ,
137
+ Image : imageutils .GetE2EImage (imageutils .Agnhost ),
138
+ Args : []string {"test-webserver" },
139
+ Ports : []v1.ContainerPort {{ContainerPort : int32 (80 )}},
140
+ ReadinessProbe : & v1.Probe {
141
+ ProbeHandler : v1.ProbeHandler {
142
+ HTTPGet : & v1.HTTPGetAction {
143
+ Path : "/" ,
144
+ Port : intstr .FromInt32 (int32 (80 )),
145
+ },
146
+ },
147
+ InitialDelaySeconds : 5 ,
148
+ TimeoutSeconds : 3 ,
149
+ FailureThreshold : 10 ,
150
+ },
151
+ },
152
+ },
153
+ },
154
+ }
155
+ }
156
+
126
157
// WaitForTerminatedContainer waits till a given container be terminated for a given pod.
127
158
func WaitForTerminatedContainer (ctx context.Context , f * framework.Framework , pod * v1.Pod , containerName string ) error {
128
159
return e2epod .WaitForPodCondition (ctx , f .ClientSet , f .Namespace .Name , pod .Name , "container terminated" , framework .PodStartTimeout , func (pod * v1.Pod ) (bool , error ) {
@@ -493,6 +524,69 @@ var _ = SIGDescribe("Kubectl Port forwarding", func() {
493
524
doTestOverWebSockets (ctx , "localhost" , f )
494
525
})
495
526
})
527
+
528
+ ginkgo .Describe ("Shutdown client connection while the remote stream is writing data to the port-forward connection" , func () {
529
+ ginkgo .It ("port-forward should keep working after detect broken connection" , func (ctx context.Context ) {
530
+ ginkgo .By ("Creating the target pod" )
531
+ pod := testWebServerPod ()
532
+ if _ , err := f .ClientSet .CoreV1 ().Pods (f .Namespace .Name ).Create (ctx , pod , metav1.CreateOptions {}); err != nil {
533
+ framework .Failf ("Couldn't create pod: %v" , err )
534
+ }
535
+ if err := e2epod .WaitTimeoutForPodReadyInNamespace (ctx , f .ClientSet , pod .Name , f .Namespace .Name , framework .PodStartTimeout ); err != nil {
536
+ framework .Failf ("Pod did not start running: %v" , err )
537
+ }
538
+
539
+ ginkgo .By ("Running 'kubectl port-forward'" )
540
+ cmd := runPortForward (f .Namespace .Name , pod .Name , 80 )
541
+ defer cmd .Stop ()
542
+
543
+ ginkgo .By ("Send a http request to verify port-forward working" )
544
+ client := http.Client {
545
+ Timeout : 5 * time .Second ,
546
+ }
547
+ resp , err := client .Get (fmt .Sprintf ("http://127.0.0.1:%d/" , cmd .port ))
548
+ if err != nil {
549
+ framework .Failf ("Couldn't get http response from port-forward: %v" , err )
550
+ }
551
+ if resp .StatusCode != http .StatusOK {
552
+ framework .Failf ("Expected status code %d, got %d" , http .StatusOK , resp .StatusCode )
553
+ }
554
+
555
+ ginkgo .By ("Dialing the local port" )
556
+ conn , err := net .Dial ("tcp" , fmt .Sprintf ("127.0.0.1:%d" , cmd .port ))
557
+ if err != nil {
558
+ framework .Failf ("Couldn't connect to port %d: %v" , cmd .port , err )
559
+ }
560
+
561
+ // use raw tcp connection to emulate client close connection without reading response
562
+ ginkgo .By ("Request agohost binary file (40MB+)" )
563
+ requestLines := []string {"GET /agnhost HTTP/1.1" , "Host: localhost" , "" }
564
+ for _ , line := range requestLines {
565
+ if _ , err := conn .Write (append ([]byte (line ), []byte ("\r \n " )... )); err != nil {
566
+ framework .Failf ("Couldn't write http request to local connection: %v" , err )
567
+ }
568
+ }
569
+
570
+ ginkgo .By ("Read only one byte from the connection" )
571
+ if _ , err := conn .Read (make ([]byte , 1 )); err != nil {
572
+ framework .Logf ("Couldn't reading from the local connection: %v" , err )
573
+ }
574
+
575
+ ginkgo .By ("Close client connection without reading remain data" )
576
+ if err := conn .Close (); err != nil {
577
+ framework .Failf ("Couldn't close local connection: %v" , err )
578
+ }
579
+
580
+ ginkgo .By ("Send another http request through port-forward again" )
581
+ resp , err = client .Get (fmt .Sprintf ("http://127.0.0.1:%d/" , cmd .port ))
582
+ if err != nil {
583
+ framework .Failf ("Couldn't get http response from port-forward: %v" , err )
584
+ }
585
+ if resp .StatusCode != http .StatusOK {
586
+ framework .Failf ("Expected status code %d, got %d" , http .StatusOK , resp .StatusCode )
587
+ }
588
+ })
589
+ })
496
590
})
497
591
498
592
func wsRead (conn * websocket.Conn ) (byte , []byte , error ) {
0 commit comments