Skip to content

Commit 930cd25

Browse files
committed
Feat: Add log_path support in containers.conf
Added log_path variable in containers/common, User sets default log path in containers.conf under the `[containers]` section. The directory has to exist beforehand. Container logs go under this directory, sub-directories named with the container id and inside the sub-directory a ctr.log file will be created where the container logs for the corresponding container will go. This path can be overridden by using the `--log-opt` flag. Signed-off-by: Joshua Arrevillaga <[email protected]>
1 parent e14b8ac commit 930cd25

File tree

6 files changed

+190
-1
lines changed

6 files changed

+190
-1
lines changed

libpod/options.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"errors"
77
"fmt"
88
"net"
9+
"os"
10+
"path/filepath"
911
"strings"
1012
"syscall"
1113
"time"
@@ -1041,8 +1043,16 @@ func WithLogPath(path string) CtrCreateOption {
10411043
if path == "" {
10421044
return fmt.Errorf("log path must be set: %w", define.ErrInvalidArg)
10431045
}
1046+
if isDirectory(path) {
1047+
containerDir := filepath.Join(path, ctr.ID())
1048+
if err := os.Mkdir(containerDir, 0755); err != nil {
1049+
return fmt.Errorf("failed to create container log directory %s: %w", containerDir, err)
1050+
}
10441051

1045-
ctr.config.LogPath = path
1052+
ctr.config.LogPath = filepath.Join(containerDir, "ctr.log")
1053+
} else {
1054+
ctr.config.LogPath = path
1055+
}
10461056

10471057
return nil
10481058
}

libpod/util.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,3 +285,11 @@ func evalSymlinksIfExists(toCheck string) (string, error) {
285285
}
286286
return checkedVal, nil
287287
}
288+
289+
func isDirectory(path string) bool {
290+
info, err := os.Stat(path)
291+
if err != nil {
292+
return false
293+
}
294+
return info.IsDir()
295+
}

pkg/specgen/generate/kube/kube.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
228228

229229
s.ImageVolumes = opts.ImageVolumes
230230

231+
if rtc.Containers.LogPath != "" {
232+
s.LogConfiguration.Path = rtc.Containers.LogPath
233+
}
234+
231235
s.LogConfiguration.Options = make(map[string]string)
232236
for _, o := range opts.LogOptions {
233237
opt, val, hasVal := strings.Cut(o, "=")

pkg/specgenutil/specgen.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,10 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
844844
return err
845845
}
846846

847+
if rtc.Containers.LogPath != "" {
848+
s.LogConfiguration.Path = rtc.Containers.LogPath
849+
}
850+
847851
logOpts := make(map[string]string)
848852
for _, o := range c.LogOptions {
849853
key, val, hasVal := strings.Cut(o, "=")
@@ -865,6 +869,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
865869
logOpts[key] = val
866870
}
867871
}
872+
868873
if len(s.LogConfiguration.Options) == 0 || len(c.LogOptions) != 0 {
869874
s.LogConfiguration.Options = logOpts
870875
}

