k8s.io/kubernetes@v1.29.3/pkg/controller/history/controller_history.go (about) 1 /* 2 Copyright 2017 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 history 18 19 import ( 20 "bytes" 21 "context" 22 "encoding/json" 23 "fmt" 24 "hash/fnv" 25 "sort" 26 "strconv" 27 28 apps "k8s.io/api/apps/v1" 29 appsinformers "k8s.io/client-go/informers/apps/v1" 30 clientset "k8s.io/client-go/kubernetes" 31 appslisters "k8s.io/client-go/listers/apps/v1" 32 "k8s.io/kubernetes/pkg/controller" 33 hashutil "k8s.io/kubernetes/pkg/util/hash" 34 35 apiequality "k8s.io/apimachinery/pkg/api/equality" 36 "k8s.io/apimachinery/pkg/api/errors" 37 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 38 "k8s.io/apimachinery/pkg/labels" 39 "k8s.io/apimachinery/pkg/runtime" 40 "k8s.io/apimachinery/pkg/types" 41 42 "k8s.io/apimachinery/pkg/runtime/schema" 43 "k8s.io/apimachinery/pkg/util/rand" 44 "k8s.io/client-go/tools/cache" 45 "k8s.io/client-go/util/retry" 46 ) 47 48 // ControllerRevisionHashLabel is the label used to indicate the hash value of a ControllerRevision's Data. 49 const ControllerRevisionHashLabel = "controller.kubernetes.io/hash" 50 51 // ControllerRevisionName returns the Name for a ControllerRevision in the form prefix-hash. If the length 52 // of prefix is greater than 223 bytes, it is truncated to allow for a name that is no larger than 253 bytes. 53 func ControllerRevisionName(prefix string, hash string) string { 54 if len(prefix) > 223 { 55 prefix = prefix[:223] 56 } 57 58 return fmt.Sprintf("%s-%s", prefix, hash) 59 } 60 61 // NewControllerRevision returns a ControllerRevision with a ControllerRef pointing to parent and indicating that 62 // parent is of parentKind. The ControllerRevision has labels matching template labels, contains Data equal to data, and 63 // has a Revision equal to revision. The collisionCount is used when creating the name of the ControllerRevision 64 // so the name is likely unique. If the returned error is nil, the returned ControllerRevision is valid. If the 65 // returned error is not nil, the returned ControllerRevision is invalid for use. 66 func NewControllerRevision(parent metav1.Object, 67 parentKind schema.GroupVersionKind, 68 templateLabels map[string]string, 69 data runtime.RawExtension, 70 revision int64, 71 collisionCount *int32) (*apps.ControllerRevision, error) { 72 labelMap := make(map[string]string) 73 for k, v := range templateLabels { 74 labelMap[k] = v 75 } 76 cr := &apps.ControllerRevision{ 77 ObjectMeta: metav1.ObjectMeta{ 78 Labels: labelMap, 79 OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(parent, parentKind)}, 80 }, 81 Data: data, 82 Revision: revision, 83 } 84 hash := HashControllerRevision(cr, collisionCount) 85 cr.Name = ControllerRevisionName(parent.GetName(), hash) 86 cr.Labels[ControllerRevisionHashLabel] = hash 87 return cr, nil 88 } 89 90 // HashControllerRevision hashes the contents of revision's Data using FNV hashing. If probe is not nil, the byte value 91 // of probe is added written to the hash as well. The returned hash will be a safe encoded string to avoid bad words. 92 func HashControllerRevision(revision *apps.ControllerRevision, probe *int32) string { 93 hf := fnv.New32() 94 if len(revision.Data.Raw) > 0 { 95 hf.Write(revision.Data.Raw) 96 } 97 if revision.Data.Object != nil { 98 hashutil.DeepHashObject(hf, revision.Data.Object) 99 } 100 if probe != nil { 101 hf.Write([]byte(strconv.FormatInt(int64(*probe), 10))) 102 } 103 return rand.SafeEncodeString(fmt.Sprint(hf.Sum32())) 104 } 105 106 // SortControllerRevisions sorts revisions by their Revision. 107 func SortControllerRevisions(revisions []*apps.ControllerRevision) { 108 sort.Stable(byRevision(revisions)) 109 } 110 111 // EqualRevision returns true if lhs and rhs are either both nil, or both point to non-nil ControllerRevisions that 112 // contain semantically equivalent data. Otherwise this method returns false. 113 func EqualRevision(lhs *apps.ControllerRevision, rhs *apps.ControllerRevision) bool { 114 var lhsHash, rhsHash *uint32 115 if lhs == nil || rhs == nil { 116 return lhs == rhs 117 } 118 if hs, found := lhs.Labels[ControllerRevisionHashLabel]; found { 119 hash, err := strconv.ParseInt(hs, 10, 32) 120 if err == nil { 121 lhsHash = new(uint32) 122 *lhsHash = uint32(hash) 123 } 124 } 125 if hs, found := rhs.Labels[ControllerRevisionHashLabel]; found { 126 hash, err := strconv.ParseInt(hs, 10, 32) 127 if err == nil { 128 rhsHash = new(uint32) 129 *rhsHash = uint32(hash) 130 } 131 } 132 if lhsHash != nil && rhsHash != nil && *lhsHash != *rhsHash { 133 return false 134 } 135 return bytes.Equal(lhs.Data.Raw, rhs.Data.Raw) && apiequality.Semantic.DeepEqual(lhs.Data.Object, rhs.Data.Object) 136 } 137 138 // FindEqualRevisions returns all ControllerRevisions in revisions that are equal to needle using EqualRevision as the 139 // equality test. The returned slice preserves the order of revisions. 140 func FindEqualRevisions(revisions []*apps.ControllerRevision, needle *apps.ControllerRevision) []*apps.ControllerRevision { 141 var eq []*apps.ControllerRevision 142 for i := range revisions { 143 if EqualRevision(revisions[i], needle) { 144 eq = append(eq, revisions[i]) 145 } 146 } 147 return eq 148 } 149 150 // byRevision implements sort.Interface to allow ControllerRevisions to be sorted by Revision. 151 type byRevision []*apps.ControllerRevision 152 153 func (br byRevision) Len() int { 154 return len(br) 155 } 156 157 // Less breaks ties first by creation timestamp, then by name 158 func (br byRevision) Less(i, j int) bool { 159 if br[i].Revision == br[j].Revision { 160 if br[j].CreationTimestamp.Equal(&br[i].CreationTimestamp) { 161 return br[i].Name < br[j].Name 162 } 163 return br[j].CreationTimestamp.After(br[i].CreationTimestamp.Time) 164 } 165 return br[i].Revision < br[j].Revision 166 } 167 168 func (br byRevision) Swap(i, j int) { 169 br[i], br[j] = br[j], br[i] 170 } 171 172 // Interface provides an interface allowing for management of a Controller's history as realized by recorded 173 // ControllerRevisions. An instance of Interface can be retrieved from NewHistory. Implementations must treat all 174 // pointer parameters as "in" parameter, and they must not be mutated. 175 type Interface interface { 176 // ListControllerRevisions lists all ControllerRevisions matching selector and owned by parent or no other 177 // controller. If the returned error is nil the returned slice of ControllerRevisions is valid. If the 178 // returned error is not nil, the returned slice is not valid. 179 ListControllerRevisions(parent metav1.Object, selector labels.Selector) ([]*apps.ControllerRevision, error) 180 // CreateControllerRevision attempts to create the revision as owned by parent via a ControllerRef. If name 181 // collision occurs, collisionCount (incremented each time collision occurs except for the first time) is 182 // added to the hash of the revision and it is renamed using ControllerRevisionName. Implementations may 183 // cease to attempt to retry creation after some number of attempts and return an error. If the returned 184 // error is not nil, creation failed. If the returned error is nil, the returned ControllerRevision has been 185 // created. 186 // Callers must make sure that collisionCount is not nil. An error is returned if it is. 187 CreateControllerRevision(parent metav1.Object, revision *apps.ControllerRevision, collisionCount *int32) (*apps.ControllerRevision, error) 188 // DeleteControllerRevision attempts to delete revision. If the returned error is not nil, deletion has failed. 189 DeleteControllerRevision(revision *apps.ControllerRevision) error 190 // UpdateControllerRevision updates revision such that its Revision is equal to newRevision. Implementations 191 // may retry on conflict. If the returned error is nil, the update was successful and returned ControllerRevision 192 // is valid. If the returned error is not nil, the update failed and the returned ControllerRevision is invalid. 193 UpdateControllerRevision(revision *apps.ControllerRevision, newRevision int64) (*apps.ControllerRevision, error) 194 // AdoptControllerRevision attempts to adopt revision by adding a ControllerRef indicating that the parent 195 // Object of parentKind is the owner of revision. If revision is already owned, an error is returned. If the 196 // resource patch fails, an error is returned. If no error is returned, the returned ControllerRevision is 197 // valid. 198 AdoptControllerRevision(parent metav1.Object, parentKind schema.GroupVersionKind, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) 199 // ReleaseControllerRevision attempts to release parent's ownership of revision by removing parent from the 200 // OwnerReferences of revision. If an error is returned, parent remains the owner of revision. If no error is 201 // returned, the returned ControllerRevision is valid. 202 ReleaseControllerRevision(parent metav1.Object, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) 203 } 204 205 // NewHistory returns an instance of Interface that uses client to communicate with the API Server and lister to list 206 // ControllerRevisions. This method should be used to create an Interface for all scenarios other than testing. 207 func NewHistory(client clientset.Interface, lister appslisters.ControllerRevisionLister) Interface { 208 return &realHistory{client, lister} 209 } 210 211 // NewFakeHistory returns an instance of Interface that uses informer to create, update, list, and delete 212 // ControllerRevisions. This method should be used to create an Interface for testing purposes. 213 func NewFakeHistory(informer appsinformers.ControllerRevisionInformer) Interface { 214 return &fakeHistory{informer.Informer().GetIndexer(), informer.Lister()} 215 } 216 217 type realHistory struct { 218 client clientset.Interface 219 lister appslisters.ControllerRevisionLister 220 } 221 222 func (rh *realHistory) ListControllerRevisions(parent metav1.Object, selector labels.Selector) ([]*apps.ControllerRevision, error) { 223 // List all revisions in the namespace that match the selector 224 history, err := rh.lister.ControllerRevisions(parent.GetNamespace()).List(selector) 225 if err != nil { 226 return nil, err 227 } 228 var owned []*apps.ControllerRevision 229 for i := range history { 230 ref := metav1.GetControllerOfNoCopy(history[i]) 231 if ref == nil || ref.UID == parent.GetUID() { 232 owned = append(owned, history[i]) 233 } 234 235 } 236 return owned, err 237 } 238 239 func (rh *realHistory) CreateControllerRevision(parent metav1.Object, revision *apps.ControllerRevision, collisionCount *int32) (*apps.ControllerRevision, error) { 240 if collisionCount == nil { 241 return nil, fmt.Errorf("collisionCount should not be nil") 242 } 243 244 // Clone the input 245 clone := revision.DeepCopy() 246 247 // Continue to attempt to create the revision updating the name with a new hash on each iteration 248 for { 249 hash := HashControllerRevision(revision, collisionCount) 250 // Update the revisions name 251 clone.Name = ControllerRevisionName(parent.GetName(), hash) 252 ns := parent.GetNamespace() 253 created, err := rh.client.AppsV1().ControllerRevisions(ns).Create(context.TODO(), clone, metav1.CreateOptions{}) 254 if errors.IsAlreadyExists(err) { 255 exists, err := rh.client.AppsV1().ControllerRevisions(ns).Get(context.TODO(), clone.Name, metav1.GetOptions{}) 256 if err != nil { 257 return nil, err 258 } 259 if bytes.Equal(exists.Data.Raw, clone.Data.Raw) { 260 return exists, nil 261 } 262 *collisionCount++ 263 continue 264 } 265 return created, err 266 } 267 } 268 269 func (rh *realHistory) UpdateControllerRevision(revision *apps.ControllerRevision, newRevision int64) (*apps.ControllerRevision, error) { 270 clone := revision.DeepCopy() 271 err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { 272 if clone.Revision == newRevision { 273 return nil 274 } 275 clone.Revision = newRevision 276 updated, updateErr := rh.client.AppsV1().ControllerRevisions(clone.Namespace).Update(context.TODO(), clone, metav1.UpdateOptions{}) 277 if updateErr == nil { 278 return nil 279 } 280 if updated != nil { 281 clone = updated 282 } 283 if updated, err := rh.lister.ControllerRevisions(clone.Namespace).Get(clone.Name); err == nil { 284 // make a copy so we don't mutate the shared cache 285 clone = updated.DeepCopy() 286 } 287 return updateErr 288 }) 289 return clone, err 290 } 291 292 func (rh *realHistory) DeleteControllerRevision(revision *apps.ControllerRevision) error { 293 return rh.client.AppsV1().ControllerRevisions(revision.Namespace).Delete(context.TODO(), revision.Name, metav1.DeleteOptions{}) 294 } 295 296 type objectForPatch struct { 297 Metadata objectMetaForPatch `json:"metadata"` 298 } 299 300 // objectMetaForPatch define object meta struct for patch operation 301 type objectMetaForPatch struct { 302 OwnerReferences []metav1.OwnerReference `json:"ownerReferences"` 303 UID types.UID `json:"uid"` 304 } 305 306 func (rh *realHistory) AdoptControllerRevision(parent metav1.Object, parentKind schema.GroupVersionKind, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) { 307 blockOwnerDeletion := true 308 isController := true 309 // Return an error if the revision is not orphan 310 if owner := metav1.GetControllerOfNoCopy(revision); owner != nil { 311 return nil, fmt.Errorf("attempt to adopt revision owned by %v", owner) 312 } 313 addControllerPatch := objectForPatch{ 314 Metadata: objectMetaForPatch{ 315 UID: revision.UID, 316 OwnerReferences: []metav1.OwnerReference{{ 317 APIVersion: parentKind.GroupVersion().String(), 318 Kind: parentKind.Kind, 319 Name: parent.GetName(), 320 UID: parent.GetUID(), 321 Controller: &isController, 322 BlockOwnerDeletion: &blockOwnerDeletion, 323 }}, 324 }, 325 } 326 patchBytes, err := json.Marshal(&addControllerPatch) 327 if err != nil { 328 return nil, err 329 } 330 // Use strategic merge patch to add an owner reference indicating a controller ref 331 return rh.client.AppsV1().ControllerRevisions(parent.GetNamespace()).Patch(context.TODO(), revision.GetName(), 332 types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}) 333 } 334 335 func (rh *realHistory) ReleaseControllerRevision(parent metav1.Object, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) { 336 dataBytes, err := controller.GenerateDeleteOwnerRefStrategicMergeBytes(revision.UID, []types.UID{parent.GetUID()}) 337 if err != nil { 338 return nil, err 339 } 340 341 // Use strategic merge patch to add an owner reference indicating a controller ref 342 released, err := rh.client.AppsV1().ControllerRevisions(revision.GetNamespace()).Patch(context.TODO(), revision.GetName(), 343 types.StrategicMergePatchType, dataBytes, metav1.PatchOptions{}) 344 345 if err != nil { 346 if errors.IsNotFound(err) { 347 // We ignore deleted revisions 348 return nil, nil 349 } 350 if errors.IsInvalid(err) { 351 // We ignore cases where the parent no longer owns the revision or where the revision has no 352 // owner. 353 return nil, nil 354 } 355 } 356 return released, err 357 } 358 359 type fakeHistory struct { 360 indexer cache.Indexer 361 lister appslisters.ControllerRevisionLister 362 } 363 364 func (fh *fakeHistory) ListControllerRevisions(parent metav1.Object, selector labels.Selector) ([]*apps.ControllerRevision, error) { 365 history, err := fh.lister.ControllerRevisions(parent.GetNamespace()).List(selector) 366 if err != nil { 367 return nil, err 368 } 369 370 var owned []*apps.ControllerRevision 371 for i := range history { 372 ref := metav1.GetControllerOf(history[i]) 373 if ref == nil || ref.UID == parent.GetUID() { 374 owned = append(owned, history[i]) 375 } 376 377 } 378 return owned, err 379 } 380 381 func (fh *fakeHistory) addRevision(revision *apps.ControllerRevision) (*apps.ControllerRevision, error) { 382 key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(revision) 383 if err != nil { 384 return nil, err 385 } 386 obj, found, err := fh.indexer.GetByKey(key) 387 if err != nil { 388 return nil, err 389 } 390 if found { 391 foundRevision := obj.(*apps.ControllerRevision) 392 return foundRevision, errors.NewAlreadyExists(apps.Resource("controllerrevision"), revision.Name) 393 } 394 return revision, fh.indexer.Update(revision) 395 } 396 397 func (fh *fakeHistory) CreateControllerRevision(parent metav1.Object, revision *apps.ControllerRevision, collisionCount *int32) (*apps.ControllerRevision, error) { 398 if collisionCount == nil { 399 return nil, fmt.Errorf("collisionCount should not be nil") 400 } 401 402 // Clone the input 403 clone := revision.DeepCopy() 404 clone.Namespace = parent.GetNamespace() 405 406 // Continue to attempt to create the revision updating the name with a new hash on each iteration 407 for { 408 hash := HashControllerRevision(revision, collisionCount) 409 // Update the revisions name and labels 410 clone.Name = ControllerRevisionName(parent.GetName(), hash) 411 created, err := fh.addRevision(clone) 412 if errors.IsAlreadyExists(err) { 413 *collisionCount++ 414 continue 415 } 416 return created, err 417 } 418 } 419 420 func (fh *fakeHistory) DeleteControllerRevision(revision *apps.ControllerRevision) error { 421 key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(revision) 422 if err != nil { 423 return err 424 } 425 obj, found, err := fh.indexer.GetByKey(key) 426 if err != nil { 427 return err 428 } 429 if !found { 430 return errors.NewNotFound(apps.Resource("controllerrevisions"), revision.Name) 431 } 432 return fh.indexer.Delete(obj) 433 } 434 435 func (fh *fakeHistory) UpdateControllerRevision(revision *apps.ControllerRevision, newRevision int64) (*apps.ControllerRevision, error) { 436 clone := revision.DeepCopy() 437 clone.Revision = newRevision 438 return clone, fh.indexer.Update(clone) 439 } 440 441 func (fh *fakeHistory) AdoptControllerRevision(parent metav1.Object, parentKind schema.GroupVersionKind, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) { 442 if owner := metav1.GetControllerOf(revision); owner != nil { 443 return nil, fmt.Errorf("attempt to adopt revision owned by %v", owner) 444 } 445 key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(revision) 446 if err != nil { 447 return nil, err 448 } 449 _, found, err := fh.indexer.GetByKey(key) 450 if err != nil { 451 return nil, err 452 } 453 if !found { 454 return nil, errors.NewNotFound(apps.Resource("controllerrevisions"), revision.Name) 455 } 456 clone := revision.DeepCopy() 457 clone.OwnerReferences = append(clone.OwnerReferences, *metav1.NewControllerRef(parent, parentKind)) 458 return clone, fh.indexer.Update(clone) 459 } 460 461 func (fh *fakeHistory) ReleaseControllerRevision(parent metav1.Object, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) { 462 key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(revision) 463 if err != nil { 464 return nil, err 465 } 466 _, found, err := fh.indexer.GetByKey(key) 467 if err != nil { 468 return nil, err 469 } 470 if !found { 471 return nil, nil 472 } 473 clone := revision.DeepCopy() 474 refs := clone.OwnerReferences 475 clone.OwnerReferences = nil 476 for i := range refs { 477 if refs[i].UID != parent.GetUID() { 478 clone.OwnerReferences = append(clone.OwnerReferences, refs[i]) 479 } 480 } 481 return clone, fh.indexer.Update(clone) 482 }