diff --git a/internal/pkg/agent/application/actions/handlers/handler_action_diagnostics.go b/internal/pkg/agent/application/actions/handlers/handler_action_diagnostics.go index 301e99d65ab..2cb28c3c579 100644 --- a/internal/pkg/agent/application/actions/handlers/handler_action_diagnostics.go +++ b/internal/pkg/agent/application/actions/handlers/handler_action_diagnostics.go @@ -16,6 +16,7 @@ import ( "github.com/elastic/elastic-agent/pkg/control/v2/client" "github.com/elastic/elastic-agent/pkg/control/v2/cproto" + "github.com/elastic/elastic-agent/internal/pkg/agent/application/paths" "github.com/elastic/elastic-agent/internal/pkg/core/monitoring/config" "github.com/elastic/elastic-agent/internal/pkg/diagnostics" "github.com/elastic/elastic-agent/internal/pkg/fleetapi" @@ -67,15 +68,20 @@ type Diagnostics struct { diagProvider diagnosticsProvider limiter *rate.Limiter uploader Uploader + topPath string } // NewDiagnostics returns a new Diagnostics handler. -func NewDiagnostics(log abstractLogger, coord diagnosticsProvider, cfg config.Limit, uploader Uploader) *Diagnostics { +func NewDiagnostics(log abstractLogger, topPath string, coord diagnosticsProvider, cfg config.Limit, uploader Uploader) *Diagnostics { + if topPath == "" { + topPath = paths.Top() + } return &Diagnostics{ log: log, diagProvider: coord, limiter: rate.NewLimiter(rate.Every(cfg.Interval), cfg.Burst), uploader: uploader, + topPath: topPath, } } @@ -155,7 +161,7 @@ func (h *Diagnostics) collectDiag(ctx context.Context, action *fleetapi.ActionDi h.log.Warn(str) } }() - err := diagnostics.ZipArchive(&wBuf, &b, aDiag, uDiag, cDiag, action.ExcludeEventsLog) + err := diagnostics.ZipArchive(&wBuf, &b, h.topPath, aDiag, uDiag, cDiag, action.ExcludeEventsLog) if err != nil { h.log.Errorw( "diagnostics action handler failed generate zip archive", @@ -344,7 +350,7 @@ func (h *Diagnostics) diagFile( h.log.Warn(str) } }() - if err := diagnostics.ZipArchive(&wBuf, f, aDiag, uDiag, cDiag, excludeEvents); err != nil { + if err := diagnostics.ZipArchive(&wBuf, f, h.topPath, aDiag, uDiag, cDiag, excludeEvents); err != nil { os.Remove(name) return nil, 0, err } diff --git a/internal/pkg/agent/application/actions/handlers/handler_action_diagnostics_test.go b/internal/pkg/agent/application/actions/handlers/handler_action_diagnostics_test.go index 6eda314ec5c..87155616c5a 100644 --- a/internal/pkg/agent/application/actions/handlers/handler_action_diagnostics_test.go +++ b/internal/pkg/agent/application/actions/handlers/handler_action_diagnostics_test.go @@ -75,16 +75,14 @@ var ( ) func TestDiagnosticHandlerHappyPathWithLogs(t *testing.T) { - tempAgentRoot := t.TempDir() - paths.SetTop(tempAgentRoot) err := os.MkdirAll(path.Join(tempAgentRoot, "data"), 0755) require.NoError(t, err) mockDiagProvider := mockhandlers.NewDiagnosticsProvider(t) mockUploader := mockhandlers.NewUploader(t) testLogger, observedLogs := logger.NewTesting("diagnostic-handler-test") - handler := NewDiagnostics(testLogger, mockDiagProvider, defaultRateLimit, mockUploader) + handler := NewDiagnostics(testLogger, tempAgentRoot, mockDiagProvider, defaultRateLimit, mockUploader) mockDiagProvider.EXPECT().DiagnosticHooks().Return([]diagnostics.Hook{hook1}) mockDiagProvider.EXPECT().PerformDiagnostics(mock.Anything, mock.Anything).Return([]runtime.ComponentUnitDiagnostic{mockUnitDiagnostic}) @@ -165,7 +163,7 @@ func TestDiagnosticHandlerUploaderErrorWithLogs(t *testing.T) { mockDiagProvider := mockhandlers.NewDiagnosticsProvider(t) mockUploader := mockhandlers.NewUploader(t) testLogger, observedLogs := logger.NewTesting("diagnostic-handler-test") - handler := NewDiagnostics(testLogger, mockDiagProvider, defaultRateLimit, mockUploader) + handler := NewDiagnostics(testLogger, tempAgentRoot, mockDiagProvider, defaultRateLimit, mockUploader) mockDiagProvider.EXPECT().DiagnosticHooks().Return([]diagnostics.Hook{}) mockDiagProvider.EXPECT().PerformDiagnostics(mock.Anything, mock.Anything).Return([]runtime.ComponentUnitDiagnostic{}) @@ -206,7 +204,7 @@ func TestDiagnosticHandlerZipArchiveErrorWithLogs(t *testing.T) { mockDiagProvider := mockhandlers.NewDiagnosticsProvider(t) mockUploader := mockhandlers.NewUploader(t) testLogger, observedLogs := logger.NewTesting("diagnostic-handler-test") - handler := NewDiagnostics(testLogger, mockDiagProvider, defaultRateLimit, mockUploader) + handler := NewDiagnostics(testLogger, tempAgentRoot, mockDiagProvider, defaultRateLimit, mockUploader) mockDiagProvider.EXPECT().DiagnosticHooks().Return([]diagnostics.Hook{}) mockDiagProvider.EXPECT().PerformDiagnostics(mock.Anything, mock.Anything).Return([]runtime.ComponentUnitDiagnostic{}) @@ -242,7 +240,7 @@ func TestDiagnosticHandlerAckErrorWithLogs(t *testing.T) { mockDiagProvider := mockhandlers.NewDiagnosticsProvider(t) mockUploader := mockhandlers.NewUploader(t) testLogger, observedLogs := logger.NewTesting("diagnostic-handler-test") - handler := NewDiagnostics(testLogger, mockDiagProvider, defaultRateLimit, mockUploader) + handler := NewDiagnostics(testLogger, tempAgentRoot, mockDiagProvider, defaultRateLimit, mockUploader) mockDiagProvider.EXPECT().DiagnosticHooks().Return([]diagnostics.Hook{}) mockDiagProvider.EXPECT().PerformDiagnostics(mock.Anything, mock.Anything).Return([]runtime.ComponentUnitDiagnostic{}) @@ -281,7 +279,7 @@ func TestDiagnosticHandlerCommitErrorWithLogs(t *testing.T) { mockDiagProvider := mockhandlers.NewDiagnosticsProvider(t) mockUploader := mockhandlers.NewUploader(t) testLogger, observedLogs := logger.NewTesting("diagnostic-handler-test") - handler := NewDiagnostics(testLogger, mockDiagProvider, defaultRateLimit, mockUploader) + handler := NewDiagnostics(testLogger, tempAgentRoot, mockDiagProvider, defaultRateLimit, mockUploader) mockDiagProvider.EXPECT().DiagnosticHooks().Return([]diagnostics.Hook{}) mockDiagProvider.EXPECT().PerformDiagnostics(mock.Anything, mock.Anything).Return([]runtime.ComponentUnitDiagnostic{}) @@ -321,7 +319,7 @@ func TestDiagnosticHandlerContexteExpiredErrorWithLogs(t *testing.T) { mockDiagProvider := mockhandlers.NewDiagnosticsProvider(t) mockUploader := mockhandlers.NewUploader(t) testLogger, observedLogs := logger.NewTesting("diagnostic-handler-test") - handler := NewDiagnostics(testLogger, mockDiagProvider, defaultRateLimit, mockUploader) + handler := NewDiagnostics(testLogger, tempAgentRoot, mockDiagProvider, defaultRateLimit, mockUploader) mockDiagProvider.EXPECT().DiagnosticHooks().Return([]diagnostics.Hook{}) @@ -365,7 +363,7 @@ func TestDiagnosticHandlerWithCPUProfile(t *testing.T) { mockDiagProvider := mockhandlers.NewDiagnosticsProvider(t) mockUploader := mockhandlers.NewUploader(t) testLogger, _ := logger.NewTesting("diagnostic-handler-test") - handler := NewDiagnostics(testLogger, mockDiagProvider, defaultRateLimit, mockUploader) + handler := NewDiagnostics(testLogger, tempAgentRoot, mockDiagProvider, defaultRateLimit, mockUploader) mockDiagProvider.EXPECT().DiagnosticHooks().Return([]diagnostics.Hook{hook1}) mockDiagProvider.EXPECT().PerformDiagnostics(mock.Anything, mock.Anything).Return([]runtime.ComponentUnitDiagnostic{mockUnitDiagnostic}) diff --git a/internal/pkg/agent/application/application.go b/internal/pkg/agent/application/application.go index cc520c44adc..ac5d73be6c9 100644 --- a/internal/pkg/agent/application/application.go +++ b/internal/pkg/agent/application/application.go @@ -177,7 +177,8 @@ func New( EndpointSignedComponentModifier(), ) - managed, err = newManagedConfigManager(ctx, log, agentInfo, cfg, store, runtime, fleetInitTimeout, upgrader) + // TODO: stop using global state + managed, err = newManagedConfigManager(ctx, log, agentInfo, cfg, store, runtime, fleetInitTimeout, paths.Top(), upgrader) if err != nil { return nil, nil, nil, err } diff --git a/internal/pkg/agent/application/dispatcher/dispatcher.go b/internal/pkg/agent/application/dispatcher/dispatcher.go index aef7bff5cdb..1ff21cc8c81 100644 --- a/internal/pkg/agent/application/dispatcher/dispatcher.go +++ b/internal/pkg/agent/application/dispatcher/dispatcher.go @@ -44,12 +44,13 @@ type ActionDispatcher struct { queue priorityQueue rt *retryConfig errCh chan error + topPath string lastUpgradeDetails *details.Details } // New creates a new action dispatcher. -func New(log *logger.Logger, def actions.Handler, queue priorityQueue) (*ActionDispatcher, error) { +func New(log *logger.Logger, topPath string, def actions.Handler, queue priorityQueue) (*ActionDispatcher, error) { var err error if log == nil { log, err = logger.New("action_dispatcher", false) @@ -69,6 +70,7 @@ func New(log *logger.Logger, def actions.Handler, queue priorityQueue) (*ActionD queue: queue, rt: defaultRetryConfig(), errCh: make(chan error), + topPath: topPath, }, nil } diff --git a/internal/pkg/agent/application/dispatcher/dispatcher_test.go b/internal/pkg/agent/application/dispatcher/dispatcher_test.go index 1e63e73e6e9..614dd536363 100644 --- a/internal/pkg/agent/application/dispatcher/dispatcher_test.go +++ b/internal/pkg/agent/application/dispatcher/dispatcher_test.go @@ -121,7 +121,7 @@ func TestActionDispatcher(t *testing.T) { queue := &mockQueue{} queue.On("Save").Return(nil).Once() queue.On("DequeueActions").Return([]fleetapi.ScheduledAction{}).Once() - d, err := New(nil, def, queue) + d, err := New(nil, t.TempDir(), def, queue) require.NoError(t, err) success1 := &mockHandler{} @@ -163,7 +163,7 @@ func TestActionDispatcher(t *testing.T) { queue := &mockQueue{} queue.On("Save").Return(nil).Once() queue.On("DequeueActions").Return([]fleetapi.ScheduledAction{}).Once() - d, err := New(nil, def, queue) + d, err := New(nil, t.TempDir(), def, queue) require.NoError(t, err) action := &mockOtherAction{} @@ -187,7 +187,7 @@ func TestActionDispatcher(t *testing.T) { def := &mockHandler{} queue := &mockQueue{} - d, err := New(nil, def, queue) + d, err := New(nil, t.TempDir(), def, queue) require.NoError(t, err) err = d.Register(&mockAction{}, success1) @@ -207,7 +207,7 @@ func TestActionDispatcher(t *testing.T) { queue.On("DequeueActions").Return([]fleetapi.ScheduledAction{}).Once() queue.On("Add", mock.Anything, mock.Anything).Once() - d, err := New(nil, def, queue) + d, err := New(nil, t.TempDir(), def, queue) require.NoError(t, err) err = d.Register(&mockAction{}, def) require.NoError(t, err) @@ -242,7 +242,7 @@ func TestActionDispatcher(t *testing.T) { queue.On("Save").Return(nil).Once() queue.On("DequeueActions").Return([]fleetapi.ScheduledAction{}).Once() - d, err := New(nil, def, queue) + d, err := New(nil, t.TempDir(), def, queue) require.NoError(t, err) err = d.Register(&mockAction{}, def) require.NoError(t, err) @@ -282,7 +282,7 @@ func TestActionDispatcher(t *testing.T) { queue.On("Save").Return(nil).Once() queue.On("DequeueActions").Return([]fleetapi.ScheduledAction{action1}).Once() - d, err := New(nil, def, queue) + d, err := New(nil, t.TempDir(), def, queue) require.NoError(t, err) err = d.Register(&mockAction{}, def) require.NoError(t, err) @@ -309,7 +309,7 @@ func TestActionDispatcher(t *testing.T) { queue.On("Save").Return(nil).Once() queue.On("DequeueActions").Return([]fleetapi.ScheduledAction{}).Once() - d, err := New(nil, def, queue) + d, err := New(nil, t.TempDir(), def, queue) require.NoError(t, err) err = d.Register(&mockAction{}, def) require.NoError(t, err) @@ -335,7 +335,7 @@ func TestActionDispatcher(t *testing.T) { queue.On("DequeueActions").Return([]fleetapi.ScheduledAction{}).Once() queue.On("Add", mock.Anything, mock.Anything).Once() - d, err := New(nil, def, queue) + d, err := New(nil, t.TempDir(), def, queue) require.NoError(t, err) err = d.Register(&mockRetryableAction{}, def) require.NoError(t, err) @@ -369,7 +369,7 @@ func TestActionDispatcher(t *testing.T) { queue.On("Save").Return(nil).Once() queue.On("DequeueActions").Return([]fleetapi.ScheduledAction{}).Once() - d, err := New(nil, def, queue) + d, err := New(nil, t.TempDir(), def, queue) require.NoError(t, err) err = d.Register(&mockAction{}, def) require.NoError(t, err) @@ -412,7 +412,7 @@ func TestActionDispatcher(t *testing.T) { queue.On("Save").Return(nil).Times(2) queue.On("DequeueActions").Return([]fleetapi.ScheduledAction{}).Times(2) - d, err := New(nil, def, queue) + d, err := New(nil, t.TempDir(), def, queue) require.NoError(t, err) err = d.Register(&mockAction{}, def) require.NoError(t, err) @@ -464,7 +464,7 @@ func TestActionDispatcher(t *testing.T) { queue.On("DequeueActions").Return([]fleetapi.ScheduledAction{}).Once() queue.On("CancelType", mock.Anything).Return(1).Once() - d, err := New(nil, def, queue) + d, err := New(nil, t.TempDir(), def, queue) require.NoError(t, err) var gotDetails *details.Details @@ -514,7 +514,7 @@ func TestActionDispatcher(t *testing.T) { Once() queue.On("CancelType", mock.Anything).Return(1).Once() - d, err := New(nil, def, queue) + d, err := New(nil, t.TempDir(), def, queue) require.NoError(t, err) var gotDetails *details.Details @@ -556,7 +556,7 @@ func TestActionDispatcher(t *testing.T) { Once() queue.On("CancelType", mock.Anything).Return(1).Once() - d, err := New(nil, def, queue) + d, err := New(nil, t.TempDir(), def, queue) require.NoError(t, err) wantDetail := &details.Details{ @@ -598,7 +598,7 @@ func TestActionDispatcher(t *testing.T) { Once() queue.On("CancelType", mock.Anything).Return(1).Once() - d, err := New(nil, def, queue) + d, err := New(nil, t.TempDir(), def, queue) require.NoError(t, err) var gotDetails *details.Details @@ -628,7 +628,7 @@ func Test_ActionDispatcher_scheduleRetry(t *testing.T) { t.Run("no more attmpts", func(t *testing.T) { queue := &mockQueue{} - d, err := New(nil, def, queue) + d, err := New(nil, t.TempDir(), def, queue) require.NoError(t, err) action := &mockRetryableAction{} @@ -645,7 +645,7 @@ func Test_ActionDispatcher_scheduleRetry(t *testing.T) { queue := &mockQueue{} queue.On("Save").Return(nil).Once() queue.On("Add", mock.Anything, mock.Anything).Once() - d, err := New(nil, def, queue) + d, err := New(nil, t.TempDir(), def, queue) require.NoError(t, err) action := &mockRetryableAction{} @@ -734,7 +734,7 @@ func TestReportNextScheduledUpgrade(t *testing.T) { def := &mockHandler{} queue := &mockQueue{} - d, err := New(nil, def, queue) + d, err := New(nil, t.TempDir(), def, queue) require.NoError(t, err, "could not create dispatcher") for name, test := range cases { diff --git a/internal/pkg/agent/application/managed_mode.go b/internal/pkg/agent/application/managed_mode.go index fafd242ec8b..a29c89c8ee3 100644 --- a/internal/pkg/agent/application/managed_mode.go +++ b/internal/pkg/agent/application/managed_mode.go @@ -65,6 +65,7 @@ func newManagedConfigManager( storeSaver storage.Store, runtime *runtime.Manager, fleetInitTimeout time.Duration, + topPath string, clientSetters ...actions.ClientSetter, ) (*managedConfigManager, error) { client, err := fleetclient.NewAuthWithConfig(log, cfg.Fleet.AccessAPIKey, cfg.Fleet.Client) @@ -86,7 +87,7 @@ func newManagedConfigManager( return nil, fmt.Errorf("unable to initialize action queue: %w", err) } - actionDispatcher, err := dispatcher.New(log, handlers.NewDefault(log), actionQueue) + actionDispatcher, err := dispatcher.New(log, topPath, handlers.NewDefault(log), actionQueue) if err != nil { return nil, fmt.Errorf("unable to initialize action dispatcher: %w", err) } @@ -392,6 +393,7 @@ func (m *managedConfigManager) initDispatcher(canceller context.CancelFunc) *han &fleetapi.ActionDiagnostics{}, handlers.NewDiagnostics( m.log, + paths.Top(), // TODO: stop using global state m.coord, m.cfg.Settings.MonitoringConfig.Diagnostics.Limit, uploader.New(m.agentInfo.AgentID(), m.client, m.cfg.Settings.MonitoringConfig.Diagnostics.Uploader), diff --git a/internal/pkg/agent/application/paths/common.go b/internal/pkg/agent/application/paths/common.go index 871435e65a1..1f784e0a50c 100644 --- a/internal/pkg/agent/application/paths/common.go +++ b/internal/pkg/agent/application/paths/common.go @@ -119,10 +119,15 @@ func TempDir() string { // Home returns a directory where binary lives func Home() string { + return HomeFrom(topPath) +} + +func HomeFrom(topDirPath string) string { if unversionedHome { - return topPath + return topDirPath } - return VersionedHome(topPath) + + return VersionedHome(topDirPath) } // IsVersionHome returns true if the Home path is versioned based on build. diff --git a/internal/pkg/agent/cmd/diagnostics.go b/internal/pkg/agent/cmd/diagnostics.go index d0f469186ca..774f29fc74e 100644 --- a/internal/pkg/agent/cmd/diagnostics.go +++ b/internal/pkg/agent/cmd/diagnostics.go @@ -16,6 +16,7 @@ import ( "github.com/spf13/cobra" + "github.com/elastic/elastic-agent/internal/pkg/agent/application/paths" "github.com/elastic/elastic-agent/internal/pkg/cli" "github.com/elastic/elastic-agent/internal/pkg/diagnostics" ) @@ -68,7 +69,7 @@ func diagnosticCmd(streams *cli.IOStreams, cmd *cobra.Command) error { return fmt.Errorf("failed collecting diagnostics: %w", err) } - if err := diagnostics.ZipArchive(streams.Err, f, agentDiag, unitDiags, compDiags, excludeEvents); err != nil { + if err := diagnostics.ZipArchive(streams.Err, f, paths.Top(), agentDiag, unitDiags, compDiags, excludeEvents); err != nil { return fmt.Errorf("unable to create archive %q: %w", filepath, err) } fmt.Fprintf(streams.Out, "Created diagnostics archive %q\n", filepath) diff --git a/internal/pkg/diagnostics/diagnostics.go b/internal/pkg/diagnostics/diagnostics.go index 5a32bc83411..023cbc0e6ed 100644 --- a/internal/pkg/diagnostics/diagnostics.go +++ b/internal/pkg/diagnostics/diagnostics.go @@ -174,6 +174,7 @@ func CreateCPUProfile(ctx context.Context, period time.Duration) ([]byte, error) func ZipArchive( errOut, w io.Writer, + topPath string, agentDiag []client.DiagnosticFileResult, unitDiags []client.DiagnosticUnitResult, compDiags []client.DiagnosticComponentResult, @@ -298,7 +299,7 @@ func ZipArchive( } // Gather Logs: - return zipLogs(zw, ts, excludeEvents) + return zipLogs(zw, ts, topPath, excludeEvents) } func writeErrorResult(zw *zip.Writer, path string, errBody string) error { @@ -396,15 +397,17 @@ func redactKey(k string) bool { strings.Contains(k, "key") } -func zipLogs(zw *zip.Writer, ts time.Time, excludeEvents bool) error { - currentDir := filepath.Base(paths.Home()) +func zipLogs(zw *zip.Writer, ts time.Time, topPath string, excludeEvents bool) error { + homePath := paths.HomeFrom(topPath) + dataPath := paths.DataFrom(topPath) + currentDir := filepath.Base(homePath) if !paths.IsVersionHome() { // running in a container with custom top path set // logs are directly under top path - return zipLogsWithPath(paths.Home(), currentDir, true, excludeEvents, zw, ts) + return zipLogsWithPath(homePath, currentDir, true, excludeEvents, zw, ts) } - dataDir, err := os.Open(paths.Data()) + dataDir, err := os.Open(dataPath) if err != nil { return err } @@ -421,7 +424,7 @@ func zipLogs(zw *zip.Writer, ts time.Time, excludeEvents bool) error { continue } collectServices := dir == currentDir - path := filepath.Join(paths.Data(), dir) + path := filepath.Join(dataPath, dir) if err := zipLogsWithPath(path, dir, collectServices, excludeEvents, zw, ts); err != nil { return err } diff --git a/internal/pkg/diagnostics/diagnostics_test.go b/internal/pkg/diagnostics/diagnostics_test.go index ce67d239ffc..628353bed65 100644 --- a/internal/pkg/diagnostics/diagnostics_test.go +++ b/internal/pkg/diagnostics/diagnostics_test.go @@ -171,12 +171,12 @@ type zippedItem struct { } func TestZipLogs(t *testing.T) { - paths.SetTop(t.TempDir()) - dir := filepath.Join(paths.Home(), "logs", "sub-dir") + topPath := t.TempDir() + dir := filepath.Join(paths.HomeFrom(topPath), "logs", "sub-dir") require.NoError(t, os.MkdirAll(dir, 0o700)) require.NoError(t, os.WriteFile(filepath.Join(dir, "log.ndjson"), []byte(".\n"), 0o600)) - eventLogs := filepath.Join(paths.Home(), "logs", "events") + eventLogs := filepath.Join(paths.HomeFrom(topPath), "logs", "events") require.NoError(t, os.MkdirAll(eventLogs, 0o700)) require.NoError(t, os.WriteFile(filepath.Join(eventLogs, "elastic-agent-events-log.ndjson"), []byte(".\n"), 0o600)) @@ -212,18 +212,18 @@ func TestZipLogs(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - zipLogsAndAssertFiles(t, tc.excludeEventsLog, tc.expectedItems) + zipLogsAndAssertFiles(t, topPath, tc.excludeEventsLog, tc.expectedItems) }) } } -func zipLogsAndAssertFiles(t *testing.T, excludeEvents bool, expected []zippedItem) { +func zipLogsAndAssertFiles(t *testing.T, topPath string, excludeEvents bool, expected []zippedItem) { t.Helper() // Zip the logs directory. buf := new(bytes.Buffer) w := zip.NewWriter(buf) - require.NoError(t, zipLogs(w, time.Now(), excludeEvents)) + require.NoError(t, zipLogs(w, time.Now(), topPath, excludeEvents)) require.NoError(t, w.Close()) // Read back the contents. @@ -235,6 +235,17 @@ func zipLogsAndAssertFiles(t *testing.T, excludeEvents bool, expected []zippedIt } assert.Equal(t, expected, observed) + if t.Failed() { + t.Log("Expected") + for _, f := range expected { + t.Logf("name: %s, dir? %t", f.Name, f.IsDir) + } + + t.Log("Got") + for _, f := range observed { + t.Logf("name: %s, dir? %t", f.Name, f.IsDir) + } + } } func TestGlobalHooks(t *testing.T) {