@@ -31,13 +31,15 @@ import (
3131 "k8s.io/apimachinery/pkg/api/errors"
3232 "k8s.io/apimachinery/pkg/api/resource"
3333 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34+ "k8s.io/apimachinery/pkg/types"
3435 "k8s.io/client-go/informers"
3536 "k8s.io/client-go/kubernetes"
3637 "k8s.io/client-go/tools/cache"
3738 "k8s.io/utils/ptr"
3839
3940 "github.com/nvidia/nvsentinel/data-models/pkg/model"
4041 "github.com/nvidia/nvsentinel/data-models/pkg/protos"
42+ "github.com/nvidia/nvsentinel/fault-quarantine/pkg/common"
4143 "github.com/nvidia/nvsentinel/node-drainer/pkg/metrics"
4244)
4345
@@ -59,15 +61,24 @@ type Informers struct {
5961}
6062
6163func NewInformers (clientset kubernetes.Interface , resyncPeriod time.Duration ,
62- notReadyTimeoutMinutes * int , drainGPUPods bool , dryRun bool ) (* Informers , error ) {
64+ notReadyTimeoutMinutes * int , drainGPUPods bool , dryRun bool , systemNamespaces string ) (* Informers , error ) {
6365 informerFactory := informers .NewSharedInformerFactoryWithOptions (
6466 clientset ,
6567 resyncPeriod ,
6668 )
6769
6870 podInformer := informerFactory .Core ().V1 ().Pods ().Informer ()
6971
70- err := podInformer .GetIndexer ().AddIndexers (
72+ systemNamespacesRegex , err := compileExcludePattern (systemNamespaces )
73+ if err != nil {
74+ return nil , fmt .Errorf ("failed to compile system namespaces regex: %w" , err )
75+ }
76+
77+ if err := podInformer .SetTransform (excludedPodTransform (systemNamespacesRegex )); err != nil {
78+ return nil , fmt .Errorf ("failed to set pod informer transform: %w" , err )
79+ }
80+
81+ err = podInformer .GetIndexer ().AddIndexers (
7182 cache.Indexers {
7283 NodeIndex : NodeIndexFunc ,
7384 NamespaceNodeIndex : NamespaceNodeIndexFunc ,
@@ -93,6 +104,9 @@ func NewInformers(clientset kubernetes.Interface, resyncPeriod time.Duration,
93104 }
94105
95106 nodeInformer := informerFactory .Core ().V1 ().Nodes ().Informer ()
107+ if err := nodeInformer .SetTransform (nodeTransform ); err != nil {
108+ return nil , fmt .Errorf ("failed to set node informer transform: %w" , err )
109+ }
96110
97111 dryRunMode := []string {}
98112 if dryRun {
@@ -111,6 +125,153 @@ func NewInformers(clientset kubernetes.Interface, resyncPeriod time.Duration,
111125 }, nil
112126}
113127
128+ func excludedPodTransform (systemNamespacesRegex * regexp.Regexp ) cache.TransformFunc {
129+ return func (obj any ) (any , error ) {
130+ pod , ok := obj .(* v1.Pod )
131+ if ! ok {
132+ return obj , nil
133+ }
134+
135+ isSystemNamespace := systemNamespacesRegex != nil && systemNamespacesRegex .MatchString (pod .Namespace )
136+ if ! isSystemNamespace && ! isDaemonSetOwned (pod .OwnerReferences ) {
137+ return drainEligiblePodCacheObject (pod ), nil
138+ }
139+
140+ return & v1.Pod {
141+ ObjectMeta : identityObjectMeta (
142+ pod .Name ,
143+ pod .Namespace ,
144+ pod .UID ,
145+ pod .ResourceVersion ,
146+ nil ,
147+ ),
148+ }, nil
149+ }
150+ }
151+
152+ // drainEligiblePodCacheObject retains only fields used by pod indexes and drain decisions.
153+ // Keep this contract in sync with the cached Pod reads in this package.
154+ func drainEligiblePodCacheObject (pod * v1.Pod ) * v1.Pod {
155+ var annotations map [string ]string
156+ if devices , exists := pod .Annotations [model .PodDeviceAnnotationName ]; exists {
157+ annotations = map [string ]string {model .PodDeviceAnnotationName : devices }
158+ }
159+
160+ ownerReferences := make ([]metav1.OwnerReference , len (pod .OwnerReferences ))
161+ for idx , owner := range pod .OwnerReferences {
162+ ownerReferences [idx ] = metav1.OwnerReference {Kind : owner .Kind }
163+ }
164+
165+ var deletionTimestamp * metav1.Time
166+ if pod .DeletionTimestamp != nil {
167+ deletionTimestamp = pod .DeletionTimestamp .DeepCopy ()
168+ }
169+
170+ var terminationGracePeriodSeconds * int64
171+ if pod .Spec .TerminationGracePeriodSeconds != nil {
172+ terminationGracePeriodSeconds = ptr .To (* pod .Spec .TerminationGracePeriodSeconds )
173+ }
174+
175+ return & v1.Pod {
176+ TypeMeta : pod .TypeMeta ,
177+ ObjectMeta : metav1.ObjectMeta {
178+ Name : pod .Name ,
179+ Namespace : pod .Namespace ,
180+ UID : pod .UID ,
181+ ResourceVersion : pod .ResourceVersion ,
182+ Annotations : annotations ,
183+ OwnerReferences : ownerReferences ,
184+ DeletionTimestamp : deletionTimestamp ,
185+ },
186+ Spec : v1.PodSpec {
187+ NodeName : pod .Spec .NodeName ,
188+ TerminationGracePeriodSeconds : terminationGracePeriodSeconds ,
189+ Containers : trimContainers (pod .Spec .Containers ),
190+ InitContainers : trimContainers (pod .Spec .InitContainers ),
191+ },
192+ Status : v1.PodStatus {
193+ Phase : pod .Status .Phase ,
194+ Conditions : trimPodReadyConditions (pod .Status .Conditions ),
195+ },
196+ }
197+ }
198+
199+ func trimContainers (containers []v1.Container ) []v1.Container {
200+ cached := make ([]v1.Container , len (containers ))
201+ for idx , container := range containers {
202+ limits := make (v1.ResourceList , len (container .Resources .Limits ))
203+ for resourceName , quantity := range container .Resources .Limits {
204+ limits [resourceName ] = quantity .DeepCopy ()
205+ }
206+
207+ cached [idx ].Resources .Limits = limits
208+ }
209+
210+ return cached
211+ }
212+
213+ func trimPodReadyConditions (conditions []v1.PodCondition ) []v1.PodCondition {
214+ var cached []v1.PodCondition
215+ for _ , condition := range conditions {
216+ if condition .Type != v1 .PodReady {
217+ continue
218+ }
219+
220+ cached = append (cached , v1.PodCondition {
221+ Type : condition .Type ,
222+ Status : condition .Status ,
223+ LastTransitionTime : condition .LastTransitionTime ,
224+ })
225+ }
226+
227+ return cached
228+ }
229+
230+ func nodeTransform (obj any ) (any , error ) {
231+ node , ok := obj .(* v1.Node )
232+ if ! ok {
233+ return obj , nil
234+ }
235+
236+ var annotations map [string ]string
237+ if quarantineHealthEvent , exists := node .Annotations [common .QuarantineHealthEventAnnotationKey ]; exists {
238+ annotations = map [string ]string {
239+ common .QuarantineHealthEventAnnotationKey : quarantineHealthEvent ,
240+ }
241+ }
242+
243+ return & v1.Node {
244+ ObjectMeta : identityObjectMeta (
245+ node .Name ,
246+ "" ,
247+ node .UID ,
248+ node .ResourceVersion ,
249+ annotations ,
250+ ),
251+ }, nil
252+ }
253+
254+ func identityObjectMeta (name , namespace string , uid types.UID , resourceVersion string ,
255+ annotations map [string ]string ) metav1.ObjectMeta {
256+ return metav1.ObjectMeta {
257+ Name : name ,
258+ Namespace : namespace ,
259+ UID : uid ,
260+ ResourceVersion : resourceVersion ,
261+ Annotations : annotations ,
262+ }
263+ }
264+
265+ func isDaemonSetOwned (ownerReferences []metav1.OwnerReference ) bool {
266+ for _ , owner := range ownerReferences {
267+ if owner .Kind == "DaemonSet" {
268+ return true
269+ }
270+ }
271+
272+ return false
273+ }
274+
114275func (i * Informers ) HasSynced () bool {
115276 return i .podInformer .HasSynced () && i .eventInformer .HasSynced () && i .nodeInformer .HasSynced ()
116277}
@@ -383,15 +544,13 @@ func (i *Informers) filterPodsWithGPURequests(pods []*v1.Pod) []*v1.Pod {
383544}
384545
385546func (i * Informers ) isDaemonSetPod (pod * v1.Pod ) bool {
386- for _ , owner := range pod .OwnerReferences {
387- if owner .Kind == "DaemonSet" {
388- slog .Info ("Ignoring DaemonSet pod in namespace on node during eviction check" ,
389- "pod" , pod .Name ,
390- "namespace" , pod .Namespace ,
391- "node" , pod .Spec .NodeName )
547+ if isDaemonSetOwned (pod .OwnerReferences ) {
548+ slog .Info ("Ignoring DaemonSet pod in namespace on node during eviction check" ,
549+ "pod" , pod .Name ,
550+ "namespace" , pod .Namespace ,
551+ "node" , pod .Spec .NodeName )
392552
393- return true
394- }
553+ return true
395554 }
396555
397556 return false
@@ -798,6 +957,10 @@ func (i *Informers) GetNamespacesMatchingPattern(ctx context.Context,
798957}
799958
800959func (i * Informers ) compileExcludePattern (excludePattern string ) (* regexp.Regexp , error ) {
960+ return compileExcludePattern (excludePattern )
961+ }
962+
963+ func compileExcludePattern (excludePattern string ) (* regexp.Regexp , error ) {
801964 if excludePattern == "" {
802965 return nil , nil
803966 }
0 commit comments