fix(fluentd): propagate sidecarContainers to the configcheck pod - #2303
Conversation
0e2ea84 to
005f85c
Compare
There was a problem hiding this comment.
🟢 Ready to approve
The change is small, mirrors the established StatefulSet behavior, and includes a focused test that validates the reported bug fix.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
This PR fixes Fluentd config validation behavior in the logging-operator by ensuring FluentdSpec.SidecarContainers are propagated not only to the Fluentd StatefulSet pods, but also to the transient fluentd-configcheck-* pod that performs pre-rollout config dry-runs—addressing the gap reported in #2103.
Changes:
- Append
fluentdSpec.SidecarContainersto the configcheck pod’s container list (while keeping the main Fluentd container atContainers[0]). - Add a unit test to assert the configcheck pod includes configured sidecars.
File summaries
| File | Description |
|---|---|
| pkg/resources/fluentd/appconfigmap.go | Updates configcheck pod container construction to include SidecarContainers. |
| pkg/resources/fluentd/appconfigmap_test.go | Adds coverage to ensure sidecars are present on the configcheck pod. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
csatib02
left a comment
There was a problem hiding this comment.
Thanks for picking this up, and for the thorough writeup + the repro-first test — that part is exactly right, and the placement matches what @aslafy-z pointed at in #2103.
That said, I don't think we can merge it as-is: appending these to Containers of a run-to-completion pod changes the pod's termination semantics, and in the common case it deadlocks the configcheck — including for the exact manifest in the issue report. Details inline, but summarised:
- Pod never reaches
Succeededwith a long-running sidecar, so the configcheck never becomes ready and the Fluentd StatefulSet stops being reconciled entirely. (inline onappconfigmap.go) - Volume sets diverge between the check pod and the StatefulSet, so a sidecar mounting e.g. the buffer volume makes the check pod un-creatable. (inline on
appconfigmap.go) - The test comment claims an ordering guarantee that plain containers don't give. (inline on
appconfigmap_test.go)
What already looks good
- Location mirrors
statefulset.go:108and is where the issue asked for it. Containers[0]indexing (TLS volume mount innewCheckPod) stays correct since sidecars are appended after — good that you called this out explicitly.- No API change, so no CRD/docs regen needed. Test compiles and passes.
Possible directions
- Native sidecars: inject into
InitContainerswithRestartPolicy: Always. kubelet terminates those once the app containers exit, soSucceededis reachable and the ordering the test comment describes becomes real. Caveat: needs k8s 1.29+, while the chart declareskubeVersion: ">=1.22.0-0"— so it needs a gate or a floor bump, worth a maintainer call. - Opt-in field (e.g.
configCheck.sidecarContainers, or a boolean on the existingconfigCheckblock) documented as "containers must terminate". Less elegant, but no version constraints. - Independently of this PR: the check pod has no
ActiveDeadlineSecondsat all, andPodCleanupdeliberately skips pods matching the current hash — so any wedged check pod is unrecoverable without manual deletion. Adding a deadline as a general safety net seems worthwhile on its own (separate PR). - A unit test can't catch pod-phase semantics; if we go ahead with any of the above, an e2e with a non-terminating sidecar would be the thing that actually pins the behaviour.
On the shared base
Not a blocker for this PR, but I want to reinforce @aslafy-z's point: the divergence in (2) is precisely the class of bug that keeps recurring because newCheckPod/volumesCheckPod/containerCheckPod and statefulset.go are maintained by hand in parallel — this same PR is the third such drift fix in that file recently (DNS settings, extraVolumes, now sidecars). I do consider a shared pod-template base a must at this point, but it should be its own PR rather than scope creep here.
|
Thanks for the detailed review — you're right, and I hadn't considered either failure mode. The deadlock (long-running sidecar → check pod never reaches Given you've flagged the native-sidecar route as needing a maintainer call on the k8s 1.29+ floor bump (vs. an opt-in |
|
Thanks for asking rather than guessing — and sorry for the long answer, but you asked exactly the right question. Why auto-propagation is off the table, in any shapeI said the
And the native-sidecar variant I floated doesn't rescue it:
What we should do insteadA third option that was sitting in the repo the whole time: an explicit, opt-in override on the check pod. syslog-ng already has exactly this — So: add // ConfigCheckPod lets you add helper containers and volumes to the transient
// configcheck pod. Long-running helpers must be declared as native sidecars
// (initContainers with restartPolicy: Always, k8s 1.29+), otherwise the pod
// never completes and config rollout stops.
ConfigCheckPod *ConfigCheckPodOverrides `json:"configCheckPod,omitempty"`
type ConfigCheckPodOverrides struct {
InitContainers []corev1.Container `json:"initContainers,omitempty"`
Volumes []corev1.Volume `json:"volumes,omitempty"`
ActiveDeadlineSeconds *int64 `json:"activeDeadlineSeconds,omitempty"`
}Why narrow rather than mirroring syslog-ng field-for-field:
This also answers your k8s-version question: no floor bump, no gate. Native sidecars become the documented shape inside the field rather than something we impose — the user picks the primitive their cluster supports. Plain init container for a run-to-completion prep step (works on any version, and this is what a GeoIP refresh actually wants — note that a native sidecar without a Scope for this PR
Keep your test — rename and re-point it, and drop the ordering claim in the comment. Two things I want to correct from my own review
Separately, if you want a small self-contained bug to pick up: |
…ing sidecars Revert the SidecarContainers-append to the configcheck pod's Containers list: it's a run-to-completion pod gated on PodSucceeded, so a long-running sidecar (e.g. the sleep-infinity example in our own docs) wedges it forever and blocks Fluentd StatefulSet reconciliation, and its volume set diverges from the StatefulSet's so a volume-mounting sidecar makes the pod uncreatable. Instead, add FluentdSpec.ConfigCheckPod, a narrow opt-in override (initContainers, volumes, activeDeadlineSeconds) merged onto the generated check pod last, mirroring the pattern already used by SyslogNGSpec.ConfigCheckPodOverrides. RestartPolicy is reasserted to Never after the merge, and a PodFailed check pod with Reason=DeadlineExceeded is now treated as not-ready and deleted for retry instead of being reported as an invalid config. Report: kube-logging#2103 PR: kube-logging#2303 Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
Motivation: FluentdSpec.SidecarContainers is added to the Fluentd StatefulSet pod but was never propagated to the transient fluentd-configcheck-* pod that dry-runs the rendered config before rollout. Users who need a sidecar to run before the aggregator starts (e.g. to refresh a GeoIP database via extraVolumes, as reported) only got it on the StatefulSet, not on the config check. Approach: Mirror the existing statefulset.go pattern in containerCheckPod (pkg/resources/fluentd/appconfigmap.go): append fluentdSpec.SidecarContainers to the check pod's container list when non-empty. The fluentd container that other code paths index at Containers[0] (e.g. the TLS volume mount) is unaffected since sidecars are appended after it. Validation: - go build ./... and go vet ./pkg/resources/fluentd/... pass. - Added TestNewCheckPodSidecarContainers to appconfigmap_test.go, which asserts the configcheck pod's container list contains a configured sidecar. Confirmed it fails without the fix (stashing only appconfigmap.go reproduces the reported bug) and passes with it: go test ./pkg/resources/fluentd/... -run TestNewCheckPod -v - make lint reports 0 issues across all three modules. - make test passes across the full suite with no failures. - make license-check fails, but identically on unmodified master (verified via git stash), so it is a pre-existing environment issue unrelated to this change. User-visible behaviour of the main Fluentd StatefulSet is unchanged; this only fixes the config check pod, whose config validation previously ran without any configured sidecars. Report: kube-logging#2103 Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
…ing sidecars Revert the SidecarContainers-append to the configcheck pod's Containers list: it's a run-to-completion pod gated on PodSucceeded, so a long-running sidecar (e.g. the sleep-infinity example in our own docs) wedges it forever and blocks Fluentd StatefulSet reconciliation, and its volume set diverges from the StatefulSet's so a volume-mounting sidecar makes the pod uncreatable. Instead, add FluentdSpec.ConfigCheckPod, a narrow opt-in override (initContainers, volumes, activeDeadlineSeconds) merged onto the generated check pod last, mirroring the pattern already used by SyslogNGSpec.ConfigCheckPodOverrides. RestartPolicy is reasserted to Never after the merge, and a PodFailed check pod with Reason=DeadlineExceeded is now treated as not-ready and deleted for retry instead of being reported as an invalid config. Report: kube-logging#2103 PR: kube-logging#2303 Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
9dbc84c to
b3ed8d4
Compare
|
Thanks for the thorough writeup — that's a much more solid design than what I had, and I've implemented it as you outlined.
I left the e2e native-sidecar test, the shared pod-template table-driven test, the default |
…edback Address csatib02's follow-up review: drop the dead post-merge RestartPolicy reassignment, correct doc comments (init container ordering, ExtraVolumes mounting, name-collision merge semantics), add validation on activeDeadlineSeconds, generalize the PodFailed handling from DeadlineExceeded to any non-empty Reason (and port it to syslog-ng), fall back to emptyDir for PVC-backed extraVolumes on the check pod, fix a shared-pointer Affinity mutation that leaked into the StatefulSet, and add tests pinning all of the above. Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
Motivation:
FluentdSpec.SidecarContainers is added to the Fluentd StatefulSet pod
but was never propagated to the transient fluentd-configcheck-* pod
that dry-runs the rendered config before rollout. Users who need a
sidecar to run before the aggregator starts (e.g. to refresh a
GeoIP database via extraVolumes, as reported) only got it on the
StatefulSet, not on the config check.
Approach:
Mirror the existing statefulset.go pattern in containerCheckPod
(pkg/resources/fluentd/appconfigmap.go): append
fluentdSpec.SidecarContainers to the check pod's container list when
non-empty. The fluentd container that other code paths index at
Containers[0] (e.g. the TLS volume mount) is unaffected since
sidecars are appended after it.
Validation:
which asserts the configcheck pod's container list contains a
configured sidecar. Confirmed it fails without the fix (stashing
only appconfigmap.go reproduces the reported bug) and passes with
it: go test ./pkg/resources/fluentd/... -run TestNewCheckPod -v
(verified via git stash), so it is a pre-existing environment
issue unrelated to this change.
User-visible behaviour of the main Fluentd StatefulSet is unchanged;
this only fixes the config check pod, whose config validation
previously ran without any configured sidecars.
Report: #2103
Signed-off-by: Pujitha Paladugu 10557236+pujitha24@users.noreply.github.com
AI assistance: this change was drafted with Claude Code.
Fixes #2103