From 9d48e7d2d4090436d37c843fdb63a6ab2f097e46 Mon Sep 17 00:00:00 2001 From: novahe Date: Tue, 21 Oct 2025 22:58:14 +0800 Subject: [PATCH] fix missing involvedObject.apiVersion in event --- cmd/kube-proxy/app/server.go | 9 ++-- .../cm/node_container_manager_linux.go | 9 ++-- .../cm/node_container_manager_linux_test.go | 48 +++++++++++++++++++ pkg/kubelet/kubelet.go | 8 ++-- pkg/proxy/healthcheck/service_health.go | 8 ++-- pkg/proxy/kubemark/hollow_proxy.go | 9 ++-- .../node_lifecycle_controller.go | 9 ++-- .../node_lifecycle_controller_test.go | 10 +++- .../controllers/route/route_controller.go | 9 ++-- .../route/route_controller_test.go | 31 +++++++++++- 10 files changed, 115 insertions(+), 35 deletions(-) diff --git a/cmd/kube-proxy/app/server.go b/cmd/kube-proxy/app/server.go index 3f3cc6571e8..81aa2b7241a 100644 --- a/cmd/kube-proxy/app/server.go +++ b/cmd/kube-proxy/app/server.go @@ -34,7 +34,6 @@ import ( "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/selection" - "k8s.io/apimachinery/pkg/types" utilerrors "k8s.io/apimachinery/pkg/util/errors" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/validation/field" @@ -236,10 +235,10 @@ func newProxyServer(ctx context.Context, config *kubeproxyconfig.KubeProxyConfig s.Recorder = s.Broadcaster.NewRecorder(proxyconfigscheme.Scheme, kubeProxy) s.NodeRef = &v1.ObjectReference{ - Kind: "Node", - Name: s.NodeName, - UID: types.UID(s.NodeName), - Namespace: "", + APIVersion: "v1", + Kind: "Node", + Name: s.NodeName, + Namespace: "", } if len(config.HealthzBindAddress) > 0 { diff --git a/pkg/kubelet/cm/node_container_manager_linux.go b/pkg/kubelet/cm/node_container_manager_linux.go index 7fa180cefdd..8364c98ac89 100644 --- a/pkg/kubelet/cm/node_container_manager_linux.go +++ b/pkg/kubelet/cm/node_container_manager_linux.go @@ -28,7 +28,6 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" - "k8s.io/apimachinery/pkg/types" utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/klog/v2" kubefeatures "k8s.io/kubernetes/pkg/features" @@ -320,9 +319,9 @@ func (cm *containerManagerImpl) validateNodeAllocatable() error { // Using ObjectReference for events as the node maybe not cached; refer to #42701 for detail. func nodeRefFromNode(nodeName string) *v1.ObjectReference { return &v1.ObjectReference{ - Kind: "Node", - Name: nodeName, - UID: types.UID(nodeName), - Namespace: "", + APIVersion: "v1", + Kind: "Node", + Name: nodeName, + Namespace: "", } } diff --git a/pkg/kubelet/cm/node_container_manager_linux_test.go b/pkg/kubelet/cm/node_container_manager_linux_test.go index a74e1c7cb23..bd3b9d616a9 100644 --- a/pkg/kubelet/cm/node_container_manager_linux_test.go +++ b/pkg/kubelet/cm/node_container_manager_linux_test.go @@ -402,6 +402,54 @@ func getEphemeralStorageResourceList(storage string) v1.ResourceList { return res } +func TestNodeRefFromNode(t *testing.T) { + testCases := []struct { + name string + nodeName string + expected *v1.ObjectReference + }{ + { + name: "normal node name", + nodeName: "test-node", + expected: &v1.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + Name: "test-node", + Namespace: "", + }, + }, + { + name: "empty node name", + nodeName: "", + expected: &v1.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + Name: "", + UID: "", + Namespace: "", + }, + }, + { + name: "node name with special characters", + nodeName: "test-node-123.domain.local", + expected: &v1.ObjectReference{ + APIVersion: "v1", + Kind: "Node", + Name: "test-node-123.domain.local", + Namespace: "", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := nodeRefFromNode(tc.nodeName) + + assert.Equal(t, tc.expected, result, "test case %q failed", tc.name) + }) + } +} + func TestGetCgroupConfig(t *testing.T) { cases := []struct { name string diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index 13622516eec..2b47fa184a4 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -548,10 +548,10 @@ func NewMainKubelet(ctx context.Context, // construct a node reference used for events nodeRef := &v1.ObjectReference{ - Kind: "Node", - Name: string(nodeName), - UID: types.UID(nodeName), - Namespace: "", + APIVersion: "v1", + Kind: "Node", + Name: string(nodeName), + Namespace: "", } oomWatcher, err := oomwatcher.NewWatcher(kubeDeps.Recorder) diff --git a/pkg/proxy/healthcheck/service_health.go b/pkg/proxy/healthcheck/service_health.go index 0d4f066dd0b..e470f94c36d 100644 --- a/pkg/proxy/healthcheck/service_health.go +++ b/pkg/proxy/healthcheck/service_health.go @@ -136,10 +136,10 @@ func (hcs *server) SyncServices(newServices map[types.NamespacedName]uint16) err if hcs.recorder != nil { hcs.recorder.Eventf( &v1.ObjectReference{ - Kind: "Service", - Namespace: nsn.Namespace, - Name: nsn.Name, - UID: types.UID(nsn.String()), + APIVersion: "v1", + Kind: "Service", + Namespace: nsn.Namespace, + Name: nsn.Name, }, nil, api.EventTypeWarning, "FailedToStartServiceHealthcheck", "Listen", msg) } klog.ErrorS(err, "Failed to start healthcheck", "node", hcs.nodeName, "service", nsn, "port", port) diff --git a/pkg/proxy/kubemark/hollow_proxy.go b/pkg/proxy/kubemark/hollow_proxy.go index 34fdc94896c..9f7b821b60f 100644 --- a/pkg/proxy/kubemark/hollow_proxy.go +++ b/pkg/proxy/kubemark/hollow_proxy.go @@ -24,7 +24,6 @@ import ( v1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" clientset "k8s.io/client-go/kubernetes" v1core "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/tools/events" @@ -76,10 +75,10 @@ func NewHollowProxy( Broadcaster: broadcaster, Recorder: recorder, NodeRef: &v1.ObjectReference{ - Kind: "Node", - Name: nodeName, - UID: types.UID(nodeName), - Namespace: "", + APIVersion: "v1", + Kind: "Node", + Name: nodeName, + Namespace: "", }, }, } diff --git a/staging/src/k8s.io/cloud-provider/controllers/nodelifecycle/node_lifecycle_controller.go b/staging/src/k8s.io/cloud-provider/controllers/nodelifecycle/node_lifecycle_controller.go index 836de29ffe2..2c8598b560c 100644 --- a/staging/src/k8s.io/cloud-provider/controllers/nodelifecycle/node_lifecycle_controller.go +++ b/staging/src/k8s.io/cloud-provider/controllers/nodelifecycle/node_lifecycle_controller.go @@ -163,10 +163,11 @@ func (c *CloudNodeLifecycleController) MonitorNodes(ctx context.Context) { klog.V(2).Infof("deleting node since it is no longer present in cloud provider: %s", node.Name) ref := &v1.ObjectReference{ - Kind: "Node", - Name: node.Name, - UID: types.UID(node.UID), - Namespace: "", + APIVersion: "v1", + Kind: "Node", + Name: node.Name, + UID: node.UID, + Namespace: "", } c.recorder.Eventf(ref, v1.EventTypeNormal, deleteNodeEvent, diff --git a/staging/src/k8s.io/cloud-provider/controllers/nodelifecycle/node_lifecycle_controller_test.go b/staging/src/k8s.io/cloud-provider/controllers/nodelifecycle/node_lifecycle_controller_test.go index 3356b6c2f68..7442f13433e 100644 --- a/staging/src/k8s.io/cloud-provider/controllers/nodelifecycle/node_lifecycle_controller_test.go +++ b/staging/src/k8s.io/cloud-provider/controllers/nodelifecycle/node_lifecycle_controller_test.go @@ -909,8 +909,14 @@ func Test_NodesShutdown(t *testing.T) { nodeMonitorPeriod: 1 * time.Second, } - w := eventBroadcaster.StartStructuredLogging(0) - defer w.Stop() + e := eventBroadcaster.StartEventWatcher(func(e *v1.Event) { + loggerV := klog.FromContext(t.Context()).V(0) + loggerV.Info("Event occurred", "object", klog.KRef(e.InvolvedObject.Namespace, e.InvolvedObject.Name), "fieldPath", e.InvolvedObject.FieldPath, "kind", e.InvolvedObject.Kind, "apiVersion", e.InvolvedObject.APIVersion, "type", e.Type, "reason", e.Reason, "message", e.Message) + if e.InvolvedObject.APIVersion == "" { + t.Fatalf("event involvedObject.apiVersion is empty") + } + }) + defer e.Stop() cloudNodeLifecycleController.MonitorNodes(ctx) updatedNode, err := clientset.CoreV1().Nodes().Get(ctx, testcase.existingNode.Name, metav1.GetOptions{}) diff --git a/staging/src/k8s.io/cloud-provider/controllers/route/route_controller.go b/staging/src/k8s.io/cloud-provider/controllers/route/route_controller.go index 96cf1a465ee..fe13c5feda5 100644 --- a/staging/src/k8s.io/cloud-provider/controllers/route/route_controller.go +++ b/staging/src/k8s.io/cloud-provider/controllers/route/route_controller.go @@ -307,10 +307,11 @@ func (rc *RouteController) reconcile(ctx context.Context, nodes []*v1.Node, rout if rc.recorder != nil { rc.recorder.Eventf( &v1.ObjectReference{ - Kind: "Node", - Name: string(nodeName), - UID: types.UID(nodeName), - Namespace: "", + APIVersion: "v1", + Kind: "Node", + Name: string(nodeName), + UID: node.UID, + Namespace: "", }, v1.EventTypeWarning, "FailedToCreateRoute", msg) klog.V(4).Info(msg) return err diff --git a/staging/src/k8s.io/cloud-provider/controllers/route/route_controller_test.go b/staging/src/k8s.io/cloud-provider/controllers/route/route_controller_test.go index 265fb048ce1..b103c4b4d55 100644 --- a/staging/src/k8s.io/cloud-provider/controllers/route/route_controller_test.go +++ b/staging/src/k8s.io/cloud-provider/controllers/route/route_controller_test.go @@ -27,7 +27,9 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" + "k8s.io/client-go/kubernetes/scheme" core "k8s.io/client-go/testing" + "k8s.io/client-go/tools/record" cloudprovider "k8s.io/cloud-provider" fakecloud "k8s.io/cloud-provider/fake" nodeutil "k8s.io/component-helpers/node/util" @@ -93,6 +95,7 @@ func TestReconcile(t *testing.T) { node3 := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node-3", UID: "03"}, Spec: v1.NodeSpec{PodCIDR: "10.120.0.0/24", PodCIDRs: []string{"10.120.0.0/24", "a00:100::/24"}}, Status: v1.NodeStatus{Addresses: []v1.NodeAddress{{Type: v1.NodeInternalIP, Address: "10.0.3.1"}}}} node4 := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node-4", UID: "04"}, Spec: v1.NodeSpec{PodCIDR: "10.120.1.0/24", PodCIDRs: []string{"10.120.1.0/24", "a00:200::/24"}}, Status: v1.NodeStatus{Addresses: []v1.NodeAddress{{Type: v1.NodeInternalIP, Address: "10.0.4.1"}}}} + nodeDuplicateCIDR := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node-4", UID: "04"}, Spec: v1.NodeSpec{PodCIDR: "10.120.1.0/24", PodCIDRs: []string{"10.120.1.0/24", "10.120.1.0/24"}}, Status: v1.NodeStatus{Addresses: []v1.NodeAddress{{Type: v1.NodeInternalIP, Address: "10.0.4.1"}}}} testCases := []struct { description string @@ -102,6 +105,7 @@ func TestReconcile(t *testing.T) { expectedNetworkUnavailable []bool clientset *fake.Clientset dualStack bool + expectError bool }{ { description: "routes have no TargetNodeAddresses at the beginning", @@ -414,6 +418,17 @@ func TestReconcile(t *testing.T) { expectedNetworkUnavailable: []bool{true, true}, clientset: fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{node1, node2}}), }, + { + description: "duplicate pod cidr", + nodes: []*v1.Node{ + &nodeDuplicateCIDR, + }, + initialRoutes: []*cloudprovider.Route{}, + expectedRoutes: []*cloudprovider.Route{}, + expectedNetworkUnavailable: []bool{true, false}, + expectError: true, + clientset: fake.NewClientset(&v1.NodeList{Items: []v1.Node{nodeDuplicateCIDR}}), + }, } for _, testCase := range testCases { t.Run(testCase.description, func(t *testing.T) { @@ -439,6 +454,16 @@ func TestReconcile(t *testing.T) { informerFactory := informers.NewSharedInformerFactory(testCase.clientset, 0) rc := New(routes, testCase.clientset, informerFactory.Core().V1().Nodes(), cluster, cidrs) + + recorder := record.NewBroadcaster(record.WithContext(ctx)) + rc.recorder = recorder.NewRecorder(scheme.Scheme, v1.EventSource{Component: "route_controller"}) + e := recorder.StartEventWatcher(func(e *v1.Event) { + if e.InvolvedObject.APIVersion == "" { + t.Fatalf("event involvedObject.apiVersion is empty") + } + }) + defer e.Stop() + rc.nodeListerSynced = alwaysReady require.NoError(t, rc.reconcile(ctx, testCase.nodes, testCase.initialRoutes), "failed to reconcile") for _, action := range testCase.clientset.Actions() { @@ -476,8 +501,10 @@ func TestReconcile(t *testing.T) { break poll } case <-timeoutChan: - t.Errorf("rc.reconcile() err is %v,\nfound routes:\n%v\nexpected routes:\n%v\n", - err, flatten(finalRoutes), flatten(testCase.expectedRoutes)) + if !testCase.expectError { + t.Errorf("rc.reconcile() err is %v,\nfound routes:\n%v\nexpected routes:\n%v\n", + err, flatten(finalRoutes), flatten(testCase.expectedRoutes)) + } break poll } }