add pub interface GetPodsForPub (#758)

Signed-off-by: liheng.zms <liheng.zms@alibaba-inc.com>
This commit is contained in:
berg 2021-09-22 14:15:09 +08:00 committed by GitHub
parent e4956e650b
commit c8f0a54c5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 1768 additions and 48 deletions

View File

@ -79,7 +79,7 @@ type PodUnavailableBudgetStatus struct {
// DesiredAvailable minimum desired number of available pods
DesiredAvailable int32 `json:"desiredAvailable"`
// TotalReplicas total number of pods counted by this budget
// TotalReplicas total number of pods counted by this unavailable budget
TotalReplicas int32 `json:"totalReplicas"`
}

View File

@ -129,7 +129,7 @@ spec:
format: int64
type: integer
totalReplicas:
description: TotalReplicas total number of pods counted by this budget
description: TotalReplicas total number of pods counted by this unavailable budget
format: int32
type: integer
unavailableAllowed:

View File

@ -18,8 +18,9 @@ package pubcontrol
import (
policyv1alpha1 "github.com/openkruise/kruise/apis/policy/v1alpha1"
"github.com/openkruise/kruise/pkg/util/controllerfinder"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
type PubControl interface {
@ -32,12 +33,17 @@ type PubControl interface {
IsPodReady(pod *corev1.Pod) bool
// IsPodStateConsistent indicates whether pod.spec and pod.status are consistent after updating containers
IsPodStateConsistent(pod *corev1.Pod) bool
// GetPodsForPub returns Pods protected by the pub object.
// return two parameters
// 1. podList
// 2. expectedCount, the default is workload.Replicas
GetPodsForPub() ([]*corev1.Pod, int32, error)
// webhook
// determine if this change to pod might cause unavailability
IsPodUnavailableChanged(oldPod, newPod *corev1.Pod) bool
}
func NewPubControl(pub *policyv1alpha1.PodUnavailableBudget) PubControl {
return &commonControl{PodUnavailableBudget: pub}
func NewPubControl(pub *policyv1alpha1.PodUnavailableBudget, controllerFinder *controllerfinder.ControllerFinder, client client.Client) PubControl {
return &commonControl{PodUnavailableBudget: pub, controllerFinder: controllerFinder, Client: client}
}

View File

@ -17,21 +17,26 @@ limitations under the License.
package pubcontrol
import (
"context"
"reflect"
"strings"
policyv1alpha1 "github.com/openkruise/kruise/apis/policy/v1alpha1"
"github.com/openkruise/kruise/pkg/control/sidecarcontrol"
"github.com/openkruise/kruise/pkg/util"
"github.com/openkruise/kruise/pkg/util/controllerfinder"
"github.com/openkruise/kruise/pkg/util/inplaceupdate"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/klog/v2"
kubecontroller "k8s.io/kubernetes/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/client"
)
type commonControl struct {
client.Client
*policyv1alpha1.PodUnavailableBudget
controllerFinder *controllerfinder.ControllerFinder
}
func (c *commonControl) GetPodUnavailableBudget() *policyv1alpha1.PodUnavailableBudget {
@ -58,6 +63,49 @@ func (c *commonControl) IsPodUnavailableChanged(oldPod, newPod *corev1.Pod) bool
return false
}
// GetPodsForPub returns Pods protected by the pub object.
// return two parameters
// 1. podList
// 2. expectedCount, the default is workload.Replicas
func (c *commonControl) GetPodsForPub() ([]*corev1.Pod, int32, error) {
pub := c.GetPodUnavailableBudget()
// if targetReference isn't nil, priority to take effect
var listOptions *client.ListOptions
if pub.Spec.TargetReference != nil {
ref := pub.Spec.TargetReference
matchedPods, expectedCount, err := c.controllerFinder.GetPodsForRef(ref.APIVersion, ref.Kind, ref.Name, pub.Namespace, true)
return matchedPods, expectedCount, err
} else if pub.Spec.Selector == nil {
klog.Warningf("pub(%s/%s) spec.Selector cannot be empty", pub.Namespace, pub.Name)
return nil, 0, nil
}
// get pods for selector
labelSelector, err := util.GetFastLabelSelector(pub.Spec.Selector)
if err != nil {
klog.Warningf("pub(%s/%s) GetFastLabelSelector failed: %s", pub.Namespace, pub.Name, err.Error())
return nil, 0, nil
}
listOptions = &client.ListOptions{Namespace: pub.Namespace, LabelSelector: labelSelector}
podList := &corev1.PodList{}
if err := c.List(context.TODO(), podList, listOptions); err != nil {
return nil, 0, err
}
matchedPods := make([]*corev1.Pod, 0, len(podList.Items))
for i := range podList.Items {
pod := &podList.Items[i]
if kubecontroller.IsPodActive(pod) {
matchedPods = append(matchedPods, pod)
}
}
expectedCount, err := c.controllerFinder.GetExpectedScaleForPods(matchedPods)
if err != nil {
return nil, 0, err
}
return matchedPods, expectedCount, nil
}
func (c *commonControl) IsPodStateConsistent(pod *corev1.Pod) bool {
// if all container image is digest format
// by comparing status.containers[x].ImageID with spec.container[x].Image can determine whether pod is consistent

View File

@ -0,0 +1,318 @@
/*
Copyright 2021 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 pubcontrol
import (
"testing"
policyv1alpha1 "github.com/openkruise/kruise/apis/policy/v1alpha1"
"github.com/openkruise/kruise/pkg/util/controllerfinder"
apps "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
utilpointer "k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
)
func init() {
scheme = runtime.NewScheme()
_ = policyv1alpha1.AddToScheme(scheme)
_ = corev1.AddToScheme(scheme)
_ = apps.AddToScheme(scheme)
}
var (
scheme *runtime.Scheme
pubDemo = policyv1alpha1.PodUnavailableBudget{
TypeMeta: metav1.TypeMeta{
APIVersion: policyv1alpha1.GroupVersion.String(),
Kind: "PodUnavailableBudget",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "pub-test",
},
Spec: policyv1alpha1.PodUnavailableBudgetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"pub-controller": "true",
},
},
MaxUnavailable: &intstr.IntOrString{
Type: intstr.String,
StrVal: "30%",
},
},
Status: policyv1alpha1.PodUnavailableBudgetStatus{
UnavailablePods: map[string]metav1.Time{},
DisruptedPods: map[string]metav1.Time{},
},
}
podDemo = &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
Labels: map[string]string{"app": "nginx", "pub-controller": "true"},
Annotations: map[string]string{},
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "apps/v1",
Kind: "ReplicaSet",
Name: "nginx",
UID: types.UID("606132e0-85ef-460a-8cf5-cd8f915a8cc3"),
Controller: utilpointer.BoolPtr(true),
},
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "nginx",
Image: "nginx:v1",
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodRunning,
Conditions: []corev1.PodCondition{
{
Type: corev1.PodReady,
Status: corev1.ConditionTrue,
},
},
ContainerStatuses: []corev1.ContainerStatus{
{
Name: "nginx",
Image: "nginx:v1",
ImageID: "nginx@sha256:a9286defaba7b3a519d585ba0e37d0b2cbee74ebfe590960b0b1d6a5e97d1e1d",
Ready: true,
},
},
},
}
deploymentDemo = &apps.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "apps/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "nginx",
Namespace: "default",
UID: types.UID("f6d5b184-d82f-461c-a432-fbd59e2f0379"),
},
Spec: apps.DeploymentSpec{
Replicas: utilpointer.Int32Ptr(10),
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "nginx",
},
},
},
}
replicaSetDemo = &apps.ReplicaSet{
TypeMeta: metav1.TypeMeta{
Kind: "ReplicaSet",
APIVersion: "apps/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "nginx",
Namespace: "default",
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "apps/v1",
Kind: "Deployment",
Name: "nginx",
UID: types.UID("f6d5b184-d82f-461c-a432-fbd59e2f0379"),
Controller: utilpointer.BoolPtr(true),
},
},
UID: types.UID("606132e0-85ef-460a-8cf5-cd8f915a8cc3"),
Labels: map[string]string{
"app": "nginx",
},
},
Spec: apps.ReplicaSetSpec{
Replicas: utilpointer.Int32Ptr(10),
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "nginx",
},
},
},
}
)
func TestGetPodUnavailableBudgetForPod(t *testing.T) {
cases := []struct {
name string
getPod func() *corev1.Pod
getDeployment func() *apps.Deployment
getReplicaSet func() *apps.ReplicaSet
getPub func() *policyv1alpha1.PodUnavailableBudget
matchedPub bool
}{
{
name: "matched pub targetRef deployment",
getPod: func() *corev1.Pod {
pod := podDemo.DeepCopy()
return pod
},
getDeployment: func() *apps.Deployment {
dep := deploymentDemo.DeepCopy()
return dep
},
getReplicaSet: func() *apps.ReplicaSet {
rep := replicaSetDemo.DeepCopy()
return rep
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
pub.Spec.Selector = nil
pub.Spec.TargetReference = &policyv1alpha1.TargetReference{
Name: deploymentDemo.Name,
Kind: deploymentDemo.Kind,
APIVersion: deploymentDemo.APIVersion,
}
return pub
},
matchedPub: true,
},
{
name: "no matched pub targetRef deployment, for unequal name",
getPod: func() *corev1.Pod {
pod := podDemo.DeepCopy()
return pod
},
getDeployment: func() *apps.Deployment {
dep := deploymentDemo.DeepCopy()
return dep
},
getReplicaSet: func() *apps.ReplicaSet {
rep := replicaSetDemo.DeepCopy()
return rep
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
pub.Spec.Selector = nil
pub.Spec.TargetReference = &policyv1alpha1.TargetReference{
Name: "no-deployment",
Kind: deploymentDemo.Kind,
APIVersion: deploymentDemo.APIVersion,
}
return pub
},
matchedPub: false,
},
{
name: "no matched pub targetRef deployment, for unequal ns",
getPod: func() *corev1.Pod {
pod := podDemo.DeepCopy()
return pod
},
getDeployment: func() *apps.Deployment {
dep := deploymentDemo.DeepCopy()
return dep
},
getReplicaSet: func() *apps.ReplicaSet {
rep := replicaSetDemo.DeepCopy()
return rep
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
pub.Namespace = "no-ns"
pub.Spec.Selector = nil
pub.Spec.TargetReference = &policyv1alpha1.TargetReference{
Name: deploymentDemo.Name,
Kind: deploymentDemo.Kind,
APIVersion: deploymentDemo.APIVersion,
}
return pub
},
matchedPub: false,
},
{
name: "matched pub selector",
getPod: func() *corev1.Pod {
pod := podDemo.DeepCopy()
return pod
},
getDeployment: func() *apps.Deployment {
dep := deploymentDemo.DeepCopy()
return dep
},
getReplicaSet: func() *apps.ReplicaSet {
rep := replicaSetDemo.DeepCopy()
return rep
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
return pub
},
matchedPub: true,
},
{
name: "no match pub selector",
getPod: func() *corev1.Pod {
pod := podDemo.DeepCopy()
return pod
},
getDeployment: func() *apps.Deployment {
dep := deploymentDemo.DeepCopy()
return dep
},
getReplicaSet: func() *apps.ReplicaSet {
rep := replicaSetDemo.DeepCopy()
return rep
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
pub.Spec.Selector = &metav1.LabelSelector{
MatchLabels: map[string]string{
"pub-controller": "false",
},
}
return pub
},
matchedPub: false,
},
}
for _, cs := range cases {
t.Run(cs.name, func(t *testing.T) {
fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(cs.getDeployment(), cs.getReplicaSet(), cs.getPub()).Build()
controllerFinder := controllerfinder.NewControllerFinder(fakeClient)
pod := cs.getPod()
pub, err := GetPodUnavailableBudgetForPod(fakeClient, controllerFinder, pod)
if err != nil {
t.Fatalf("GetPodUnavailableBudgetForPod failed: %s", err.Error())
}
if cs.matchedPub && pub == nil {
t.Fatalf("GetPodUnavailableBudgetForPod failed")
}
if !cs.matchedPub && pub != nil {
t.Fatalf("GetPodUnavailableBudgetForPod failed")
}
})
}
}

View File

@ -21,6 +21,7 @@ import (
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
clonesetutils "github.com/openkruise/kruise/pkg/controller/cloneset/utils"
"github.com/openkruise/kruise/pkg/util/controllerfinder"
"github.com/openkruise/kruise/pkg/util/inplaceupdate"
"github.com/openkruise/kruise/pkg/util/lifecycle"
apps "k8s.io/api/apps/v1"
@ -48,6 +49,7 @@ type realControl struct {
lifecycleControl lifecycle.Interface
inplaceControl inplaceupdate.Interface
recorder record.EventRecorder
controllerFinder *controllerfinder.ControllerFinder
}
func New(c client.Client, recorder record.EventRecorder) Interface {
@ -56,5 +58,6 @@ func New(c client.Client, recorder record.EventRecorder) Interface {
inplaceControl: inplaceupdate.New(c, clonesetutils.RevisionAdapterImpl),
lifecycleControl: lifecycle.New(c),
recorder: recorder,
controllerFinder: controllerfinder.NewControllerFinder(c),
}
}

View File

@ -29,7 +29,6 @@ import (
clonesetutils "github.com/openkruise/kruise/pkg/controller/cloneset/utils"
"github.com/openkruise/kruise/pkg/features"
"github.com/openkruise/kruise/pkg/util"
"github.com/openkruise/kruise/pkg/util/controllerfinder"
utilfeature "github.com/openkruise/kruise/pkg/util/feature"
"github.com/openkruise/kruise/pkg/util/inplaceupdate"
"github.com/openkruise/kruise/pkg/util/lifecycle"
@ -122,7 +121,7 @@ func (c *realControl) Update(cs *appsv1alpha1.CloneSet,
var pub *policyv1alpha1.PodUnavailableBudget
var err error
if utilfeature.DefaultFeatureGate.Enabled(features.PodUnavailableBudgetUpdateGate) && len(waitUpdateIndexes) > 0 {
pub, err = pubcontrol.GetPodUnavailableBudgetForPod(c.Client, controllerfinder.NewControllerFinder(c.Client), pods[waitUpdateIndexes[0]])
pub, err = pubcontrol.GetPodUnavailableBudgetForPod(c.Client, c.controllerFinder, pods[waitUpdateIndexes[0]])
if err != nil {
return requeueDuration.Get(), err
}
@ -132,7 +131,7 @@ func (c *realControl) Update(cs *appsv1alpha1.CloneSet,
pod := pods[idx]
// Determine the pub before updating the pod
if pub != nil {
allowed, _, err := pubcontrol.PodUnavailableBudgetValidatePod(c.Client, pod, pubcontrol.NewPubControl(pub), pubcontrol.UpdateOperation, false)
allowed, _, err := pubcontrol.PodUnavailableBudgetValidatePod(c.Client, pod, pubcontrol.NewPubControl(pub, c.controllerFinder, c.Client), pubcontrol.UpdateOperation, false)
if err != nil {
return requeueDuration.Get(), err
// pub check does not pass, try again in seconds

View File

@ -28,6 +28,7 @@ import (
clonesetcore "github.com/openkruise/kruise/pkg/controller/cloneset/core"
clonesetutils "github.com/openkruise/kruise/pkg/controller/cloneset/utils"
"github.com/openkruise/kruise/pkg/util"
"github.com/openkruise/kruise/pkg/util/controllerfinder"
"github.com/openkruise/kruise/pkg/util/inplaceupdate"
"github.com/openkruise/kruise/pkg/util/lifecycle"
apps "k8s.io/api/apps/v1"
@ -573,6 +574,7 @@ func TestUpdate(t *testing.T) {
lifecycle.NewForTest(fakeClient),
inplaceupdate.NewForTest(fakeClient, clonesetutils.RevisionAdapterImpl, func() metav1.Time { return now }),
record.NewFakeRecorder(10),
controllerfinder.NewControllerFinder(fakeClient),
}
currentRevision := mc.updateRevision
if len(mc.revisions) > 0 {

View File

@ -181,7 +181,8 @@ func (r *ReconcilePodUnavailableBudget) Reconcile(_ context.Context, req ctrl.Re
}
func (r *ReconcilePodUnavailableBudget) syncPodUnavailableBudget(pub *policyv1alpha1.PodUnavailableBudget) (*time.Time, error) {
pods, err := r.getPodsForPub(pub)
control := pubcontrol.NewPubControl(pub, r.controllerFinder, r.Client)
pods, expectedCount, err := control.GetPodsForPub()
if err != nil {
return nil, err
}
@ -189,8 +190,8 @@ func (r *ReconcilePodUnavailableBudget) syncPodUnavailableBudget(pub *policyv1al
r.recorder.Eventf(pub, corev1.EventTypeNormal, "NoPods", "No matching pods found")
}
klog.V(3).Infof("pub(%s.%s) controller len(%d) pods", pub.Namespace, pub.Name, len(pods))
expectedCount, desiredAvailable, err := r.getExpectedPodCount(pub, pods)
klog.V(3).Infof("pub(%s.%s) controller pods(%d) expectedCount(%d)", pub.Namespace, pub.Name, len(pods), expectedCount)
desiredAvailable, err := r.getDesiredAvailableForPub(pub, expectedCount)
if err != nil {
r.recorder.Eventf(pub, corev1.EventTypeWarning, "CalculateExpectedPodCountFailed", "Failed to calculate the number of expected pods: %v", err)
return nil, err
@ -200,7 +201,6 @@ func (r *ReconcilePodUnavailableBudget) syncPodUnavailableBudget(pub *policyv1al
var conflictTimes int
var costOfGet, costOfUpdate time.Duration
control := pubcontrol.NewPubControl(pub)
currentTime := time.Now()
var pubClone *policyv1alpha1.PodUnavailableBudget
refresh := false
@ -328,14 +328,10 @@ func (r *ReconcilePodUnavailableBudget) getPodsForPub(pub *policyv1alpha1.PodUna
return matchedPods, nil
}
func (r *ReconcilePodUnavailableBudget) getExpectedPodCount(pub *policyv1alpha1.PodUnavailableBudget, pods []*corev1.Pod) (expectedCount, desiredAvailable int32, err error) {
func (r *ReconcilePodUnavailableBudget) getDesiredAvailableForPub(pub *policyv1alpha1.PodUnavailableBudget, expectedCount int32) (desiredAvailable int32, err error) {
if pub.Spec.MaxUnavailable != nil {
expectedCount, err = r.getExpectedScale(pub, pods)
if err != nil {
return
}
var maxUnavailable int
maxUnavailable, err = intstr.GetValueFromIntOrPercent(pub.Spec.MaxUnavailable, int(expectedCount), false)
maxUnavailable, err = intstr.GetScaledValueFromIntOrPercent(pub.Spec.MaxUnavailable, int(expectedCount), false)
if err != nil {
return
}
@ -347,15 +343,9 @@ func (r *ReconcilePodUnavailableBudget) getExpectedPodCount(pub *policyv1alpha1.
} else if pub.Spec.MinAvailable != nil {
if pub.Spec.MinAvailable.Type == intstr.Int {
desiredAvailable = pub.Spec.MinAvailable.IntVal
expectedCount = int32(len(pods))
} else if pub.Spec.MinAvailable.Type == intstr.String {
expectedCount, err = r.getExpectedScale(pub, pods)
if err != nil {
return
}
var minAvailable int
minAvailable, err = intstr.GetValueFromIntOrPercent(pub.Spec.MinAvailable, int(expectedCount), true)
minAvailable, err = intstr.GetScaledValueFromIntOrPercent(pub.Spec.MinAvailable, int(expectedCount), true)
if err != nil {
return
}
@ -457,7 +447,7 @@ func (r *ReconcilePodUnavailableBudget) buildDisruptedAndUnavailablePods(pods []
// handle unavailable pods which have been in-updated specification
unavailableTime, found := unavailablePods[pod.Name]
if found {
// in case of informer cache latency, after 5 seconds to remove it
// in case of informer cache latency, after 10 seconds to remove it
expectedUpdate := unavailableTime.Time.Add(UpdatedDelayCheckTime)
if expectedUpdate.Before(currentTime) {
continue

View File

@ -26,7 +26,6 @@ import (
policyv1alpha1 "github.com/openkruise/kruise/apis/policy/v1alpha1"
"github.com/openkruise/kruise/pkg/util"
"github.com/openkruise/kruise/pkg/util/controllerfinder"
apps "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -425,7 +424,7 @@ func TestPubReconcile(t *testing.T) {
UnavailableAllowed: 0,
CurrentAvailable: 5,
DesiredAvailable: 100,
TotalReplicas: 5,
TotalReplicas: 10,
}
},
},

View File

@ -87,7 +87,7 @@ func (p *enqueueRequestForPod) updatePod(q workqueue.RateLimitingInterface, old,
oldPub, _ := pubcontrol.GetPodUnavailableBudgetForPod(p.client, p.controllerFinder, oldPod)
newPub, _ := pubcontrol.GetPodUnavailableBudgetForPod(p.client, p.controllerFinder, newPod)
if oldPub != nil && newPub != nil && oldPub.Name == newPub.Name {
control := pubcontrol.NewPubControl(newPub)
control := pubcontrol.NewPubControl(newPub, p.controllerFinder, p.client)
if isReconcile, enqueueDelayTime := isPodAvailableChanged(oldPod, newPod, newPub, control); isReconcile {
q.AddAfter(reconcile.Request{
NamespacedName: types.NamespacedName{
@ -124,7 +124,7 @@ func (p *enqueueRequestForPod) updatePod(q workqueue.RateLimitingInterface, old,
if pub == nil {
return
}
control := pubcontrol.NewPubControl(pub)
control := pubcontrol.NewPubControl(pub, p.controllerFinder, p.client)
if isReconcile, enqueueDelayTime := isPodAvailableChanged(oldPod, newPod, pub, control); isReconcile {
q.AddAfter(reconcile.Request{
NamespacedName: types.NamespacedName{

View File

@ -68,6 +68,39 @@ func NewControllerFinder(c client.Client) *ControllerFinder {
}
}
func (r *ControllerFinder) GetExpectedScaleForPods(pods []*corev1.Pod) (int32, error) {
// 1. Find the controller for each pod. If any pod has 0 controllers,
// that's an error. With ControllerRef, a pod can only have 1 controller.
// A mapping from controllers to their scale.
controllerScale := map[types.UID]int32{}
for _, pod := range pods {
ref := metav1.GetControllerOf(pod)
if ref == nil {
continue
}
// If we already know the scale of the controller there is no need to do anything.
if _, found := controllerScale[ref.UID]; found {
continue
}
// Check all the supported controllers to find the desired scale.
workload, err := r.GetScaleAndSelectorForRef(ref.APIVersion, ref.Kind, pod.Namespace, ref.Name, ref.UID)
if err != nil && !errors.IsNotFound(err) {
return 0, err
}
if workload != nil && workload.Metadata.DeletionTimestamp.IsZero() {
controllerScale[workload.UID] = workload.Scale
}
}
// 2. Add up all the controllers.
var expectedCount int32
for _, count := range controllerScale {
expectedCount += count
}
return expectedCount, nil
}
func (r *ControllerFinder) GetScaleAndSelectorForRef(apiVersion, kind, ns, name string, uid types.UID) (*ScaleAndSelector, error) {
targetRef := ControllerReference{
APIVersion: apiVersion,

View File

@ -150,7 +150,7 @@ func (p *PodCreateHandler) podUnavailableBudgetValidatingPod(ctx context.Context
if pub == nil {
return true, "", nil
}
control := pubcontrol.NewPubControl(pub)
control := pubcontrol.NewPubControl(pub, p.finders, p.Client)
klog.V(3).Infof("validating pod(%s.%s) operation(%s) for pub(%s.%s)", newPod.Namespace, newPod.Name, req.Operation, pub.Namespace, pub.Name)
// the change will not cause pod unavailability, then pass

View File

@ -217,7 +217,7 @@ var _ = SIGDescribe("SidecarSet", func() {
gomega.Expect(err).NotTo(gomega.HaveOccurred())
// volume
for _, volume := range cs.exceptVolumes {
object := util.GetPodVolume(&pods[0], volume)
object := util.GetPodVolume(pods[0], volume)
gomega.Expect(object).ShouldNot(gomega.BeNil())
}
// volumeMounts
@ -318,7 +318,7 @@ var _ = SIGDescribe("SidecarSet", func() {
gomega.Expect(err).NotTo(gomega.HaveOccurred())
// volume
for _, volume := range cs.exceptVolumes {
object := util.GetPodVolume(&pods[0], volume)
object := util.GetPodVolume(pods[0], volume)
gomega.Expect(object).ShouldNot(gomega.BeNil())
}
// volumeMounts
@ -486,13 +486,13 @@ var _ = SIGDescribe("SidecarSet", func() {
origin.Insert(sidecar.Name)
}
// SidecarSetHashAnnotation = "kruise.io/sidecarset-hash"
upgradeSpec1 := sidecarcontrol.GetPodSidecarSetUpgradeSpecInAnnotations(sidecarSetIn.Name, sidecarcontrol.SidecarSetHashAnnotation, &pod)
upgradeSpec1 := sidecarcontrol.GetPodSidecarSetUpgradeSpecInAnnotations(sidecarSetIn.Name, sidecarcontrol.SidecarSetHashAnnotation, pod)
gomega.Expect(upgradeSpec1.SidecarSetName).To(gomega.Equal(sidecarSetIn.Name))
gomega.Expect(upgradeSpec1.SidecarSetHash).To(gomega.Equal(sidecarcontrol.GetSidecarSetRevision(sidecarSetIn)))
target1 := sets.NewString(upgradeSpec1.SidecarList...)
gomega.Expect(reflect.DeepEqual(origin.List(), target1.List())).To(gomega.Equal(true))
// SidecarSetHashWithoutImageAnnotation = "kruise.io/sidecarset-hash-without-image"
upgradeSpec2 := sidecarcontrol.GetPodSidecarSetUpgradeSpecInAnnotations(sidecarSetIn.Name, sidecarcontrol.SidecarSetHashWithoutImageAnnotation, &pod)
upgradeSpec2 := sidecarcontrol.GetPodSidecarSetUpgradeSpecInAnnotations(sidecarSetIn.Name, sidecarcontrol.SidecarSetHashWithoutImageAnnotation, pod)
gomega.Expect(upgradeSpec2.SidecarSetName).To(gomega.Equal(sidecarSetIn.Name))
gomega.Expect(upgradeSpec2.SidecarSetHash).To(gomega.Equal(sidecarcontrol.GetSidecarSetWithoutImageRevision(sidecarSetIn)))
target2 := sets.NewString(upgradeSpec2.SidecarList...)
@ -534,13 +534,13 @@ var _ = SIGDescribe("SidecarSet", func() {
origin.Insert(sidecar.Name)
}
// SidecarSetHashAnnotation = "kruise.io/sidecarset-hash"
upgradeSpec1 := sidecarcontrol.GetPodSidecarSetUpgradeSpecInAnnotations(sidecarSetIn.Name, sidecarcontrol.SidecarSetHashAnnotation, &pod)
upgradeSpec1 := sidecarcontrol.GetPodSidecarSetUpgradeSpecInAnnotations(sidecarSetIn.Name, sidecarcontrol.SidecarSetHashAnnotation, pod)
gomega.Expect(upgradeSpec1.SidecarSetName).To(gomega.Equal(sidecarSetIn.Name))
gomega.Expect(upgradeSpec1.SidecarSetHash).To(gomega.Equal(sidecarcontrol.GetSidecarSetRevision(sidecarSetIn)))
target1 := sets.NewString(upgradeSpec1.SidecarList...)
gomega.Expect(reflect.DeepEqual(origin.List(), target1.List())).To(gomega.Equal(true))
// SidecarSetHashWithoutImageAnnotation = "kruise.io/sidecarset-hash-without-image"
upgradeSpec2 := sidecarcontrol.GetPodSidecarSetUpgradeSpecInAnnotations(sidecarSetIn.Name, sidecarcontrol.SidecarSetHashWithoutImageAnnotation, &pod)
upgradeSpec2 := sidecarcontrol.GetPodSidecarSetUpgradeSpecInAnnotations(sidecarSetIn.Name, sidecarcontrol.SidecarSetHashWithoutImageAnnotation, pod)
gomega.Expect(upgradeSpec2.SidecarSetName).To(gomega.Equal(sidecarSetIn.Name))
gomega.Expect(upgradeSpec2.SidecarSetHash).To(gomega.Equal(sidecarcontrol.GetSidecarSetWithoutImageRevision(sidecarSetIn)))
target2 := sets.NewString(upgradeSpec2.SidecarList...)
@ -614,7 +614,7 @@ var _ = SIGDescribe("SidecarSet", func() {
gomega.Expect(pods).To(gomega.HaveLen(int(*deploymentIn.Spec.Replicas)))
canaryPod := pods[0]
canaryPod.Labels["canary.release"] = "true"
tester.UpdatePod(&canaryPod)
tester.UpdatePod(canaryPod)
time.Sleep(time.Second)
// update sidecarSet sidecar container
sidecarSetIn.Spec.Containers[0].Image = "busybox:latest"

View File

@ -89,7 +89,7 @@ var _ = SIGDescribe("SidecarSet", func() {
"nginx-sidecar-2": "busybox:latest",
}
for sidecar, image := range exceptContainer {
sidecarContainer := util.GetContainer(sidecar, &pods[0])
sidecarContainer := util.GetContainer(sidecar, pods[0])
gomega.Expect(sidecarContainer).ShouldNot(gomega.BeNil())
gomega.Expect(sidecarContainer.Image).To(gomega.Equal(image))
}
@ -120,7 +120,7 @@ var _ = SIGDescribe("SidecarSet", func() {
pods, err := tester.GetSelectorPods(deploymentIn.Namespace, deploymentIn.Spec.Selector)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
gomega.Expect(pods).To(gomega.HaveLen(int(*deploymentIn.Spec.Replicas)))
podIn := &pods[0]
podIn := pods[0]
workSidecarContainer := util.GetContainer(sidecarcontrol.GetPodHotUpgradeInfoInAnnotations(podIn)["nginx-sidecar"], podIn)
gomega.Expect(workSidecarContainer.Name).To(gomega.Equal("nginx-sidecar-1"))
gomega.Expect(workSidecarContainer.Image).To(gomega.Equal("nginx:1.18"))
@ -150,7 +150,7 @@ var _ = SIGDescribe("SidecarSet", func() {
pods, err = tester.GetSelectorPods(deploymentIn.Namespace, deploymentIn.Spec.Selector)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
gomega.Expect(pods).To(gomega.HaveLen(int(*deploymentIn.Spec.Replicas)))
podIn = &pods[0]
podIn = pods[0]
workSidecarContainer = util.GetContainer(sidecarcontrol.GetPodHotUpgradeInfoInAnnotations(podIn)["nginx-sidecar"], podIn)
gomega.Expect(workSidecarContainer.Name).To(gomega.Equal("nginx-sidecar-2"))
gomega.Expect(workSidecarContainer.Image).To(gomega.Equal("nginx:1.19"))
@ -175,7 +175,7 @@ var _ = SIGDescribe("SidecarSet", func() {
pods, err = tester.GetSelectorPods(deploymentIn.Namespace, deploymentIn.Spec.Selector)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
gomega.Expect(pods).To(gomega.HaveLen(int(*deploymentIn.Spec.Replicas)))
podIn = &pods[0]
podIn = pods[0]
workSidecarContainer = util.GetContainer(sidecarcontrol.GetPodHotUpgradeInfoInAnnotations(podIn)["nginx-sidecar"], podIn)
gomega.Expect(workSidecarContainer.Name).To(gomega.Equal("nginx-sidecar-1"))
gomega.Expect(workSidecarContainer.Image).To(gomega.Equal("nginx:1.18"))
@ -230,7 +230,7 @@ var _ = SIGDescribe("SidecarSet", func() {
pods, err := tester.GetSelectorPods(deploymentIn.Namespace, deploymentIn.Spec.Selector)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
gomega.Expect(pods).To(gomega.HaveLen(int(*deploymentIn.Spec.Replicas)))
podIn := &pods[0]
podIn := pods[0]
workSidecarContainer := util.GetContainer(sidecarcontrol.GetPodHotUpgradeInfoInAnnotations(podIn)["nginx-sidecar"], podIn)
gomega.Expect(workSidecarContainer.Name).To(gomega.Equal("nginx-sidecar-1"))
gomega.Expect(workSidecarContainer.Image).To(gomega.Equal("nginx:1.18"))
@ -269,7 +269,7 @@ var _ = SIGDescribe("SidecarSet", func() {
gomega.Expect(err).NotTo(gomega.HaveOccurred())
gomega.Expect(pods).To(gomega.HaveLen(int(*deploymentIn.Spec.Replicas)))
// pod[0]
podIn1 := &pods[0]
podIn1 := pods[0]
workSidecarContainer = util.GetContainer(sidecarcontrol.GetPodHotUpgradeInfoInAnnotations(podIn1)["nginx-sidecar"], podIn1)
gomega.Expect(workSidecarContainer.Name).To(gomega.Equal("nginx-sidecar-2"))
gomega.Expect(workSidecarContainer.Image).To(gomega.Equal("nginx:1.19"))
@ -278,7 +278,7 @@ var _ = SIGDescribe("SidecarSet", func() {
gomega.Expect(emptySidecarContainer.Name).To(gomega.Equal("nginx-sidecar-1"))
gomega.Expect(emptySidecarContainer.Image).To(gomega.Equal("busybox:latest"))
// pod[1]
podIn2 := &pods[1]
podIn2 := pods[1]
workSidecarContainer = util.GetContainer(sidecarcontrol.GetPodHotUpgradeInfoInAnnotations(podIn2)["nginx-sidecar"], podIn2)
gomega.Expect(workSidecarContainer.Name).To(gomega.Equal("nginx-sidecar-2"))
gomega.Expect(workSidecarContainer.Image).To(gomega.Equal("nginx:1.19"))

View File

@ -0,0 +1,416 @@
/*
Copyright 2021 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 framework
import (
"context"
"fmt"
"time"
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
policyv1alpha1 "github.com/openkruise/kruise/apis/policy/v1alpha1"
kruiseclientset "github.com/openkruise/kruise/pkg/client/clientset/versioned"
"github.com/onsi/gomega"
apps "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/wait"
clientset "k8s.io/client-go/kubernetes"
utilpointer "k8s.io/utils/pointer"
)
type PodUnavailableBudgetTester struct {
c clientset.Interface
kc kruiseclientset.Interface
}
func NewPodUnavailableBudgetTester(c clientset.Interface, kc kruiseclientset.Interface) *PodUnavailableBudgetTester {
return &PodUnavailableBudgetTester{
c: c,
kc: kc,
}
}
func (t *PodUnavailableBudgetTester) NewBasePub(namespace string) *policyv1alpha1.PodUnavailableBudget {
return &policyv1alpha1.PodUnavailableBudget{
TypeMeta: metav1.TypeMeta{
APIVersion: policyv1alpha1.GroupVersion.String(),
Kind: "PodUnavailableBudget",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: "busybox-pub",
},
Spec: policyv1alpha1.PodUnavailableBudgetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"pub-controller": "true",
},
},
MaxUnavailable: &intstr.IntOrString{
Type: intstr.Int,
IntVal: 1,
},
},
}
}
func (s *PodUnavailableBudgetTester) NewBaseDeployment(namespace string) *apps.Deployment {
return &apps.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "apps/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "busybox",
Namespace: namespace,
},
Spec: apps.DeploymentSpec{
Replicas: utilpointer.Int32Ptr(2),
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "busybox",
"pub-controller": "true",
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": "busybox",
"pub-controller": "true",
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "main",
Image: "busybox:1.32",
Command: []string{"/bin/sh", "-c", "sleep 10000000"},
},
},
},
},
Strategy: apps.DeploymentStrategy{
Type: apps.RollingUpdateDeploymentStrategyType,
RollingUpdate: &apps.RollingUpdateDeployment{
MaxUnavailable: &intstr.IntOrString{
Type: intstr.String,
StrVal: "100%",
},
MaxSurge: &intstr.IntOrString{
Type: intstr.Int,
IntVal: 0,
},
},
},
},
}
}
func (s *PodUnavailableBudgetTester) NewBaseCloneSet(namespace string) *appsv1alpha1.CloneSet {
return &appsv1alpha1.CloneSet{
TypeMeta: metav1.TypeMeta{
Kind: "CloneSet",
APIVersion: appsv1alpha1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "busybox",
Namespace: namespace,
},
Spec: appsv1alpha1.CloneSetSpec{
Replicas: utilpointer.Int32Ptr(2),
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "busybox",
"pub-controller": "true",
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": "busybox",
"pub-controller": "true",
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "main",
Image: "busybox:1.32",
Command: []string{"/bin/sh", "-c", "sleep 10000000"},
},
},
},
},
UpdateStrategy: appsv1alpha1.CloneSetUpdateStrategy{
Type: appsv1alpha1.RecreateCloneSetUpdateStrategyType,
MaxUnavailable: &intstr.IntOrString{
Type: intstr.String,
StrVal: "100%",
},
MaxSurge: &intstr.IntOrString{
Type: intstr.Int,
IntVal: 0,
},
},
},
}
}
func (t *PodUnavailableBudgetTester) CreatePub(pub *policyv1alpha1.PodUnavailableBudget) *policyv1alpha1.PodUnavailableBudget {
Logf("create PodUnavailableBudget(%s.%s)", pub.Namespace, pub.Name)
_, err := t.kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Create(context.TODO(), pub, metav1.CreateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
t.WaitForPubCreated(pub)
pub, _ = t.kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
return pub
}
func (t *PodUnavailableBudgetTester) CreateDeployment(deployment *apps.Deployment) {
Logf("create deployment(%s.%s)", deployment.Namespace, deployment.Name)
_, err := t.c.AppsV1().Deployments(deployment.Namespace).Create(context.TODO(), deployment, metav1.CreateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
t.WaitForDeploymentRunning(deployment)
Logf("create deployment(%s.%s) done", deployment.Namespace, deployment.Name)
}
func (t *PodUnavailableBudgetTester) CreateCloneSet(cloneset *appsv1alpha1.CloneSet) *appsv1alpha1.CloneSet {
Logf("create CloneSet(%s.%s)", cloneset.Namespace, cloneset.Name)
_, err := t.kc.AppsV1alpha1().CloneSets(cloneset.Namespace).Create(context.TODO(), cloneset, metav1.CreateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
t.WaitForCloneSetRunning(cloneset)
Logf("create cloneset(%s.%s) done", cloneset.Namespace, cloneset.Name)
cloneset, _ = t.kc.AppsV1alpha1().CloneSets(cloneset.Namespace).Get(context.TODO(), cloneset.Name, metav1.GetOptions{})
return cloneset
}
func (t *PodUnavailableBudgetTester) WaitForPubCreated(pub *policyv1alpha1.PodUnavailableBudget) {
pollErr := wait.PollImmediate(time.Second, time.Minute,
func() (bool, error) {
_, err := t.kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
return true, nil
})
if pollErr != nil {
Failf("Failed waiting for PodUnavailableBudget to enter running: %v", pollErr)
}
}
func (t *PodUnavailableBudgetTester) WaitForDeploymentRunning(deployment *apps.Deployment) {
pollErr := wait.PollImmediate(time.Second, time.Minute*5,
func() (bool, error) {
inner, err := t.c.AppsV1().Deployments(deployment.Namespace).Get(context.TODO(), deployment.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
if *inner.Spec.Replicas == inner.Status.ReadyReplicas {
return true, nil
}
return false, nil
})
if pollErr != nil {
Failf("Failed waiting for deployment to enter running: %v", pollErr)
}
}
func (t *PodUnavailableBudgetTester) WaitForCloneSetRunning(cloneset *appsv1alpha1.CloneSet) {
pollErr := wait.PollImmediate(time.Second, time.Minute*5,
func() (bool, error) {
inner, err := t.kc.AppsV1alpha1().CloneSets(cloneset.Namespace).Get(context.TODO(), cloneset.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
if *inner.Spec.Replicas == inner.Status.ReadyReplicas {
return true, nil
}
return false, nil
})
if pollErr != nil {
Failf("Failed waiting for cloneset to enter running: %v", pollErr)
}
}
func (t *PodUnavailableBudgetTester) WaitForDeploymentMinReadyAndRunning(deployments []*apps.Deployment, minReady int32) {
pollErr := wait.PollImmediate(time.Second, time.Minute*5,
func() (bool, error) {
var readyReplicas int32 = 0
completed := 0
for _, deployment := range deployments {
inner, err := t.c.AppsV1().Deployments(deployment.Namespace).Get(context.TODO(), deployment.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
readyReplicas += inner.Status.ReadyReplicas
count := *inner.Spec.Replicas
if inner.Status.UpdatedReplicas == count && count == inner.Status.ReadyReplicas && count == inner.Status.Replicas {
completed++
}
}
if readyReplicas < minReady {
return false, fmt.Errorf("deployment ReadyReplicas(%d) < except(%d)", readyReplicas, minReady)
}
if completed == len(deployments) {
return true, nil
}
return false, nil
})
if pollErr != nil {
Failf("Failed waiting for deployment to enter running: %v", pollErr)
}
}
func (t *PodUnavailableBudgetTester) WaitForCloneSetMinReadyAndRunning(cloneSets []*appsv1alpha1.CloneSet, minReady int32) {
pollErr := wait.PollImmediate(time.Second, time.Minute*10,
func() (bool, error) {
var readyReplicas int32 = 0
completed := 0
for _, cloneSet := range cloneSets {
inner, err := t.kc.AppsV1alpha1().CloneSets(cloneSet.Namespace).Get(context.TODO(), cloneSet.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
readyReplicas += inner.Status.ReadyReplicas
count := *inner.Spec.Replicas
if inner.Status.UpdatedReplicas == count && count == inner.Status.ReadyReplicas && count == inner.Status.Replicas {
completed++
}
}
if readyReplicas < minReady {
return false, fmt.Errorf("deployment ReadyReplicas(%d) < except(%d)", readyReplicas, minReady)
}
if completed == len(cloneSets) {
return true, nil
}
return false, nil
})
if pollErr != nil {
Failf("Failed waiting for cloneSet to enter running: %v", pollErr)
}
}
func (t *PodUnavailableBudgetTester) DeletePubs(namespace string) {
pubList, err := t.kc.PolicyV1alpha1().PodUnavailableBudgets(namespace).List(context.TODO(), metav1.ListOptions{})
if err != nil {
Logf("List sidecarSets failed: %s", err.Error())
return
}
for _, pub := range pubList.Items {
err := t.kc.PolicyV1alpha1().PodUnavailableBudgets(namespace).Delete(context.TODO(), pub.Name, metav1.DeleteOptions{})
if err != nil {
Logf("delete PodUnavailableBudget(%s.%s) failed: %s", pub.Namespace, pub.Name, err.Error())
}
}
}
func (t *PodUnavailableBudgetTester) DeleteDeployments(namespace string) {
deploymentList, err := t.c.AppsV1().Deployments(namespace).List(context.TODO(), metav1.ListOptions{})
if err != nil {
Logf("List Deployments failed: %s", err.Error())
return
}
for _, deployment := range deploymentList.Items {
err := t.c.AppsV1().Deployments(namespace).Delete(context.TODO(), deployment.Name, metav1.DeleteOptions{})
if err != nil {
Logf("delete Deployment(%s.%s) failed: %s", deployment.Namespace, deployment.Name, err.Error())
continue
}
t.WaitForDeploymentDeleted(&deployment)
}
}
func (t *PodUnavailableBudgetTester) DeleteCloneSets(namespace string) {
objectList, err := t.kc.AppsV1alpha1().CloneSets(namespace).List(context.TODO(), metav1.ListOptions{})
if err != nil {
Logf("List CloneSets failed: %s", err.Error())
return
}
for _, object := range objectList.Items {
err := t.kc.AppsV1alpha1().CloneSets(namespace).Delete(context.TODO(), object.Name, metav1.DeleteOptions{})
if err != nil {
Logf("delete CloneSet(%s.%s) failed: %s", object.Namespace, object.Name, err.Error())
continue
}
t.WaitForCloneSetDeleted(&object)
}
}
func (t *PodUnavailableBudgetTester) WaitForDeploymentDeleted(deployment *apps.Deployment) {
pollErr := wait.PollImmediate(time.Second, time.Minute,
func() (bool, error) {
_, err := t.c.AppsV1().Deployments(deployment.Namespace).Get(context.TODO(), deployment.Name, metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
return true, nil
}
return false, err
}
return false, nil
})
if pollErr != nil {
Failf("Failed waiting for deployment to enter Deleted: %v", pollErr)
}
}
func (t *PodUnavailableBudgetTester) WaitForCloneSetDeleted(cloneset *appsv1alpha1.CloneSet) {
pollErr := wait.PollImmediate(time.Second, time.Minute,
func() (bool, error) {
_, err := t.kc.AppsV1alpha1().CloneSets(cloneset.Namespace).Get(context.TODO(), cloneset.Name, metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
return true, nil
}
return false, err
}
return false, nil
})
if pollErr != nil {
Failf("Failed waiting for cloneset to enter Deleted: %v", pollErr)
}
}
func (s *SidecarSetTester) WaitForSidecarSetMinReadyAndUpgrade(sidecarSet *appsv1alpha1.SidecarSet, exceptStatus *appsv1alpha1.SidecarSetStatus, minReady int32) {
pollErr := wait.PollImmediate(time.Second, time.Minute*5,
func() (bool, error) {
inner, err := s.kc.AppsV1alpha1().SidecarSets().Get(context.TODO(), sidecarSet.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
if minReady > 0 && inner.Status.ReadyPods < minReady {
return false, fmt.Errorf("sidecarSet(%s) ReadyReplicas(%d) < except(%d)", sidecarSet.Name, inner.Status.ReadyPods, minReady)
}
if inner.Status.MatchedPods == exceptStatus.MatchedPods &&
inner.Status.UpdatedPods == exceptStatus.UpdatedPods &&
inner.Status.UpdatedReadyPods == exceptStatus.UpdatedReadyPods &&
inner.Status.ReadyPods == exceptStatus.ReadyPods {
return true, nil
}
return false, nil
})
if pollErr != nil {
Failf("Failed waiting for sidecarSet to upgrade complete: %v", pollErr)
}
}

View File

@ -310,7 +310,7 @@ func (s *SidecarSetTester) WaitForSidecarSetDeleted(sidecarSet *appsv1alpha1.Sid
}
}
func (s *SidecarSetTester) GetSelectorPods(namespace string, selector *metav1.LabelSelector) ([]corev1.Pod, error) {
func (s *SidecarSetTester) GetSelectorPods(namespace string, selector *metav1.LabelSelector) ([]*corev1.Pod, error) {
faster, err := util.GetFastLabelSelector(selector)
if err != nil {
return nil, err
@ -319,7 +319,14 @@ func (s *SidecarSetTester) GetSelectorPods(namespace string, selector *metav1.La
if err != nil {
return nil, err
}
return podList.Items, nil
pods := make([]*corev1.Pod, 0)
for i := range podList.Items {
pod := &podList.Items[i]
if pod.DeletionTimestamp.IsZero() {
pods = append(pods, pod)
}
}
return pods, nil
}
func (s *SidecarSetTester) NewBaseCloneSet(namespace string) *appsv1alpha1.CloneSet {

View File

@ -0,0 +1,899 @@
/*
Copyright 2021 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 policy
import (
"context"
"fmt"
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
"k8s.io/apimachinery/pkg/util/intstr"
utilpointer "k8s.io/utils/pointer"
"time"
policyv1alpha1 "github.com/openkruise/kruise/apis/policy/v1alpha1"
kruiseclientset "github.com/openkruise/kruise/pkg/client/clientset/versioned"
"github.com/openkruise/kruise/test/e2e/framework"
"github.com/onsi/ginkgo"
"github.com/onsi/gomega"
apps "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientset "k8s.io/client-go/kubernetes"
)
var _ = SIGDescribe("PodUnavailableBudget", func() {
f := framework.NewDefaultFramework("podunavailablebudget")
var ns string
var c clientset.Interface
var kc kruiseclientset.Interface
var tester *framework.PodUnavailableBudgetTester
var sidecarTester *framework.SidecarSetTester
ginkgo.BeforeEach(func() {
c = f.ClientSet
kc = f.KruiseClientSet
ns = f.Namespace.Name
tester = framework.NewPodUnavailableBudgetTester(c, kc)
sidecarTester = framework.NewSidecarSetTester(c, kc)
})
framework.KruiseDescribe("podUnavailableBudget functionality [podUnavailableBudget]", func() {
ginkgo.AfterEach(func() {
if ginkgo.CurrentGinkgoTestDescription().Failed {
framework.DumpDebugInfo(c, ns)
}
framework.Logf("Deleting all PodUnavailableBudgets and Deployments in cluster")
tester.DeletePubs(ns)
tester.DeleteDeployments(ns)
tester.DeleteCloneSets(ns)
sidecarTester.DeleteSidecarSets()
})
ginkgo.It("PodUnavailableBudget selector no matched pods", func() {
// create pub
pub := tester.NewBasePub(ns)
ginkgo.By(fmt.Sprintf("Creating PodUnavailableBudget(%s.%s)", pub.Namespace, pub.Name))
tester.CreatePub(pub)
// create deployment
deployment := tester.NewBaseDeployment(ns)
deployment.Spec.Selector.MatchLabels["pub-controller"] = "false"
deployment.Spec.Template.Labels["pub-controller"] = "false"
ginkgo.By(fmt.Sprintf("Creating Deployment(%s.%s)", deployment.Namespace, deployment.Name))
tester.CreateDeployment(deployment)
// wait 10 seconds
time.Sleep(time.Second * 10)
ginkgo.By(fmt.Sprintf("check PodUnavailableBudget(%s.%s) Status", pub.Namespace, pub.Name))
expectStatus := &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 0,
DesiredAvailable: 0,
CurrentAvailable: 0,
TotalReplicas: 0,
}
setPubStatus(expectStatus)
pub, err := kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus := &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
ginkgo.By("PodUnavailableBudget selector no matched pods done")
})
ginkgo.It("PodUnavailableBudget selector pods and delete deployment ignore", func() {
// create pub
pub := tester.NewBasePub(ns)
ginkgo.By(fmt.Sprintf("Creating PodUnavailableBudget(%s.%s)", pub.Namespace, pub.Name))
tester.CreatePub(pub)
// create deployment
deployment := tester.NewBaseDeployment(ns)
ginkgo.By(fmt.Sprintf("Creating Deployment(%s.%s)", deployment.Namespace, deployment.Name))
tester.CreateDeployment(deployment)
// wait 10 seconds
time.Sleep(time.Second * 10)
ginkgo.By(fmt.Sprintf("check PodUnavailableBudget(%s.%s) Status", pub.Namespace, pub.Name))
expectStatus := &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 1,
DesiredAvailable: 1,
CurrentAvailable: 2,
TotalReplicas: 2,
}
setPubStatus(expectStatus)
pub, err := kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus := pub.Status.DeepCopy()
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
// delete deployment
ginkgo.By(fmt.Sprintf("Deleting Deployment(%s.%s)", deployment.Namespace, deployment.Name))
err = c.AppsV1().Deployments(deployment.Namespace).Delete(context.TODO(), deployment.Name, metav1.DeleteOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
// wait 30 seconds
time.Sleep(time.Second * 10)
ginkgo.By(fmt.Sprintf("waiting 10 seconds, and check PodUnavailableBudget(%s.%s) Status", pub.Namespace, pub.Name))
expectStatus = &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 0,
DesiredAvailable: 0,
CurrentAvailable: 0,
TotalReplicas: 0,
}
setPubStatus(expectStatus)
pub, err = kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus = pub.Status.DeepCopy()
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
pods, err := sidecarTester.GetSelectorPods(deployment.Namespace, deployment.Spec.Selector)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
gomega.Expect(pods).To(gomega.HaveLen(0))
ginkgo.By("PodUnavailableBudget selector pods and delete deployment reject done")
})
ginkgo.It("PodUnavailableBudget targetReference pods, update failed image and block", func() {
// create pub
pub := tester.NewBasePub(ns)
pub.Spec.Selector = nil
pub.Spec.TargetReference = &policyv1alpha1.TargetReference{
APIVersion: "apps/v1",
Kind: "Deployment",
Name: "busybox",
}
ginkgo.By(fmt.Sprintf("Creating PodUnavailableBudget(%s.%s)", pub.Namespace, pub.Name))
tester.CreatePub(pub)
// create deployment
deployment := tester.NewBaseDeployment(ns)
ginkgo.By(fmt.Sprintf("Creating Deployment(%s.%s)", deployment.Namespace, deployment.Name))
tester.CreateDeployment(deployment)
// wait 10 seconds
time.Sleep(time.Second * 10)
ginkgo.By(fmt.Sprintf("check PodUnavailableBudget(%s.%s) Status", pub.Namespace, pub.Name))
expectStatus := &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 1,
DesiredAvailable: 1,
CurrentAvailable: 2,
TotalReplicas: 2,
}
setPubStatus(expectStatus)
pub, err := kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus := &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
// update failed image
ginkgo.By(fmt.Sprintf("update Deployment(%s.%s) failed image(busybox:failed)", deployment.Namespace, deployment.Name))
deployment.Spec.Template.Spec.Containers[0].Image = "busybox:failed"
_, err = c.AppsV1().Deployments(deployment.Namespace).Update(context.TODO(), deployment, metav1.UpdateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
//wait 20 seconds
ginkgo.By(fmt.Sprintf("waiting 20 seconds, and check PodUnavailableBudget(%s.%s) Status", pub.Namespace, pub.Name))
time.Sleep(time.Second * 20)
expectStatus = &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 0,
DesiredAvailable: 1,
CurrentAvailable: 1,
TotalReplicas: 2,
}
setPubStatus(expectStatus)
pub, err = kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus = &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
// check now pod
pods, err := sidecarTester.GetSelectorPods(deployment.Namespace, deployment.Spec.Selector)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
noUpdatePods := make([]corev1.Pod, 0)
for _, pod := range pods {
if pod.Spec.Containers[0].Image == "busybox:failed" || !pod.DeletionTimestamp.IsZero() {
continue
}
noUpdatePods = append(noUpdatePods, *pod)
}
gomega.Expect(noUpdatePods).To(gomega.HaveLen(1))
// update success image
ginkgo.By(fmt.Sprintf("update Deployment(%s.%s) success image(busybox:1.33)", deployment.Namespace, deployment.Name))
deployment.Spec.Template.Spec.Containers[0].Image = "busybox:1.33"
_, err = c.AppsV1().Deployments(deployment.Namespace).Update(context.TODO(), deployment, metav1.UpdateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
//wait 20 seconds
ginkgo.By(fmt.Sprintf("waiting 20 seconds, and check PodUnavailableBudget(%s.%s) Status", pub.Namespace, pub.Name))
time.Sleep(time.Second * 20)
expectStatus = &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 1,
DesiredAvailable: 1,
CurrentAvailable: 2,
TotalReplicas: 2,
}
setPubStatus(expectStatus)
pub, err = kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus = &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
//check pods
pods, err = sidecarTester.GetSelectorPods(deployment.Namespace, deployment.Spec.Selector)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
newPods := make([]corev1.Pod, 0)
for _, pod := range pods {
if !pod.DeletionTimestamp.IsZero() || pod.Spec.Containers[0].Image != "busybox:1.33" {
continue
}
newPods = append(newPods, *pod)
}
gomega.Expect(newPods).To(gomega.HaveLen(2))
ginkgo.By("PodUnavailableBudget targetReference pods, update failed image and block done")
})
ginkgo.It("PodUnavailableBudget selector two deployments, deployment.strategy.maxUnavailable=100%, pub.spec.maxUnavailable=20%, and update success image", func() {
// create pub
pub := tester.NewBasePub(ns)
pub.Spec.MaxUnavailable = &intstr.IntOrString{
Type: intstr.String,
StrVal: "20%",
}
ginkgo.By(fmt.Sprintf("Creating PodUnavailableBudget(%s.%s)", pub.Namespace, pub.Name))
tester.CreatePub(pub)
// create deployment1
deployment := tester.NewBaseDeployment(ns)
deployment.Spec.Replicas = utilpointer.Int32Ptr(5)
deploymentIn1 := deployment.DeepCopy()
deploymentIn1.Name = fmt.Sprintf("%s-1", deploymentIn1.Name)
ginkgo.By(fmt.Sprintf("Creating Deployment1(%s.%s)", deploymentIn1.Namespace, deploymentIn1.Name))
tester.CreateDeployment(deploymentIn1)
// create deployment2
deploymentIn2 := deployment.DeepCopy()
deploymentIn2.Name = fmt.Sprintf("%s-2", deploymentIn1.Name)
ginkgo.By(fmt.Sprintf("Creating Deployment2(%s.%s)", deploymentIn2.Namespace, deploymentIn2.Name))
tester.CreateDeployment(deploymentIn2)
// wait 1 seconds
ginkgo.By(fmt.Sprintf("wait 1 seconds, check PodUnavailableBudget(%s.%s) Status", pub.Namespace, pub.Name))
time.Sleep(time.Second)
expectStatus := &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 2,
DesiredAvailable: 8,
CurrentAvailable: 10,
TotalReplicas: 10,
}
setPubStatus(expectStatus)
pub, err := kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus := &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
// update success image
ginkgo.By(fmt.Sprintf("update Deployment-1 and deployment-2 with success image(busybox:1.33)"))
deploymentIn1.Spec.Template.Spec.Containers[0].Image = "busybox:1.33"
_, err = c.AppsV1().Deployments(deploymentIn1.Namespace).Update(context.TODO(), deploymentIn1, metav1.UpdateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
deploymentIn2.Spec.Template.Spec.Containers[0].Image = "busybox:1.33"
_, err = c.AppsV1().Deployments(deploymentIn2.Namespace).Update(context.TODO(), deploymentIn2, metav1.UpdateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
// wait 1 seconds, and check deployment, pub Status
ginkgo.By(fmt.Sprintf("wait 1 seconds, and check deployment, pub Status"))
time.Sleep(time.Second)
// check deployment
tester.WaitForDeploymentMinReadyAndRunning([]*apps.Deployment{deploymentIn1, deploymentIn2}, 8)
// check pods
pods, err := sidecarTester.GetSelectorPods(deployment.Namespace, deployment.Spec.Selector)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
newPods := make([]corev1.Pod, 0)
for _, pod := range pods {
if !pod.DeletionTimestamp.IsZero() || pod.Spec.Containers[0].Image != "busybox:1.33" {
continue
}
newPods = append(newPods, *pod)
}
gomega.Expect(newPods).To(gomega.HaveLen(10))
time.Sleep(time.Second * 5)
expectStatus = &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 2,
DesiredAvailable: 8,
CurrentAvailable: 10,
TotalReplicas: 10,
}
setPubStatus(expectStatus)
pub, err = kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus = &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
ginkgo.By("PodUnavailableBudget selector two deployments, deployment.strategy.maxUnavailable=100%, pub.spec.maxUnavailable=20%, and update success image done")
})
ginkgo.It("PodUnavailableBudget selector SidecarSet, inject sidecar container, update failed sidecar image, block", func() {
// create pub
pub := tester.NewBasePub(ns)
ginkgo.By(fmt.Sprintf("Creating PodUnavailableBudget(%s.%s)", pub.Namespace, pub.Name))
tester.CreatePub(pub)
// create sidecarset
sidecarSet := sidecarTester.NewBaseSidecarSet(ns)
sidecarSet.Spec.Namespace = ns
sidecarSet.Spec.Selector = &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "busybox",
},
}
sidecarSet.Spec.Containers = []appsv1alpha1.SidecarContainer{
{
Container: corev1.Container{
Name: "nginx-sidecar",
Image: "nginx:1.18",
Command: []string{"tail", "-f", "/dev/null"},
},
},
}
sidecarSet.Spec.UpdateStrategy = appsv1alpha1.SidecarSetUpdateStrategy{
Type: appsv1alpha1.RollingUpdateSidecarSetStrategyType,
MaxUnavailable: &intstr.IntOrString{
Type: intstr.String,
StrVal: "100%",
},
}
ginkgo.By(fmt.Sprintf("Creating SidecarSet %s", sidecarSet.Name))
sidecarSet = sidecarTester.CreateSidecarSet(sidecarSet)
// create deployment
deployment := tester.NewBaseDeployment(ns)
deployment.Spec.Replicas = utilpointer.Int32Ptr(5)
ginkgo.By(fmt.Sprintf("Creating Deployment(%s.%s)", deployment.Namespace, deployment.Name))
tester.CreateDeployment(deployment)
time.Sleep(time.Second)
// check sidecarSet inject sidecar container
ginkgo.By(fmt.Sprintf("check sidecarSet inject sidecar container and pub status"))
pods, err := sidecarTester.GetSelectorPods(deployment.Namespace, deployment.Spec.Selector)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
pod := pods[0]
gomega.Expect(pod.Spec.Containers).To(gomega.HaveLen(len(deployment.Spec.Template.Spec.Containers) + len(sidecarSet.Spec.Containers)))
//check pub status
time.Sleep(time.Second)
expectStatus := &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 1,
DesiredAvailable: 4,
CurrentAvailable: 5,
TotalReplicas: 5,
}
setPubStatus(expectStatus)
pub, err = kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus := &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
// update sidecar container failed image
ginkgo.By(fmt.Sprintf("update sidecar container failed image(nginx:failed)"))
sidecarSet, err = kc.AppsV1alpha1().SidecarSets().Get(context.TODO(), sidecarSet.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
sidecarSet.Spec.Containers[0].Image = "nginx:failed"
sidecarTester.UpdateSidecarSet(sidecarSet)
// wait 1 seconds, and check sidecarSet upgrade block
ginkgo.By(fmt.Sprintf("wait 1 seconds, and check sidecarSet upgrade block"))
time.Sleep(time.Second)
except := &appsv1alpha1.SidecarSetStatus{
MatchedPods: 5,
UpdatedPods: 1,
UpdatedReadyPods: 0,
ReadyPods: 4,
}
sidecarTester.WaitForSidecarSetMinReadyAndUpgrade(sidecarSet, except, 4)
time.Sleep(time.Second)
//check pub status
expectStatus = &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 0,
DesiredAvailable: 4,
CurrentAvailable: 4,
TotalReplicas: 5,
}
setPubStatus(expectStatus)
pub, err = kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus = &pub.Status
// check unavailablePods
gomega.Expect(nowStatus.UnavailablePods).To(gomega.HaveLen(1))
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
// update sidecar container success image
ginkgo.By(fmt.Sprintf("update sidecar container success image"))
sidecarSet, err = kc.AppsV1alpha1().SidecarSets().Get(context.TODO(), sidecarSet.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
sidecarSet.Spec.Containers[0].Image = "nginx:1.19"
sidecarTester.UpdateSidecarSet(sidecarSet)
time.Sleep(time.Second)
// check sidecarSet upgrade success
ginkgo.By(fmt.Sprintf("check sidecarSet upgrade success"))
except = &appsv1alpha1.SidecarSetStatus{
MatchedPods: 5,
UpdatedPods: 5,
UpdatedReadyPods: 5,
ReadyPods: 5,
}
sidecarTester.WaitForSidecarSetMinReadyAndUpgrade(sidecarSet, except, 4)
time.Sleep(time.Second)
//check pub status
expectStatus = &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 1,
DesiredAvailable: 4,
CurrentAvailable: 5,
TotalReplicas: 5,
}
setPubStatus(expectStatus)
pub, err = kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus = &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
ginkgo.By("PodUnavailableBudget selector pods, inject sidecar container, update failed sidecar image, block done")
})
ginkgo.It("PodUnavailableBudget selector cloneSet, strategy.type=recreate, update failed image and block", func() {
// create pub
pub := tester.NewBasePub(ns)
ginkgo.By(fmt.Sprintf("Creating PodUnavailableBudget(%s.%s)", pub.Namespace, pub.Name))
tester.CreatePub(pub)
// create cloneset
cloneset := tester.NewBaseCloneSet(ns)
ginkgo.By(fmt.Sprintf("Creating CloneSet(%s.%s)", cloneset.Namespace, cloneset.Name))
cloneset = tester.CreateCloneSet(cloneset)
// wait 10 seconds
time.Sleep(time.Second * 10)
ginkgo.By(fmt.Sprintf("check PodUnavailableBudget(%s.%s) Status", pub.Namespace, pub.Name))
expectStatus := &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 1,
DesiredAvailable: 1,
CurrentAvailable: 2,
TotalReplicas: 2,
}
setPubStatus(expectStatus)
pub, err := kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus := &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
// update failed image
ginkgo.By(fmt.Sprintf("update CloneSet(%s.%s) with failed image(busybox:failed)", cloneset.Namespace, cloneset.Name))
cloneset.Spec.Template.Spec.Containers[0].Image = "busybox:failed"
_, err = kc.AppsV1alpha1().CloneSets(cloneset.Namespace).Update(context.TODO(), cloneset, metav1.UpdateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
//wait 20 seconds
ginkgo.By(fmt.Sprintf("waiting 20 seconds, and check PodUnavailableBudget(%s.%s) Status", pub.Namespace, pub.Name))
time.Sleep(time.Second * 20)
expectStatus = &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 0,
DesiredAvailable: 1,
CurrentAvailable: 1,
TotalReplicas: 2,
}
setPubStatus(expectStatus)
pub, err = kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus = &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
// check now pod
pods, err := sidecarTester.GetSelectorPods(cloneset.Namespace, cloneset.Spec.Selector)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
noUpdatePods := make([]corev1.Pod, 0)
for _, pod := range pods {
if pod.Spec.Containers[0].Image == "busybox:failed" || !pod.DeletionTimestamp.IsZero() {
continue
}
noUpdatePods = append(noUpdatePods, *pod)
}
gomega.Expect(noUpdatePods).To(gomega.HaveLen(1))
// update success image
ginkgo.By(fmt.Sprintf("update CloneSet(%s.%s) success image(busybox:1.33)", cloneset.Namespace, cloneset.Name))
cloneset, _ = kc.AppsV1alpha1().CloneSets(cloneset.Namespace).Get(context.TODO(), cloneset.Name, metav1.GetOptions{})
cloneset.Spec.Template.Spec.Containers[0].Image = "busybox:1.33"
cloneset, err = kc.AppsV1alpha1().CloneSets(cloneset.Namespace).Update(context.TODO(), cloneset, metav1.UpdateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
tester.WaitForCloneSetMinReadyAndRunning([]*appsv1alpha1.CloneSet{cloneset}, 1)
// check pub status
ginkgo.By(fmt.Sprintf("check PodUnavailableBudget(%s.%s) Status", pub.Namespace, pub.Name))
expectStatus = &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 1,
DesiredAvailable: 1,
CurrentAvailable: 2,
TotalReplicas: 2,
}
setPubStatus(expectStatus)
pub, err = kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus = &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
//check pods
pods, err = sidecarTester.GetSelectorPods(cloneset.Namespace, cloneset.Spec.Selector)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
newPods := make([]corev1.Pod, 0)
for _, pod := range pods {
if !pod.DeletionTimestamp.IsZero() || pod.Spec.Containers[0].Image != "busybox:1.33" {
continue
}
newPods = append(newPods, *pod)
}
gomega.Expect(newPods).To(gomega.HaveLen(2))
ginkgo.By("PodUnavailableBudget selector cloneSet, update failed image and block done")
})
ginkgo.It("PodUnavailableBudget selector cloneSet, strategy.type=in-place, update failed image and block", func() {
// create pub
pub := tester.NewBasePub(ns)
ginkgo.By(fmt.Sprintf("Creating PodUnavailableBudget(%s.%s)", pub.Namespace, pub.Name))
tester.CreatePub(pub)
// create cloneset
cloneset := tester.NewBaseCloneSet(ns)
cloneset.Spec.UpdateStrategy.Type = appsv1alpha1.InPlaceOnlyCloneSetUpdateStrategyType
ginkgo.By(fmt.Sprintf("Creating CloneSet(%s.%s)", cloneset.Namespace, cloneset.Name))
cloneset = tester.CreateCloneSet(cloneset)
// wait 10 seconds
time.Sleep(time.Second * 10)
ginkgo.By(fmt.Sprintf("check PodUnavailableBudget(%s.%s) Status", pub.Namespace, pub.Name))
expectStatus := &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 1,
DesiredAvailable: 1,
CurrentAvailable: 2,
TotalReplicas: 2,
}
setPubStatus(expectStatus)
pub, err := kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus := &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
// update failed image
ginkgo.By(fmt.Sprintf("update CloneSet(%s.%s) with failed image(busybox:failed)", cloneset.Namespace, cloneset.Name))
cloneset.Spec.Template.Spec.Containers[0].Image = "busybox:failed"
_, err = kc.AppsV1alpha1().CloneSets(cloneset.Namespace).Update(context.TODO(), cloneset, metav1.UpdateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
//wait 20 seconds
ginkgo.By(fmt.Sprintf("waiting 20 seconds, and check PodUnavailableBudget(%s.%s) Status", pub.Namespace, pub.Name))
time.Sleep(time.Second * 20)
expectStatus = &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 0,
DesiredAvailable: 1,
CurrentAvailable: 1,
TotalReplicas: 2,
}
setPubStatus(expectStatus)
pub, err = kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus = &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
// check now pod
pods, err := sidecarTester.GetSelectorPods(cloneset.Namespace, cloneset.Spec.Selector)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
noUpdatePods := make([]corev1.Pod, 0)
for _, pod := range pods {
if pod.Spec.Containers[0].Image == "busybox:failed" || !pod.DeletionTimestamp.IsZero() {
continue
}
noUpdatePods = append(noUpdatePods, *pod)
}
gomega.Expect(noUpdatePods).To(gomega.HaveLen(1))
// update success image
ginkgo.By(fmt.Sprintf("update CloneSet(%s.%s) success image(busybox:1.33)", cloneset.Namespace, cloneset.Name))
cloneset, _ = kc.AppsV1alpha1().CloneSets(cloneset.Namespace).Get(context.TODO(), cloneset.Name, metav1.GetOptions{})
cloneset.Spec.Template.Spec.Containers[0].Image = "busybox:1.33"
cloneset, err = kc.AppsV1alpha1().CloneSets(cloneset.Namespace).Update(context.TODO(), cloneset, metav1.UpdateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
tester.WaitForCloneSetMinReadyAndRunning([]*appsv1alpha1.CloneSet{cloneset}, 1)
//wait 20 seconds
ginkgo.By(fmt.Sprintf("check PodUnavailableBudget(%s.%s) Status", pub.Namespace, pub.Name))
expectStatus = &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 1,
DesiredAvailable: 1,
CurrentAvailable: 2,
TotalReplicas: 2,
}
setPubStatus(expectStatus)
pub, err = kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus = &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
//check pods
pods, err = sidecarTester.GetSelectorPods(cloneset.Namespace, cloneset.Spec.Selector)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
newPods := make([]corev1.Pod, 0)
for _, pod := range pods {
if !pod.DeletionTimestamp.IsZero() || pod.Spec.Containers[0].Image != "busybox:1.33" {
continue
}
newPods = append(newPods, *pod)
}
gomega.Expect(newPods).To(gomega.HaveLen(2))
ginkgo.By("PodUnavailableBudget selector cloneSet, update failed image and block done")
})
ginkgo.It("PodUnavailableBudget selector two cloneSets, strategy.type=in-place, update success image", func() {
// create pub
pub := tester.NewBasePub(ns)
pub.Spec.MaxUnavailable = &intstr.IntOrString{
Type: intstr.String,
StrVal: "20%",
}
ginkgo.By(fmt.Sprintf("Creating PodUnavailableBudget(%s.%s)", pub.Namespace, pub.Name))
tester.CreatePub(pub)
// create cloneset1
cloneset := tester.NewBaseCloneSet(ns)
cloneset.Spec.Replicas = utilpointer.Int32Ptr(5)
cloneset.Spec.UpdateStrategy.Type = appsv1alpha1.InPlaceIfPossibleCloneSetUpdateStrategyType
clonesetIn1 := cloneset.DeepCopy()
clonesetIn1.Name = fmt.Sprintf("%s-1", clonesetIn1.Name)
ginkgo.By(fmt.Sprintf("Creating CloneSet1(%s.%s)", clonesetIn1.Namespace, clonesetIn1.Name))
clonesetIn1 = tester.CreateCloneSet(clonesetIn1)
//create cloneSet2
clonesetIn2 := cloneset.DeepCopy()
clonesetIn2.Name = fmt.Sprintf("%s-2", clonesetIn2.Name)
ginkgo.By(fmt.Sprintf("Creating CloneSet2(%s.%s)", clonesetIn2.Namespace, clonesetIn2.Name))
clonesetIn2 = tester.CreateCloneSet(clonesetIn2)
// wait 10 seconds
time.Sleep(time.Second * 10)
ginkgo.By(fmt.Sprintf("check PodUnavailableBudget(%s.%s) Status", pub.Namespace, pub.Name))
expectStatus := &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 2,
DesiredAvailable: 8,
CurrentAvailable: 10,
TotalReplicas: 10,
}
setPubStatus(expectStatus)
pub, err := kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus := &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
// update failed image
ginkgo.By(fmt.Sprintf("update CloneSet(%s.%s) with failed image(busybox:failed)", cloneset.Namespace, cloneset.Name))
clonesetIn1.Spec.Template.Spec.Containers[0].Image = "busybox:failed"
_, err = kc.AppsV1alpha1().CloneSets(clonesetIn1.Namespace).Update(context.TODO(), clonesetIn1, metav1.UpdateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
clonesetIn2.Spec.Template.Spec.Containers[0].Image = "busybox:failed"
_, err = kc.AppsV1alpha1().CloneSets(clonesetIn2.Namespace).Update(context.TODO(), clonesetIn2, metav1.UpdateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
//wait 20 seconds
ginkgo.By(fmt.Sprintf("waiting 20 seconds, and check PodUnavailableBudget(%s.%s) Status", pub.Namespace, pub.Name))
time.Sleep(time.Second * 20)
expectStatus = &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 0,
DesiredAvailable: 8,
CurrentAvailable: 8,
TotalReplicas: 10,
}
setPubStatus(expectStatus)
pub, err = kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus = &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
// check now pod
pods, err := sidecarTester.GetSelectorPods(cloneset.Namespace, cloneset.Spec.Selector)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
noUpdatePods := make([]corev1.Pod, 0)
for _, pod := range pods {
if pod.Spec.Containers[0].Image == "busybox:failed" || !pod.DeletionTimestamp.IsZero() {
continue
}
noUpdatePods = append(noUpdatePods, *pod)
}
gomega.Expect(noUpdatePods).To(gomega.HaveLen(8))
// update success image
ginkgo.By(fmt.Sprintf("update CloneSet(%s.%s) success image(busybox:1.33)", cloneset.Namespace, cloneset.Name))
clonesetIn1, _ = kc.AppsV1alpha1().CloneSets(clonesetIn1.Namespace).Get(context.TODO(), clonesetIn1.Name, metav1.GetOptions{})
clonesetIn1.Spec.Template.Spec.Containers[0].Image = "busybox:1.33"
clonesetIn1, err = kc.AppsV1alpha1().CloneSets(cloneset.Namespace).Update(context.TODO(), clonesetIn1, metav1.UpdateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
// update success image
clonesetIn2, _ = kc.AppsV1alpha1().CloneSets(clonesetIn2.Namespace).Get(context.TODO(), clonesetIn2.Name, metav1.GetOptions{})
clonesetIn2.Spec.Template.Spec.Containers[0].Image = "busybox:1.33"
clonesetIn2, err = kc.AppsV1alpha1().CloneSets(clonesetIn2.Namespace).Update(context.TODO(), clonesetIn2, metav1.UpdateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
tester.WaitForCloneSetMinReadyAndRunning([]*appsv1alpha1.CloneSet{clonesetIn1, clonesetIn2}, 7)
// check pub status
ginkgo.By(fmt.Sprintf("check PodUnavailableBudget(%s.%s) Status", pub.Namespace, pub.Name))
expectStatus = &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 2,
DesiredAvailable: 8,
CurrentAvailable: 10,
TotalReplicas: 10,
}
setPubStatus(expectStatus)
pub, err = kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus = &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
//check pods
pods, err = sidecarTester.GetSelectorPods(cloneset.Namespace, cloneset.Spec.Selector)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
newPods := make([]corev1.Pod, 0)
for _, pod := range pods {
if pod.DeletionTimestamp.IsZero() && pod.Spec.Containers[0].Image == "busybox:1.33" {
newPods = append(newPods, *pod)
}
}
gomega.Expect(newPods).To(gomega.HaveLen(10))
ginkgo.By("PodUnavailableBudget selector two cloneSets, strategy.type=in-place, update success image done")
})
ginkgo.It("PodUnavailableBudget selector cloneSet and sidecarSet, strategy.type=in-place, update success image", func() {
// create pub
pub := tester.NewBasePub(ns)
pub.Spec.MaxUnavailable = &intstr.IntOrString{
Type: intstr.String,
StrVal: "20%",
}
ginkgo.By(fmt.Sprintf("Creating PodUnavailableBudget(%s.%s)", pub.Namespace, pub.Name))
tester.CreatePub(pub)
// create sidecarSet
sidecarSet := sidecarTester.NewBaseSidecarSet(ns)
sidecarSet.Spec.Namespace = ns
sidecarSet.Spec.Selector = &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "busybox",
},
}
sidecarSet.Spec.Containers = []appsv1alpha1.SidecarContainer{
{
Container: corev1.Container{
Name: "nginx-sidecar",
Image: "nginx:1.18",
Command: []string{"tail", "-f", "/dev/null"},
},
},
}
sidecarSet.Spec.UpdateStrategy = appsv1alpha1.SidecarSetUpdateStrategy{
Type: appsv1alpha1.RollingUpdateSidecarSetStrategyType,
MaxUnavailable: &intstr.IntOrString{
Type: intstr.String,
StrVal: "100%",
},
}
ginkgo.By(fmt.Sprintf("Creating SidecarSet %s", sidecarSet.Name))
sidecarSet = sidecarTester.CreateSidecarSet(sidecarSet)
// create cloneset
cloneset := tester.NewBaseCloneSet(ns)
cloneset.Spec.UpdateStrategy.Type = appsv1alpha1.InPlaceOnlyCloneSetUpdateStrategyType
cloneset.Spec.Replicas = utilpointer.Int32Ptr(10)
ginkgo.By(fmt.Sprintf("Creating CloneSet(%s.%s)", cloneset.Namespace, cloneset.Name))
cloneset = tester.CreateCloneSet(cloneset)
time.Sleep(time.Second)
// check sidecarSet inject sidecar container
ginkgo.By(fmt.Sprintf("check sidecarSet inject sidecar container and pub status"))
pods, err := sidecarTester.GetSelectorPods(cloneset.Namespace, cloneset.Spec.Selector)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
pod := pods[0]
gomega.Expect(pod.Spec.Containers).To(gomega.HaveLen(len(cloneset.Spec.Template.Spec.Containers) + len(sidecarSet.Spec.Containers)))
// wait 10 seconds
time.Sleep(time.Second * 5)
ginkgo.By(fmt.Sprintf("check PodUnavailableBudget(%s.%s) Status", pub.Namespace, pub.Name))
expectStatus := &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 2,
DesiredAvailable: 8,
CurrentAvailable: 10,
TotalReplicas: 10,
}
setPubStatus(expectStatus)
pub, err = kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus := &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
// update success image
ginkgo.By(fmt.Sprintf("update CloneSet(%s.%s) success image(busybox:1.33)", cloneset.Namespace, cloneset.Name))
cloneset, _ = kc.AppsV1alpha1().CloneSets(cloneset.Namespace).Get(context.TODO(), cloneset.Name, metav1.GetOptions{})
cloneset.Spec.Template.Spec.Containers[0].Image = "busybox:1.33"
cloneset, err = kc.AppsV1alpha1().CloneSets(cloneset.Namespace).Update(context.TODO(), cloneset, metav1.UpdateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
// update sidecar container success image
ginkgo.By(fmt.Sprintf("update sidecar container success image"))
sidecarSet.Spec.Containers[0].Image = "nginx:1.19"
sidecarTester.UpdateSidecarSet(sidecarSet)
time.Sleep(time.Second)
tester.WaitForCloneSetMinReadyAndRunning([]*appsv1alpha1.CloneSet{cloneset}, 2)
//wait 20 seconds
ginkgo.By(fmt.Sprintf("check PodUnavailableBudget(%s.%s) Status", pub.Namespace, pub.Name))
expectStatus = &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 2,
DesiredAvailable: 8,
CurrentAvailable: 10,
TotalReplicas: 10,
}
setPubStatus(expectStatus)
pub, err = kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
nowStatus = &pub.Status
setPubStatus(nowStatus)
gomega.Expect(nowStatus).To(gomega.Equal(expectStatus))
//check pods
pods, err = sidecarTester.GetSelectorPods(cloneset.Namespace, cloneset.Spec.Selector)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
newPods := make([]corev1.Pod, 0)
for _, pod := range pods {
if pod.DeletionTimestamp.IsZero() && pod.Spec.Containers[1].Image == "busybox:1.33" && pod.Spec.Containers[0].Image == "nginx:1.19" {
newPods = append(newPods, *pod)
}
}
gomega.Expect(newPods).To(gomega.HaveLen(10))
ginkgo.By("PodUnavailableBudget selector cloneSet, update failed image and block done")
})
})
})
func setPubStatus(status *policyv1alpha1.PodUnavailableBudgetStatus) {
status.DisruptedPods = nil
status.UnavailablePods = nil
status.ObservedGeneration = 0
}