Skip to content

Commit 5f11733

Browse files
fix fluentd filter / output config (#1783)
* fix fluentd filter / output config Signed-off-by: Kilian Ries <[email protected]> * add e2e test Signed-off-by: Kilian Ries <[email protected]> --------- Signed-off-by: Kilian Ries <[email protected]> Co-authored-by: Josh Baird <[email protected]>
1 parent ecd97cc commit 5f11733

File tree

2 files changed

+274
-2
lines changed

2 files changed

+274
-2
lines changed

controllers/fluentdconfig_controller.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ func (r *FluentdConfigReconciler) ListNamespacedLevelResources(
607607
return nil, nil, nil, err
608608
}
609609
matchingLabelSelector := client.MatchingLabelsSelector{Selector: selector}
610-
if err = r.List(ctx, &inputs, client.InNamespace(namespace), matchingLabelSelector); err != nil {
610+
if err = r.List(ctx, &filters, client.InNamespace(namespace), matchingLabelSelector); err != nil {
611611
return nil, nil, nil, err
612612
}
613613
}
@@ -620,7 +620,7 @@ func (r *FluentdConfigReconciler) ListNamespacedLevelResources(
620620
return nil, nil, nil, err
621621
}
622622
matchingLabelSelector := client.MatchingLabelsSelector{Selector: selector}
623-
if err = r.List(ctx, &inputs, client.InNamespace(namespace), matchingLabelSelector); err != nil {
623+
if err = r.List(ctx, &outputs, client.InNamespace(namespace), matchingLabelSelector); err != nil {
624624
return nil, nil, nil, err
625625
}
626626
}
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
package fluentd
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
"time"
8+
9+
. "github.com/onsi/ginkgo/v2"
10+
. "github.com/onsi/gomega"
11+
"k8s.io/apimachinery/pkg/types"
12+
"sigs.k8s.io/controller-runtime/pkg/client"
13+
"sigs.k8s.io/yaml"
14+
15+
fluentdv1alpha1 "github.com/fluent/fluent-operator/v3/apis/fluentd/v1alpha1"
16+
)
17+
18+
var (
19+
// Fluentd instance for label selector tests
20+
FluentdLabelSelectorRaw = `
21+
apiVersion: fluentd.fluent.io/v1alpha1
22+
kind: Fluentd
23+
metadata:
24+
name: fluentd-label-selector-test
25+
namespace: fluent
26+
labels:
27+
app.kubernetes.io/name: fluentd
28+
spec:
29+
globalInputs:
30+
- forward:
31+
bind: 0.0.0.0
32+
port: 24224
33+
replicas: 1
34+
image: ghcr.io/fluent/fluent-operator/fluentd:v1.19.1
35+
fluentdCfgSelector:
36+
matchLabels:
37+
config.fluentd.fluent.io/enabled: "true"
38+
`
39+
40+
// FluentdConfig with filterSelector and outputSelector
41+
FluentdConfigLabelSelectorRaw = `
42+
apiVersion: fluentd.fluent.io/v1alpha1
43+
kind: FluentdConfig
44+
metadata:
45+
name: fluentd-config-label-selector-test
46+
namespace: fluent
47+
labels:
48+
config.fluentd.fluent.io/enabled: "true"
49+
spec:
50+
filterSelector:
51+
matchLabels:
52+
filter.fluentd.fluent.io/enabled: "true"
53+
filter.fluentd.fluent.io/mode: "namespace"
54+
outputSelector:
55+
matchLabels:
56+
output.fluentd.fluent.io/enabled: "true"
57+
output.fluentd.fluent.io/mode: "namespace"
58+
`
59+
60+
// Filter with matching labels
61+
FilterLabelSelectorRaw = `
62+
apiVersion: fluentd.fluent.io/v1alpha1
63+
kind: Filter
64+
metadata:
65+
name: test-filter-recordtransformer
66+
namespace: fluent
67+
labels:
68+
filter.fluentd.fluent.io/enabled: "true"
69+
filter.fluentd.fluent.io/mode: "namespace"
70+
spec:
71+
filters:
72+
- recordTransformer:
73+
records:
74+
- key: hostname
75+
value: test-host
76+
- key: environment
77+
value: testing
78+
`
79+
80+
// Output with matching labels
81+
OutputLabelSelectorRaw = `
82+
apiVersion: fluentd.fluent.io/v1alpha1
83+
kind: Output
84+
metadata:
85+
name: test-output-stdout
86+
namespace: fluent
87+
labels:
88+
output.fluentd.fluent.io/enabled: "true"
89+
output.fluentd.fluent.io/mode: "namespace"
90+
spec:
91+
outputs:
92+
- stdout: {}
93+
`
94+
95+
// FluentdConfig with only filterSelector
96+
FluentdConfigFilterOnlyRaw = `
97+
apiVersion: fluentd.fluent.io/v1alpha1
98+
kind: FluentdConfig
99+
metadata:
100+
name: fluentd-config-filter-only
101+
namespace: fluent
102+
labels:
103+
config.fluentd.fluent.io/enabled: "true"
104+
spec:
105+
filterSelector:
106+
matchLabels:
107+
filter.fluentd.fluent.io/type: "grep"
108+
clusterOutputSelector:
109+
matchLabels:
110+
output.fluentd.fluent.io/enabled: "true"
111+
`
112+
113+
// Grep Filter
114+
FilterGrepRaw = `
115+
apiVersion: fluentd.fluent.io/v1alpha1
116+
kind: Filter
117+
metadata:
118+
name: test-filter-grep
119+
namespace: fluent
120+
labels:
121+
filter.fluentd.fluent.io/type: "grep"
122+
spec:
123+
filters:
124+
- grep:
125+
regexp:
126+
- key: level
127+
pattern: /error/
128+
`
129+
130+
// ClusterOutput for filter-only test
131+
ClusterOutputStdoutRaw = `
132+
apiVersion: fluentd.fluent.io/v1alpha1
133+
kind: ClusterOutput
134+
metadata:
135+
name: cluster-output-stdout-filter-test
136+
labels:
137+
output.fluentd.fluent.io/enabled: "true"
138+
spec:
139+
outputs:
140+
- stdout: {}
141+
`
142+
)
143+
144+
// This test verifies the fix for the bug where filterSelector and outputSelector
145+
// were incorrectly writing to the inputs list instead of their respective lists.
146+
var _ = Describe("Test FluentdConfig with namespace-level filter and output selectors", func() {
147+
148+
ctx := context.TODO()
149+
150+
BeforeEach(func() {
151+
time.Sleep(time.Second * 1)
152+
})
153+
154+
AfterEach(func() {
155+
time.Sleep(time.Second * 1)
156+
})
157+
158+
Describe("Test namespace-level resources with label selectors", func() {
159+
It("E2E_FLUENTD_NAMESPACE_FILTER_OUTPUT_SELECTORS: FluentdConfig with filterSelector and outputSelector", func() {
160+
161+
// Parse YAML into objects
162+
var fluentd fluentdv1alpha1.Fluentd
163+
err := yaml.Unmarshal([]byte(FluentdLabelSelectorRaw), &fluentd)
164+
Expect(err).NotTo(HaveOccurred())
165+
166+
var fluentdConfig fluentdv1alpha1.FluentdConfig
167+
err = yaml.Unmarshal([]byte(FluentdConfigLabelSelectorRaw), &fluentdConfig)
168+
Expect(err).NotTo(HaveOccurred())
169+
170+
var testFilter fluentdv1alpha1.Filter
171+
err = yaml.Unmarshal([]byte(FilterLabelSelectorRaw), &testFilter)
172+
Expect(err).NotTo(HaveOccurred())
173+
174+
var testOutput fluentdv1alpha1.Output
175+
err = yaml.Unmarshal([]byte(OutputLabelSelectorRaw), &testOutput)
176+
Expect(err).NotTo(HaveOccurred())
177+
178+
// Create all objects
179+
objects := []client.Object{
180+
&fluentd,
181+
&fluentdConfig,
182+
&testFilter,
183+
&testOutput,
184+
}
185+
186+
err = CreateObjs(ctx, objects)
187+
Expect(err).NotTo(HaveOccurred())
188+
189+
// Wait for reconciliation
190+
time.Sleep(time.Second * 3)
191+
192+
// Get the generated configuration
193+
seckey := types.NamespacedName{
194+
Namespace: fluentd.Namespace,
195+
Name: fmt.Sprintf("%s-config", fluentd.Name),
196+
}
197+
config, err := GetCfgFromSecret(ctx, seckey)
198+
Expect(err).NotTo(HaveOccurred())
199+
200+
// Verify that the filter configuration is present
201+
// Before the fix, the filter would not be loaded because it was written to the inputs list
202+
Expect(config).To(ContainSubstring("<filter"))
203+
Expect(config).To(ContainSubstring("@type record_transformer"))
204+
Expect(config).To(ContainSubstring("hostname"))
205+
Expect(config).To(ContainSubstring("test-host"))
206+
Expect(config).To(ContainSubstring("environment"))
207+
Expect(config).To(ContainSubstring("testing"))
208+
209+
// Verify that the output configuration is present
210+
// Before the fix, the output would not be loaded because it was written to the inputs list
211+
Expect(config).To(ContainSubstring("<match"))
212+
Expect(config).To(ContainSubstring("@type stdout"))
213+
214+
// Verify that filter and output are in the correct order (filter before output)
215+
filterIndex := strings.Index(config, "<filter")
216+
outputIndex := strings.Index(config, "<match")
217+
Expect(filterIndex).To(BeNumerically("<", outputIndex), "Filter should appear before output in configuration")
218+
219+
// Clean up
220+
err = DeleteObjs(ctx, objects)
221+
Expect(err).NotTo(HaveOccurred())
222+
})
223+
224+
It("E2E_FLUENTD_NAMESPACE_MIXED_SELECTORS: FluentdConfig with only filterSelector", func() {
225+
226+
// Parse YAML into objects
227+
var fluentd fluentdv1alpha1.Fluentd
228+
err := yaml.Unmarshal([]byte(FluentdLabelSelectorRaw), &fluentd)
229+
Expect(err).NotTo(HaveOccurred())
230+
231+
var fluentdConfig fluentdv1alpha1.FluentdConfig
232+
err = yaml.Unmarshal([]byte(FluentdConfigFilterOnlyRaw), &fluentdConfig)
233+
Expect(err).NotTo(HaveOccurred())
234+
235+
var grepFilter fluentdv1alpha1.Filter
236+
err = yaml.Unmarshal([]byte(FilterGrepRaw), &grepFilter)
237+
Expect(err).NotTo(HaveOccurred())
238+
239+
var clusterOutput fluentdv1alpha1.ClusterOutput
240+
err = yaml.Unmarshal([]byte(ClusterOutputStdoutRaw), &clusterOutput)
241+
Expect(err).NotTo(HaveOccurred())
242+
243+
objects := []client.Object{
244+
&fluentd,
245+
&fluentdConfig,
246+
&grepFilter,
247+
&clusterOutput,
248+
}
249+
250+
err = CreateObjs(ctx, objects)
251+
Expect(err).NotTo(HaveOccurred())
252+
253+
time.Sleep(time.Second * 3)
254+
255+
seckey := types.NamespacedName{
256+
Namespace: fluentd.Namespace,
257+
Name: fmt.Sprintf("%s-config", fluentd.Name),
258+
}
259+
config, err := GetCfgFromSecret(ctx, seckey)
260+
Expect(err).NotTo(HaveOccurred())
261+
262+
// Verify grep filter is present
263+
Expect(config).To(ContainSubstring("@type grep"))
264+
Expect(config).To(ContainSubstring("key level"))
265+
Expect(config).To(ContainSubstring("pattern /error/"))
266+
267+
// Clean up
268+
err = DeleteObjs(ctx, objects)
269+
Expect(err).NotTo(HaveOccurred())
270+
})
271+
})
272+
})

0 commit comments

Comments
 (0)