115 lines
4.2 KiB
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)
|
|
}
|