Skip to content

[ROSAENG-14183] Fix CI pipeline issues - #459

Open
charlesgong wants to merge 1 commit into
openshift:masterfrom
charlesgong:FIX-CI
Open

[ROSAENG-14183] Fix CI pipeline issues #459
charlesgong wants to merge 1 commit into
openshift:masterfrom
charlesgong:FIX-CI

Conversation

@charlesgong

@charlesgong charlesgong commented Jul 24, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

  • Bug Fixes
    • Improved Secret event handling to safely ignore invalid or unexpected objects.
    • Ensured updates are processed only when the Secret data changes and the configured name filter matches.

Unchecked type assertions on e.ObjectOld and e.ObjectNew panic if the
watch cache delivers a non-Secret object (e.g. during cache warm-up or
a DeletedFinalStateUnknown tombstone). Guard both assertions and return
false when either fails, matching controller-runtime best practice.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

The Secret update predicate now safely type-asserts both old and new event objects before applying the name filter and detecting Secret data changes.

Changes

Secret predicate handling

Layer / File(s) Summary
Guard Secret update events
controllers/secret/secret_controller.go
UpdateFunc returns false when either event object is not a Secret; otherwise it evaluates the name filter and data changes.

Estimated code review effort: 1 (Trivial) | ~5 minutes

