fix the bug that pub calculates pods(deployment) totalReplicas incorrectly (#1164)

Signed-off-by: liheng.zms <liheng.zms@alibaba-inc.com>
This commit is contained in:
berg 2023-02-10 14:02:59 +08:00 committed by GitHub
parent 3dd4e7d443
commit 83029a47df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 568 additions and 213 deletions

View File

@ -137,6 +137,7 @@ func (c *commonControl) IsPodStateConsistent(pod *corev1.Pod) bool {
sidecarSets, sidecars := getSidecarSetsInPod(pod)
if sidecarSets.Len() > 0 && sidecars.Len() > 0 {
if !sidecarcontrol.IsSidecarContainerUpdateCompleted(pod, sidecarSets, sidecars) {
klog.V(5).Infof("PodUnavailableBudget check Pod(%s/%s) is inconsistent", pod.Namespace, pod.Name)
return false
}
}

View File

@ -176,95 +176,211 @@ var (
func TestPubReconcile(t *testing.T) {
cases := []struct {
name string
getPods func() []*corev1.Pod
getPods func(rs ...*apps.ReplicaSet) []*corev1.Pod
getDeployment func() *apps.Deployment
getReplicaSet func() *apps.ReplicaSet
getReplicaSet func() []*apps.ReplicaSet
getPub func() *policyv1alpha1.PodUnavailableBudget
expectPubStatus func() policyv1alpha1.PodUnavailableBudgetStatus
}{
{
name: "selector no matched deployment",
getPods: func() []*corev1.Pod {
pod := podDemo.DeepCopy()
pod.Labels["pub-controller"] = "false"
return []*corev1.Pod{pod}
},
getDeployment: func() *apps.Deployment {
return deploymentDemo.DeepCopy()
},
getReplicaSet: func() *apps.ReplicaSet {
return replicaSetDemo.DeepCopy()
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
return pubDemo.DeepCopy()
},
expectPubStatus: func() policyv1alpha1.PodUnavailableBudgetStatus {
return policyv1alpha1.PodUnavailableBudgetStatus{}
},
},
{
name: "selector no matched namespace deployment",
getPods: func() []*corev1.Pod {
pod := podDemo.DeepCopy()
pod.Namespace = "other-ns"
return []*corev1.Pod{pod}
},
getDeployment: func() *apps.Deployment {
object := deploymentDemo.DeepCopy()
object.Namespace = "other-ns"
return object
},
getReplicaSet: func() *apps.ReplicaSet {
object := replicaSetDemo.DeepCopy()
object.Namespace = "other-ns"
return object
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
return pubDemo.DeepCopy()
},
expectPubStatus: func() policyv1alpha1.PodUnavailableBudgetStatus {
return policyv1alpha1.PodUnavailableBudgetStatus{}
},
},
{
name: "select matched deployment, TargetReference and maxUnavailable 30%",
getPods: func() []*corev1.Pod {
name: "select matched deployment(replicas=0), selector and maxUnavailable 30%",
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; int32(i) < *deploymentDemo.Spec.Replicas; i++ {
for i := 0; int32(i) < 5; i++ {
pod := podDemo.DeepCopy()
pod.OwnerReferences = []metav1.OwnerReference{
{
APIVersion: "apps/v1",
Kind: "ReplicaSet",
Name: rs[0].Name,
UID: rs[0].UID,
Controller: utilpointer.BoolPtr(true),
},
}
pod.Name = fmt.Sprintf("%s-%d", pod.Name, i)
matchedPods = append(matchedPods, pod)
}
for i := 5; int32(i) < 10; i++ {
pod := podDemo.DeepCopy()
pod.OwnerReferences = []metav1.OwnerReference{
{
APIVersion: "apps/v1",
Kind: "ReplicaSet",
Name: rs[1].Name,
UID: rs[1].UID,
Controller: utilpointer.BoolPtr(true),
},
}
pod.Name = fmt.Sprintf("%s-%d", pod.Name, i)
matchedPods = append(matchedPods, pod)
}
return matchedPods
},
getDeployment: func() *apps.Deployment {
obj := deploymentDemo.DeepCopy()
obj.Spec.Replicas = utilpointer.Int32(0)
return obj
},
getReplicaSet: func() []*apps.ReplicaSet {
obj1 := replicaSetDemo.DeepCopy()
obj1.Name = "nginx-rs-1"
obj2 := replicaSetDemo.DeepCopy()
obj2.Name = "nginx-rs-2"
obj2.UID = "a34b0453-3426-4685-a79c-752e7062a523"
return []*apps.ReplicaSet{obj1, obj2}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
return pub
},
expectPubStatus: func() policyv1alpha1.PodUnavailableBudgetStatus {
return policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 10,
CurrentAvailable: 10,
DesiredAvailable: 0,
TotalReplicas: 0,
}
},
},
{
name: "select matched deployment(replicas=10,maxSurge=30%,maxUnavailable=0), and pub(selector,maxUnavailable=30%)",
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; int32(i) < 10; i++ {
pod := podDemo.DeepCopy()
pod.OwnerReferences = []metav1.OwnerReference{
{
APIVersion: "apps/v1",
Kind: "ReplicaSet",
Name: rs[0].Name,
UID: rs[0].UID,
Controller: utilpointer.BoolPtr(true),
},
}
pod.Name = fmt.Sprintf("%s-%d", pod.Name, i)
matchedPods = append(matchedPods, pod)
}
for i := 10; int32(i) < 13; i++ {
pod := podDemo.DeepCopy()
pod.OwnerReferences = []metav1.OwnerReference{
{
APIVersion: "apps/v1",
Kind: "ReplicaSet",
Name: rs[1].Name,
UID: rs[1].UID,
Controller: utilpointer.BoolPtr(true),
},
}
pod.Name = fmt.Sprintf("%s-%d", pod.Name, i)
if i == 12 {
pod.Status.Conditions = []corev1.PodCondition{
{
Type: corev1.PodReady,
Status: corev1.ConditionFalse,
},
}
}
matchedPods = append(matchedPods, pod)
}
return matchedPods
},
getDeployment: func() *apps.Deployment {
return deploymentDemo.DeepCopy()
},
getReplicaSet: func() *apps.ReplicaSet {
return replicaSetDemo.DeepCopy()
getReplicaSet: func() []*apps.ReplicaSet {
obj1 := replicaSetDemo.DeepCopy()
obj1.Name = "nginx-rs-1"
obj2 := replicaSetDemo.DeepCopy()
obj2.Name = "nginx-rs-2"
obj2.UID = "a34b0453-3426-4685-a79c-752e7062a523"
return []*apps.ReplicaSet{obj1, obj2}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
pub.Spec.Selector = nil
pub.Spec.TargetReference = &policyv1alpha1.TargetReference{
APIVersion: "apps/v1",
Kind: "Deployment",
Name: "nginx",
}
return pub
},
expectPubStatus: func() policyv1alpha1.PodUnavailableBudgetStatus {
return policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 3,
CurrentAvailable: *deploymentDemo.Spec.Replicas,
UnavailableAllowed: 5,
CurrentAvailable: 12,
DesiredAvailable: 7,
TotalReplicas: *deploymentDemo.Spec.Replicas,
TotalReplicas: 10,
}
},
},
{
name: "select matched deployment(replicas=10,maxSurge=0,maxUnavailable=30%), and pub(selector,maxUnavailable=30%)",
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; int32(i) < 10; i++ {
pod := podDemo.DeepCopy()
pod.OwnerReferences = []metav1.OwnerReference{
{
APIVersion: "apps/v1",
Kind: "ReplicaSet",
Name: rs[0].Name,
UID: rs[0].UID,
Controller: utilpointer.BoolPtr(true),
},
}
pod.Name = fmt.Sprintf("%s-%d", pod.Name, i)
t := metav1.Now()
if i >= 7 && i < 10 {
pod.DeletionTimestamp = &t
}
matchedPods = append(matchedPods, pod)
}
for i := 10; int32(i) < 13; i++ {
pod := podDemo.DeepCopy()
pod.OwnerReferences = []metav1.OwnerReference{
{
APIVersion: "apps/v1",
Kind: "ReplicaSet",
Name: rs[1].Name,
UID: rs[1].UID,
Controller: utilpointer.BoolPtr(true),
},
}
pod.Name = fmt.Sprintf("%s-%d", pod.Name, i)
if i == 12 {
pod.Status.Conditions = []corev1.PodCondition{
{
Type: corev1.PodReady,
Status: corev1.ConditionFalse,
},
}
}
matchedPods = append(matchedPods, pod)
}
return matchedPods
},
getDeployment: func() *apps.Deployment {
return deploymentDemo.DeepCopy()
},
getReplicaSet: func() []*apps.ReplicaSet {
obj1 := replicaSetDemo.DeepCopy()
obj1.Name = "nginx-rs-1"
obj2 := replicaSetDemo.DeepCopy()
obj2.Name = "nginx-rs-2"
obj2.UID = "a34b0453-3426-4685-a79c-752e7062a523"
return []*apps.ReplicaSet{obj1, obj2}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
return pub
},
expectPubStatus: func() policyv1alpha1.PodUnavailableBudgetStatus {
return policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 2,
CurrentAvailable: 9,
DesiredAvailable: 7,
TotalReplicas: 10,
}
},
},
{
name: "select matched deployment(Deletion), selector and maxUnavailable 30%",
getPods: func() []*corev1.Pod {
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; int32(i) < *deploymentDemo.Spec.Replicas; i++ {
pod := podDemo.DeepCopy()
@ -279,12 +395,11 @@ func TestPubReconcile(t *testing.T) {
obj.DeletionTimestamp = &t
return obj
},
getReplicaSet: func() *apps.ReplicaSet {
return replicaSetDemo.DeepCopy()
getReplicaSet: func() []*apps.ReplicaSet {
return []*apps.ReplicaSet{replicaSetDemo.DeepCopy()}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
pub.Name = "liheng"
return pub
},
expectPubStatus: func() policyv1alpha1.PodUnavailableBudgetStatus {
@ -296,9 +411,210 @@ func TestPubReconcile(t *testing.T) {
}
},
},
{
name: "select matched deployment(replicas=0,maxSurge=0,maxUnavailable=30%), and pub(targetRef,maxUnavailable=30%)",
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; int32(i) < 10; i++ {
pod := podDemo.DeepCopy()
pod.OwnerReferences = []metav1.OwnerReference{
{
APIVersion: "apps/v1",
Kind: "ReplicaSet",
Name: rs[0].Name,
UID: rs[0].UID,
Controller: utilpointer.BoolPtr(true),
},
}
pod.Name = fmt.Sprintf("%s-%d", pod.Name, i)
matchedPods = append(matchedPods, pod)
}
return matchedPods
},
getDeployment: func() *apps.Deployment {
obj := deploymentDemo.DeepCopy()
obj.Spec.Replicas = utilpointer.Int32(0)
return obj
},
getReplicaSet: func() []*apps.ReplicaSet {
obj1 := replicaSetDemo.DeepCopy()
obj1.Name = "nginx-rs-1"
return []*apps.ReplicaSet{obj1}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
pub.Spec.Selector = nil
pub.Spec.TargetReference = &policyv1alpha1.TargetReference{
APIVersion: "apps/v1",
Kind: "Deployment",
Name: "nginx",
}
return pub
},
expectPubStatus: func() policyv1alpha1.PodUnavailableBudgetStatus {
return policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 0,
CurrentAvailable: 0,
DesiredAvailable: 0,
TotalReplicas: 0,
}
},
},
{
name: "select matched deployment(replicas=1,maxSurge=0,maxUnavailable=30%), and pub(targetRef,maxUnavailable=30%)",
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; int32(i) < 10; i++ {
pod := podDemo.DeepCopy()
pod.OwnerReferences = []metav1.OwnerReference{
{
APIVersion: "apps/v1",
Kind: "ReplicaSet",
Name: rs[0].Name,
UID: rs[0].UID,
Controller: utilpointer.BoolPtr(true),
},
}
pod.Name = fmt.Sprintf("%s-%d", pod.Name, i)
matchedPods = append(matchedPods, pod)
}
return matchedPods
},
getDeployment: func() *apps.Deployment {
obj := deploymentDemo.DeepCopy()
obj.Spec.Replicas = utilpointer.Int32(1)
return obj
},
getReplicaSet: func() []*apps.ReplicaSet {
obj1 := replicaSetDemo.DeepCopy()
obj1.Name = "nginx-rs-1"
return []*apps.ReplicaSet{obj1}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
pub.Spec.Selector = nil
pub.Spec.TargetReference = &policyv1alpha1.TargetReference{
APIVersion: "apps/v1",
Kind: "Deployment",
Name: "nginx",
}
return pub
},
expectPubStatus: func() policyv1alpha1.PodUnavailableBudgetStatus {
return policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 10,
CurrentAvailable: 10,
DesiredAvailable: 0,
TotalReplicas: 1,
}
},
},
{
name: "select matched deployment(replicas=10,maxSurge=0,maxUnavailable=30%), and pub(targetRef,maxUnavailable=30%)",
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; int32(i) < 10; i++ {
pod := podDemo.DeepCopy()
pod.OwnerReferences = []metav1.OwnerReference{
{
APIVersion: "apps/v1",
Kind: "ReplicaSet",
Name: rs[0].Name,
UID: rs[0].UID,
Controller: utilpointer.BoolPtr(true),
},
}
if i >= 7 {
t := metav1.Now()
pod.DeletionTimestamp = &t
}
pod.Name = fmt.Sprintf("%s-%d", pod.Name, i)
matchedPods = append(matchedPods, pod)
}
return matchedPods
},
getDeployment: func() *apps.Deployment {
obj := deploymentDemo.DeepCopy()
obj.Spec.Replicas = utilpointer.Int32(10)
return obj
},
getReplicaSet: func() []*apps.ReplicaSet {
obj1 := replicaSetDemo.DeepCopy()
obj1.Name = "nginx-rs-1"
return []*apps.ReplicaSet{obj1}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
pub.Spec.Selector = nil
pub.Spec.TargetReference = &policyv1alpha1.TargetReference{
APIVersion: "apps/v1",
Kind: "Deployment",
Name: "nginx",
}
return pub
},
expectPubStatus: func() policyv1alpha1.PodUnavailableBudgetStatus {
return policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 0,
CurrentAvailable: 7,
DesiredAvailable: 7,
TotalReplicas: 10,
}
},
},
{
name: "select matched deployment(deletion), and pub(targetRef,maxUnavailable=30%)",
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; int32(i) < 10; i++ {
pod := podDemo.DeepCopy()
pod.OwnerReferences = []metav1.OwnerReference{
{
APIVersion: "apps/v1",
Kind: "ReplicaSet",
Name: rs[0].Name,
UID: rs[0].UID,
Controller: utilpointer.BoolPtr(true),
},
}
pod.Name = fmt.Sprintf("%s-%d", pod.Name, i)
matchedPods = append(matchedPods, pod)
}
return matchedPods
},
getDeployment: func() *apps.Deployment {
obj := deploymentDemo.DeepCopy()
t := metav1.Now()
obj.DeletionTimestamp = &t
return obj
},
getReplicaSet: func() []*apps.ReplicaSet {
obj1 := replicaSetDemo.DeepCopy()
obj1.Name = "nginx-rs-1"
return []*apps.ReplicaSet{obj1}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
pub.Spec.Selector = nil
pub.Spec.TargetReference = &policyv1alpha1.TargetReference{
APIVersion: "apps/v1",
Kind: "Deployment",
Name: "nginx",
}
return pub
},
expectPubStatus: func() policyv1alpha1.PodUnavailableBudgetStatus {
return policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 0,
CurrentAvailable: 0,
DesiredAvailable: 0,
TotalReplicas: 0,
}
},
},
{
name: "select matched deployment, maxUnavailable 0%",
getPods: func() []*corev1.Pod {
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; int32(i) < *deploymentDemo.Spec.Replicas; i++ {
pod := podDemo.DeepCopy()
@ -310,8 +626,8 @@ func TestPubReconcile(t *testing.T) {
getDeployment: func() *apps.Deployment {
return deploymentDemo.DeepCopy()
},
getReplicaSet: func() *apps.ReplicaSet {
return replicaSetDemo.DeepCopy()
getReplicaSet: func() []*apps.ReplicaSet {
return []*apps.ReplicaSet{replicaSetDemo.DeepCopy()}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
@ -332,7 +648,7 @@ func TestPubReconcile(t *testing.T) {
},
{
name: "select matched deployment, maxUnavailable 100%",
getPods: func() []*corev1.Pod {
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; int32(i) < *deploymentDemo.Spec.Replicas; i++ {
pod := podDemo.DeepCopy()
@ -344,8 +660,8 @@ func TestPubReconcile(t *testing.T) {
getDeployment: func() *apps.Deployment {
return deploymentDemo.DeepCopy()
},
getReplicaSet: func() *apps.ReplicaSet {
return replicaSetDemo.DeepCopy()
getReplicaSet: func() []*apps.ReplicaSet {
return []*apps.ReplicaSet{replicaSetDemo.DeepCopy()}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
@ -366,7 +682,7 @@ func TestPubReconcile(t *testing.T) {
},
{
name: "select matched deployment, maxUnavailable 100, and expect scale 10",
getPods: func() []*corev1.Pod {
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; int32(i) < *deploymentDemo.Spec.Replicas; i++ {
pod := podDemo.DeepCopy()
@ -378,8 +694,8 @@ func TestPubReconcile(t *testing.T) {
getDeployment: func() *apps.Deployment {
return deploymentDemo.DeepCopy()
},
getReplicaSet: func() *apps.ReplicaSet {
return replicaSetDemo.DeepCopy()
getReplicaSet: func() []*apps.ReplicaSet {
return []*apps.ReplicaSet{replicaSetDemo.DeepCopy()}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
@ -400,7 +716,7 @@ func TestPubReconcile(t *testing.T) {
},
{
name: "select matched deployment, minAvailable 100 int",
getPods: func() []*corev1.Pod {
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; i < 5; i++ {
pod := podDemo.DeepCopy()
@ -412,8 +728,8 @@ func TestPubReconcile(t *testing.T) {
getDeployment: func() *apps.Deployment {
return deploymentDemo.DeepCopy()
},
getReplicaSet: func() *apps.ReplicaSet {
return replicaSetDemo.DeepCopy()
getReplicaSet: func() []*apps.ReplicaSet {
return []*apps.ReplicaSet{replicaSetDemo.DeepCopy()}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
@ -435,7 +751,7 @@ func TestPubReconcile(t *testing.T) {
},
{
name: "select matched deployment, minAvailable 50%",
getPods: func() []*corev1.Pod {
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; i < 5; i++ {
pod := podDemo.DeepCopy()
@ -447,8 +763,8 @@ func TestPubReconcile(t *testing.T) {
getDeployment: func() *apps.Deployment {
return deploymentDemo.DeepCopy()
},
getReplicaSet: func() *apps.ReplicaSet {
return replicaSetDemo.DeepCopy()
getReplicaSet: func() []*apps.ReplicaSet {
return []*apps.ReplicaSet{replicaSetDemo.DeepCopy()}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
@ -470,7 +786,7 @@ func TestPubReconcile(t *testing.T) {
},
{
name: "select matched deployment, minAvailable 80%",
getPods: func() []*corev1.Pod {
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; i < 10; i++ {
pod := podDemo.DeepCopy()
@ -482,8 +798,8 @@ func TestPubReconcile(t *testing.T) {
getDeployment: func() *apps.Deployment {
return deploymentDemo.DeepCopy()
},
getReplicaSet: func() *apps.ReplicaSet {
return replicaSetDemo.DeepCopy()
getReplicaSet: func() []*apps.ReplicaSet {
return []*apps.ReplicaSet{replicaSetDemo.DeepCopy()}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
@ -505,7 +821,7 @@ func TestPubReconcile(t *testing.T) {
},
{
name: "select matched deployment, 10 UnavailablePods and 10 DisruptionPods",
getPods: func() []*corev1.Pod {
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; i < 100; i++ {
pod := podDemo.DeepCopy()
@ -519,10 +835,10 @@ func TestPubReconcile(t *testing.T) {
object.Spec.Replicas = utilpointer.Int32Ptr(100)
return object
},
getReplicaSet: func() *apps.ReplicaSet {
getReplicaSet: func() []*apps.ReplicaSet {
object := replicaSetDemo.DeepCopy()
object.Spec.Replicas = utilpointer.Int32Ptr(100)
return object
return []*apps.ReplicaSet{object}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
@ -551,7 +867,7 @@ func TestPubReconcile(t *testing.T) {
},
{
name: "select matched deployment, 10 UnavailablePods, 10 DisruptionPods and 5 not ready",
getPods: func() []*corev1.Pod {
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; i < 100; i++ {
pod := podDemo.DeepCopy()
@ -569,10 +885,10 @@ func TestPubReconcile(t *testing.T) {
object.Spec.Replicas = utilpointer.Int32Ptr(100)
return object
},
getReplicaSet: func() *apps.ReplicaSet {
getReplicaSet: func() []*apps.ReplicaSet {
object := replicaSetDemo.DeepCopy()
object.Spec.Replicas = utilpointer.Int32Ptr(100)
return object
return []*apps.ReplicaSet{object}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
@ -601,7 +917,7 @@ func TestPubReconcile(t *testing.T) {
},
{
name: "select matched deployment, 10 UnavailablePods, 10 DisruptionPods and 5 deletion",
getPods: func() []*corev1.Pod {
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; i < 100; i++ {
pod := podDemo.DeepCopy()
@ -618,10 +934,10 @@ func TestPubReconcile(t *testing.T) {
object.Spec.Replicas = utilpointer.Int32Ptr(100)
return object
},
getReplicaSet: func() *apps.ReplicaSet {
getReplicaSet: func() []*apps.ReplicaSet {
object := replicaSetDemo.DeepCopy()
object.Spec.Replicas = utilpointer.Int32Ptr(100)
return object
return []*apps.ReplicaSet{object}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
@ -650,7 +966,7 @@ func TestPubReconcile(t *testing.T) {
},
{
name: "select matched deployment, 10 UnavailablePods(5 ready), 10 DisruptionPods(5 deletion)",
getPods: func() []*corev1.Pod {
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; i < 100; i++ {
pod := podDemo.DeepCopy()
@ -667,10 +983,10 @@ func TestPubReconcile(t *testing.T) {
object.Spec.Replicas = utilpointer.Int32Ptr(100)
return object
},
getReplicaSet: func() *apps.ReplicaSet {
getReplicaSet: func() []*apps.ReplicaSet {
object := replicaSetDemo.DeepCopy()
object.Spec.Replicas = utilpointer.Int32Ptr(100)
return object
return []*apps.ReplicaSet{object}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
@ -703,7 +1019,7 @@ func TestPubReconcile(t *testing.T) {
},
{
name: "select matched deployment, 10 UnavailablePods(5 ready), 10 DisruptionPods(5 delay) and 5 deletion",
getPods: func() []*corev1.Pod {
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; i < 100; i++ {
pod := podDemo.DeepCopy()
@ -720,10 +1036,10 @@ func TestPubReconcile(t *testing.T) {
object.Spec.Replicas = utilpointer.Int32Ptr(100)
return object
},
getReplicaSet: func() *apps.ReplicaSet {
getReplicaSet: func() []*apps.ReplicaSet {
object := replicaSetDemo.DeepCopy()
object.Spec.Replicas = utilpointer.Int32Ptr(100)
return object
return []*apps.ReplicaSet{object}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
@ -760,7 +1076,7 @@ func TestPubReconcile(t *testing.T) {
},
{
name: "test select matched deployment, 10 UnavailablePods(5 ready), 10 DisruptionPods(5 delay) and 5 deletion",
getPods: func() []*corev1.Pod {
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
for i := 0; i < 100; i++ {
pod := podDemo.DeepCopy()
@ -778,10 +1094,10 @@ func TestPubReconcile(t *testing.T) {
object.Spec.Replicas = utilpointer.Int32Ptr(100)
return object
},
getReplicaSet: func() *apps.ReplicaSet {
getReplicaSet: func() []*apps.ReplicaSet {
object := replicaSetDemo.DeepCopy()
object.Spec.Replicas = utilpointer.Int32Ptr(100)
return object
return []*apps.ReplicaSet{object}
},
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
@ -824,14 +1140,15 @@ func TestPubReconcile(t *testing.T) {
t.Run(cs.name, func(t *testing.T) {
pub := cs.getPub()
defer util.GlobalCache.Delete(pub)
fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(cs.getDeployment(), cs.getReplicaSet(), pub).Build()
for _, pod := range cs.getPods() {
fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(cs.getDeployment(), pub).Build()
for _, pod := range cs.getPods(cs.getReplicaSet()...) {
podIn := pod.DeepCopy()
err := fakeClient.Create(context.TODO(), podIn)
if err != nil {
t.Fatalf("create pod failed: %s", err.Error())
}
_ = fakeClient.Create(context.TODO(), podIn)
}
for _, obj := range cs.getReplicaSet() {
_ = fakeClient.Create(context.TODO(), obj)
}
controllerfinder.Finder = &controllerfinder.ControllerFinder{Client: fakeClient}
reconciler := ReconcilePodUnavailableBudget{
Client: fakeClient,

View File

@ -31,6 +31,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
clientset "k8s.io/client-go/kubernetes"
@ -108,27 +109,24 @@ func (r *ControllerFinder) GetExpectedScaleForPods(pods []*corev1.Pod) (int32, e
// 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.
podRefs := sets.NewString()
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 {
// ref has already been got, so there is no need to get again
if ref == nil || podRefs.Has(string(ref.UID)) {
continue
}
podRefs.Insert(string(ref.UID))
// 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) {
if err != nil {
return 0, err
} else if workload != nil && workload.Metadata.DeletionTimestamp.IsZero() {
controllerScale[ref.UID] = workload.Scale
} else {
controllerScale[ref.UID] = 0
} else if workload == nil || !workload.Metadata.DeletionTimestamp.IsZero() {
continue
}
controllerScale[workload.UID] = workload.Scale
}
// 2. Add up all the controllers.
var expectedCount int32
for _, count := range controllerScale {

View File

@ -34,17 +34,15 @@ import (
// GetPodsForRef return target workload's podList and spec.replicas.
func (r *ControllerFinder) GetPodsForRef(apiVersion, kind, ns, name string, active bool) ([]*corev1.Pod, int32, error) {
workloadUIDs := make([]types.UID, 0)
var workloadUIDs []types.UID
var workloadReplicas int32
switch kind {
// ReplicaSet
case ControllerKindRS.Kind:
rs, err := r.getReplicaSet(ControllerReference{APIVersion: apiVersion, Kind: kind, Name: name}, ns)
if err != nil {
return nil, -1, err
}
if rs == nil || !rs.DeletionTimestamp.IsZero() {
} else if rs == nil || !rs.DeletionTimestamp.IsZero() {
return nil, 0, nil
}
workloadReplicas = *rs.Spec.Replicas
@ -59,7 +57,7 @@ func (r *ControllerFinder) GetPodsForRef(apiVersion, kind, ns, name string, acti
}
workloadReplicas = obj.Scale
workloadUIDs = append(workloadUIDs, obj.UID)
// Deployment, Deployment-like workload, and other workload
// Deployment, Deployment-like workload or other custom workload(support scale sub-resources)
default:
obj, err := r.GetScaleAndSelectorForRef(apiVersion, kind, ns, name, "")
if err != nil {
@ -69,11 +67,10 @@ func (r *ControllerFinder) GetPodsForRef(apiVersion, kind, ns, name string, acti
}
workloadReplicas = obj.Scale
// try to get replicaSets
rss, err := r.getReplicaSetsForDeployment(apiVersion, kind, ns, name)
rss, err := r.getReplicaSetsForObject(obj)
if err != nil {
return nil, -1, err
}
if len(rss) == 0 {
workloadUIDs = append(workloadUIDs, obj.UID)
} else {
@ -82,6 +79,9 @@ func (r *ControllerFinder) GetPodsForRef(apiVersion, kind, ns, name string, acti
}
}
}
if workloadReplicas == 0 {
return nil, workloadReplicas, nil
}
// List all Pods owned by workload UID.
matchedPods := make([]*corev1.Pod, 0)
@ -107,29 +107,26 @@ func (r *ControllerFinder) GetPodsForRef(apiVersion, kind, ns, name string, acti
return matchedPods, workloadReplicas, nil
}
func (r *ControllerFinder) getReplicaSetsForDeployment(apiVersion, kind, ns, name string) ([]appsv1.ReplicaSet, error) {
scaleNSelector, err := r.GetScaleAndSelectorForRef(apiVersion, kind, ns, name, "")
if err != nil || scaleNSelector == nil {
return nil, err
}
func (r *ControllerFinder) getReplicaSetsForObject(scale *ScaleAndSelector) ([]appsv1.ReplicaSet, error) {
// List ReplicaSets owned by this Deployment
rsList := &appsv1.ReplicaSetList{}
selector, err := util.ValidatedLabelSelectorAsSelector(scaleNSelector.Selector)
selector, err := util.ValidatedLabelSelectorAsSelector(scale.Selector)
if err != nil {
klog.Errorf("Deployment (%s/%s) get labelSelector failed: %s", ns, name, err.Error())
klog.Warningf("Object (%s/%s) get labelSelector failed: %s", scale.Metadata.Namespace, scale.Metadata.Name, err.Error())
return nil, nil
}
err = r.List(context.TODO(), rsList, &client.ListOptions{Namespace: ns, LabelSelector: selector})
err = r.List(context.TODO(), rsList, &client.ListOptions{Namespace: scale.Metadata.Namespace, LabelSelector: selector}, utilclient.DisableDeepCopy)
if err != nil {
return nil, err
}
rss := make([]appsv1.ReplicaSet, 0)
for i := range rsList.Items {
rs := rsList.Items[i]
if ref := metav1.GetControllerOf(&rs); ref != nil {
if ref.UID == scaleNSelector.UID {
rss = append(rss, rs)
}
if *rs.Spec.Replicas == 0 || !rs.DeletionTimestamp.IsZero() {
continue
}
if ref := metav1.GetControllerOf(&rs); ref != nil && ref.UID == scale.UID {
rss = append(rss, rs)
}
}
return rss, nil

View File

@ -113,7 +113,7 @@ func (s *PodUnavailableBudgetTester) NewBaseDeployment(namespace string) *apps.D
RollingUpdate: &apps.RollingUpdateDeployment{
MaxUnavailable: &intstr.IntOrString{
Type: intstr.String,
StrVal: "100%",
StrVal: "50%",
},
MaxSurge: &intstr.IntOrString{
Type: intstr.Int,

View File

@ -459,90 +459,132 @@ var _ = SIGDescribe("PodUnavailableBudget", func() {
ginkgo.By("PodUnavailableBudget targetReference pods, update failed image and block done")
})
/*
ginkgo.It("PodUnavailableBudget selector two deployments, deployment.strategy.maxUnavailable=100%, pub.spec.maxUnavailable=50%, and update success image", func() {
// create pub
pub := tester.NewBasePub(ns)
pub.Spec.MaxUnavailable = &intstr.IntOrString{
Type: intstr.String,
StrVal: "50%",
}
ginkgo.By(fmt.Sprintf("Creating PodUnavailableBudget(%s/%s)", pub.Namespace, pub.Name))
tester.CreatePub(pub)
ginkgo.It("PodUnavailableBudget selector two deployments, deployment.strategy.maxUnavailable=25% and maxSurge=25%, pub.spec.maxUnavailable=25%, and update success image", func() {
// create pub
var err error
pub := tester.NewBasePub(ns)
pub.Spec.MaxUnavailable = &intstr.IntOrString{
Type: intstr.String,
StrVal: "25%",
}
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)
// create deployment1
deployment := tester.NewBaseDeployment(ns)
deployment.Spec.Strategy.RollingUpdate.MaxUnavailable = &intstr.IntOrString{
Type: intstr.String,
StrVal: "25%",
}
deployment.Spec.Strategy.RollingUpdate.MaxSurge = &intstr.IntOrString{
Type: intstr.String,
StrVal: "25%",
}
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)
ginkgo.By(fmt.Sprintf("PodUnavailableBudget(%s/%s) Status", pub.Namespace, pub.Name))
expectStatus := &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 5,
DesiredAvailable: 5,
CurrentAvailable: 10,
TotalReplicas: 10,
}
setPubStatus(expectStatus)
gomega.Eventually(func() *policyv1alpha1.PodUnavailableBudgetStatus {
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)
return nowStatus
}, 30*time.Second, time.Second).Should(gomega.Equal(expectStatus))
// update success image
ginkgo.By(fmt.Sprintf("update Deployment-1 and deployment-2 with success image"))
deploymentIn1.Spec.Template.Spec.Containers[0].Image = NewWebserverImage
_, err = c.AppsV1().Deployments(deploymentIn1.Namespace).Update(context.TODO(), deploymentIn1, metav1.UpdateOptions{})
ginkgo.By(fmt.Sprintf("PodUnavailableBudget(%s/%s) Status", pub.Namespace, pub.Name))
expectStatus := &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 3,
DesiredAvailable: 7,
CurrentAvailable: 10,
TotalReplicas: 10,
}
setPubStatus(expectStatus)
gomega.Eventually(func() *policyv1alpha1.PodUnavailableBudgetStatus {
pub, err = kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
deploymentIn2.Spec.Template.Spec.Containers[0].Image = NewWebserverImage
_, err = c.AppsV1().Deployments(deploymentIn2.Namespace).Update(context.TODO(), deploymentIn2, metav1.UpdateOptions{})
nowStatus := &pub.Status
setPubStatus(nowStatus)
return nowStatus
}, 30*time.Second, time.Second).Should(gomega.Equal(expectStatus))
// update success image
ginkgo.By("update Deployment-1 and deployment-2 with success image")
deploymentIn1.Spec.Template.Spec.Containers[0].Image = NewWebserverImage
_, 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 = NewWebserverImage
_, 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("wait 1 seconds, and check deployment, pub Status")
time.Sleep(time.Second)
// check deployment
tester.WaitForDeploymentReadyAndRunning(deploymentIn1)
tester.WaitForDeploymentReadyAndRunning(deploymentIn2)
// check pods
pods, err := sidecarTester.GetSelectorPods(deployment.Namespace, deployment.Spec.Selector)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
for _, pod := range pods {
gomega.Expect(pod.Spec.Containers[0].Image).To(gomega.Equal(NewWebserverImage))
}
expectStatus = &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 3,
DesiredAvailable: 7,
CurrentAvailable: 10,
TotalReplicas: 10,
}
setPubStatus(expectStatus)
gomega.Eventually(func() *policyv1alpha1.PodUnavailableBudgetStatus {
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)
return nowStatus
}, 5*time.Second, time.Second).Should(gomega.Equal(expectStatus))
// 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}, 5)
// check pods
pods, err := sidecarTester.GetSelectorPods(deployment.Namespace, deployment.Spec.Selector)
// scale down replicas = 0
ginkgo.By("scale down Deployment-1 replicas to 0")
deploymentIn1.Spec.Replicas = utilpointer.Int32(0)
_, err = c.AppsV1().Deployments(deploymentIn1.Namespace).Update(context.TODO(), deploymentIn1, metav1.UpdateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
tester.WaitForDeploymentReadyAndRunning(deploymentIn1)
expectStatus = &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 2,
DesiredAvailable: 3,
CurrentAvailable: 5,
TotalReplicas: 5,
}
setPubStatus(expectStatus)
gomega.Eventually(func() *policyv1alpha1.PodUnavailableBudgetStatus {
pub, err = kc.PolicyV1alpha1().PodUnavailableBudgets(pub.Namespace).Get(context.TODO(), pub.Name, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
newPods := make([]corev1.Pod, 0)
for _, pod := range pods {
if !pod.DeletionTimestamp.IsZero() || pod.Spec.Containers[0].Image != NewWebserverImage {
continue
}
newPods = append(newPods, *pod)
}
gomega.Expect(newPods).To(gomega.HaveLen(10))
nowStatus := &pub.Status
setPubStatus(nowStatus)
return nowStatus
}, 5*time.Second, time.Second).Should(gomega.Equal(expectStatus))
expectStatus = &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 5,
DesiredAvailable: 5,
CurrentAvailable: 10,
TotalReplicas: 10,
}
setPubStatus(expectStatus)
gomega.Eventually(func() *policyv1alpha1.PodUnavailableBudgetStatus {
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)
return nowStatus
}, 30*time.Second, time.Second).Should(gomega.Equal(expectStatus))
// delete deployment directly
err = c.AppsV1().Deployments(deploymentIn2.Namespace).Delete(context.TODO(), deploymentIn2.Name, metav1.DeleteOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
time.Sleep(time.Second * 3)
expectStatus = &policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 0,
DesiredAvailable: 0,
CurrentAvailable: 0,
TotalReplicas: 0,
}
setPubStatus(expectStatus)
gomega.Eventually(func() *policyv1alpha1.PodUnavailableBudgetStatus {
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)
return nowStatus
}, 5*time.Second, time.Second).Should(gomega.Equal(expectStatus))
ginkgo.By("PodUnavailableBudget selector two deployments, deployment.strategy.maxUnavailable=100%, pub.spec.maxUnavailable=50%, and update success image done")
})
*/
ginkgo.By("PodUnavailableBudget selector two deployments, deployment.strategy.maxUnavailable=100%, pub.spec.maxUnavailable=50%, and update success image done")
})
ginkgo.It("PodUnavailableBudget selector SidecarSet, inject sidecar container, update failed sidecar image, block", func() {
// create pub