...

Source file src/k8s.io/kubernetes/pkg/controller/history/controller_history.go

Documentation: k8s.io/kubernetes/pkg/controller/history

     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  }
   483  

View as plain text