@@ -28,12 +28,15 @@ import (
28
28
"time"
29
29
30
30
"github.com/onsi/gomega"
31
+
31
32
v1 "k8s.io/api/core/v1"
32
33
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33
34
"k8s.io/apimachinery/pkg/util/sets"
34
35
"k8s.io/apimachinery/pkg/util/uuid"
35
36
"k8s.io/apimachinery/pkg/util/wait"
36
37
clientset "k8s.io/client-go/kubernetes"
38
+ admissionapi "k8s.io/pod-security-admission/api"
39
+
37
40
"k8s.io/kubernetes/test/e2e/feature"
38
41
"k8s.io/kubernetes/test/e2e/framework"
39
42
e2ekubectl "k8s.io/kubernetes/test/e2e/framework/kubectl"
@@ -46,7 +49,6 @@ import (
46
49
e2evolume "k8s.io/kubernetes/test/e2e/framework/volume"
47
50
testutils "k8s.io/kubernetes/test/utils"
48
51
imageutils "k8s.io/kubernetes/test/utils/image"
49
- admissionapi "k8s.io/pod-security-admission/api"
50
52
51
53
"github.com/onsi/ginkgo/v2"
52
54
)
@@ -144,7 +146,7 @@ func createPodUsingNfs(ctx context.Context, f *framework.Framework, c clientset.
144
146
},
145
147
},
146
148
},
147
- RestartPolicy : v1 .RestartPolicyNever , //don't restart pod
149
+ RestartPolicy : v1 .RestartPolicyNever , // don't restart pod
148
150
Volumes : []v1.Volume {
149
151
{
150
152
Name : "nfs-vol" ,
@@ -640,6 +642,76 @@ var _ = SIGDescribe("kubelet", func() {
640
642
})
641
643
})
642
644
645
+ var _ = SIGDescribe ("specific log stream" , feature .PodLogsQuerySplitStreams , func () {
646
+ var (
647
+ c clientset.Interface
648
+ ns string
649
+ )
650
+ f := framework .NewDefaultFramework ("pod-log-stream" )
651
+ f .NamespacePodSecurityLevel = admissionapi .LevelPrivileged
652
+
653
+ ginkgo .BeforeEach (func () {
654
+ c = f .ClientSet
655
+ ns = f .Namespace .Name
656
+ })
657
+
658
+ ginkgo .It ("kubectl get --raw /api/v1/namespaces/default/pods/<pod-name>/log?stream" , func (ctx context.Context ) {
659
+ ginkgo .By ("create pod" )
660
+
661
+ pod := & v1.Pod {
662
+ TypeMeta : metav1.TypeMeta {
663
+ Kind : "Pod" ,
664
+ APIVersion : "v1" ,
665
+ },
666
+ ObjectMeta : metav1.ObjectMeta {
667
+ GenerateName : "log-stream-" ,
668
+ Namespace : ns ,
669
+ },
670
+ Spec : v1.PodSpec {
671
+ Containers : []v1.Container {
672
+ {
673
+ Name : "log-stream" ,
674
+ Image : imageutils .GetE2EImage (imageutils .BusyBox ),
675
+ Command : []string {"/bin/sh" },
676
+ Args : []string {"-c" , "echo out1; echo err1 >&2; tail -f /dev/null" },
677
+ },
678
+ },
679
+ RestartPolicy : v1 .RestartPolicyNever , // don't restart pod
680
+ },
681
+ }
682
+ rtnPod , err := c .CoreV1 ().Pods (ns ).Create (ctx , pod , metav1.CreateOptions {})
683
+ framework .ExpectNoError (err )
684
+
685
+ err = e2epod .WaitTimeoutForPodReadyInNamespace (ctx , f .ClientSet , rtnPod .Name , f .Namespace .Name , framework .PodStartTimeout ) // running & ready
686
+ framework .ExpectNoError (err )
687
+
688
+ rtnPod , err = c .CoreV1 ().Pods (ns ).Get (ctx , rtnPod .Name , metav1.GetOptions {}) // return fresh pod
689
+ framework .ExpectNoError (err )
690
+
691
+ ginkgo .By ("Starting the command" )
692
+ tk := e2ekubectl .NewTestKubeconfig (framework .TestContext .CertDir , framework .TestContext .Host , framework .TestContext .KubeConfig , framework .TestContext .KubeContext , framework .TestContext .KubectlPath , ns )
693
+
694
+ queryCommand := fmt .Sprintf ("/api/v1/namespaces/%s/pods/%s/log?stream=All" , rtnPod .Namespace , rtnPod .Name )
695
+ cmd := tk .KubectlCmd ("get" , "--raw" , queryCommand )
696
+ result := runKubectlCommand (cmd )
697
+ // the order of the logs is indeterminate
698
+ assertContains ("out1" , result )
699
+ assertContains ("err1" , result )
700
+
701
+ queryCommand = fmt .Sprintf ("/api/v1/namespaces/%s/pods/%s/log?stream=Stdout" , rtnPod .Namespace , rtnPod .Name )
702
+ cmd = tk .KubectlCmd ("get" , "--raw" , queryCommand )
703
+ result = runKubectlCommand (cmd )
704
+ assertContains ("out1" , result )
705
+ assertNotContains ("err1" , result )
706
+
707
+ queryCommand = fmt .Sprintf ("/api/v1/namespaces/%s/pods/%s/log?stream=Stderr" , rtnPod .Namespace , rtnPod .Name )
708
+ cmd = tk .KubectlCmd ("get" , "--raw" , queryCommand )
709
+ result = runKubectlCommand (cmd )
710
+ assertContains ("err1" , result )
711
+ assertNotContains ("out1" , result )
712
+ })
713
+ })
714
+
643
715
func getLinuxNodes (nodes * v1.NodeList ) * v1.NodeList {
644
716
filteredNodes := nodes
645
717
e2enode .Filter (filteredNodes , func (node v1.Node ) bool {
@@ -696,6 +768,13 @@ func assertContains(expectedString string, result string) {
696
768
framework .Failf ("Failed to find \" %s\" " , expectedString )
697
769
}
698
770
771
+ func assertNotContains (expectedString string , result string ) {
772
+ if ! strings .Contains (result , expectedString ) {
773
+ return
774
+ }
775
+ framework .Failf ("Found unexpected \" %s\" " , expectedString )
776
+ }
777
+
699
778
func commandOnNode (nodeName string , cmd string ) string {
700
779
result , err := e2essh .NodeExec (context .Background (), nodeName , cmd , framework .TestContext .Provider )
701
780
framework .ExpectNoError (err )
0 commit comments