rollouts/pkg/controller/batchrelease/context/context.go

115 lines
4.2 KiB
Go

/*
Copyright 2022 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package context
import (
"encoding/json"
"fmt"
"github.com/openkruise/rollouts/pkg/util"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)
type BatchContext struct {
RolloutID string `json:"rolloutID,omitempty"`
// current batch index, start from 0
CurrentBatch int32 `json:"currentBatchIndex"`
// workload update revision
UpdateRevision string `json:"updateRevision,omitempty"`
// workload replicas
Replicas int32 `json:"replicas"`
// Updated replicas
UpdatedReplicas int32 `json:"updatedReplicas"`
// Updated ready replicas
UpdatedReadyReplicas int32 `json:"updatedReadyReplicas"`
// no need update replicas that marked before rollout
NoNeedUpdatedReplicas *int32 `json:"noNeedUpdatedReplicas,omitempty"`
// the planned number of Pods should be upgrade in current batch
// this field corresponds to releasePlan.Batches[currentBatch]
PlannedUpdatedReplicas int32 `json:"plannedUpdatedReplicas,omitempty"`
// the total number of the really updated pods you desired in current batch.
// In most normal cases, this field will equal to PlannedUpdatedReplicas,
// but in some scene, e.g., rolling back in batches, the really desired updated
// replicas will not equal to planned update replicas, because we just roll the
// pods that really need update back in batches.
DesiredUpdatedReplicas int32 `json:"desiredUpdatedReplicas,omitempty"`
// workload current partition
CurrentPartition intstr.IntOrString `json:"currentPartition,omitempty"`
// desired partition replicas in current batch
DesiredPartition intstr.IntOrString `json:"desiredPartition,omitempty"`
// failureThreshold to tolerate unready updated replicas;
FailureThreshold *intstr.IntOrString `json:"failureThreshold,omitempty"`
// the pods owned by workload
Pods []*corev1.Pod `json:"-"`
// filter or sort pods before patch label
FilterFunc FilterFuncType `json:"-"`
}
type FilterFuncType func(pods []*corev1.Pod, ctx *BatchContext) []*corev1.Pod
func (bc *BatchContext) Log() string {
marshal, _ := json.Marshal(bc)
return fmt.Sprintf("%s with %d pods", string(marshal), len(bc.Pods))
}
// IsBatchReady return nil if the batch is ready
func (bc *BatchContext) IsBatchReady() error {
if bc.UpdatedReplicas < bc.DesiredUpdatedReplicas {
return fmt.Errorf("current batch not ready: updated replicas not satified")
}
unavailableToleration := allowedUnavailable(bc.FailureThreshold, bc.UpdatedReplicas)
if unavailableToleration+bc.UpdatedReadyReplicas < bc.DesiredUpdatedReplicas {
return fmt.Errorf("current batch not ready: updated ready replicas not satified")
}
if bc.DesiredUpdatedReplicas > 0 && bc.UpdatedReadyReplicas == 0 {
return fmt.Errorf("current batch not ready: no updated ready replicas")
}
if !batchLabelSatisfied(bc.Pods, bc.RolloutID, bc.PlannedUpdatedReplicas) {
return fmt.Errorf("current batch not ready: pods with batch label not satified")
}
return nil
}
// batchLabelSatisfied return true if the expected batch label has been patched
func batchLabelSatisfied(pods []*corev1.Pod, rolloutID string, targetCount int32) bool {
if rolloutID == "" || len(pods) == 0 {
return true
}
patchedCount := util.WrappedPodCount(pods, func(pod *corev1.Pod) bool {
if !pod.DeletionTimestamp.IsZero() {
return false
}
return pod.Labels[util.RolloutIDLabel] == rolloutID
})
return patchedCount >= int(targetCount)
}
// allowedUnavailable return absolute number of failure threshold
func allowedUnavailable(threshold *intstr.IntOrString, replicas int32) int32 {
failureThreshold := 0
if threshold != nil {
failureThreshold, _ = intstr.GetScaledValueFromIntOrPercent(threshold, int(replicas), true)
}
return int32(failureThreshold)
}