k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/kubelet/cm/dra/claiminfo.go (about) 1 /* 2 Copyright 2022 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package dra 18 19 import ( 20 "fmt" 21 "sync" 22 23 resourcev1alpha2 "k8s.io/api/resource/v1alpha2" 24 "k8s.io/apimachinery/pkg/types" 25 "k8s.io/apimachinery/pkg/util/sets" 26 "k8s.io/kubernetes/pkg/kubelet/cm/dra/state" 27 "k8s.io/kubernetes/pkg/kubelet/cm/util/cdi" 28 kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" 29 ) 30 31 // ClaimInfo holds information required 32 // to prepare and unprepare a resource claim. 33 // +k8s:deepcopy-gen=true 34 type ClaimInfo struct { 35 state.ClaimInfoState 36 // annotations is a mapping of container annotations per DRA plugin associated with 37 // a prepared resource 38 annotations map[string][]kubecontainer.Annotation 39 prepared bool 40 } 41 42 // claimInfoCache is a cache of processed resource claims keyed by namespace/claimname. 43 type claimInfoCache struct { 44 sync.RWMutex 45 state state.CheckpointState 46 claimInfo map[string]*ClaimInfo 47 } 48 49 // newClaimInfoFromClaim creates a new claim info from a resource claim. 50 func newClaimInfoFromClaim(claim *resourcev1alpha2.ResourceClaim) *ClaimInfo { 51 // Grab the allocation.resourceHandles. If there are no 52 // allocation.resourceHandles, create a single resourceHandle with no 53 // content. This will trigger processing of this claim by a single 54 // kubelet plugin whose name matches resourceClaim.Status.DriverName. 55 resourceHandles := claim.Status.Allocation.ResourceHandles 56 if len(resourceHandles) == 0 { 57 resourceHandles = make([]resourcev1alpha2.ResourceHandle, 1) 58 } 59 claimInfoState := state.ClaimInfoState{ 60 DriverName: claim.Status.DriverName, 61 ClassName: claim.Spec.ResourceClassName, 62 ClaimUID: claim.UID, 63 ClaimName: claim.Name, 64 Namespace: claim.Namespace, 65 PodUIDs: sets.New[string](), 66 ResourceHandles: resourceHandles, 67 CDIDevices: make(map[string][]string), 68 } 69 info := &ClaimInfo{ 70 ClaimInfoState: claimInfoState, 71 annotations: make(map[string][]kubecontainer.Annotation), 72 prepared: false, 73 } 74 return info 75 } 76 77 // newClaimInfoFromClaim creates a new claim info from a checkpointed claim info state object. 78 func newClaimInfoFromState(state *state.ClaimInfoState) *ClaimInfo { 79 info := &ClaimInfo{ 80 ClaimInfoState: *state.DeepCopy(), 81 annotations: make(map[string][]kubecontainer.Annotation), 82 prepared: false, 83 } 84 for pluginName, devices := range info.CDIDevices { 85 annotations, _ := cdi.GenerateAnnotations(info.ClaimUID, info.DriverName, devices) 86 info.annotations[pluginName] = append(info.annotations[pluginName], annotations...) 87 } 88 return info 89 } 90 91 // setCDIDevices adds a set of CDI devices to the claim info. 92 func (info *ClaimInfo) setCDIDevices(pluginName string, cdiDevices []string) error { 93 // NOTE: Passing CDI device names as annotations is a temporary solution 94 // It will be removed after all runtimes are updated 95 // to get CDI device names from the ContainerConfig.CDIDevices field 96 annotations, err := cdi.GenerateAnnotations(info.ClaimUID, info.DriverName, cdiDevices) 97 if err != nil { 98 return fmt.Errorf("failed to generate container annotations, err: %+v", err) 99 } 100 101 if info.CDIDevices == nil { 102 info.CDIDevices = make(map[string][]string) 103 } 104 105 if info.annotations == nil { 106 info.annotations = make(map[string][]kubecontainer.Annotation) 107 } 108 109 info.CDIDevices[pluginName] = cdiDevices 110 info.annotations[pluginName] = annotations 111 112 return nil 113 } 114 115 // annotationsAsList returns container annotations as a single list. 116 func (info *ClaimInfo) annotationsAsList() []kubecontainer.Annotation { 117 var lst []kubecontainer.Annotation 118 for _, v := range info.annotations { 119 lst = append(lst, v...) 120 } 121 return lst 122 } 123 124 // cdiDevicesAsList returns a list of CDIDevices from the provided claim info. 125 func (info *ClaimInfo) cdiDevicesAsList() []kubecontainer.CDIDevice { 126 var cdiDevices []kubecontainer.CDIDevice 127 for _, devices := range info.CDIDevices { 128 for _, device := range devices { 129 cdiDevices = append(cdiDevices, kubecontainer.CDIDevice{Name: device}) 130 } 131 } 132 return cdiDevices 133 } 134 135 // addPodReference adds a pod reference to the claim info. 136 func (info *ClaimInfo) addPodReference(podUID types.UID) { 137 info.PodUIDs.Insert(string(podUID)) 138 } 139 140 // hasPodReference checks if a pod reference exists in the claim info. 141 func (info *ClaimInfo) hasPodReference(podUID types.UID) bool { 142 return info.PodUIDs.Has(string(podUID)) 143 } 144 145 // deletePodReference deletes a pod reference from the claim info. 146 func (info *ClaimInfo) deletePodReference(podUID types.UID) { 147 info.PodUIDs.Delete(string(podUID)) 148 } 149 150 // setPrepared marks the claim info as prepared. 151 func (info *ClaimInfo) setPrepared() { 152 info.prepared = true 153 } 154 155 // isPrepared checks if claim info is prepared or not. 156 func (info *ClaimInfo) isPrepared() bool { 157 return info.prepared 158 } 159 160 // newClaimInfoCache creates a new claim info cache object, pre-populated from a checkpoint (if present). 161 func newClaimInfoCache(stateDir, checkpointName string) (*claimInfoCache, error) { 162 stateImpl, err := state.NewCheckpointState(stateDir, checkpointName) 163 if err != nil { 164 return nil, fmt.Errorf("could not initialize checkpoint manager, please drain node and remove dra state file, err: %+v", err) 165 } 166 167 curState, err := stateImpl.GetOrCreate() 168 if err != nil { 169 return nil, fmt.Errorf("error calling GetOrCreate() on checkpoint state: %v", err) 170 } 171 172 cache := &claimInfoCache{ 173 state: stateImpl, 174 claimInfo: make(map[string]*ClaimInfo), 175 } 176 177 for _, entry := range curState { 178 info := newClaimInfoFromState(&entry) 179 cache.claimInfo[info.Namespace+"/"+info.ClaimName] = info 180 } 181 182 return cache, nil 183 } 184 185 // withLock runs a function while holding the claimInfoCache lock. 186 func (cache *claimInfoCache) withLock(f func() error) error { 187 cache.Lock() 188 defer cache.Unlock() 189 return f() 190 } 191 192 // withRLock runs a function while holding the claimInfoCache rlock. 193 func (cache *claimInfoCache) withRLock(f func() error) error { 194 cache.RLock() 195 defer cache.RUnlock() 196 return f() 197 } 198 199 // add adds a new claim info object into the claim info cache. 200 func (cache *claimInfoCache) add(info *ClaimInfo) *ClaimInfo { 201 cache.claimInfo[info.Namespace+"/"+info.ClaimName] = info 202 return info 203 } 204 205 // contains checks to see if a specific claim info object is already in the cache. 206 func (cache *claimInfoCache) contains(claimName, namespace string) bool { 207 _, exists := cache.claimInfo[namespace+"/"+claimName] 208 return exists 209 } 210 211 // get gets a specific claim info object from the cache. 212 func (cache *claimInfoCache) get(claimName, namespace string) (*ClaimInfo, bool) { 213 info, exists := cache.claimInfo[namespace+"/"+claimName] 214 return info, exists 215 } 216 217 // delete deletes a specific claim info object from the cache. 218 func (cache *claimInfoCache) delete(claimName, namespace string) { 219 delete(cache.claimInfo, namespace+"/"+claimName) 220 } 221 222 // hasPodReference checks if there is at least one claim 223 // that is referenced by the pod with the given UID 224 // This function is used indirectly by the status manager 225 // to check if pod can enter termination status 226 func (cache *claimInfoCache) hasPodReference(UID types.UID) bool { 227 for _, claimInfo := range cache.claimInfo { 228 if claimInfo.hasPodReference(UID) { 229 return true 230 } 231 } 232 return false 233 } 234 235 // syncToCheckpoint syncs the full claim info cache state to a checkpoint. 236 func (cache *claimInfoCache) syncToCheckpoint() error { 237 claimInfoStateList := make(state.ClaimInfoStateList, 0, len(cache.claimInfo)) 238 for _, infoClaim := range cache.claimInfo { 239 claimInfoStateList = append(claimInfoStateList, infoClaim.ClaimInfoState) 240 } 241 return cache.state.Store(claimInfoStateList) 242 }