...

Source file src/k8s.io/kubernetes/pkg/controller/replicaset/replica_set_utils.go

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

     1  /*
     2  Copyright 2016 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  // If you make changes to this file, you should also make the corresponding change in ReplicationController.
    18  
    19  package replicaset
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"reflect"
    25  
    26  	"k8s.io/klog/v2"
    27  
    28  	apps "k8s.io/api/apps/v1"
    29  	v1 "k8s.io/api/core/v1"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/apimachinery/pkg/labels"
    32  	appsclient "k8s.io/client-go/kubernetes/typed/apps/v1"
    33  	podutil "k8s.io/kubernetes/pkg/api/v1/pod"
    34  )
    35  
    36  // updateReplicaSetStatus attempts to update the Status.Replicas of the given ReplicaSet, with a single GET/PUT retry.
    37  func updateReplicaSetStatus(logger klog.Logger, c appsclient.ReplicaSetInterface, rs *apps.ReplicaSet, newStatus apps.ReplicaSetStatus) (*apps.ReplicaSet, error) {
    38  	// This is the steady state. It happens when the ReplicaSet doesn't have any expectations, since
    39  	// we do a periodic relist every 30s. If the generations differ but the replicas are
    40  	// the same, a caller might've resized to the same replica count.
    41  	if rs.Status.Replicas == newStatus.Replicas &&
    42  		rs.Status.FullyLabeledReplicas == newStatus.FullyLabeledReplicas &&
    43  		rs.Status.ReadyReplicas == newStatus.ReadyReplicas &&
    44  		rs.Status.AvailableReplicas == newStatus.AvailableReplicas &&
    45  		rs.Generation == rs.Status.ObservedGeneration &&
    46  		reflect.DeepEqual(rs.Status.Conditions, newStatus.Conditions) {
    47  		return rs, nil
    48  	}
    49  
    50  	// Save the generation number we acted on, otherwise we might wrongfully indicate
    51  	// that we've seen a spec update when we retry.
    52  	// TODO: This can clobber an update if we allow multiple agents to write to the
    53  	// same status.
    54  	newStatus.ObservedGeneration = rs.Generation
    55  
    56  	var getErr, updateErr error
    57  	var updatedRS *apps.ReplicaSet
    58  	for i, rs := 0, rs; ; i++ {
    59  		logger.V(4).Info(fmt.Sprintf("Updating status for %v: %s/%s, ", rs.Kind, rs.Namespace, rs.Name) +
    60  			fmt.Sprintf("replicas %d->%d (need %d), ", rs.Status.Replicas, newStatus.Replicas, *(rs.Spec.Replicas)) +
    61  			fmt.Sprintf("fullyLabeledReplicas %d->%d, ", rs.Status.FullyLabeledReplicas, newStatus.FullyLabeledReplicas) +
    62  			fmt.Sprintf("readyReplicas %d->%d, ", rs.Status.ReadyReplicas, newStatus.ReadyReplicas) +
    63  			fmt.Sprintf("availableReplicas %d->%d, ", rs.Status.AvailableReplicas, newStatus.AvailableReplicas) +
    64  			fmt.Sprintf("sequence No: %v->%v", rs.Status.ObservedGeneration, newStatus.ObservedGeneration))
    65  
    66  		rs.Status = newStatus
    67  		updatedRS, updateErr = c.UpdateStatus(context.TODO(), rs, metav1.UpdateOptions{})
    68  		if updateErr == nil {
    69  			return updatedRS, nil
    70  		}
    71  		// Stop retrying if we exceed statusUpdateRetries - the replicaSet will be requeued with a rate limit.
    72  		if i >= statusUpdateRetries {
    73  			break
    74  		}
    75  		// Update the ReplicaSet with the latest resource version for the next poll
    76  		if rs, getErr = c.Get(context.TODO(), rs.Name, metav1.GetOptions{}); getErr != nil {
    77  			// If the GET fails we can't trust status.Replicas anymore. This error
    78  			// is bound to be more interesting than the update failure.
    79  			return nil, getErr
    80  		}
    81  	}
    82  
    83  	return nil, updateErr
    84  }
    85  
    86  func calculateStatus(rs *apps.ReplicaSet, filteredPods []*v1.Pod, manageReplicasErr error) apps.ReplicaSetStatus {
    87  	newStatus := rs.Status
    88  	// Count the number of pods that have labels matching the labels of the pod
    89  	// template of the replica set, the matching pods may have more
    90  	// labels than are in the template. Because the label of podTemplateSpec is
    91  	// a superset of the selector of the replica set, so the possible
    92  	// matching pods must be part of the filteredPods.
    93  	fullyLabeledReplicasCount := 0
    94  	readyReplicasCount := 0
    95  	availableReplicasCount := 0
    96  	templateLabel := labels.Set(rs.Spec.Template.Labels).AsSelectorPreValidated()
    97  	for _, pod := range filteredPods {
    98  		if templateLabel.Matches(labels.Set(pod.Labels)) {
    99  			fullyLabeledReplicasCount++
   100  		}
   101  		if podutil.IsPodReady(pod) {
   102  			readyReplicasCount++
   103  			if podutil.IsPodAvailable(pod, rs.Spec.MinReadySeconds, metav1.Now()) {
   104  				availableReplicasCount++
   105  			}
   106  		}
   107  	}
   108  
   109  	failureCond := GetCondition(rs.Status, apps.ReplicaSetReplicaFailure)
   110  	if manageReplicasErr != nil && failureCond == nil {
   111  		var reason string
   112  		if diff := len(filteredPods) - int(*(rs.Spec.Replicas)); diff < 0 {
   113  			reason = "FailedCreate"
   114  		} else if diff > 0 {
   115  			reason = "FailedDelete"
   116  		}
   117  		cond := NewReplicaSetCondition(apps.ReplicaSetReplicaFailure, v1.ConditionTrue, reason, manageReplicasErr.Error())
   118  		SetCondition(&newStatus, cond)
   119  	} else if manageReplicasErr == nil && failureCond != nil {
   120  		RemoveCondition(&newStatus, apps.ReplicaSetReplicaFailure)
   121  	}
   122  
   123  	newStatus.Replicas = int32(len(filteredPods))
   124  	newStatus.FullyLabeledReplicas = int32(fullyLabeledReplicasCount)
   125  	newStatus.ReadyReplicas = int32(readyReplicasCount)
   126  	newStatus.AvailableReplicas = int32(availableReplicasCount)
   127  	return newStatus
   128  }
   129  
   130  // NewReplicaSetCondition creates a new replicaset condition.
   131  func NewReplicaSetCondition(condType apps.ReplicaSetConditionType, status v1.ConditionStatus, reason, msg string) apps.ReplicaSetCondition {
   132  	return apps.ReplicaSetCondition{
   133  		Type:               condType,
   134  		Status:             status,
   135  		LastTransitionTime: metav1.Now(),
   136  		Reason:             reason,
   137  		Message:            msg,
   138  	}
   139  }
   140  
   141  // GetCondition returns a replicaset condition with the provided type if it exists.
   142  func GetCondition(status apps.ReplicaSetStatus, condType apps.ReplicaSetConditionType) *apps.ReplicaSetCondition {
   143  	for _, c := range status.Conditions {
   144  		if c.Type == condType {
   145  			return &c
   146  		}
   147  	}
   148  	return nil
   149  }
   150  
   151  // SetCondition adds/replaces the given condition in the replicaset status. If the condition that we
   152  // are about to add already exists and has the same status and reason then we are not going to update.
   153  func SetCondition(status *apps.ReplicaSetStatus, condition apps.ReplicaSetCondition) {
   154  	currentCond := GetCondition(*status, condition.Type)
   155  	if currentCond != nil && currentCond.Status == condition.Status && currentCond.Reason == condition.Reason {
   156  		return
   157  	}
   158  	newConditions := filterOutCondition(status.Conditions, condition.Type)
   159  	status.Conditions = append(newConditions, condition)
   160  }
   161  
   162  // RemoveCondition removes the condition with the provided type from the replicaset status.
   163  func RemoveCondition(status *apps.ReplicaSetStatus, condType apps.ReplicaSetConditionType) {
   164  	status.Conditions = filterOutCondition(status.Conditions, condType)
   165  }
   166  
   167  // filterOutCondition returns a new slice of replicaset conditions without conditions with the provided type.
   168  func filterOutCondition(conditions []apps.ReplicaSetCondition, condType apps.ReplicaSetConditionType) []apps.ReplicaSetCondition {
   169  	var newConditions []apps.ReplicaSetCondition
   170  	for _, c := range conditions {
   171  		if c.Type == condType {
   172  			continue
   173  		}
   174  		newConditions = append(newConditions, c)
   175  	}
   176  	return newConditions
   177  }
   178  

View as plain text