Skip to content

Commit ca46237

Browse files
belimawrmergify[bot]
authored andcommitted
[Integration Test Framework] fix createTempDir and flaky tests (#5409)
createTempDir register a test cleanup function to remove the folder it created, however, on Windows, this folder sometimes fails to be removed because there are still open file handlers for the files within the folder. We fix this problem calling install.RemovePath that will retry removing the folder for about 2s. This is a very similar approach to what Go's t.TempDir does. Fix the flakiness from TestUpgradeHandler* tests by re-working the mockUpgradeManager, now it accepts a function for its Upgrade method and their implementation is goroutine safe (cherry picked from commit 1242e71) # Conflicts: # internal/pkg/agent/application/actions/handlers/handler_action_upgrade_test.go # pkg/testing/fixture.go # testing/integration/event_logging_test.go
1 parent 75d6eca commit ca46237

File tree

3 files changed

+185
-2
lines changed

3 files changed

+185
-2
lines changed

internal/pkg/agent/application/actions/handlers/handler_action_upgrade_test.go

+153-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ package handlers
66

77
import (
88
"context"
9+
"errors"
10+
"sync/atomic"
911
"testing"
1012
"time"
1113

@@ -26,7 +28,19 @@ import (
2628
)
2729

2830
type mockUpgradeManager struct {
31+
<<<<<<< HEAD
2932
msgChan chan string
33+
=======
34+
UpgradeFn func(
35+
ctx context.Context,
36+
version string,
37+
sourceURI string,
38+
action *fleetapi.ActionUpgrade,
39+
details *details.Details,
40+
skipVerifyOverride bool,
41+
skipDefaultPgp bool,
42+
pgpBytes ...string) (reexec.ShutdownCallbackFn, error)
43+
>>>>>>> 1242e7186a ([Integration Test Framework] fix createTempDir and flaky tests (#5409))
3044
}
3145

3246
func (u *mockUpgradeManager) Upgradeable() bool {
@@ -37,6 +51,7 @@ func (u *mockUpgradeManager) Reload(rawConfig *config.Config) error {
3751
return nil
3852
}
3953

54+
<<<<<<< HEAD
4055
func (u *mockUpgradeManager) Upgrade(ctx context.Context, version string, sourceURI string, action *fleetapi.ActionUpgrade, details *details.Details, skipVerifyOverride bool, skipDefaultPgp bool, pgpBytes ...string) (_ reexec.ShutdownCallbackFn, err error) {
4156
select {
4257
case <-time.After(2 * time.Second):
@@ -46,6 +61,27 @@ func (u *mockUpgradeManager) Upgrade(ctx context.Context, version string, source
4661
u.msgChan <- "canceled " + version
4762
return nil, ctx.Err()
4863
}
64+
=======
65+
func (u *mockUpgradeManager) Upgrade(
66+
ctx context.Context,
67+
version string,
68+
sourceURI string,
69+
action *fleetapi.ActionUpgrade,
70+
details *details.Details,
71+
skipVerifyOverride bool,
72+
skipDefaultPgp bool,
73+
pgpBytes ...string) (reexec.ShutdownCallbackFn, error) {
74+
75+
return u.UpgradeFn(
76+
ctx,
77+
version,
78+
sourceURI,
79+
action,
80+
details,
81+
skipVerifyOverride,
82+
skipDefaultPgp,
83+
pgpBytes...)
84+
>>>>>>> 1242e7186a ([Integration Test Framework] fix createTempDir and flaky tests (#5409))
4985
}
5086

5187
func (u *mockUpgradeManager) Ack(ctx context.Context, acker acker.Acker) error {
@@ -65,7 +101,11 @@ func TestUpgradeHandler(t *testing.T) {
65101
log, _ := logger.New("", false)
66102

67103
agentInfo := &info.AgentInfo{}
104+
<<<<<<< HEAD
68105
msgChan := make(chan string)
106+
=======
107+
upgradeCalledChan := make(chan struct{})
108+
>>>>>>> 1242e7186a ([Integration Test Framework] fix createTempDir and flaky tests (#5409))
69109

70110
// Create and start the coordinator
71111
c := coordinator.New(
@@ -75,7 +115,25 @@ func TestUpgradeHandler(t *testing.T) {
75115
agentInfo,
76116
component.RuntimeSpecs{},
77117
nil,
118+
<<<<<<< HEAD
78119
&mockUpgradeManager{msgChan: msgChan},
120+
=======
121+
&mockUpgradeManager{
122+
UpgradeFn: func(
123+
ctx context.Context,
124+
version string,
125+
sourceURI string,
126+
action *fleetapi.ActionUpgrade,
127+
details *details.Details,
128+
skipVerifyOverride bool,
129+
skipDefaultPgp bool,
130+
pgpBytes ...string) (reexec.ShutdownCallbackFn, error) {
131+
132+
upgradeCalledChan <- struct{}{}
133+
return nil, nil
134+
},
135+
},
136+
>>>>>>> 1242e7186a ([Integration Test Framework] fix createTempDir and flaky tests (#5409))
79137
nil, nil, nil, nil, nil, false)
80138
//nolint:errcheck // We don't need the termination state of the Coordinator
81139
go c.Run(ctx)
@@ -86,8 +144,13 @@ func TestUpgradeHandler(t *testing.T) {
86144
ack := noopacker.New()
87145
err := u.Handle(ctx, &a, ack)
88146
require.NoError(t, err)
89-
msg := <-msgChan
90-
require.Equal(t, "completed 8.3.0", msg)
147+
148+
// Make sure this test does not dead lock or wait for too long
149+
select {
150+
case <-time.Tick(50 * time.Millisecond):
151+
t.Fatal("mockUpgradeManager.Upgrade was not called")
152+
case <-upgradeCalledChan:
153+
}
91154
}
92155

93156
func TestUpgradeHandlerSameVersion(t *testing.T) {
@@ -99,17 +162,45 @@ func TestUpgradeHandlerSameVersion(t *testing.T) {
99162
log, _ := logger.New("", false)
100163

101164
agentInfo := &info.AgentInfo{}
165+
<<<<<<< HEAD
102166
msgChan := make(chan string)
167+
=======
168+
upgradeCalledChan := make(chan struct{})
169+
>>>>>>> 1242e7186a ([Integration Test Framework] fix createTempDir and flaky tests (#5409))
103170

104171
// Create and start the Coordinator
172+
upgradeCalled := atomic.Bool{}
105173
c := coordinator.New(
106174
log,
107175
configuration.DefaultConfiguration(),
108176
logger.DefaultLogLevel,
109177
agentInfo,
110178
component.RuntimeSpecs{},
111179
nil,
180+
<<<<<<< HEAD
112181
&mockUpgradeManager{msgChan: msgChan},
182+
=======
183+
&mockUpgradeManager{
184+
UpgradeFn: func(
185+
ctx context.Context,
186+
version string,
187+
sourceURI string,
188+
action *fleetapi.ActionUpgrade,
189+
details *details.Details,
190+
skipVerifyOverride bool,
191+
skipDefaultPgp bool,
192+
pgpBytes ...string) (reexec.ShutdownCallbackFn, error) {
193+
194+
if upgradeCalled.CompareAndSwap(false, true) {
195+
upgradeCalledChan <- struct{}{}
196+
return nil, nil
197+
}
198+
err := errors.New("mockUpgradeManager.Upgrade called more than once")
199+
t.Error(err.Error())
200+
return nil, err
201+
},
202+
},
203+
>>>>>>> 1242e7186a ([Integration Test Framework] fix createTempDir and flaky tests (#5409))
113204
nil, nil, nil, nil, nil, false)
114205
//nolint:errcheck // We don't need the termination state of the Coordinator
115206
go c.Run(ctx)
@@ -122,8 +213,18 @@ func TestUpgradeHandlerSameVersion(t *testing.T) {
122213
err2 := u.Handle(ctx, &a, ack)
123214
require.NoError(t, err1)
124215
require.NoError(t, err2)
216+
<<<<<<< HEAD
125217
msg := <-msgChan
126218
require.Equal(t, "completed 8.3.0", msg)
219+
=======
220+
221+
// Make sure this test does not dead lock or wait for too long
222+
select {
223+
case <-time.Tick(50 * time.Millisecond):
224+
t.Fatal("mockUpgradeManager.Upgrade was not called")
225+
case <-upgradeCalledChan:
226+
}
227+
>>>>>>> 1242e7186a ([Integration Test Framework] fix createTempDir and flaky tests (#5409))
127228
}
128229

129230
func TestUpgradeHandlerNewVersion(t *testing.T) {
@@ -133,9 +234,13 @@ func TestUpgradeHandlerNewVersion(t *testing.T) {
133234
defer cancel()
134235

135236
log, _ := logger.New("", false)
237+
upgradeCalledChan := make(chan string)
136238

137239
agentInfo := &info.AgentInfo{}
240+
<<<<<<< HEAD
138241
msgChan := make(chan string)
242+
=======
243+
>>>>>>> 1242e7186a ([Integration Test Framework] fix createTempDir and flaky tests (#5409))
139244

140245
// Create and start the Coordinator
141246
c := coordinator.New(
@@ -145,7 +250,31 @@ func TestUpgradeHandlerNewVersion(t *testing.T) {
145250
agentInfo,
146251
component.RuntimeSpecs{},
147252
nil,
253+
<<<<<<< HEAD
148254
&mockUpgradeManager{msgChan: msgChan},
255+
=======
256+
&mockUpgradeManager{
257+
UpgradeFn: func(
258+
ctx context.Context,
259+
version string,
260+
sourceURI string,
261+
action *fleetapi.ActionUpgrade,
262+
details *details.Details,
263+
skipVerifyOverride bool,
264+
skipDefaultPgp bool,
265+
pgpBytes ...string) (reexec.ShutdownCallbackFn, error) {
266+
267+
defer func() {
268+
upgradeCalledChan <- version
269+
}()
270+
if version == "8.2.0" {
271+
return nil, errors.New("upgrade to 8.2.0 will always fail")
272+
}
273+
274+
return nil, nil
275+
},
276+
},
277+
>>>>>>> 1242e7186a ([Integration Test Framework] fix createTempDir and flaky tests (#5409))
149278
nil, nil, nil, nil, nil, false)
150279
//nolint:errcheck // We don't need the termination state of the Coordinator
151280
go c.Run(ctx)
@@ -156,13 +285,35 @@ func TestUpgradeHandlerNewVersion(t *testing.T) {
156285
a2 := fleetapi.ActionUpgrade{Data: fleetapi.ActionUpgradeData{
157286
Version: "8.5.0", SourceURI: "http://localhost"}}
158287
ack := noopacker.New()
288+
289+
checkMsg := func(c <-chan string, expected, errMsg string) {
290+
t.Helper()
291+
// Make sure this test does not dead lock or wait for too long
292+
// For some reason < 1s sometimes makes the test fail.
293+
select {
294+
case <-time.Tick(1300 * time.Millisecond):
295+
t.Fatal("timed out waiting for Upgrade to return")
296+
case msg := <-c:
297+
require.Equal(t, expected, msg, errMsg)
298+
}
299+
}
300+
301+
// Send both upgrade actions, a1 will error before a2 succeeds
159302
err1 := u.Handle(ctx, &a1, ack)
160303
require.NoError(t, err1)
304+
<<<<<<< HEAD
161305
time.Sleep(1 * time.Second)
162306
err2 := u.Handle(ctx, &a2, ack)
163307
require.NoError(t, err2)
164308
msg1 := <-msgChan
165309
require.Equal(t, "canceled 8.2.0", msg1)
166310
msg2 := <-msgChan
167311
require.Equal(t, "completed 8.5.0", msg2)
312+
=======
313+
checkMsg(upgradeCalledChan, "8.2.0", "first call must be with version 8.2.0")
314+
315+
err2 := u.Handle(ctx, &a2, ack)
316+
require.NoError(t, err2)
317+
checkMsg(upgradeCalledChan, "8.5.0", "second call to Upgrade must be with version 8.5.0")
318+
>>>>>>> 1242e7186a ([Integration Test Framework] fix createTempDir and flaky tests (#5409))
168319
}

pkg/testing/fixture.go

+29
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626

2727
"github.com/elastic/elastic-agent/internal/pkg/agent/application/paths"
2828
"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/details"
29+
"github.com/elastic/elastic-agent/internal/pkg/agent/install"
2930
"github.com/elastic/elastic-agent/pkg/component"
3031
"github.com/elastic/elastic-agent/pkg/control"
3132
"github.com/elastic/elastic-agent/pkg/control/v2/client"
@@ -1196,6 +1197,34 @@ func performConfigure(ctx context.Context, c client.Client, cfg string, timeout
11961197
return nil
11971198
}
11981199

1200+
<<<<<<< HEAD
1201+
=======
1202+
// createTempDir creates a temporary directory that will be
1203+
// removed after the tests passes. If the test fails, the
1204+
// directory is kept for further investigation.
1205+
//
1206+
// If the test is run with -v and fails the temporary directory is logged
1207+
func createTempDir(t *testing.T) string {
1208+
tempDir, err := os.MkdirTemp("", strings.ReplaceAll(t.Name(), "/", "-"))
1209+
if err != nil {
1210+
t.Fatalf("failed to make temp directory: %s", err)
1211+
}
1212+
1213+
cleanup := func() {
1214+
if !t.Failed() {
1215+
if err := install.RemovePath(tempDir); err != nil {
1216+
t.Errorf("could not remove temp dir '%s': %s", tempDir, err)
1217+
}
1218+
} else {
1219+
t.Logf("Temporary directory %q preserved for investigation/debugging", tempDir)
1220+
}
1221+
}
1222+
t.Cleanup(cleanup)
1223+
1224+
return tempDir
1225+
}
1226+
1227+
>>>>>>> 1242e7186a ([Integration Test Framework] fix createTempDir and flaky tests (#5409))
11991228
type AgentStatusOutput struct {
12001229
Info struct {
12011230
ID string `json:"id"`

testing/integration/event_logging_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,10 @@ func TestEventLogFile(t *testing.T) {
7575
Local: true,
7676
Sudo: false,
7777
})
78+
<<<<<<< HEAD
7879

80+
=======
81+
>>>>>>> 1242e7186a ([Integration Test Framework] fix createTempDir and flaky tests (#5409))
7982
ctx, cancel := testcontext.WithDeadline(
8083
t,
8184
context.Background(),

0 commit comments

Comments
 (0)