@@ -17,12 +17,15 @@ limitations under the License.
1717package cache
1818
1919import (
20+ "slices"
2021 "sort"
2122 "sync"
2223
2324 v1 "k8s.io/api/core/v1"
25+ "k8s.io/apimachinery/pkg/util/sets"
2426 "k8s.io/client-go/tools/cache"
2527 "k8s.io/klog/v2"
28+ "k8s.io/kubernetes/pkg/controller/volume/selinuxwarning/internal/parse"
2629 "k8s.io/kubernetes/pkg/controller/volume/selinuxwarning/translator"
2730)
2831
@@ -45,8 +48,8 @@ type VolumeCache interface {
4548 // change their SELinux support dynamically.
4649 GetPodsForCSIDriver (driverName string ) []cache.ObjectName
4750
48- // SendConflicts sends all current conflicts to the given channel .
49- SendConflicts (logger klog.Logger , ch chan <- Conflict )
51+ // GetConflicts returns the current set of active conflicts (both directions) .
52+ GetConflicts (logger klog.Logger ) [] Conflict
5053}
5154
5255// VolumeCache stores all volumes used by Pods and their properties that the controller needs to track,
@@ -56,6 +59,11 @@ type volumeCache struct {
5659 seLinuxTranslator * translator.ControllerSELinuxTranslator
5760 // All volumes of all existing Pods.
5861 volumes map [v1.UniqueVolumeName ]usedVolume
62+ // Reverse index: maps each pod to the list of volumes it uses.
63+ // The index is used during pod deletion.
64+ podToVolumes map [cache.ObjectName ]sets.Set [v1.UniqueVolumeName ]
65+ // Currently active conflicts per volume (both directions, symmetric pairs).
66+ conflicts map [v1.UniqueVolumeName ][]Conflict
5967}
6068
6169var _ VolumeCache = & volumeCache {}
@@ -65,6 +73,8 @@ func NewVolumeLabelCache(seLinuxTranslator *translator.ControllerSELinuxTranslat
6573 return & volumeCache {
6674 seLinuxTranslator : seLinuxTranslator ,
6775 volumes : make (map [v1.UniqueVolumeName ]usedVolume ),
76+ podToVolumes : make (map [cache.ObjectName ]sets.Set [v1.UniqueVolumeName ]),
77+ conflicts : make (map [v1.UniqueVolumeName ][]Conflict ),
6878 }
6979}
7080
@@ -81,6 +91,8 @@ type podInfo struct {
8191 // SELinux seLinuxLabel to be applied to the volume in the Pod.
8292 // Either as mount option or recursively by the container runtime.
8393 seLinuxLabel string
94+ // Pre-parsed SELinux label parts for fast conflict detection.
95+ seLinuxParts [4 ]string
8496 // SELinuxChangePolicy of the Pod.
8597 changePolicy v1.PodSELinuxChangePolicy
8698}
@@ -89,6 +101,7 @@ func newPodInfoListForPod(podKey cache.ObjectName, seLinuxLabel string, changePo
89101 return map [cache.ObjectName ]podInfo {
90102 podKey : {
91103 seLinuxLabel : seLinuxLabel ,
104+ seLinuxParts : parse .ParseSELinuxLabel (seLinuxLabel ),
92105 changePolicy : changePolicy ,
93106 },
94107 }
@@ -110,12 +123,16 @@ func (c *volumeCache) AddVolume(logger klog.Logger, volumeName v1.UniqueVolumeNa
110123 pods : newPodInfoListForPod (podKey , label , changePolicy ),
111124 }
112125 c .volumes [volumeName ] = volume
126+
127+ // Add to reverse index
128+ c .registerPodVolume (podKey , volumeName )
113129 return conflicts
114130 }
115131
116132 // The volume is already known
117133 podInfo := podInfo {
118134 seLinuxLabel : label ,
135+ seLinuxParts : parse .ParseSELinuxLabel (label ),
119136 changePolicy : changePolicy ,
120137 }
121138 oldPodInfo , found := volume .pods [podKey ]
@@ -128,6 +145,9 @@ func (c *volumeCache) AddVolume(logger klog.Logger, volumeName v1.UniqueVolumeNa
128145 // Add the updated pod info to the cache
129146 volume .pods [podKey ] = podInfo
130147
148+ // Add to reverse index
149+ c .registerPodVolume (podKey , volumeName )
150+
131151 // Emit conflicts for the pod
132152 for otherPodKey , otherPodInfo := range volume .pods {
133153 if otherPodInfo .changePolicy != changePolicy {
@@ -147,8 +167,9 @@ func (c *volumeCache) AddVolume(logger klog.Logger, volumeName v1.UniqueVolumeNa
147167 OtherPod : podKey ,
148168 OtherPropertyValue : string (changePolicy ),
149169 })
170+
150171 }
151- if c .seLinuxTranslator .Conflicts (otherPodInfo .seLinuxLabel , label ) {
172+ if c .seLinuxTranslator .ConflictsParsed (otherPodInfo .seLinuxParts , podInfo . seLinuxParts ) {
152173 // Send conflict to both pods
153174 conflicts = append (conflicts , Conflict {
154175 PropertyName : "SELinuxLabel" ,
@@ -167,6 +188,21 @@ func (c *volumeCache) AddVolume(logger klog.Logger, volumeName v1.UniqueVolumeNa
167188 })
168189 }
169190 }
191+ // Update the conflict cache for this volume: remove stale conflicts for this pod, then add new ones
192+ volumeConflicts := c .conflicts [volumeName ]
193+ updated := make ([]Conflict , 0 , len (volumeConflicts ))
194+ for _ , existing := range volumeConflicts {
195+ if existing .Pod != podKey && existing .OtherPod != podKey {
196+ updated = append (updated , existing )
197+ }
198+ }
199+ updated = append (updated , conflicts ... )
200+ if len (updated ) == 0 {
201+ delete (c .conflicts , volumeName )
202+ } else {
203+ c .conflicts [volumeName ] = updated
204+ }
205+
170206 return conflicts
171207}
172208
@@ -176,12 +212,47 @@ func (c *volumeCache) DeletePod(logger klog.Logger, podKey cache.ObjectName) {
176212 defer c .mutex .Unlock ()
177213 defer c .dump (logger )
178214
179- for volumeName , volume := range c .volumes {
215+ for volumeName := range c .podToVolumes [podKey ] {
216+ conflicts , found := c .conflicts [volumeName ]
217+ if ! found {
218+ continue
219+ }
220+ updated := make ([]Conflict , 0 , len (conflicts ))
221+ for _ , existing := range conflicts {
222+ // preserve other conflicts belonging to volume
223+ if existing .Pod != podKey && existing .OtherPod != podKey {
224+ updated = append (updated , existing )
225+ }
226+ }
227+ if len (updated ) == 0 {
228+ delete (c .conflicts , volumeName )
229+ } else {
230+ c .conflicts [volumeName ] = updated
231+ }
232+ }
233+
234+ // Use reverse index to only iterate through volumes this pod actually uses.
235+ for volumeName := range c .podToVolumes [podKey ] {
236+ volume , found := c .volumes [volumeName ]
237+ if ! found {
238+ continue
239+ }
180240 delete (volume .pods , podKey )
181241 if len (volume .pods ) == 0 {
182242 delete (c .volumes , volumeName )
183243 }
184244 }
245+ delete (c .podToVolumes , podKey )
246+ }
247+
248+ // registerPodVolume adds volumeName to the pod volume index.
249+ // Make sure to hold c.mutex when calling this function.
250+ func (c * volumeCache ) registerPodVolume (podKey cache.ObjectName , volumeName v1.UniqueVolumeName ) {
251+ if podVolumes , ok := c .podToVolumes [podKey ]; ok {
252+ podVolumes .Insert (volumeName )
253+ } else {
254+ c .podToVolumes [podKey ] = sets .New (volumeName )
255+ }
185256}
186257
187258func (c * volumeCache ) dump (logger klog.Logger ) {
@@ -215,6 +286,22 @@ func (c *volumeCache) dump(logger klog.Logger) {
215286 logger .Info (" pod" , "pod" , podKey , "seLinuxLabel" , podInfo .seLinuxLabel , "changePolicy" , podInfo .changePolicy )
216287 }
217288 }
289+
290+ // Collect all pods, sort them and print the associated volumes.
291+ podKeys := make ([]cache.ObjectName , 0 , len (c .podToVolumes ))
292+ for podKey := range c .podToVolumes {
293+ podKeys = append (podKeys , podKey )
294+ }
295+ sort .Slice (podKeys , func (i , j int ) bool {
296+ return podKeys [i ].String () < podKeys [j ].String ()
297+ })
298+
299+ logger .Info ("VolumeCache reverse index dump:" )
300+ for _ , podKey := range podKeys {
301+ podVolumes := sets .List (c .podToVolumes [podKey ])
302+ slices .Sort (podVolumes )
303+ logger .Info (" pod" , "pod" , podKey , "volumes" , podVolumes )
304+ }
218305}
219306
220307// GetPodsForCSIDriver returns all pods that use volumes with the given CSI driver.
@@ -234,42 +321,16 @@ func (c *volumeCache) GetPodsForCSIDriver(driverName string) []cache.ObjectName
234321 return pods
235322}
236323
237- // SendConflicts sends all current conflicts to the given channel .
238- func (c * volumeCache ) SendConflicts (logger klog.Logger , ch chan <- Conflict ) {
324+ // GetConflicts returns the current set of active conflicts (both directions, symmetric pairs) .
325+ func (c * volumeCache ) GetConflicts (logger klog.Logger ) [] Conflict {
239326 c .mutex .RLock ()
240327 defer c .mutex .RUnlock ()
241328 logger .V (4 ).Info ("Scraping conflicts" )
242329 c .dump (logger )
243330
244- for _ , volume := range c .volumes {
245- // compare pods that use the same volume with each other
246- for podKey , podInfo := range volume .pods {
247- for otherPodKey , otherPodInfo := range volume .pods {
248- if podKey == otherPodKey {
249- continue
250- }
251- // create conflict only for the first pod. The other pod will get the same conflict in its own iteration of `volume.pods` loop.
252- if podInfo .changePolicy != otherPodInfo .changePolicy {
253- ch <- Conflict {
254- PropertyName : "SELinuxChangePolicy" ,
255- EventReason : "SELinuxChangePolicyConflict" ,
256- Pod : podKey ,
257- PropertyValue : string (podInfo .changePolicy ),
258- OtherPod : otherPodKey ,
259- OtherPropertyValue : string (otherPodInfo .changePolicy ),
260- }
261- }
262- if c .seLinuxTranslator .Conflicts (podInfo .seLinuxLabel , otherPodInfo .seLinuxLabel ) {
263- ch <- Conflict {
264- PropertyName : "SELinuxLabel" ,
265- EventReason : "SELinuxLabelConflict" ,
266- Pod : podKey ,
267- PropertyValue : podInfo .seLinuxLabel ,
268- OtherPod : otherPodKey ,
269- OtherPropertyValue : otherPodInfo .seLinuxLabel ,
270- }
271- }
272- }
273- }
331+ result := sets .New [Conflict ]()
332+ for _ , volConflicts := range c .conflicts {
333+ result .Insert (volConflicts ... )
274334 }
335+ return result .UnsortedList ()
275336}
0 commit comments