diff --git a/apis/apps/v1alpha1/imagepulljob_types.go b/apis/apps/v1alpha1/imagepulljob_types.go index 1b5e5fc693..0b897a1a74 100644 --- a/apis/apps/v1alpha1/imagepulljob_types.go +++ b/apis/apps/v1alpha1/imagepulljob_types.go @@ -22,8 +22,9 @@ import ( ) const ( - ImagePreDownloadParallelismKey = "apps.kruise.io/image-predownload-parallelism" - ImagePreDownloadTimeoutSecondsKey = "apps.kruise.io/image-predownload-timeout-seconds" + ImagePreDownloadParallelismKey = "apps.kruise.io/image-predownload-parallelism" + ImagePreDownloadTimeoutSecondsKey = "apps.kruise.io/image-predownload-timeout-seconds" + ImagePreDownloadMinUpdatedReadyPods = "apps.kruise.io/image-predownload-min-updated-ready-pods" ) // ImagePullJobSpec defines the desired state of ImagePullJob diff --git a/apis/apps/v1beta1/statefulset_types.go b/apis/apps/v1beta1/statefulset_types.go index 9571725d96..fda75b395c 100644 --- a/apis/apps/v1beta1/statefulset_types.go +++ b/apis/apps/v1beta1/statefulset_types.go @@ -224,6 +224,9 @@ type StatefulSetStatus struct { // indicated by updateRevision. UpdatedReplicas int32 `json:"updatedReplicas"` + // updatedReadyReplicas is the number of updated Pods created by the StatefulSet controller that have a Ready Condition. + UpdatedReadyReplicas int32 `json:"updatedReadyReplicas,omitempty"` + // currentRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the // sequence [0,currentReplicas). CurrentRevision string `json:"currentRevision,omitempty"` diff --git a/config/crd/bases/apps.kruise.io_statefulsets.yaml b/config/crd/bases/apps.kruise.io_statefulsets.yaml index 52b45336f6..b6b80775bb 100644 --- a/config/crd/bases/apps.kruise.io_statefulsets.yaml +++ b/config/crd/bases/apps.kruise.io_statefulsets.yaml @@ -905,6 +905,11 @@ spec: description: updateRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [replicas-updatedReplicas,replicas) type: string + updatedReadyReplicas: + description: updatedReadyReplicas is the number of updated Pods created + by the StatefulSet controller that have a Ready Condition. + format: int32 + type: integer updatedReplicas: description: updatedReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by diff --git a/pkg/controller/cloneset/cloneset_controller.go b/pkg/controller/cloneset/cloneset_controller.go index b5a545d96b..54f5f7705c 100644 --- a/pkg/controller/cloneset/cloneset_controller.go +++ b/pkg/controller/cloneset/cloneset_controller.go @@ -44,6 +44,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" + intstrutil "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/sets" v1core "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/tools/record" @@ -301,9 +302,24 @@ func (r *ReconcileCloneSet) doReconcile(request reconcile.Request) (res reconcil if !isPreDownloadDisabled { if currentRevision.Name != updateRevision.Name { - // pre-download images for new revision - if err := r.createImagePullJobsForInPlaceUpdate(instance, currentRevision, updateRevision); err != nil { - klog.Errorf("Failed to create ImagePullJobs for %s: %v", request, err) + // get clone pre-download annotation + minUpdatedReadyPodsCount := 0 + if minUpdatedReadyPods, ok := instance.Annotations[appsv1alpha1.ImagePreDownloadMinUpdatedReadyPods]; ok { + minUpdatedReadyPodsIntStr := intstrutil.Parse(minUpdatedReadyPods) + minUpdatedReadyPodsCount, err = intstrutil.GetScaledValueFromIntOrPercent(&minUpdatedReadyPodsIntStr, int(*instance.Spec.Replicas), true) + if err != nil { + klog.Errorf("Failed to GetScaledValueFromIntOrPercent of minUpdatedReadyPods for %s: %v", request, err) + } + } + updatedReadyReplicas := instance.Status.UpdatedReadyReplicas + if updateRevision.Name != instance.Status.UpdateRevision { + updatedReadyReplicas = 0 + } + if int32(minUpdatedReadyPodsCount) <= updatedReadyReplicas { + // pre-download images for new revision + if err := r.createImagePullJobsForInPlaceUpdate(instance, currentRevision, updateRevision); err != nil { + klog.Errorf("Failed to create ImagePullJobs for %s: %v", request, err) + } } } else { // delete ImagePullJobs if revisions have been consistent diff --git a/pkg/controller/statefulset/stateful_set_control.go b/pkg/controller/statefulset/stateful_set_control.go index 8ade085ba6..94879915a8 100644 --- a/pkg/controller/statefulset/stateful_set_control.go +++ b/pkg/controller/statefulset/stateful_set_control.go @@ -34,6 +34,7 @@ import ( utilpointer "k8s.io/utils/pointer" appspub "github.com/openkruise/kruise/apis/apps/pub" + appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1" appsv1beta1 "github.com/openkruise/kruise/apis/apps/v1beta1" imagejobutilfunc "github.com/openkruise/kruise/pkg/util/imagejob/utilfunction" "github.com/openkruise/kruise/pkg/util/inplaceupdate" @@ -310,9 +311,24 @@ func (ssc *defaultStatefulSetControl) updateStatefulSet( if !isPreDownloadDisabled && sigsruntimeClient != nil { if currentRevision.Name != updateRevision.Name { - // pre-download images for new revision - if err := ssc.createImagePullJobsForInPlaceUpdate(set, currentRevision, updateRevision); err != nil { - klog.Errorf("Failed to create ImagePullJobs for %v: %v", set, err) + // get asts pre-download annotation + minUpdatedReadyPodsCount := 0 + if minUpdatedReadyPods, ok := set.Annotations[appsv1alpha1.ImagePreDownloadMinUpdatedReadyPods]; ok { + minUpdatedReadyPodsIntStr := intstrutil.Parse(minUpdatedReadyPods) + minUpdatedReadyPodsCount, err = intstrutil.GetScaledValueFromIntOrPercent(&minUpdatedReadyPodsIntStr, int(*set.Spec.Replicas), true) + if err != nil { + klog.Errorf("Failed to GetScaledValueFromIntOrPercent of minUpdatedReadyPods for %s: %v", set, err) + } + } + updatedReadyReplicas := set.Status.UpdatedReadyReplicas + if updateRevision.Name != set.Status.UpdateRevision { + updatedReadyReplicas = 0 + } + if int32(minUpdatedReadyPodsCount) <= updatedReadyReplicas { + // pre-download images for new revision + if err := ssc.createImagePullJobsForInPlaceUpdate(set, currentRevision, updateRevision); err != nil { + klog.Errorf("Failed to create ImagePullJobs for %v: %v", set, err) + } } } else { // delete ImagePullJobs if revisions have been consistent @@ -367,6 +383,9 @@ func (ssc *defaultStatefulSetControl) updateStatefulSet( // count the number of running and ready replicas if isRunningAndReady(pods[i]) { status.ReadyReplicas++ + if getPodRevision(pods[i]) == updateRevision.Name { + status.UpdatedReadyReplicas++ + } if avail, _ := isRunningAndAvailable(pods[i], minReadySeconds); avail { status.AvailableReplicas++ }