test/e2e/play_kube_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6310,4 +6310,85 @@ spec:
63106310
session.WaitWithDefaultTimeout()
63116311
Expect(session).Should(ExitWithError(125, "invalid signal: noSuchSignal"))
63126312
})
6313+
6314+
It("test with custom log path from containers.conf", func() {
6315+
customLogPath := filepath.Join(podmanTest.TempDir, "custom-logs")
6316+
expectedMessage := "Pod started, checking logs from test"
6317+
6318+
conffile := filepath.Join(podmanTest.TempDir, "containers.conf")
6319+
configContent := fmt.Sprintf(`[containers]
6320+
log_driver = "k8s-file"
6321+
log_path = "%s"
6322+
`, customLogPath)
6323+
6324+
err := os.WriteFile(conffile, []byte(configContent), 0644)
6325+
Expect(err).ToNot(HaveOccurred())
6326+
6327+
err = os.MkdirAll(customLogPath, 0755)
6328+
Expect(err).ToNot(HaveOccurred())
6329+
6330+
os.Setenv("CONTAINERS_CONF_OVERRIDE", conffile)
6331+
defer os.Unsetenv("CONTAINERS_CONF_OVERRIDE")
6332+
6333+
if IsRemote() {
6334+
podmanTest.RestartRemoteService()
6335+
}
6336+
6337+
kubeYaml := filepath.Join(podmanTest.TempDir, "test-pod.yaml")
6338+
podYamlContent := fmt.Sprintf(`apiVersion: v1
6339+
kind: Pod
6340+
metadata:
6341+
name: log-test-pod
6342+
spec:
6343+
restartPolicy: Never
6344+
containers:
6345+
- name: logger-container
6346+
image: %s
6347+
command: ["/bin/sh", "-c", "echo '%s'; sleep 2"]
6348+
`, CITEST_IMAGE, expectedMessage)
6349+
6350+
err = os.WriteFile(kubeYaml, []byte(podYamlContent), 0644)
6351+
Expect(err).ToNot(HaveOccurred())
6352+
6353+
podmanTest.PodmanExitCleanly("kube", "play", kubeYaml)
6354+
6355+
podmanTest.PodmanExitCleanly("wait", "log-test-pod-logger-container")
6356+
6357+
customLogDirs, err := os.ReadDir(customLogPath)
6358+
Expect(err).ToNot(HaveOccurred())
6359+
Expect(customLogDirs).To(HaveLen(2), "Should have exactly two container log directories (infra + app container)")
6360+
6361+
var appContainerLogDir string
6362+
var logContent string
6363+
found := false
6364+
6365+
for _, dir := range customLogDirs {
6366+
if !dir.IsDir() {
6367+
continue
6368+
}
6369+
containerLogDir := dir.Name()
6370+
logFilePath := filepath.Join(customLogPath, containerLogDir, "ctr.log")
6371+
6372+
if _, err := os.Stat(logFilePath); err != nil {
6373+
continue
6374+
}
6375+
6376+
content, err := os.ReadFile(logFilePath)
6377+
if err != nil {
6378+
continue
6379+
}
6380+
6381+
if strings.Contains(string(content), expectedMessage) {
6382+
appContainerLogDir = containerLogDir
6383+
logContent = string(content)
6384+
found = true
6385+
break
6386+
}
6387+
}
6388+
6389+
Expect(found).To(BeTrue(), "Should find log file with expected message")
6390+
6391+
Expect(appContainerLogDir).ToNot(BeEmpty(), "Should have found application container log directory")
6392+
Expect(logContent).To(ContainSubstring(expectedMessage), "Log file should contain the expected message")
6393+
})
63136394
})

