1
16
17
18
19 package kubectl
20
21 import (
22 "context"
23 "strconv"
24 "strings"
25 "time"
26
27 v1 "k8s.io/api/core/v1"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 "k8s.io/apimachinery/pkg/util/uuid"
30 clientset "k8s.io/client-go/kubernetes"
31 "k8s.io/kubectl/pkg/cmd/util/podcmd"
32 "k8s.io/kubernetes/test/e2e/framework"
33 e2ekubectl "k8s.io/kubernetes/test/e2e/framework/kubectl"
34 e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
35 e2eoutput "k8s.io/kubernetes/test/e2e/framework/pod/output"
36 admissionapi "k8s.io/pod-security-admission/api"
37
38 "github.com/onsi/ginkgo/v2"
39 "github.com/onsi/gomega"
40 )
41
42 func testingPod(name, value, defaultContainerName string) v1.Pod {
43 return v1.Pod{
44 ObjectMeta: metav1.ObjectMeta{
45 Name: name,
46 Labels: map[string]string{
47 "name": "foo",
48 "time": value,
49 },
50 Annotations: map[string]string{
51 podcmd.DefaultContainerAnnotationName: defaultContainerName,
52 },
53 },
54 Spec: v1.PodSpec{
55 Containers: []v1.Container{
56 {
57 Name: "container-1",
58 Image: agnhostImage,
59 Args: []string{"logs-generator", "--log-lines-total", "10", "--run-duration", "5s"},
60 },
61 {
62 Name: defaultContainerName,
63 Image: agnhostImage,
64 Args: []string{"logs-generator", "--log-lines-total", "20", "--run-duration", "5s"},
65 },
66 },
67 RestartPolicy: v1.RestartPolicyNever,
68 },
69 }
70 }
71
72 var _ = SIGDescribe("Kubectl logs", func() {
73 f := framework.NewDefaultFramework("kubectl-logs")
74 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline
75 defer ginkgo.GinkgoRecover()
76
77 var c clientset.Interface
78 var ns string
79 ginkgo.BeforeEach(func() {
80 c = f.ClientSet
81 ns = f.Namespace.Name
82 })
83
84
85
86 lines := func(out string) []string {
87 return strings.Split(strings.TrimRight(out, "\n"), "\n")
88 }
89
90 ginkgo.Describe("logs", func() {
91
92 podName := "logs-generator"
93 containerName := "logs-generator"
94 ginkgo.BeforeEach(func() {
95 ginkgo.By("creating an pod")
96
97 e2ekubectl.RunKubectlOrDie(ns, "run", podName, "--image="+agnhostImage, "--restart=Never", podRunningTimeoutArg, "--", "logs-generator", "--log-lines-total", "100", "--run-duration", "20s")
98 })
99 ginkgo.AfterEach(func() {
100 e2ekubectl.RunKubectlOrDie(ns, "delete", "pod", podName)
101 })
102
103
114 framework.ConformanceIt("should be able to retrieve and filter logs", func(ctx context.Context) {
115
116 ginkgo.By("Waiting for log generator to start.")
117 if !e2epod.CheckPodsRunningReadyOrSucceeded(ctx, c, ns, []string{podName}, framework.PodStartTimeout) {
118 framework.Failf("Pod %s was not ready", podName)
119 }
120
121 ginkgo.By("checking for a matching strings")
122 _, err := e2eoutput.LookForStringInLog(ns, podName, containerName, "/api/v1/namespaces/kube-system", framework.PodStartTimeout)
123 framework.ExpectNoError(err)
124
125 ginkgo.By("limiting log lines")
126 out := e2ekubectl.RunKubectlOrDie(ns, "logs", podName, containerName, "--tail=1")
127 framework.Logf("got output %q", out)
128 gomega.Expect(out).NotTo(gomega.BeEmpty())
129 gomega.Expect(lines(out)).To(gomega.HaveLen(1))
130
131 ginkgo.By("limiting log bytes")
132 out = e2ekubectl.RunKubectlOrDie(ns, "logs", podName, containerName, "--limit-bytes=1")
133 framework.Logf("got output %q", out)
134 gomega.Expect(lines(out)).To(gomega.HaveLen(1))
135 gomega.Expect(out).To(gomega.HaveLen(1))
136
137 ginkgo.By("exposing timestamps")
138 out = e2ekubectl.RunKubectlOrDie(ns, "logs", podName, containerName, "--tail=1", "--timestamps")
139 framework.Logf("got output %q", out)
140 l := lines(out)
141 gomega.Expect(l).To(gomega.HaveLen(1))
142 words := strings.Split(l[0], " ")
143 gomega.Expect(len(words)).To(gomega.BeNumerically(">", 1))
144 if _, err := time.Parse(time.RFC3339Nano, words[0]); err != nil {
145 if _, err := time.Parse(time.RFC3339, words[0]); err != nil {
146 framework.Failf("expected %q to be RFC3339 or RFC3339Nano", words[0])
147 }
148 }
149
150 ginkgo.By("restricting to a time range")
151
152
153
154 time.Sleep(2500 * time.Millisecond)
155 recentOut := e2ekubectl.RunKubectlOrDie(ns, "logs", podName, containerName, "--since=1s")
156 recent := len(strings.Split(recentOut, "\n"))
157 olderOut := e2ekubectl.RunKubectlOrDie(ns, "logs", podName, containerName, "--since=24h")
158 older := len(strings.Split(olderOut, "\n"))
159 gomega.Expect(recent).To(gomega.BeNumerically("<", older), "expected recent(%v) to be less than older(%v)\nrecent lines:\n%v\nolder lines:\n%v\n", recent, older, recentOut, olderOut)
160 })
161 })
162
163 ginkgo.Describe("default container logs", func() {
164 ginkgo.Describe("the second container is the default-container by annotation", func() {
165 var pod *v1.Pod
166 podName := "pod" + string(uuid.NewUUID())
167 defaultContainerName := "container-2"
168 ginkgo.BeforeEach(func(ctx context.Context) {
169 podClient := f.ClientSet.CoreV1().Pods(ns)
170 ginkgo.By("constructing the pod")
171 value := strconv.Itoa(time.Now().Nanosecond())
172 podCopy := testingPod(podName, value, defaultContainerName)
173 pod = &podCopy
174 ginkgo.By("creating the pod")
175 _, err := podClient.Create(ctx, pod, metav1.CreateOptions{})
176 if err != nil {
177 framework.Failf("Failed to create pod: %v", err)
178 }
179 })
180 ginkgo.AfterEach(func() {
181 e2ekubectl.RunKubectlOrDie(ns, "delete", "pod", podName)
182 })
183
184 ginkgo.It("should log default container if not specified", func(ctx context.Context) {
185 ginkgo.By("Waiting for log generator to start.")
186
187 if err := e2epod.WaitForPodSuccessInNamespaceTimeout(ctx, c, podName, ns, framework.PodStartTimeout); err != nil {
188 framework.Failf("Pod %s did not finish: %v", podName, err)
189 }
190
191 ginkgo.By("specified container log lines")
192 out := e2ekubectl.RunKubectlOrDie(ns, "logs", podName, "-c", "container-1")
193 framework.Logf("got output %q", out)
194 gomega.Expect(out).NotTo(gomega.BeEmpty())
195 gomega.Expect(lines(out)).To(gomega.HaveLen(10))
196
197 ginkgo.By("log all containers log lines")
198 out = e2ekubectl.RunKubectlOrDie(ns, "logs", podName, "--all-containers")
199 framework.Logf("got output %q", out)
200 gomega.Expect(out).NotTo(gomega.BeEmpty())
201 gomega.Expect(lines(out)).To(gomega.HaveLen(30))
202
203 ginkgo.By("default container logs")
204 out = e2ekubectl.RunKubectlOrDie(ns, "logs", podName)
205 framework.Logf("got output %q", out)
206 gomega.Expect(lines(out)).To(gomega.HaveLen(20))
207 })
208 })
209 })
210
211 })
212
View as plain text