@@ -192,13 +192,24 @@ pub struct K8sListItem {
192192#[ serde( rename_all = "camelCase" ) ]
193193pub enum WatchEvent {
194194 /// A new resource was created or discovered
195- Added ( K8sListItem ) ,
195+ Added {
196+ item : K8sListItem ,
197+ cluster_context : String ,
198+ } ,
196199 /// An existing resource was updated
197- Modified ( K8sListItem ) ,
200+ Modified {
201+ item : K8sListItem ,
202+ cluster_context : String ,
203+ } ,
198204 /// A resource was deleted
199- Deleted ( K8sListItem ) ,
205+ Deleted {
206+ item : K8sListItem ,
207+ cluster_context : String ,
208+ } ,
200209 /// Initial watch synchronization is complete (no more items)
201- InitialSyncComplete ,
210+ InitialSyncComplete {
211+ cluster_context : String ,
212+ } ,
202213}
203214
204215/// Returns all supported Kubernetes resource types organized by category.
@@ -754,20 +765,199 @@ mod tests {
754765 subsets : None ,
755766 } ;
756767
757- let event = WatchEvent :: Added ( item) ;
768+ let event = WatchEvent :: Added {
769+ item : item. clone ( ) ,
770+ cluster_context : "test-cluster" . to_string ( ) ,
771+ } ;
758772 let json = serde_json:: to_string ( & event) . unwrap ( ) ;
759773 let deserialized: WatchEvent = serde_json:: from_str ( & json) . unwrap ( ) ;
760774
761775 match deserialized {
762- WatchEvent :: Added ( item) => {
776+ WatchEvent :: Added { item, cluster_context } => {
763777 assert_eq ! ( item. metadata. name, Some ( "test-pod" . to_string( ) ) ) ;
764778 assert_eq ! ( item. kind, "Pod" ) ;
765779 assert_eq ! ( item. pod_status. as_ref( ) . unwrap( ) . phase, Some ( "Running" . to_string( ) ) ) ;
780+ assert_eq ! ( cluster_context, "test-cluster" ) ;
766781 }
767782 _ => panic ! ( "Expected Added event" ) ,
768783 }
769784 }
770785
786+ #[ tokio:: test]
787+ async fn test_cross_cluster_event_isolation ( ) {
788+ // Test that watch events include cluster context for proper isolation
789+ use k8s_openapi:: api:: core:: v1:: Pod ;
790+
791+ let mut pod = Pod :: default ( ) ;
792+ pod. metadata . name = Some ( "test-pod" . to_string ( ) ) ;
793+ pod. metadata . namespace = Some ( "default" . to_string ( ) ) ;
794+ pod. metadata . uid = Some ( "test-uid" . to_string ( ) ) ;
795+
796+ let item = K8sListItem {
797+ metadata : pod. metadata . clone ( ) ,
798+ kind : "Pod" . to_string ( ) ,
799+ api_version : "v1" . to_string ( ) ,
800+ complete_object : None ,
801+ pod_spec : None ,
802+ service_spec : None ,
803+ config_map_spec : None ,
804+ secret_spec : None ,
805+ namespace_spec : None ,
806+ node_spec : None ,
807+ persistent_volume_spec : None ,
808+ persistent_volume_claim_spec : None ,
809+ endpoints_spec : None ,
810+ deployment_spec : None ,
811+ replica_set_spec : None ,
812+ stateful_set_spec : None ,
813+ daemon_set_spec : None ,
814+ job_spec : None ,
815+ cron_job_spec : None ,
816+ ingress_spec : None ,
817+ network_policy_spec : None ,
818+ endpoint_slice : None ,
819+ storage_class_spec : None ,
820+ role_spec : None ,
821+ role_binding_spec : None ,
822+ cluster_role_spec : None ,
823+ cluster_role_binding_spec : None ,
824+ service_account_spec : None ,
825+ pod_disruption_budget_spec : None ,
826+ horizontal_pod_autoscaler_spec : None ,
827+ pod_status : None ,
828+ service_status : None ,
829+ namespace_status : None ,
830+ node_status : None ,
831+ persistent_volume_status : None ,
832+ persistent_volume_claim_status : None ,
833+ deployment_status : None ,
834+ replica_set_status : None ,
835+ stateful_set_status : None ,
836+ daemon_set_status : None ,
837+ job_status : None ,
838+ cron_job_status : None ,
839+ ingress_status : None ,
840+ pod_disruption_budget_status : None ,
841+ horizontal_pod_autoscaler_status : None ,
842+ subsets : None ,
843+ } ;
844+
845+ // Test events from different clusters
846+ let cluster_a_event = WatchEvent :: Added {
847+ item : item. clone ( ) ,
848+ cluster_context : "cluster-production" . to_string ( ) ,
849+ } ;
850+
851+ let cluster_b_event = WatchEvent :: Added {
852+ item : item. clone ( ) ,
853+ cluster_context : "cluster-staging" . to_string ( ) ,
854+ } ;
855+
856+ // Serialize and verify both events
857+ let json_a = serde_json:: to_string ( & cluster_a_event) . unwrap ( ) ;
858+ let json_b = serde_json:: to_string ( & cluster_b_event) . unwrap ( ) ;
859+
860+ // Events should be different due to cluster context
861+ assert_ne ! ( json_a, json_b, "Events from different clusters should serialize differently" ) ;
862+
863+ // Deserialize and verify cluster context is preserved
864+ let deserialized_a: WatchEvent = serde_json:: from_str ( & json_a) . unwrap ( ) ;
865+ let deserialized_b: WatchEvent = serde_json:: from_str ( & json_b) . unwrap ( ) ;
866+
867+ match ( deserialized_a, deserialized_b) {
868+ ( WatchEvent :: Added { cluster_context : ctx_a, .. } , WatchEvent :: Added { cluster_context : ctx_b, .. } ) => {
869+ assert_eq ! ( ctx_a, "cluster-production" ) ;
870+ assert_eq ! ( ctx_b, "cluster-staging" ) ;
871+ assert_ne ! ( ctx_a, ctx_b, "Cluster contexts should be different" ) ;
872+ }
873+ _ => panic ! ( "Both events should be Added events" ) ,
874+ }
875+ }
876+
877+ #[ tokio:: test]
878+ async fn test_watch_event_cluster_context_required ( ) {
879+ // Test that all WatchEvent variants require cluster context
880+ use k8s_openapi:: api:: core:: v1:: Node ;
881+
882+ let mut node = Node :: default ( ) ;
883+ node. metadata . name = Some ( "worker-node-1" . to_string ( ) ) ;
884+ node. metadata . uid = Some ( "node-uid" . to_string ( ) ) ;
885+
886+ let item = K8sListItem {
887+ metadata : node. metadata . clone ( ) ,
888+ kind : "Node" . to_string ( ) ,
889+ api_version : "v1" . to_string ( ) ,
890+ complete_object : None ,
891+ pod_spec : None ,
892+ service_spec : None ,
893+ config_map_spec : None ,
894+ secret_spec : None ,
895+ namespace_spec : None ,
896+ node_spec : None ,
897+ persistent_volume_spec : None ,
898+ persistent_volume_claim_spec : None ,
899+ endpoints_spec : None ,
900+ deployment_spec : None ,
901+ replica_set_spec : None ,
902+ stateful_set_spec : None ,
903+ daemon_set_spec : None ,
904+ job_spec : None ,
905+ cron_job_spec : None ,
906+ ingress_spec : None ,
907+ network_policy_spec : None ,
908+ endpoint_slice : None ,
909+ storage_class_spec : None ,
910+ role_spec : None ,
911+ role_binding_spec : None ,
912+ cluster_role_spec : None ,
913+ cluster_role_binding_spec : None ,
914+ service_account_spec : None ,
915+ pod_disruption_budget_spec : None ,
916+ horizontal_pod_autoscaler_spec : None ,
917+ pod_status : None ,
918+ service_status : None ,
919+ namespace_status : None ,
920+ node_status : None ,
921+ persistent_volume_status : None ,
922+ persistent_volume_claim_status : None ,
923+ deployment_status : None ,
924+ replica_set_status : None ,
925+ stateful_set_status : None ,
926+ daemon_set_status : None ,
927+ job_status : None ,
928+ cron_job_status : None ,
929+ ingress_status : None ,
930+ pod_disruption_budget_status : None ,
931+ horizontal_pod_autoscaler_status : None ,
932+ subsets : None ,
933+ } ;
934+
935+ let cluster_context = "test-cluster" . to_string ( ) ;
936+
937+ // Test all event types include cluster context
938+ let test_events = vec ! [
939+ WatchEvent :: Added { item: item. clone( ) , cluster_context: cluster_context. clone( ) } ,
940+ WatchEvent :: Modified { item: item. clone( ) , cluster_context: cluster_context. clone( ) } ,
941+ WatchEvent :: Deleted { item, cluster_context: cluster_context. clone( ) } ,
942+ WatchEvent :: InitialSyncComplete { cluster_context: cluster_context. clone( ) } ,
943+ ] ;
944+
945+ for event in test_events {
946+ let json = serde_json:: to_string ( & event) . unwrap ( ) ;
947+ let deserialized: WatchEvent = serde_json:: from_str ( & json) . unwrap ( ) ;
948+
949+ // Verify cluster context is preserved in all event types
950+ let preserved_context = match deserialized {
951+ WatchEvent :: Added { cluster_context, .. } => cluster_context,
952+ WatchEvent :: Modified { cluster_context, .. } => cluster_context,
953+ WatchEvent :: Deleted { cluster_context, .. } => cluster_context,
954+ WatchEvent :: InitialSyncComplete { cluster_context } => cluster_context,
955+ } ;
956+
957+ assert_eq ! ( preserved_context, "test-cluster" , "Cluster context must be preserved in all event types" ) ;
958+ }
959+ }
960+
771961 #[ test]
772962 fn test_k8s_object_meta_with_labels ( ) {
773963 let mut labels = std:: collections:: BTreeMap :: new ( ) ;
0 commit comments