Skip to content

Commit 2283cdc

Browse files
committed
handle PreInit error case for runReady.Close
1 parent d884560 commit 2283cdc

2 files changed

Lines changed: 50 additions & 0 deletions

File tree

filebeat/beater/filebeat.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ func (fb *Filebeat) Run(b *beat.Beat) error {
312312
// Start the check-in loop, so Filebeat can respond to Elastic Agent,
313313
// but it won't start any inputs/output
314314
if err := b.Manager.PreInit(); err != nil {
315+
fb.runReady.Close()
315316
return err
316317
}
317318

filebeat/beater/stop_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,61 @@
1818
package beater
1919

2020
import (
21+
"errors"
2122
"testing"
2223
"time"
2324

25+
"github.com/stretchr/testify/require"
26+
27+
"github.com/elastic/beats/v7/filebeat/fileset"
28+
"github.com/elastic/beats/v7/libbeat/beat"
29+
"github.com/elastic/beats/v7/libbeat/beatmonitoring"
30+
"github.com/elastic/beats/v7/libbeat/management"
2431
"github.com/elastic/elastic-agent-libs/logp"
2532
)
2633

34+
// TestRunClosesRunReadyOnPreInitFailure guards against the regression in
35+
// commit 00068f7 where the defer fb.runReady.Close() was placed after the
36+
// PreInit call, leaving runReady unclosed when PreInit fails. An unclosed
37+
// runReady causes StopWithContext to wait the full five-second timeout.
38+
func TestRunClosesRunReadyOnPreInitFailure(t *testing.T) {
39+
preInitErr := errors.New("preinit failed")
40+
fb := &Filebeat{
41+
done: make(chan struct{}),
42+
runReady: &closeOnce{ch: make(chan struct{})},
43+
logger: logp.NewNopLogger(),
44+
moduleRegistry: new(fileset.ModuleRegistry),
45+
}
46+
b := &beat.Beat{
47+
Info: beat.Info{Logger: logp.NewNopLogger()},
48+
Manager: &preinitFailManager{err: preInitErr},
49+
Monitoring: beatmonitoring.NewMonitoring(),
50+
}
51+
52+
err := fb.Run(b)
53+
require.ErrorIs(t, err, preInitErr)
54+
55+
select {
56+
case <-fb.runReady.ch:
57+
// runReady was closed — StopWithContext will not block
58+
default:
59+
t.Fatal("runReady was not closed after PreInit failure; StopWithContext would wait five seconds")
60+
}
61+
}
62+
63+
// preinitFailManager is a management.Manager stub whose PreInit returns an
64+
// error. Only the methods called by Run before PreInit are implemented; any
65+
// other call panics, catching unexpected code paths during the test.
66+
type preinitFailManager struct {
67+
management.Manager // zero-valued; panics on any unimplemented method
68+
err error
69+
}
70+
71+
func (m *preinitFailManager) Enabled() bool { return true }
72+
func (m *preinitFailManager) PreInit() error { return m.err }
73+
func (m *preinitFailManager) RegisterDiagnosticHook(_, _, _, _ string, _ management.DiagnosticHook) {
74+
}
75+
2776
// TestStopWaitsForRunReady proves that Stop does not close the done channel
2877
// until Run has closed runReady (i.e., reached waitFinished.Wait).
2978
// This guards against a race where the OTel collector calls Shutdown before

0 commit comments

Comments
 (0)