1
16
17 package e2enode
18
19 import (
20 "bufio"
21 "bytes"
22 "context"
23 "fmt"
24 "sort"
25 "strconv"
26 "strings"
27 "time"
28
29 v1 "k8s.io/api/core/v1"
30 "k8s.io/apimachinery/pkg/api/resource"
31
32 "k8s.io/kubernetes/test/e2e/framework"
33 e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
34 )
35
36 type execCommand struct {
37
38 ExitCode int
39
40 StartDelay int
41
42 Delay int
43
44
45 LoopForever bool
46
47
48 TerminationSeconds int
49
50
51 ContainerName string
52 }
53
54
55
56
57
58
59
60 func ExecCommand(name string, c execCommand) []string {
61 var cmd bytes.Buffer
62
63
64
65
66
67 timeCmd := "`date +%s` `cat /proc/uptime | awk '{print $1}'`"
68 containerName := name
69 if c.ContainerName != "" {
70 containerName = c.ContainerName
71 }
72 containerLog := fmt.Sprintf("/persistent/%s.log", containerName)
73
74 fmt.Fprintf(&cmd, "touch %s; ", containerLog)
75 if c.ContainerName == "" {
76 fmt.Fprintf(&cmd, "cat %s >> /proc/1/fd/1; ", containerLog)
77 }
78
79 fmt.Fprintf(&cmd, "echo %s '%s Starting %d' | tee -a %s >> /proc/1/fd/1; ", timeCmd, name, c.StartDelay, containerLog)
80 fmt.Fprintf(&cmd, "_term() { sleep %d; echo %s '%s Exiting' | tee -a %s >> /proc/1/fd/1; exit %d; }; ", c.TerminationSeconds, timeCmd, name, containerLog, c.ExitCode)
81 fmt.Fprintf(&cmd, "trap _term TERM; ")
82 if c.StartDelay != 0 {
83 fmt.Fprint(&cmd, sleepCommand(c.StartDelay))
84 }
85
86 fmt.Fprintf(&cmd, "touch started; ")
87 fmt.Fprintf(&cmd, "echo %s '%s Started' | tee -a %s >> /proc/1/fd/1; ", timeCmd, name, containerLog)
88 fmt.Fprintf(&cmd, "echo %s '%s Delaying %d' | tee -a %s >> /proc/1/fd/1; ", timeCmd, name, c.Delay, containerLog)
89 if c.Delay != 0 {
90 fmt.Fprint(&cmd, sleepCommand(c.Delay))
91 }
92 if c.LoopForever {
93 fmt.Fprintf(&cmd, "while true; do echo %s '%s Looping' | tee -a %s >> /proc/1/fd/1 ; sleep 1 ; done; ", timeCmd, name, containerLog)
94 }
95 fmt.Fprintf(&cmd, "echo %s '%s Exiting' | tee -a %s >> /proc/1/fd/1; ", timeCmd, name, containerLog)
96 fmt.Fprintf(&cmd, "exit %d", c.ExitCode)
97 return []string{"sh", "-c", cmd.String()}
98 }
99
100
101
102
103 func sleepCommand(seconds int) string {
104 return fmt.Sprintf("exec sleep %d & wait $!; ", seconds)
105 }
106
107 type containerOutput struct {
108
109 timestamp time.Time
110
111 timeSinceBoot float64
112 containerName string
113 command string
114 }
115 type containerOutputList []containerOutput
116
117 func (o containerOutputList) String() string {
118 var b bytes.Buffer
119 for i, v := range o {
120 fmt.Fprintf(&b, "%d) %s %f %s %s\n", i, v.timestamp, v.timeSinceBoot, v.containerName, v.command)
121 }
122 return b.String()
123 }
124
125
126 func (o containerOutputList) RunTogether(lhs, rhs string) error {
127 lhsStart := o.findIndex(lhs, "Started", 0)
128 rhsStart := o.findIndex(rhs, "Started", 0)
129
130 lhsFinish := o.findIndex(lhs, "Finishing", 0)
131 rhsFinish := o.findIndex(rhs, "Finishing", 0)
132
133 if lhsStart == -1 {
134 return fmt.Errorf("couldn't find that %s ever started, got\n%v", lhs, o)
135 }
136 if rhsStart == -1 {
137 return fmt.Errorf("couldn't find that %s ever started, got\n%v", rhs, o)
138 }
139
140 if lhsFinish != -1 && rhsStart > lhsFinish {
141 return fmt.Errorf("expected %s to start before finishing %s, got\n%v", rhs, lhs, o)
142 }
143
144 if rhsFinish != -1 && lhsStart > rhsFinish {
145 return fmt.Errorf("expected %s to start before finishing %s, got\n%v", lhs, rhs, o)
146 }
147
148 return nil
149 }
150
151
152 func (o containerOutputList) StartsBefore(lhs, rhs string) error {
153 lhsStart := o.findIndex(lhs, "Started", 0)
154
155 if lhsStart == -1 {
156 return fmt.Errorf("couldn't find that %s ever started, got\n%v", lhs, o)
157 }
158
159
160 rhsStart := o.findIndex(rhs, "Starting", lhsStart+1)
161
162 if rhsStart == -1 {
163 return fmt.Errorf("couldn't find that %s started after %s, got\n%v", rhs, lhs, o)
164 }
165 return nil
166 }
167
168
169 func (o containerOutputList) DoesntStartAfter(lhs, rhs string) error {
170 rhsStart := o.findIndex(rhs, "Starting", 0)
171
172 if rhsStart == -1 {
173 return fmt.Errorf("couldn't find that %s ever started, got\n%v", rhs, o)
174 }
175
176
177 lhsStart := o.findIndex(lhs, "Started", rhsStart+1)
178
179 if lhsStart != -1 {
180 return fmt.Errorf("expected %s to not start after %s, got\n%v", lhs, rhs, o)
181 }
182
183 return nil
184 }
185
186
187 func (o containerOutputList) ExitsBefore(lhs, rhs string) error {
188 lhsExit := o.findIndex(lhs, "Exiting", 0)
189
190 if lhsExit == -1 {
191 return fmt.Errorf("couldn't find that %s ever exited, got\n%v", lhs, o)
192 }
193
194
195 rhsExit := o.findIndex(rhs, "Exiting", lhsExit+1)
196
197 if rhsExit == -1 {
198 return fmt.Errorf("couldn't find that %s starting before %s exited (starting at idx %d), got\n%v", rhs, lhs, lhsExit+1, o)
199 }
200 return nil
201 }
202
203
204 func (o containerOutputList) Starts(name string) error {
205 if idx := o.findIndex(name, "Started", 0); idx == -1 {
206 return fmt.Errorf("couldn't find that %s ever started, got\n%v", name, o)
207 }
208 return nil
209 }
210
211
212 func (o containerOutputList) DoesntStart(name string) error {
213 if idx := o.findIndex(name, "Started", 0); idx != -1 {
214 return fmt.Errorf("find %s started, but didn't expect to, got\n%v", name, o)
215 }
216 return nil
217 }
218
219
220 func (o containerOutputList) Exits(name string) error {
221 if idx := o.findIndex(name, "Exiting", 0); idx == -1 {
222 return fmt.Errorf("couldn't find that %s ever exited, got\n%v", name, o)
223 }
224 return nil
225 }
226
227
228 func (o containerOutputList) HasRestarted(name string) error {
229 idx := o.findIndex(name, "Starting", 0)
230 if idx == -1 {
231 return fmt.Errorf("couldn't find that %s ever started, got\n%v", name, o)
232 }
233
234 idx = o.findIndex(name, "Starting", idx+1)
235
236 if idx == -1 {
237 return fmt.Errorf("couldn't find that %s ever restarted, got\n%v", name, o)
238 }
239
240 return nil
241 }
242
243
244 func (o containerOutputList) HasNotRestarted(name string) error {
245 idx := o.findIndex(name, "Starting", 0)
246 if idx == -1 {
247 return fmt.Errorf("couldn't find that %s ever started, got\n%v", name, o)
248 }
249
250 idx = o.findIndex(name, "Starting", idx+1)
251
252 if idx != -1 {
253 return fmt.Errorf("found that %s restarted but wasn't expected to, got\n%v", name, o)
254 }
255
256 return nil
257 }
258
259 type containerOutputIndex int
260
261 func (i containerOutputIndex) IsBefore(other containerOutputIndex) error {
262 if i >= other {
263 return fmt.Errorf("%d should be before %d", i, other)
264 }
265 return nil
266 }
267
268 func (o containerOutputList) FindIndex(name string, command string, startIdx containerOutputIndex) (containerOutputIndex, error) {
269 idx := o.findIndex(name, command, int(startIdx))
270 if idx == -1 {
271 return -1, fmt.Errorf("couldn't find %s %s, got\n%v", name, command, o)
272 }
273 return containerOutputIndex(idx), nil
274 }
275
276 func (o containerOutputList) findIndex(name string, command string, startIdx int) int {
277 for i, v := range o {
278 if i < startIdx {
279 continue
280 }
281 if v.containerName == name && v.command == command {
282 return i
283 }
284 }
285 return -1
286 }
287 func (o containerOutputList) findLastIndex(name string, command string) int {
288 found := -1
289 for i, v := range o {
290 if v.containerName == name && v.command == command {
291 found = i
292 }
293 }
294 return found
295 }
296
297
298 func (o containerOutputList) TimeOfStart(name string) (float64, error) {
299 idx := o.findIndex(name, "Starting", 0)
300 if idx == -1 {
301 return 0.0, fmt.Errorf("couldn't find that %s ever started, got\n%v", name, o)
302 }
303 return o[idx].timeSinceBoot, nil
304 }
305
306 func (o containerOutputList) TimeOfLastLoop(name string) (float64, error) {
307 idx := o.findLastIndex(name, "Looping")
308 if idx == -1 {
309 return 0.0, fmt.Errorf("couldn't find that %s ever looped, got\n%v", name, o)
310 }
311 return o[idx].timeSinceBoot, nil
312 }
313
314
315
316 func parseOutput(ctx context.Context, f *framework.Framework, pod *v1.Pod) containerOutputList {
317
318 var statuses []v1.ContainerStatus
319 statuses = append(statuses, pod.Status.InitContainerStatuses...)
320 statuses = append(statuses, pod.Status.ContainerStatuses...)
321
322 var buf bytes.Buffer
323 for _, cs := range statuses {
324 log, err := e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, pod.Name, cs.Name)
325 if err != nil {
326 framework.Logf("error getting logs for %s: %v", cs.Name, err)
327 log, err = e2epod.GetPreviousPodLogs(ctx, f.ClientSet, f.Namespace.Name, pod.Name, cs.Name)
328 if err != nil {
329 framework.Logf("error getting previous logs for %s: %v", cs.Name, err)
330 }
331 }
332 buf.WriteString(log)
333 }
334
335
336 sc := bufio.NewScanner(&buf)
337 var res containerOutputList
338 for sc.Scan() {
339 fields := strings.Fields(sc.Text())
340 if len(fields) < 4 {
341 framework.ExpectNoError(fmt.Errorf("%v should have at least length 3", fields))
342 }
343 timestamp, err := strconv.ParseInt(fields[0], 10, 64)
344 framework.ExpectNoError(err)
345 timeSinceBoot, err := strconv.ParseFloat(fields[1], 64)
346 framework.ExpectNoError(err)
347 res = append(res, containerOutput{
348 timestamp: time.Unix(timestamp, 0),
349 timeSinceBoot: timeSinceBoot,
350 containerName: fields[2],
351 command: fields[3],
352 })
353 }
354
355
356 sort.Slice(res, func(i, j int) bool {
357 return res[i].timeSinceBoot < res[j].timeSinceBoot
358 })
359 return res
360 }
361
362
363 func preparePod(pod *v1.Pod) {
364 var defaultResourceRequirements = v1.ResourceRequirements{
365 Requests: v1.ResourceList{
366 v1.ResourceMemory: resource.MustParse("15Mi"),
367 },
368 Limits: v1.ResourceList{
369 v1.ResourceMemory: resource.MustParse("15Mi"),
370 },
371 }
372
373 for i := range pod.Spec.Containers {
374 c := &pod.Spec.Containers[i]
375 c.Resources = defaultResourceRequirements
376 c.VolumeMounts = []v1.VolumeMount{
377 {
378 Name: "persistent",
379 MountPath: "/persistent",
380 },
381 }
382 }
383 for i := range pod.Spec.InitContainers {
384 c := &pod.Spec.InitContainers[i]
385 c.Resources = defaultResourceRequirements
386 c.VolumeMounts = []v1.VolumeMount{
387 {
388 Name: "persistent",
389 MountPath: "/persistent",
390 },
391 }
392 }
393
394 pod.Spec.Volumes = []v1.Volume{
395 {
396 Name: "persistent",
397 VolumeSource: v1.VolumeSource{
398 EmptyDir: &v1.EmptyDirVolumeSource{},
399 },
400 },
401 }
402 }
403
View as plain text