From 853ab47f348ff95ca9019ef1749b34e20838310d Mon Sep 17 00:00:00 2001 From: Dorian Villet Date: Wed, 29 Nov 2023 11:37:42 +0100 Subject: [PATCH] Add steps logs to shared output (saved in artifact). --- provisioner/internal/docker.go | 17 +++++++---------- provisioner/internal/workspacefs.go | 5 +++++ provisioner/local/fs.go | 11 +++++++++++ provisioner/openstack/fs.go | 14 ++++++++++++++ 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/provisioner/internal/docker.go b/provisioner/internal/docker.go index f40ea2a..c6be54a 100644 --- a/provisioner/internal/docker.go +++ b/provisioner/internal/docker.go @@ -6,7 +6,6 @@ import ( "fmt" "io" "log/slog" - "os" "sync" "time" @@ -17,7 +16,6 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" - "github.com/docker/docker/pkg/stdcopy" "github.com/gammadia/alfred/proto" "github.com/gammadia/alfred/scheduler" "github.com/samber/lo" @@ -309,17 +307,16 @@ func RunContainer( return fmt.Errorf("failed to start docker container for step %d: %w", stepIndex, err) } - out, err := docker.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Timestamps: true, Details: true}) - if err != nil { - return fmt.Errorf("failed to get docker container logs for step %d: %w", stepIndex, err) - } - defer out.Close() - - _, _ = stdcopy.StdCopy(os.Stdout, os.Stderr, out) - // Wait for the container to finish select { case status = <-wait: + tryTo( + "save container logs", + func() error { + return taskFs.SaveContainerLogs(resp.ID, fmt.Sprintf("/output/step-%d.log", stepIndex)) + }, + ) + // Container is done if status.StatusCode != 0 { return fmt.Errorf("step %d failed with status: %d", stepIndex, status.StatusCode) diff --git a/provisioner/internal/workspacefs.go b/provisioner/internal/workspacefs.go index 13d2c40..d447fb6 100644 --- a/provisioner/internal/workspacefs.go +++ b/provisioner/internal/workspacefs.go @@ -8,6 +8,7 @@ import ( type WorkspaceFS interface { HostPath(p string) string MkDir(p string) error + SaveContainerLogs(containerId, p string) error Archive(p string) (io.ReadCloser, error) Delete(p string) error Scope(p string) WorkspaceFS @@ -29,6 +30,10 @@ func (f *ScopedFS) MkDir(p string) error { return f.Parent.MkDir(path.Join(f.Prefix, p)) } +func (f *ScopedFS) SaveContainerLogs(containerId, p string) error { + return f.Parent.SaveContainerLogs(containerId, path.Join(f.Prefix, p)) +} + func (f *ScopedFS) Archive(p string) (io.ReadCloser, error) { return f.Parent.Archive(path.Join(f.Prefix, p)) } diff --git a/provisioner/local/fs.go b/provisioner/local/fs.go index c2e2170..8818c5a 100644 --- a/provisioner/local/fs.go +++ b/provisioner/local/fs.go @@ -29,6 +29,17 @@ func (f *fs) MkDir(p string) error { return os.MkdirAll(f.HostPath(p), 0777) } +func (f *fs) SaveContainerLogs(containerId, p string) error { + cmd := exec.Command("docker", "logs", "--timestamps", containerId) + if out, err := os.Create(f.HostPath(p)); err != nil { + return err + } else { + cmd.Stdout = out + cmd.Stderr = out + } + return cmd.Run() +} + func (f *fs) Archive(p string) (rc io.ReadCloser, err error) { cmd := exec.Command("tar", "-c", "-f", "-", "-z", "-C", f.root, strings.TrimLeft(p, "/")) if rc, err = cmd.StdoutPipe(); err != nil { diff --git a/provisioner/openstack/fs.go b/provisioner/openstack/fs.go index e8dac35..57492ad 100644 --- a/provisioner/openstack/fs.go +++ b/provisioner/openstack/fs.go @@ -51,6 +51,20 @@ func (f *fs) MkDir(p string) error { }) } +func (f *fs) SaveContainerLogs(containerId, p string) error { + session, err := f.ssh.NewSession() + if err != nil { + return fmt.Errorf("failed to create SSH session: %w", err) + } + defer session.Close() + + return session.Run(fmt.Sprintf( + "docker logs --timestamps %s 2>&1 > %s", + shellescape.Quote(containerId), + shellescape.Quote(f.HostPath(p)), + )) +} + // Archive returns a .tar.gz of the given path func (f *fs) Archive(p string) (io.ReadCloser, error) { session, err := f.ssh.NewSession()