Skip to content

Commit f2e504a

Browse files
committed
Support CONTAINER_LOG_PATH.
Signed-off-by: Josh Baird <[email protected]>
1 parent 52cf079 commit f2e504a

File tree

2 files changed

+195
-1
lines changed

2 files changed

+195
-1
lines changed

cmd/fluent-manager/main.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,10 +230,20 @@ func main() {
230230
}
231231
}
232232

233-
if envs, err := godotenv.Read("/fluent-operator/fluent-bit.env"); err == nil {
233+
// CONTAINER_ROOT_DIR is the location that Fluent Bit looks for container logs on nodes
234+
// Falls back to legacy file-based configuration for backward compatibility
235+
if envLogPath := os.Getenv("CONTAINER_LOG_PATH"); envLogPath != "" {
236+
logPath = envLogPath
237+
} else if envs, err := godotenv.Read("/fluent-operator/fluent-bit.env"); err == nil {
234238
logPath = envs["CONTAINER_ROOT_DIR"] + "/containers"
235239
}
236240

241+
// If neither the env var or file based config is available, fall back to the
242+
// default path for containerd/CRI-O
243+
if logPath == "" {
244+
logPath = "/var/log/containers"
245+
}
246+
237247
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrlOpts)
238248
if err != nil {
239249
setupLog.Error(err, "unable to start manager")

cmd/fluent-manager/main_test.go

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
8+
"github.com/joho/godotenv"
9+
)
10+
11+
// TestContainerLogPathResolution tests the container log path resolution logic
12+
// that will be added to main.go
13+
func TestContainerLogPathResolution(t *testing.T) {
14+
tests := []struct {
15+
name string
16+
envVar string
17+
fileContent string
18+
expectedPath string
19+
setupFile bool
20+
}{
21+
{
22+
name: "prefer environment variable over file",
23+
envVar: "/custom/containers",
24+
fileContent: "CONTAINER_ROOT_DIR=/var/log",
25+
expectedPath: "/custom/containers",
26+
setupFile: true,
27+
},
28+
{
29+
name: "fallback to file when env var not set",
30+
envVar: "",
31+
fileContent: "CONTAINER_ROOT_DIR=/var/log",
32+
expectedPath: "/var/log/containers",
33+
setupFile: true,
34+
},
35+
{
36+
name: "fallback to file for docker path",
37+
envVar: "",
38+
fileContent: "CONTAINER_ROOT_DIR=/var/lib/docker",
39+
expectedPath: "/var/lib/docker/containers",
40+
setupFile: true,
41+
},
42+
{
43+
name: "use default when neither env var nor file present",
44+
envVar: "",
45+
fileContent: "",
46+
expectedPath: "/var/log/containers",
47+
setupFile: false,
48+
},
49+
{
50+
name: "env var takes precedence even with docker in file",
51+
envVar: "/override/containers",
52+
fileContent: "CONTAINER_ROOT_DIR=/var/lib/docker",
53+
expectedPath: "/override/containers",
54+
setupFile: true,
55+
},
56+
{
57+
name: "handle empty env var (should fallback)",
58+
envVar: "",
59+
fileContent: "CONTAINER_ROOT_DIR=/var/log",
60+
expectedPath: "/var/log/containers",
61+
setupFile: true,
62+
},
63+
}
64+
65+
for _, tt := range tests {
66+
t.Run(tt.name, func(t *testing.T) {
67+
// Create temporary directory for test file
68+
tmpDir := t.TempDir()
69+
testFile := filepath.Join(tmpDir, "fluent-bit.env")
70+
71+
// Setup environment variable
72+
if tt.envVar != "" {
73+
os.Setenv("CONTAINER_LOG_PATH", tt.envVar)
74+
defer os.Unsetenv("CONTAINER_LOG_PATH")
75+
} else {
76+
os.Unsetenv("CONTAINER_LOG_PATH")
77+
}
78+
79+
// Setup file if needed
80+
if tt.setupFile && tt.fileContent != "" {
81+
err := os.WriteFile(testFile, []byte(tt.fileContent), 0644)
82+
if err != nil {
83+
t.Fatalf("failed to create test file: %v", err)
84+
}
85+
}
86+
87+
// Execute the resolution logic (mimics what will be in main.go)
88+
var logPath string
89+
90+
// Prefer environment variable for container log path (modern approach)
91+
// Fall back to file-based configuration for backward compatibility
92+
if envLogPath := os.Getenv("CONTAINER_LOG_PATH"); envLogPath != "" {
93+
logPath = envLogPath
94+
} else if tt.setupFile {
95+
if envs, err := godotenv.Read(testFile); err == nil {
96+
logPath = envs["CONTAINER_ROOT_DIR"] + "/containers"
97+
}
98+
}
99+
100+
// Final fallback to safe default for containerd/CRI-O
101+
if logPath == "" {
102+
logPath = "/var/log/containers"
103+
}
104+
105+
// Verify result
106+
if logPath != tt.expectedPath {
107+
t.Errorf("expected logPath to be %q, got %q", tt.expectedPath, logPath)
108+
}
109+
})
110+
}
111+
}
112+
113+
// TestContainerLogPathEnvVarPrecedence specifically tests that env var always wins
114+
func TestContainerLogPathEnvVarPrecedence(t *testing.T) {
115+
tmpDir := t.TempDir()
116+
testFile := filepath.Join(tmpDir, "fluent-bit.env")
117+
118+
// Create file with one path
119+
err := os.WriteFile(testFile, []byte("CONTAINER_ROOT_DIR=/var/lib/docker"), 0644)
120+
if err != nil {
121+
t.Fatalf("failed to create test file: %v", err)
122+
}
123+
124+
// Set env var with different path
125+
envPath := "/env/var/wins"
126+
os.Setenv("CONTAINER_LOG_PATH", envPath)
127+
defer os.Unsetenv("CONTAINER_LOG_PATH")
128+
129+
// Execute resolution logic
130+
var logPath string
131+
if envLogPath := os.Getenv("CONTAINER_LOG_PATH"); envLogPath != "" {
132+
logPath = envLogPath
133+
} else if envs, err := godotenv.Read(testFile); err == nil {
134+
logPath = envs["CONTAINER_ROOT_DIR"] + "/containers"
135+
}
136+
if logPath == "" {
137+
logPath = "/var/log/containers"
138+
}
139+
140+
// Env var should win
141+
if logPath != envPath {
142+
t.Errorf("environment variable should take precedence: expected %q, got %q", envPath, logPath)
143+
}
144+
}
145+
146+
// TestContainerLogPathFilePathsAppendContainers verifies /containers is appended
147+
func TestContainerLogPathFilePathsAppendContainers(t *testing.T) {
148+
tmpDir := t.TempDir()
149+
testFile := filepath.Join(tmpDir, "fluent-bit.env")
150+
151+
// Unset env var
152+
os.Unsetenv("CONTAINER_LOG_PATH")
153+
154+
tests := []struct {
155+
rootDir string
156+
expectedPath string
157+
}{
158+
{"/var/log", "/var/log/containers"},
159+
{"/var/lib/docker", "/var/lib/docker/containers"},
160+
{"/custom/path", "/custom/path/containers"},
161+
}
162+
163+
for _, tt := range tests {
164+
t.Run(tt.rootDir, func(t *testing.T) {
165+
// Write test file
166+
content := "CONTAINER_ROOT_DIR=" + tt.rootDir
167+
err := os.WriteFile(testFile, []byte(content), 0644)
168+
if err != nil {
169+
t.Fatalf("failed to create test file: %v", err)
170+
}
171+
172+
// Execute resolution logic
173+
var logPath string
174+
if envs, err := godotenv.Read(testFile); err == nil {
175+
logPath = envs["CONTAINER_ROOT_DIR"] + "/containers"
176+
}
177+
178+
if logPath != tt.expectedPath {
179+
t.Errorf("expected %q, got %q", tt.expectedPath, logPath)
180+
}
181+
})
182+
}
183+
}
184+

0 commit comments

Comments
 (0)