support pub pub.kruise.io/disable-fetch-replicas-from-workload=true

Signed-off-by: liheng.zms <liheng.zms@alibaba-inc.com>
This commit is contained in:
liheng.zms 2024-09-24 19:28:20 +08:00 committed by Zhen Zhang
parent 450dc5e0d7
commit d79f404e1f
5 changed files with 130 additions and 15 deletions

View File

@ -35,9 +35,11 @@ const (
PubUpdateOperation PubOperation = "UPDATE"
PubDeleteOperation PubOperation = "DELETE"
PubEvictOperation PubOperation = "EVICT"
// PubProtectTotalReplicas indicates the pub protected total replicas, rather than workload.spec.replicas.
// and must be used with pub.spec.selector.
PubProtectTotalReplicas = "pub.kruise.io/protect-total-replicas"
// PubProtectTotalReplicasAnnotation is the target replicas.
// By default, PUB will get the target replicas through workload.spec.replicas. but there are some scenarios that may workload doesn't
// implement scale subresources or Pod doesn't have workload management. In this scenario, you can set pub.kruise.io/protect-total-replicas
// in pub annotations to get the target replicas to realize the same effect of protection ability.
PubProtectTotalReplicasAnnotation = "pub.kruise.io/protect-total-replicas"
// Marked the pod will not be pub-protected, solving the scenario of force pod deletion
PodPubNoProtectionAnnotation = "pub.kruise.io/no-protect"
)

View File

@ -78,6 +78,10 @@ func (c *commonControl) GetPodsForPub(pub *policyv1alpha1.PodUnavailableBudget)
if pub.Spec.TargetReference != nil {
ref := pub.Spec.TargetReference
matchedPods, expectedCount, err := c.controllerFinder.GetPodsForRef(ref.APIVersion, ref.Kind, pub.Namespace, ref.Name, true)
if value, _ := pub.Annotations[policyv1alpha1.PubProtectTotalReplicasAnnotation]; value != "" {
count, _ := strconv.ParseInt(value, 10, 32)
expectedCount = int32(count)
}
return matchedPods, expectedCount, err
} else if pub.Spec.Selector == nil {
klog.InfoS("Pub spec.Selector could not be empty", "pub", klog.KObj(pub))
@ -101,14 +105,14 @@ func (c *commonControl) GetPodsForPub(pub *policyv1alpha1.PodUnavailableBudget)
matchedPods = append(matchedPods, pod)
}
}
if value, _ := pub.Annotations[policyv1alpha1.PubProtectTotalReplicasAnnotation]; value != "" {
expectedCount, _ := strconv.ParseInt(value, 10, 32)
return matchedPods, int32(expectedCount), nil
}
expectedCount, err := c.controllerFinder.GetExpectedScaleForPods(matchedPods)
if err != nil {
return nil, 0, err
}
if expectedCount == 0 && pub.Annotations[policyv1alpha1.PubProtectTotalReplicas] != "" {
expectedCount, _ := strconv.ParseInt(pub.Annotations[policyv1alpha1.PubProtectTotalReplicas], 10, 32)
return matchedPods, int32(expectedCount), nil
}
return matchedPods, expectedCount, nil
}

View File

@ -246,6 +246,67 @@ func TestPubReconcile(t *testing.T) {
}
},
},
{
name: "select matched pub.annotations[pub.kruise.io/protect-total-replicas]=15 and selector, selector and maxUnavailable 30%",
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
var matchedPods []*corev1.Pod
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()
pub.Annotations[policyv1alpha1.PubProtectTotalReplicasAnnotation] = "15"
return pub
},
expectPubStatus: func() policyv1alpha1.PodUnavailableBudgetStatus {
return policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 0,
CurrentAvailable: 10,
DesiredAvailable: 10,
TotalReplicas: 15,
}
},
},
{
name: "select matched deployment(replicas=10,maxSurge=30%,maxUnavailable=0), and pub(selector,maxUnavailable=30%)",
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
@ -466,6 +527,56 @@ func TestPubReconcile(t *testing.T) {
}
},
},
{
name: "select matched deployment(replicas=1,maxSurge=0,maxUnavailable=30%), pub.kruise.io/protect-total-replicas=15 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",
}
pub.Annotations[policyv1alpha1.PubProtectTotalReplicasAnnotation] = "15"
return pub
},
expectPubStatus: func() policyv1alpha1.PodUnavailableBudgetStatus {
return policyv1alpha1.PodUnavailableBudgetStatus{
UnavailableAllowed: 0,
CurrentAvailable: 10,
DesiredAvailable: 10,
TotalReplicas: 15,
}
},
},
{
name: "select matched deployment(replicas=1,maxSurge=0,maxUnavailable=30%), and pub(targetRef,maxUnavailable=30%)",
getPods: func(rs ...*apps.ReplicaSet) []*corev1.Pod {
@ -1114,7 +1225,7 @@ func TestPubReconcile(t *testing.T) {
getPub: func() *policyv1alpha1.PodUnavailableBudget {
pub := pubDemo.DeepCopy()
pub.Annotations[policyv1alpha1.PubProtectTotalReplicas] = "50"
pub.Annotations[policyv1alpha1.PubProtectTotalReplicasAnnotation] = "50"
for i := 0; i < 10; i++ {
if i >= 0 && i < 5 {
pub.Status.UnavailablePods[fmt.Sprintf("test-pod-%d", i)] = metav1.Time{Time: time.Now().Add(-10 * time.Second)}

View File

@ -94,9 +94,9 @@ func (h *PodUnavailableBudgetCreateUpdateHandler) validatingPodUnavailableBudget
}
}
}
if replicasValue, ok := obj.Annotations[policyv1alpha1.PubProtectTotalReplicas]; ok {
if replicasValue, ok := obj.Annotations[policyv1alpha1.PubProtectTotalReplicasAnnotation]; ok {
if _, err := strconv.ParseInt(replicasValue, 10, 32); err != nil {
allErrs = append(allErrs, field.InternalError(field.NewPath("metadata"), fmt.Errorf("annotation[%s] is invalid", policyv1alpha1.PubProtectTotalReplicas)))
allErrs = append(allErrs, field.InternalError(field.NewPath("metadata"), fmt.Errorf("annotation[%s] is invalid", policyv1alpha1.PubProtectTotalReplicasAnnotation)))
}
}

View File

@ -20,13 +20,11 @@ import (
"context"
"testing"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
policyv1alpha1 "github.com/openkruise/kruise/apis/policy/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)
@ -166,7 +164,7 @@ func TestValidatingPub(t *testing.T) {
pub := pubDemo.DeepCopy()
pub.Spec.Selector = nil
pub.Spec.MinAvailable = nil
pub.Annotations[policyv1alpha1.PubProtectTotalReplicas] = "%%"
pub.Annotations[policyv1alpha1.PubProtectTotalReplicasAnnotation] = "%%"
return pub
},
expectErrList: 1,
@ -177,7 +175,7 @@ func TestValidatingPub(t *testing.T) {
pub := pubDemo.DeepCopy()
pub.Spec.Selector = nil
pub.Spec.MinAvailable = nil
pub.Annotations[policyv1alpha1.PubProtectTotalReplicas] = "1000"
pub.Annotations[policyv1alpha1.PubProtectTotalReplicasAnnotation] = "1000"
return pub
},
expectErrList: 0,