From 3b0e5c790ff51756c1317f4af83a2f0b0e66efd9 Mon Sep 17 00:00:00 2001 From: JP Appel Date: Tue, 8 Oct 2024 13:11:40 -0400 Subject: Refactor ansi stripper to be synchronous --- api/docker.go | 67 +++++++++++++++++++++++++++--------------------------- api/docker_test.go | 4 ++-- 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/api/docker.go b/api/docker.go index 0968a11..c79b76d 100644 --- a/api/docker.go +++ b/api/docker.go @@ -1,7 +1,6 @@ package api import ( - "bufio" "context" "fmt" "io" @@ -17,37 +16,34 @@ const vttContainerId string = "foundry-foundry-1" // Strip ansi control characters from source // -// The source reader is automatically closed when stripped reader is -func StripAnsi(source io.ReadCloser) io.ReadCloser { - - regex := regexp.MustCompile(`\x1b[[0-9;]*m`) - reader, writer := io.Pipe() - - go func() { - bufReader := bufio.NewReader(source) - defer source.Close() - defer writer.Close() - - cleanSource: - for { - line, err := bufReader.ReadBytes('\n') - switch { - case err == io.EOF: - if len(line) > 0 { - cleaned := regex.ReplaceAll(line, []byte("")) - writer.Write(cleaned) - } - break cleanSource - case err != nil: - panic(err) - default: - cleaned := regex.ReplaceAll(line, []byte("")) - writer.Write(cleaned) - } - } - }() - - return reader +// The source reader is automatically closed when stripper reader is +type ansiStripper struct { + regex regexp.Regexp + source io.ReadCloser +} + +func NewAnsiStripper(source io.ReadCloser) *ansiStripper { + striper := new(ansiStripper) + striper.regex = *regexp.MustCompile(`\x1b[[0-9;]*m`) + striper.source = source + + return striper +} + +func (rc ansiStripper) Read(p []byte) (int, error) { + n, err := rc.source.Read(p) + if err != io.EOF && err != nil { + return n, err + } + + stripped := rc.regex.ReplaceAll(p[:n], []byte("")) + n = copy(p, stripped) + + return n, err +} + +func (rc ansiStripper) Close() error { + return rc.source.Close() } func stopVtt(ctx context.Context) error { @@ -56,6 +52,7 @@ func stopVtt(ctx context.Context) error { Logger.ErrorContext(ctx, "Failed to get docker api client", slog.Any("err", err)) return err } + defer apiClient.Close() Logger.InfoContext(ctx, "Stopping foundry container") err = apiClient.ContainerStop(ctx, vttContainerId, container.StopOptions{}) @@ -75,10 +72,11 @@ func vttLogs(ctx context.Context, lines uint) (io.ReadCloser, error) { Logger.ErrorContext(ctx, "Failed to get docker api client", slog.Any("err", err)) return nil, err } + defer apiClient.Close() opts := container.LogsOptions{ ShowStdout: true, - ShowStderr: true, + // ShowStderr: true, } if lines != 0 { opts.Tail = fmt.Sprint(lines) @@ -91,7 +89,7 @@ func vttLogs(ctx context.Context, lines uint) (io.ReadCloser, error) { return nil, err } - return StripAnsi(r), nil + return r, nil } func vttStatus(ctx context.Context) ServerStatus { @@ -104,6 +102,7 @@ func vttStatus(ctx context.Context) ServerStatus { Logger.ErrorContext(ctx, "Failed to get docker api client", slog.Any("err", err)) return status } + defer apiClient.Close() json, err := apiClient.ContainerInspect(ctx, vttContainerId) if err != nil { diff --git a/api/docker_test.go b/api/docker_test.go index acd92bc..7d85803 100644 --- a/api/docker_test.go +++ b/api/docker_test.go @@ -24,7 +24,7 @@ func (stringReadCloser) Close() error { func testAnsiStripper(t *testing.T, input string, expected string) { reader := newStringReadCloser(input) - cleanReader := api.StripAnsi(reader) + cleanReader := api.NewAnsiStripper(reader) defer cleanReader.Close() buf := new(strings.Builder) @@ -46,5 +46,5 @@ func testAnsiStripper(t *testing.T, input string, expected string) { func TestStripAnsiColors(t *testing.T) { testAnsiStripper(t, "a\x1b[31mbc", "abc") - testAnsiStripper(t, "[\x1b[32minfo\x1b[39m]", "[info]") + testAnsiStripper(t, "[\x1b[32minfo\x1b[39m]", "[info]") } -- cgit v1.2.3