Skip to content

Commit 85e3ace

Browse files
authored
Add a function for streaming Docker logs from containers and a predefined filter to exited/dead CTF containers (#2197)
1 parent 03b5f48 commit 85e3ace

File tree

2 files changed

+58
-20
lines changed

2 files changed

+58
-20
lines changed

framework/.changeset/v0.11.1.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Add a function for streaming Docker logs from containers and a predefined filter to exited/dead CTF containers

framework/docker.go

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -279,34 +279,23 @@ func SaveContainerLogs(dir string) ([]string, error) {
279279
return nil, fmt.Errorf("failed to create directory %s: %w", dir, err)
280280
}
281281
}
282-
provider, err := tc.NewDockerProvider()
283-
if err != nil {
284-
return nil, fmt.Errorf("failed to create Docker provider: %w", err)
285-
}
286-
containers, err := provider.Client().ContainerList(context.Background(), container.ListOptions{
282+
283+
logStream, lErr := StreamContainerLogs(container.ListOptions{
287284
All: true,
288285
Filters: dfilter.NewArgs(dfilter.KeyValuePair{
289286
Key: "label",
290287
Value: "framework=ctf",
291288
}),
292-
})
293-
if err != nil {
294-
return nil, fmt.Errorf("failed to list Docker containers: %w", err)
289+
}, container.LogsOptions{ShowStdout: true, ShowStderr: true})
290+
291+
if lErr != nil {
292+
return nil, lErr
295293
}
296294

297295
eg := &errgroup.Group{}
298296
logFilePaths := make([]string, 0)
299-
300-
for _, containerInfo := range containers {
297+
for containerName, reader := range logStream {
301298
eg.Go(func() error {
302-
containerName := containerInfo.Names[0]
303-
L.Debug().Str("Container", containerName).Msg("Collecting logs")
304-
logOptions := container.LogsOptions{ShowStdout: true, ShowStderr: true}
305-
logs, err := provider.Client().ContainerLogs(context.Background(), containerInfo.ID, logOptions)
306-
if err != nil {
307-
L.Error().Err(err).Str("Container", containerName).Msg("failed to fetch logs for container")
308-
return err
309-
}
310299
logFilePath := filepath.Join(dir, fmt.Sprintf("%s.log", containerName))
311300
logFile, err := os.Create(logFilePath)
312301
if err != nil {
@@ -317,7 +306,7 @@ func SaveContainerLogs(dir string) ([]string, error) {
317306
// Parse and write logs
318307
header := make([]byte, 8) // Docker stream header is 8 bytes
319308
for {
320-
_, err := io.ReadFull(logs, header)
309+
_, err := io.ReadFull(reader, header)
321310
if err == io.EOF {
322311
break
323312
}
@@ -331,7 +320,7 @@ func SaveContainerLogs(dir string) ([]string, error) {
331320

332321
// Read the log message
333322
msg := make([]byte, msgSize)
334-
_, err = io.ReadFull(logs, msg)
323+
_, err = io.ReadFull(reader, msg)
335324
if err != nil {
336325
L.Error().Err(err).Str("Container", containerName).Msg("failed to read log message")
337326
break
@@ -352,6 +341,54 @@ func SaveContainerLogs(dir string) ([]string, error) {
352341
return logFilePaths, nil
353342
}
354343

344+
var ExitedCtfContainersListOpts = container.ListOptions{
345+
All: true,
346+
Filters: dfilter.NewArgs(dfilter.KeyValuePair{
347+
Key: "label",
348+
Value: "framework=ctf",
349+
},
350+
dfilter.KeyValuePair{
351+
Key: "status",
352+
Value: "exited"},
353+
dfilter.KeyValuePair{
354+
Key: "status",
355+
Value: "dead"}),
356+
}
357+
358+
func StreamContainerLogs(listOptions container.ListOptions, logOptions container.LogsOptions) (map[string]io.ReadCloser, error) {
359+
L.Info().Msg("Streaming Docker containers logs")
360+
provider, err := tc.NewDockerProvider()
361+
if err != nil {
362+
return nil, fmt.Errorf("failed to create Docker provider: %w", err)
363+
}
364+
containers, err := provider.Client().ContainerList(context.Background(), listOptions)
365+
if err != nil {
366+
return nil, fmt.Errorf("failed to list Docker containers: %w", err)
367+
}
368+
369+
eg := &errgroup.Group{}
370+
logMap := make(map[string]io.ReadCloser)
371+
372+
for _, containerInfo := range containers {
373+
eg.Go(func() error {
374+
containerName := containerInfo.Names[0]
375+
L.Debug().Str("Container", containerName).Msg("Collecting logs")
376+
logs, err := provider.Client().ContainerLogs(context.Background(), containerInfo.ID, logOptions)
377+
if err != nil {
378+
L.Error().Err(err).Str("Container", containerName).Msg("failed to fetch logs for container")
379+
return err
380+
}
381+
logMap[containerName] = logs
382+
return nil
383+
})
384+
}
385+
if err := eg.Wait(); err != nil {
386+
return nil, err
387+
}
388+
389+
return logMap, nil
390+
}
391+
355392
func BuildImageOnce(once *sync.Once, dctx, dfile, nameAndTag string, buildArgs map[string]string) error {
356393
var err error
357394
once.Do(func() {

0 commit comments

Comments
 (0)