diff --git a/internal/runtime/podman.go b/internal/runtime/podman.go index 8a759b4..33e741d 100644 --- a/internal/runtime/podman.go +++ b/internal/runtime/podman.go @@ -180,36 +180,33 @@ func (p *Podman) Inspect(ctx context.Context, name string) (ContainerInfo, error } // Logs returns an exec.Cmd that streams container logs. For containers -// using the journald log driver, it uses journalctl (podman logs can't -// read journald outside the originating user session). For k8s-file or -// other drivers, it uses podman logs directly. +// using the journald log driver, it tries journalctl first (podman logs +// can't read journald outside the originating user session). If journalctl +// can't access the journal, it falls back to podman logs. func (p *Podman) Logs(ctx context.Context, containerName string, tail int, follow, timestamps bool, since string) *exec.Cmd { // Check if this container uses the journald log driver. inspectCmd := exec.CommandContext(ctx, p.command(), "inspect", "--format", "{{.HostConfig.LogConfig.Type}}", containerName) //nolint:gosec if out, err := inspectCmd.Output(); err == nil && strings.TrimSpace(string(out)) == "journald" { - return p.journalLogs(ctx, containerName, tail, follow, since) + if p.journalAccessible(ctx, containerName) { + return p.journalLogs(ctx, containerName, tail, follow, since) + } } - args := []string{"logs"} - if tail > 0 { - args = append(args, "--tail", fmt.Sprintf("%d", tail)) + return p.podmanLogs(ctx, containerName, tail, follow, timestamps, since) +} + +// journalAccessible probes whether journalctl can read logs for the container. +func (p *Podman) journalAccessible(ctx context.Context, containerName string) bool { + args := []string{"--no-pager", "-n", "0"} + if os.Getuid() != 0 { + args = append(args, "--user") } - if follow { - args = append(args, "--follow") - } - if timestamps { - args = append(args, "--timestamps") - } - if since != "" { - args = append(args, "--since", since) - } - args = append(args, containerName) - return exec.CommandContext(ctx, p.command(), args...) //nolint:gosec // args built programmatically + args = append(args, "CONTAINER_NAME="+containerName) + cmd := exec.CommandContext(ctx, "journalctl", args...) //nolint:gosec + return cmd.Run() == nil } // journalLogs returns a journalctl command filtered by container name. -// For rootless podman, container logs go to the user journal, so we -// need --user to read them. func (p *Podman) journalLogs(ctx context.Context, containerName string, tail int, follow bool, since string) *exec.Cmd { args := []string{"--no-pager", "--output", "cat"} if os.Getuid() != 0 { @@ -228,6 +225,25 @@ func (p *Podman) journalLogs(ctx context.Context, containerName string, tail int return exec.CommandContext(ctx, "journalctl", args...) //nolint:gosec // args built programmatically } +// podmanLogs returns a podman logs command. +func (p *Podman) podmanLogs(ctx context.Context, containerName string, tail int, follow, timestamps bool, since string) *exec.Cmd { + args := []string{"logs"} + if tail > 0 { + args = append(args, "--tail", fmt.Sprintf("%d", tail)) + } + if follow { + args = append(args, "--follow") + } + if timestamps { + args = append(args, "--timestamps") + } + if since != "" { + args = append(args, "--since", since) + } + args = append(args, containerName) + return exec.CommandContext(ctx, p.command(), args...) //nolint:gosec // args built programmatically +} + // Login authenticates to a container registry using the given token as // the password. This enables non-interactive push with service account // tokens (MCR accepts MCIAS JWTs as passwords).