Skip to content

Commit 68703a4

Browse files
fix: container stat test flakiness (runfinch#303)
Signed-off-by: Shubhranshu Mahapatra <shubhum@amazon.com>
1 parent 0565571 commit 68703a4

File tree

1 file changed

+88
-40
lines changed

1 file changed

+88
-40
lines changed

e2e/tests/container_stats.go

Lines changed: 88 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,39 @@ import (
2121
"github.com/runfinch/finch-daemon/e2e/client"
2222
)
2323

24+
// waitForContainerRunning waits for a container to reach the running state.
25+
func waitForContainerRunning(uClient *http.Client, version string, containerID string, maxRetries int) bool {
26+
const retryInterval = 500 * time.Millisecond
27+
28+
for i := 0; i < maxRetries; i++ {
29+
res, err := uClient.Get(client.ConvertToFinchUrl(version, fmt.Sprintf("/containers/%s/json", containerID)))
30+
if err != nil {
31+
time.Sleep(retryInterval)
32+
continue
33+
}
34+
35+
if res.StatusCode != http.StatusOK {
36+
time.Sleep(retryInterval)
37+
continue
38+
}
39+
40+
var container types.Container
41+
err = json.NewDecoder(res.Body).Decode(&container)
42+
res.Body.Close()
43+
if err != nil {
44+
time.Sleep(retryInterval)
45+
continue
46+
}
47+
48+
if container.State.Status == "running" {
49+
return true
50+
}
51+
52+
time.Sleep(retryInterval)
53+
}
54+
return false
55+
}
56+
2457
// ContainerStats tests the `GET containers/{id}/stats` API.
2558
func ContainerStats(opt *option.Option) {
2659
Describe("get container stats", func() {
@@ -30,9 +63,7 @@ func ContainerStats(opt *option.Option) {
3063
wantContainerName string
3164
)
3265
BeforeEach(func() {
33-
// create a custom client to use http over unix sockets
3466
uClient = client.NewClient(GetDockerHostUrl())
35-
// get the docker api version that will be tested
3667
version = GetDockerApiVersion()
3768
wantContainerName = fmt.Sprintf("/%s", testContainerName)
3869
})
@@ -55,13 +86,14 @@ func ContainerStats(opt *option.Option) {
5586
opt, "run", "-d", "--name", testContainerName, defaultImage, "sleep", "Infinity",
5687
)
5788

58-
// get container stats
89+
isRunning := waitForContainerRunning(uClient, version, cid, 10)
90+
Expect(isRunning).Should(BeTrue(), "Container should be in running state before checking stats")
91+
5992
relativeUrl := fmt.Sprintf("/containers/%s/stats?stream=false", testContainerName)
6093
res, err := uClient.Get(client.ConvertToFinchUrl(version, relativeUrl))
6194
Expect(err).Should(BeNil())
6295
Expect(res.StatusCode).Should(Equal(http.StatusOK))
6396

64-
// check json contents
6597
var statsJSON types.StatsJSON
6698
err = json.NewDecoder(res.Body).Decode(&statsJSON)
6799
Expect(err).Should(BeNil())
@@ -72,13 +104,14 @@ func ContainerStats(opt *option.Option) {
72104
opt, "run", "-d", "--name", testContainerName, defaultImage, "sleep", "Infinity",
73105
)
74106

75-
// get container stats
107+
isRunning := waitForContainerRunning(uClient, version, cid, 10)
108+
Expect(isRunning).Should(BeTrue(), "Container should be in running state before checking stats")
109+
76110
relativeUrl := fmt.Sprintf("/containers/%s/stats?stream=false", cid)
77111
res, err := uClient.Get(client.ConvertToFinchUrl(version, relativeUrl))
78112
Expect(err).Should(BeNil())
79113
Expect(res.StatusCode).Should(Equal(http.StatusOK))
80114

81-
// check json contents
82115
var statsJSON types.StatsJSON
83116
err = json.NewDecoder(res.Body).Decode(&statsJSON)
84117
Expect(err).Should(BeNil())
@@ -89,13 +122,14 @@ func ContainerStats(opt *option.Option) {
89122
opt, "run", "-d", "--name", testContainerName, defaultImage, "sleep", "Infinity",
90123
)
91124

92-
// get container stats
125+
isRunning := waitForContainerRunning(uClient, version, cid, 10)
126+
Expect(isRunning).Should(BeTrue(), "Container should be in running state before checking stats")
127+
93128
relativeUrl := fmt.Sprintf("/containers/%s/stats?stream=false", cid[:12])
94129
res, err := uClient.Get(client.ConvertToFinchUrl(version, relativeUrl))
95130
Expect(err).Should(BeNil())
96131
Expect(res.StatusCode).Should(Equal(http.StatusOK))
97132

98-
// check json contents
99133
var statsJSON types.StatsJSON
100134
err = json.NewDecoder(res.Body).Decode(&statsJSON)
101135
Expect(err).Should(BeNil())
@@ -106,51 +140,62 @@ func ContainerStats(opt *option.Option) {
106140
opt, "run", "-d", "--name", testContainerName, defaultImage, "sleep", "Infinity",
107141
)
108142

109-
// start a routine to remove the container in 3 seconds
143+
isRunning := waitForContainerRunning(uClient, version, cid, 10)
144+
Expect(isRunning).Should(BeTrue(), "Container should be in running state before checking stats")
145+
110146
go func() {
111-
time.Sleep(time.Second * 3)
147+
time.Sleep(time.Second * 5)
112148
command.Run(opt, "rm", "-f", testContainerName)
113149
}()
114150

115-
// get container stats
116151
relativeUrl := fmt.Sprintf("/containers/%s/stats", testContainerName)
117152
res, err := uClient.Get(client.ConvertToFinchUrl(version, relativeUrl))
118153
Expect(err).Should(BeNil())
119154
Expect(res.StatusCode).Should(Equal(http.StatusOK))
120155

121-
// check json contents
122156
scanner := bufio.NewScanner(res.Body)
123157
num := 0
124158
for scanner.Scan() {
125159
var statsJSON types.StatsJSON
126160
err = json.Unmarshal(scanner.Bytes(), &statsJSON)
127161
Expect(err).Should(BeNil())
162+
isRunning := waitForContainerRunning(uClient, version, cid, 1)
163+
// confirm need to check the stats were obtained for container running state
164+
if !isRunning {
165+
break
166+
}
128167
expectValidStats(&statsJSON, wantContainerName, cid, 1)
129168
num += 1
130169
}
131-
// should tick at least twice in 3 seconds
170+
132171
Expect(num).Should(BeNumerically(">", 1))
133-
Expect(num).Should(BeNumerically("<", 5))
172+
Expect(num).Should(BeNumerically("<", 10))
134173
})
135174
It("should stream stats for a stopped container", func() {
136175
cid := command.StdoutStr(
137176
opt, "run", "-d", "--name", testContainerName, defaultImage, "echo", "hello",
138177
)
139178
command.Run(opt, "wait", testContainerName)
140179

141-
// start a routine to remove the container in 3 seconds
180+
res, err := uClient.Get(client.ConvertToFinchUrl(version, fmt.Sprintf("/containers/%s/json", cid)))
181+
Expect(err).Should(BeNil())
182+
Expect(res.StatusCode).Should(Equal(http.StatusOK))
183+
var container types.Container
184+
err = json.NewDecoder(res.Body).Decode(&container)
185+
res.Body.Close()
186+
Expect(err).Should(BeNil())
187+
Expect(container.State.Status).ShouldNot(Equal("running"))
188+
142189
go func() {
143-
time.Sleep(time.Second * 3)
190+
time.Sleep(time.Second * 5)
144191
command.Run(opt, "rm", "-f", testContainerName)
145192
}()
146193

147-
// get container stats
148194
relativeUrl := fmt.Sprintf("/containers/%s/stats", testContainerName)
149-
res, err := uClient.Get(client.ConvertToFinchUrl(version, relativeUrl))
195+
res, err = uClient.Get(client.ConvertToFinchUrl(version, relativeUrl))
150196
Expect(err).Should(BeNil())
151197
Expect(res.StatusCode).Should(Equal(http.StatusOK))
152198

153-
// check json contents
154199
scanner := bufio.NewScanner(res.Body)
155200
num := 0
156201
for scanner.Scan() {
@@ -160,9 +205,9 @@ func ContainerStats(opt *option.Option) {
160205
expectEmptyStats(&statsJSON, wantContainerName, cid)
161206
num += 1
162207
}
163-
// should tick at least twice in 3 seconds
208+
164209
Expect(num).Should(BeNumerically(">", 1))
165-
Expect(num).Should(BeNumerically("<", 5))
210+
Expect(num).Should(BeNumerically("<", 10))
166211
})
167212
It("should stream stats when no network interface is created", func() {
168213
cid := command.StdoutStr(
@@ -175,34 +220,38 @@ func ContainerStats(opt *option.Option) {
175220
"sleep", "Infinity",
176221
)
177222

178-
// start a routine to remove the container in 3 seconds
223+
isRunning := waitForContainerRunning(uClient, version, cid, 10)
224+
Expect(isRunning).Should(BeTrue(), "Container should be in running state before checking stats")
225+
179226
go func() {
180-
time.Sleep(time.Second * 3)
227+
time.Sleep(time.Second * 5)
181228
command.Run(opt, "rm", "-f", testContainerName)
182229
}()
183230

184-
// get container stats
185231
relativeUrl := fmt.Sprintf("/containers/%s/stats", testContainerName)
186232
res, err := uClient.Get(client.ConvertToFinchUrl(version, relativeUrl))
187233
Expect(err).Should(BeNil())
188234
Expect(res.StatusCode).Should(Equal(http.StatusOK))
189235

190-
// check json contents
191236
scanner := bufio.NewScanner(res.Body)
192237
num := 0
193238
for scanner.Scan() {
194239
var statsJSON types.StatsJSON
195240
err = json.Unmarshal(scanner.Bytes(), &statsJSON)
196241
Expect(err).Should(BeNil())
242+
isRunning := waitForContainerRunning(uClient, version, cid, 1)
243+
// confirm need to check the stats were obtained for container running state
244+
if !isRunning {
245+
break
246+
}
197247
expectValidStats(&statsJSON, wantContainerName, cid, 0)
198248
num += 1
199249
}
200-
// should tick at least twice in 3 seconds
250+
201251
Expect(num).Should(BeNumerically(">", 1))
202-
Expect(num).Should(BeNumerically("<", 5))
252+
Expect(num).Should(BeNumerically("<", 10))
203253
})
204254
It("should stream stats with multiple network interfaces", func() {
205-
// create networks and run a container with multiple networks
206255
command.Run(opt, "network", "create", "net1")
207256
command.Run(opt, "network", "create", "net2")
208257
cid := command.StdoutStr(
@@ -216,31 +265,36 @@ func ContainerStats(opt *option.Option) {
216265
"sleep", "Infinity",
217266
)
218267

219-
// start a routine to remove the container in 3 seconds
268+
isRunning := waitForContainerRunning(uClient, version, cid, 10)
269+
Expect(isRunning).Should(BeTrue(), "Container should be in running state before checking stats")
270+
220271
go func() {
221-
time.Sleep(time.Second * 3)
272+
time.Sleep(time.Second * 5)
222273
command.Run(opt, "rm", "-f", testContainerName)
223274
}()
224275

225-
// get container stats
226276
relativeUrl := fmt.Sprintf("/containers/%s/stats", testContainerName)
227277
res, err := uClient.Get(client.ConvertToFinchUrl(version, relativeUrl))
228278
Expect(err).Should(BeNil())
229279
Expect(res.StatusCode).Should(Equal(http.StatusOK))
230280

231-
// check json contents
232281
scanner := bufio.NewScanner(res.Body)
233282
num := 0
234283
for scanner.Scan() {
235284
var statsJSON types.StatsJSON
236285
err = json.Unmarshal(scanner.Bytes(), &statsJSON)
237286
Expect(err).Should(BeNil())
287+
isRunning := waitForContainerRunning(uClient, version, cid, 1)
288+
// confirm need to check the stats were obtained for container running state
289+
if !isRunning {
290+
break
291+
}
238292
expectValidStats(&statsJSON, wantContainerName, cid, 2)
239293
num += 1
240294
}
241-
// should tick at least twice in 3 seconds
295+
242296
Expect(num).Should(BeNumerically(">", 1))
243-
Expect(num).Should(BeNumerically("<", 5))
297+
Expect(num).Should(BeNumerically("<", 10))
244298
})
245299
})
246300
}
@@ -258,14 +312,10 @@ func expectValidStats(st *types.StatsJSON, name, id string, numNetworks int) {
258312
Expect(st.Read).Should(BeTemporally("~", st.PreRead.Add(time.Second), time.Millisecond*100))
259313
}
260314

261-
// check that number of current PIDs is > 0
262315
Expect(st.PidsStats.Current).ShouldNot(BeZero())
263-
264-
// check that number of CPUs and system usage is > 0
265316
Expect(st.CPUStats.OnlineCPUs).ShouldNot(BeZero())
266317
Expect(st.CPUStats.SystemUsage).ShouldNot(BeZero())
267318

268-
// check that object contains networks data
269319
if numNetworks == 0 {
270320
Expect(st.Networks).Should(BeNil())
271321
} else {
@@ -277,10 +327,8 @@ func expectValidStats(st *types.StatsJSON, name, id string, numNetworks int) {
277327
// expectEmptyStats ensures that the data contained in the stats object is empty
278328
// which is the case with containers that are not running.
279329
func expectEmptyStats(st *types.StatsJSON, name, id string) {
280-
// verify container name and ID
281330
Expect(st.Name).Should(Equal(name))
282331
Expect(st.ID).Should(Equal(id))
283-
284332
Expect(st.Read).Should(Equal(time.Time{}))
285333
Expect(st.PreRead).Should(Equal(time.Time{}))
286334
Expect(st.PidsStats).Should(Equal(dockertypes.PidsStats{}))

0 commit comments

Comments
 (0)