1package deployment
2
3import (
4 "bufio"
5 "context"
6 "io"
7 "strings"
8
9 "github.com/bnema/gordon/internal/domain"
10)
11
12// logTail returns the bounded recent log tail of the failing container.
13// The read is bounded by the caller's deadline and by an internal cap so
14// a slow or hung log stream cannot stall the operation.
15func (s *Service) logTail(ctx context.Context, containerID string) []string {
16 if containerID == "" || ctx.Err() != nil {
17 return nil
18 }
19 tailCtx, cancel := context.WithTimeout(ctx, failLogTailTimeout)
20 defer cancel()
21 stream, err := s.deps.Runtime.GetContainerLogs(tailCtx, containerID, false)
22 if err != nil {
23 return nil
24 }
25 defer func() { _ = stream.Close() }()
26 lines, err := tailLines(stream, failLogTailLines)
27 if err != nil {
28 return nil
29 }
30 return lines
31}
32
33// redactDiagnostics replaces every value of this service's secrets in the
34// given log lines. Secret values are read into memory only. When a secret
35// cannot be read, diagnostics are dropped entirely: unredacted application
36// output is never persisted.
37func (s *Service) redactDiagnostics(ctx context.Context, app string, p pinnedService, lines []string) []string {
38 if len(lines) == 0 {
39 return nil
40 }
41 if len(p.spec.Secrets) == 0 || s.deps.Secrets == nil {
42 return lines
43 }
44 id, err := s.appSecretID(ctx, app)
45 if err != nil {
46 return nil
47 }
48 redacted := append([]string(nil), lines...)
49 for _, name := range p.spec.Secrets {
50 path := domain.AppSecretPathForID(id, app, p.spec.Name, name)
51 value, err := s.deps.Secrets.GetSecret(ctx, path)
52 if err != nil || value == "" {
53 return nil
54 }
55 for i, line := range redacted {
56 redacted[i] = strings.ReplaceAll(line, value, "[redacted]")
57 }
58 }
59 return redacted
60}
61
62// tailLines keeps the last N lines; logs are untrusted app output.
63func tailLines(r io.Reader, n int) ([]string, error) {
64 scanner := bufio.NewScanner(r)
65 scanner.Buffer(make([]byte, 64*1024), 1024*1024)
66 var lines []string
67 for scanner.Scan() {
68 lines = append(lines, scanner.Text())
69 if len(lines) > n {
70 lines = lines[len(lines)-n:]
71 }
72 }
73 if err := scanner.Err(); err != nil {
74 return nil, err
75 }
76 return lines, nil
77}