mirror of https://github.com/openkruise/kruise.git
				
				
				
			add pub interface GetPodsForPub (#758)
Signed-off-by: liheng.zms <liheng.zms@alibaba-inc.com>
This commit is contained in:
		
							parent
							
								
									e4956e650b
								
							
						
					
					
						commit
						c8f0a54c5d
					
				|  | @ -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"` | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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: | ||||
|  |  | |||
|  | @ -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} | ||||
| } | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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") | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | @ -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), | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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, | ||||
| 				} | ||||
| 			}, | ||||
| 		}, | ||||
|  |  | |||
|  | @ -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{ | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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" | ||||
|  |  | |||
|  | @ -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")) | ||||
|  |  | |||
|  | @ -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) | ||||
| 	} | ||||
| } | ||||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
		Loading…
	
		Reference in New Issue