test/e2e/run_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"net"
99
"os"
1010
"path/filepath"
11+
"regexp"
1112
"strconv"
1213
"strings"
1314
"syscall"
@@ -2456,4 +2457,84 @@ WORKDIR /madethis`, BB)
24562457
Expect(inspectData[0].Config.Annotations).To(Not(HaveKey(annoName)))
24572458
Expect(inspectData[0].Config.Annotations).To(Not(HaveKey("testlabel")))
24582459
})
2460+
2461+
It("podman run log-opt overrides containers.conf path", func() {
2462+
expectedMessage := "CLI override test message"
2463+
confLogPath := filepath.Join(podmanTest.TempDir, "conf-logs")
2464+
2465+
conffile := filepath.Join(podmanTest.TempDir, "containers.conf")
2466+
configContent := fmt.Sprintf(`[containers]
2467+
log_driver = "k8s-file"
2468+
log_path = "%s"
2469+
`, confLogPath)
2470+
2471+
err := os.WriteFile(conffile, []byte(configContent), 0644)
2472+
Expect(err).ToNot(HaveOccurred())
2473+
2474+
err = os.MkdirAll(confLogPath, 0755)
2475+
Expect(err).ToNot(HaveOccurred())
2476+
2477+
os.Setenv("CONTAINERS_CONF_OVERRIDE", conffile)
2478+
defer os.Unsetenv("CONTAINERS_CONF_OVERRIDE")
2479+
2480+
if IsRemote() {
2481+
podmanTest.RestartRemoteService()
2482+
}
2483+
2484+
cliLogPath := filepath.Join(podmanTest.TempDir, "cli-override.log")
2485+
podmanTest.PodmanExitCleanly("run", "--rm", "--log-driver", "k8s-file", "--log-opt", fmt.Sprintf("path=%s", cliLogPath), ALPINE, "echo", expectedMessage)
2486+
2487+
confLogDirs, err := os.ReadDir(confLogPath)
2488+
Expect(err).ToNot(HaveOccurred(), "Should be able to read config log directory that we created")
2489+
Expect(confLogDirs).To(BeEmpty(), "Config file log path should not be used when CLI overrides")
2490+
2491+
content, err := os.ReadFile(cliLogPath)
2492+
Expect(err).ToNot(HaveOccurred())
2493+
Expect(string(content)).To(ContainSubstring(expectedMessage))
2494+
Expect(string(content)).To(MatchRegexp(`\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.*stdout F ` + regexp.QuoteMeta(expectedMessage)))
2495+
2496+
_ = os.Remove(cliLogPath)
2497+
})
2498+
2499+
It("podman run uses containers.conf log_path", func() {
2500+
expectedMessage := "Config file path test message"
2501+
confLogPath := filepath.Join(podmanTest.TempDir, "conf-logs")
2502+
2503+
conffile := filepath.Join(podmanTest.TempDir, "containers.conf")
2504+
configContent := fmt.Sprintf(`[containers]
2505+
log_driver = "k8s-file"
2506+
log_path = "%s"
2507+
`, confLogPath)
2508+
2509+
err := os.WriteFile(conffile, []byte(configContent), 0644)
2510+
Expect(err).ToNot(HaveOccurred())
2511+
2512+
err = os.MkdirAll(confLogPath, 0755)
2513+
Expect(err).ToNot(HaveOccurred())
2514+
2515+
os.Setenv("CONTAINERS_CONF_OVERRIDE", conffile)
2516+
defer os.Unsetenv("CONTAINERS_CONF_OVERRIDE")
2517+
2518+
if IsRemote() {
2519+
podmanTest.RestartRemoteService()
2520+
}
2521+
2522+
containerName := "test-conf-log-container"
2523+
2524+
podmanTest.PodmanExitCleanly("run", "--name", containerName, ALPINE, "echo", expectedMessage)
2525+
session := podmanTest.PodmanExitCleanly("inspect", "--format", "{{.Id}}", containerName)
2526+
2527+
containerID := strings.TrimSpace(session.OutputToString())
2528+
logFilePath := filepath.Join(confLogPath, containerID, "ctr.log")
2529+
2530+
inspectSession := podmanTest.PodmanExitCleanly("inspect", "--format", "{{.HostConfig.LogConfig.Path}}", containerName)
2531+
inspectedPath := strings.TrimSpace(inspectSession.OutputToString())
2532+
Expect(inspectedPath).To(Equal(logFilePath), "Log path in inspect data should match the path from containers.conf")
2533+
2534+
content, err := os.ReadFile(logFilePath)
2535+
Expect(err).ToNot(HaveOccurred())
2536+
2537+
Expect(string(content)).To(ContainSubstring(expectedMessage), "Log should contain expected message")
2538+
Expect(string(content)).To(MatchRegexp(`\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.*stdout F `+regexp.QuoteMeta(expectedMessage)), "Log should follow k8s-file format")
2539+
})
24592540
})

0 commit comments

Comments
 (0)