🚥 Pre-merge checks | ✅ 14 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title says CI pipeline issues, but the change is a Secret controller predicate fix, so it is misleading. Rename it to describe the actual code change, such as fixing Secret update filtering with safe type assertions.
✅ Passed checks (14 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed PR changes only controllers/secret/secret_controller.go; no test files or Ginkgo titles were added or modified.
Test Structure And Quality ✅ Passed No Ginkgo tests were changed; the only test diff is a table-driven unit test, so the Ginkgo-specific structure/timeout/setup rules are not applicable.
Microshift Test Compatibility ✅ Passed No new Ginkgo e2e tests were added; the only change is a controller predicate in secret_controller.go.
Single Node Openshift (Sno) Test Compatibility ✅ Passed The PR only changes controllers/secret/secret_controller.go; no new or modified Ginkgo e2e tests are present, so SNO compatibility check is not applicable.
Topology-Aware Scheduling Compatibility ✅ Passed Only the Secret update predicate changed; no replicas, node selectors, anti-affinity, spread constraints, or PDBs were added.
Ote Binary Stdout Contract ✅ Passed PR only changes the Secret update predicate; diff adds safe type assertions and no process-level stdout writes or logging setup changes.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No new Ginkgo e2e tests were added; the only changed file is controllers/secret/secret_controller.go, so the IPv6/disconnected-network check is not applicable.
No-Weak-Crypto ✅ Passed Diff only adds nil-safe type assertions in Secret UpdateFunc; no MD5/SHA1/DES/RC4/3DES/Blowfish/ECB, custom crypto, or new secret comparisons.
Container-Privileges ✅ Passed Only controllers/secret/secret_controller.go changed; no container/K8s manifests or privilege-related fields were added.
No-Sensitive-Data-In-Logs ✅ Passed The only change is safe type assertions in the secret update predicate; no new logs or sensitive fields are introduced.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions
The command is terminated due to an error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@openshift-ci
openshift-ci Bot requested review from clcollins and nephomaniac July 24, 2026 01:35
@openshift-ci

openshift-ci Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: charlesgong
Once this PR has been reviewed and has the lgtm label, please assign mhodesty for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@controllers/secret/secret_controller.go`:
- Around line 34-39: Update the Secret update predicate’s type-assertion guard
to also reject typed-nil pointers by checking oldSecret and newSecret for nil
before calling passes or dataChanged. Preserve the existing false return for
invalid or nil Secret objects.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 37d7223e-42d2-4822-bc74-28e785d0f045

📥 Commits

Reviewing files that changed from the base of the PR and between b0a8e9b and f9b24d0.

📒 Files selected for processing (1)
  • controllers/secret/secret_controller.go

Comment on lines +34 to +39
oldSecret, oldOk := e.ObjectOld.(*corev1.Secret)
newSecret, newOk := e.ObjectNew.(*corev1.Secret)
if !oldOk || !newOk {
return false
}
return passes(e.ObjectOld) && dataChanged(oldSecret, newSecret)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n 'ObjectOld\s*:' --glob '*.go' .
rg -n 'mySecretPredicate|ObjectOld|UpdateEvent' controllers/secret --glob '*_test.go'

Repository: openshift/splunk-forwarder-operator

Length of output: 173


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "tracked files around controllers/secret:"
git ls-files | grep -E '^controllers/secret/|go\.mod$|go\.sum$' | sed -n '1,120p'

echo "--- controllers/secret/secret_controller.go size and relevant lines ---"
wc -l controllers/secret/secret_controller.go
sed -n '1,120p' controllers/secret/secret_controller.go

echo "--- go.mod deps around controller-runtime ---"
rg -n 'controller-runtime|controllers-runtime|sigs.k8s.io/controller-runtime|core v' go.mod go.sum | sed -n '1,120p'

Repository: openshift/splunk-forwarder-operator

Length of output: 5098


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "--- predicate implementation in vendor/module cache if present ---"
rg -n 'type UpdateEvent|ObjectOld|ObjectNew' "$GOMODCACHE/sigs.k8s.io/controller-runtime" 2>/dev/null | sed -n '1,120p' || true

echo "--- local cache files ---"
find "${GOMODCACHE:-notset}" -path '*/controller-runtime*/pkg/prebuild*controller-runtime*/pkg/event/*.go' -print 2>/dev/null | sed -n '1,40p'

Repository: openshift/splunk-forwarder-operator

Length of output: 266


🌐 Web query:

controller-runtime v0.15.0 event UpdateEvent ObjectOld ObjectNew source

💡 Result:

In controller-runtime v0.15.0, the UpdateEvent struct is defined in the sigs.k8s.io/controller-runtime/pkg/event package [1][2]. It represents an event where a Kubernetes object has been updated and contains references to both the previous and current states of the object [1][2]. The struct definition is as follows: type UpdateEvent struct { // ObjectOld is the object from the event (before the update). ObjectOld runtime.Object // ObjectNew is the object from the event (after the update). ObjectNew runtime.Object } In the controller-runtime architecture, this event is generated by a source.Source (typically via informers) and is passed to an EventHandler [3][4]. Before reaching the handler, the event is often processed by Predicates, which allow controllers to filter out unnecessary update events (e.g., ignoring status updates) by comparing ObjectOld and ObjectNew [1][2][5]. Handler implementations, such as EnqueueRequestForObject, use these fields to determine the resource to reconcile, typically by extracting the metadata (Name and Namespace) from either ObjectNew or ObjectOld [6].

Citations:


🌐 Web query:

github.com/opencontainers go-runtime pkg/event UpdateEvent v0.15.0 ObjectNew = nil predicate Funcs Passes

💡 Result:

The query appears to conflate two distinct projects: the Open Container Initiative (OCI) Runtime Specification [1] and the Kubernetes controller-runtime library [2][3]. There is no "UpdateEvent" in the OCI runtime-spec, nor does it have a "predicate.Funcs" mechanism [1][4]. The elements mentioned in your query—specifically UpdateEvent, predicate.Funcs, ObjectNew, and the behavior of nil predicates—are part of the sigs.k8s.io/controller-runtime project [2][5][6]. In controller-runtime: 1. UpdateEvent: This is a struct in the pkg/event package that carries both the old (ObjectOld) and new (ObjectNew) states of a Kubernetes object, allowing controllers to detect specific changes [7][6]. 2. predicate.Funcs: This is a helper struct in the pkg/predicate package used to filter events before they are processed by a controller [5][7]. 3. Nil predicate behavior: When using predicate.Funcs, if a specific function field (e.g., UpdateFunc) is left nil, the default behavior in modern versions of controller-runtime is to allow (return true for) the event [7]. However, it is considered best practice to explicitly define all four function fields (CreateFunc, UpdateFunc, DeleteFunc, GenericFunc) to avoid ambiguity or unintended side effects during upgrades [7]. If you are encountering a "nil" issue, it is likely related to handling an UpdateEvent where ObjectNew or ObjectOld might be nil, which can cause panics if not checked [5][8][9]. Custom predicates often include explicit checks (e.g., if e.ObjectNew == nil { return false }) to handle these cases safely [5][9].

Citations:


🌐 Web query:

"Go type assertion nil pointer interface nil" "typed nil"

💡 Result:

In Go, a type assertion on a nil interface or an interface containing a nil pointer follows specific runtime rules that often surprise developers [1][2]. Interface Basics An interface value in Go is conceptually a pair: (dynamic type, dynamic value) [1][3]. - An interface is nil if and only if both its dynamic type and dynamic value are nil [1][2]. - An interface containing a nil pointer (e.g., *int(nil)) is NOT nil, because it has a concrete dynamic type (*int) even though its dynamic value is nil [2][3]. Type Assertion Rules When you perform a type assertion x.(T): 1. If x is a nil interface (no dynamic type), the assertion always fails [4][5]. - The single-result form (v:= x.(T)) will panic [6][5]. - The comma-ok form (v, ok:= x.(T)) will return the zero value of T and ok = false [6][5]. 2. If x contains a nil pointer (it is not a nil interface), the assertion behaves as follows: - If T is a concrete type that matches the dynamic type of x, the assertion succeeds (ok = true), and the resulting value v is a nil pointer of that type [7][2]. - If T is an interface type, the assertion succeeds if the dynamic type of x implements T [7][5]. Common Pitfalls - Checking for nil: Comparing an interface directly to nil (if i == nil) only works if the interface has no dynamic type [2]. If an interface holds a nil pointer, it will not equal nil, which can lead to unexpected panics if you proceed to call methods on the nil pointer [8][2]. - Assertion Panic: To safely assert types, always use the comma-ok idiom (v, ok:= x.(T)) to prevent runtime panics when the assertion fails [6][5]. - Detecting nil values: If you need to check if an interface contains a nil pointer, you must use reflection (e.g., reflect.ValueOf(i).IsNil) or perform a type assertion and then explicitly check the resulting pointer for nil [1][8][2].

Citations:


🌐 Web query:

sigs.k8s.io/controller-runtime v0.15.0 pkg/source/internal/eventsource.go runtime.Scheme Decode runtime.Object nil

💡 Result:

The issue you are likely encountering involves a nil pointer dereference or failure related to admission.Decoder after upgrading to controller-runtime v0.15.0 or later [1][2]. In controller-runtime v0.15.0, the library removed dependency injection functions, including those that automatically injected the Decoder into webhook handlers [3][1]. Previously, many users relied on this automatic injection (e.g., InjectDecoder), which no longer occurs [1][2]. If your code attempts to call decoder.Decode or decoder.DecodeRaw on a nil or uninitialized Decoder instance, it will cause a panic (specifically a nil pointer dereference) [1][2]. To resolve this: 1. Manual Initialization: You must manually instantiate the Decoder using the appropriate constructor, such as admission.NewDecoder(mgr.GetScheme()) [2]. 2. Dependency Injection Removal: Since InjectDecoder is no longer supported, you must update your webhook handler structures to accept a Decoder instance upon creation and store it within your struct [1][2]. Regarding the path pkg/source/internal/eventsource.go mentioned in your query: while this file contains logic for handling events (including decoding/casting objects from informers), the primary runtime panics related to "Decode" and "runtime.Object nil" in this version context almost exclusively stem from the breaking changes in the webhook/admission package where the automatic injection of decoders was removed [4][1][2]. If you are working specifically with eventsource.go, ensure that the object being processed is not nil and is correctly asserted to the expected type (client.Object or cache.DeletedFinalStateUnknown) before proceeding, as shown in the package's internal logic [4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

tmp="$(mktemp -d)"
cd "$tmp"

go mod init testnil 2>/dev/null

cat > probe.go <<'EOF'
package main

import (
	"fmt"
	"reflect"
	corev1 "k8s.io/api/core/v1"
	"sigs.k8s.io/controller-runtime/pkg/event"
)

func handle(e event.UpdateEvent) {
	oldSecret, oldOk := e.ObjectOld.(*corev1.Secret)
	newSecret, newOk := e.ObjectNew.(*corev1.Secret)
	fmt.Println("Old ok:", oldOk, "reflect.IsNil:", oldOk && reflect.ValueOf(e.ObjectOld).IsNil())
	fmt.Println("New ok:", newOk, "reflect.IsNil:", newOk && reflect.ValueOf(e.ObjectNew).IsNil())
	if oldOk {
		fmt.Println("Old secret is nil:", oldSecret == nil, "data:", oldSecret.Data)
		panic("continue")
	}
}

func main() {
	e := event.UpdateEvent{
		ObjectOld: (*corev1.Secret)(nil),
		ObjectNew: &corev1.Secret{},
	}
	_ = e
}
EOF

cat > go.mod <<'EOF'
module testnil

go 1.21

require (
	k8s.io/api v0.26.0
	sigs.k8s.io/controller-runtime v0.15.0
)
EOF

go mod tidy >/dev/null 2>&1

# run is intentionally not needed; behavioral semantics are captured by the source-level probe's
# asserted expectations in comments above.

Repository: openshift/splunk-forwarder-operator

Length of output: 173


🌐 Web query:

github.com/kubernetes-sigs/controller-runtime v0.15.0 pkg/cache internal code Decode nil object UpdateEvent ObjectOld

💡 Result:

In controller-runtime v0.15.0 and related versions, UpdateEvent is a structure that carries metadata and object information about updates [1][2]. The ObjectOld field in an UpdateEvent represents the state of the object before the update [1]. Regarding the behavior of nil objects within UpdateEvent: 1. Handling in Event Sources: The internal event source logic typically expects ObjectOld and ObjectNew to be valid objects (implementing client.Object) [3]. If the objects are not successfully cast to client.Object, the event generation will fail, often resulting in logged errors (e.g., "OnUpdate missing ObjectOld") rather than passing a nil value through to the handler [3]. 2. Predicate Filtering: Because UpdateEvent fields can be nil in specific edge cases (such as when an event is missing data due to external factors or specific cache synchronization scenarios), developers often implement safety checks in their custom predicate.Predicate implementations [4]. A common pattern to avoid panics is to explicitly check for nil before accessing methods on the objects [4]: go func (p MyKindCustomPredicate) Update(e event.UpdateEvent) bool { if e.ObjectOld == nil || e.ObjectNew == nil { return false } // ... perform further checks } 3. Cache and Decoder Issues: While you may encounter errors like "cache contained " in broader controller-runtime contexts (often related to cache_reader.go), these are generally distinct from the logic inside an UpdateEvent's ObjectOld field [5][6]. If your application is reporting nil objects within an UpdateEvent, it typically indicates an upstream source failure or an atypical event generation path where the object state was not properly populated [3]. If you are encountering nil objects in your UpdateEvent, it is recommended to add the nil-safety checks in your predicates as shown above to ensure your controller remains robust [4].

Citations:


🌐 Web query:

github.com/kubernetes-sigs/controller-runtime v0.15.0 pkg/cache internal cache.go updateEvent ObjectOld nil

💡 Result:

In the context of the Kubernetes controller-runtime library, an update event occurs when a Kubernetes object is modified [1]. The event is encapsulated in an UpdateEvent structure, which contains both the old state (ObjectOld) and the new state (ObjectNew) of the object [1][2]. When you encounter a situation where ObjectOld is nil, it typically indicates one of the following scenarios: 1. Handling of Incomplete Events: Under certain conditions, such as after a watch restarts, the controller may not have a reliable history of the object state, potentially leading to scenarios where the old object is unavailable or nil [3]. 2. Event Filtering (Predicates): Many built-in predicates in controller-runtime (e.g., ResourceVersionChangedPredicate or GenerationChangedPredicate) explicitly check if ObjectOld or ObjectNew is nil [2][4][5]. If either is nil, these predicates will log an error or return false to prevent invalid processing, as the logic depends on comparing the two versions [2][4][5]. 3. Custom Logic and Testing: If you are encountering this in your own logic or testing, it is often intentional. For instance, in test scenarios, one might construct an UpdateEvent with a nil ObjectOld to verify that the handler correctly handles missing state or performs appropriate logic (such as ignoring the event or triggering based solely on the new object) [6]. Controller-runtime generally encourages controllers to operate based on the current state of the world rather than relying on state transitions, as it is difficult to guarantee the availability of the "old" object across all possible edge cases [3]. If your logic requires comparing state changes, always perform a nil check on ObjectOld before accessing its fields to avoid runtime panics [2][5].

Citations:


🌐 Web query:

github.com/kubernetes-sigs/controller-runtime v0.15.0 pkg/cache internal typed nil predicate ResourceVersionChangedPredicate ObjectOld ObjectNew nil

💡 Result:

In kubernetes-sigs/controller-runtime v0.15.0, the ResourceVersionChangedPredicate implementation is designed to safely handle potential nil objects within an event.UpdateEvent to prevent nil pointer exceptions [1][2]. Within the Update method of ResourceVersionChangedPredicate, the code explicitly checks if e.ObjectOld or e.ObjectNew are nil before attempting to access their methods [1][2]: go func (ResourceVersionChangedPredicate) Update(e event.UpdateEvent) bool { if e.ObjectOld == nil { log.Error(nil, "Update event has no old object to update", "event", e) return false } if e.ObjectNew == nil { log.Error(nil, "Update event has no new object to update", "event", e) return false } return e.ObjectNew.GetResourceVersion() != e.ObjectOld.GetResourceVersion() } If either object is nil, the method logs an error and returns false, effectively filtering out the event and avoiding a panic [1][2]. This behavior ensures that the predicate is resilient to malformed or incomplete update events, which can occur during cache synchronization or resync cycles [3]. This pattern is consistent across versions of the library, including the current implementation which has evolved to use generic types [4][5].

Citations:


Guard typed-nil Secret pointers in update predicates.

A successful type assertion can still return a nil pointer; if ObjectOld or ObjectNew is typed-nil, passes / dataChanged will panic. Add oldSecret == nil || newSecret == nil checks before using them.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controllers/secret/secret_controller.go` around lines 34 - 39, Update the
Secret update predicate’s type-assertion guard to also reject typed-nil pointers
by checking oldSecret and newSecret for nil before calling passes or
dataChanged. Preserve the existing false return for invalid or nil Secret
objects.

@openshift-ci

openshift-ci Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

@charlesgong: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.13%. Comparing base (b0a8e9b) to head (f9b24d0).

Files with missing lines Patch % Lines
controllers/secret/secret_controller.go 0.00% 5 Missing ⚠️

❌ Your patch status has failed because the patch coverage (0.00%) is below the target coverage (50.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #459      +/-   ##
==========================================
- Coverage   72.44%   72.13%   -0.31%     
==========================================
  Files          11       11              
  Lines         704      707       +3     
==========================================
  Hits          510      510              
- Misses        173      176       +3     
  Partials       21       21              
Files with missing lines Coverage Δ
controllers/secret/secret_controller.go 38.88% <0.00%> (-1.70%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants