Skip to content

Commit 9ba7dc0

Browse files
authored
Merge pull request #7010 from zhzhuang-zju/operator-pdb-e2e
operator e2e: add pdb e2e test
2 parents 277d118 + c691fb9 commit 9ba7dc0

File tree

1 file changed

+198
-0
lines changed

1 file changed

+198
-0
lines changed
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/*
2+
Copyright 2025 The Karmada Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package e2e
18+
19+
import (
20+
"context"
21+
"time"
22+
23+
"github.com/onsi/ginkgo/v2"
24+
"github.com/onsi/gomega"
25+
appsv1 "k8s.io/api/apps/v1"
26+
policyv1 "k8s.io/api/policy/v1"
27+
apierrors "k8s.io/apimachinery/pkg/api/errors"
28+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29+
"k8s.io/apimachinery/pkg/util/intstr"
30+
"k8s.io/apimachinery/pkg/util/rand"
31+
"k8s.io/utils/ptr"
32+
33+
operatorv1alpha1 "github.com/karmada-io/karmada/operator/pkg/apis/operator/v1alpha1"
34+
"github.com/karmada-io/karmada/operator/pkg/util"
35+
operatorresource "github.com/karmada-io/karmada/test/e2e/framework/resource/operator"
36+
"github.com/karmada-io/karmada/test/helper"
37+
)
38+
39+
var deploymentGVK = appsv1.SchemeGroupVersion.WithKind("Deployment")
40+
var statefulSetGVK = appsv1.SchemeGroupVersion.WithKind("StatefulSet")
41+
42+
var _ = ginkgo.Describe("PodDisruptionBudget configuration testing", func() {
43+
var karmadaName string
44+
var karmada *operatorv1alpha1.Karmada
45+
var err error
46+
47+
var minAvailable intstr.IntOrString
48+
var maxUnavailable intstr.IntOrString
49+
50+
ginkgo.BeforeEach(func() {
51+
karmadaName = KarmadaInstanceNamePrefix + rand.String(RandomStrLength)
52+
karmada = helper.NewKarmada(testNamespace, karmadaName)
53+
})
54+
55+
ginkgo.Context("PodDisruptionBudget validation testing", func() {
56+
ginkgo.It("Should fail to deploy Karmada with invalid PDB configuration when minAvailable or maxUnavailable are both empty", func() {
57+
karmada.Spec.Components.KarmadaControllerManager.CommonSettings.PodDisruptionBudgetConfig = &operatorv1alpha1.PodDisruptionBudgetConfig{}
58+
err = operatorresource.CreateKarmadaInstance(operatorClient, karmada)
59+
gomega.Expect(err).Should(gomega.HaveOccurred())
60+
gomega.Expect(err.Error()).Should(gomega.ContainSubstring("either minAvailable or maxUnavailable must be set"))
61+
})
62+
63+
ginkgo.It("Should fail to deploy Karmada with invalid PDB configuration when both minAvailable and maxUnavailable are set", func() {
64+
minAvailable = intstr.FromInt32(1)
65+
maxUnavailable = intstr.FromInt32(1)
66+
karmada.Spec.Components.KarmadaControllerManager.CommonSettings.PodDisruptionBudgetConfig = &operatorv1alpha1.PodDisruptionBudgetConfig{
67+
MinAvailable: &minAvailable,
68+
MaxUnavailable: &maxUnavailable,
69+
}
70+
err = operatorresource.CreateKarmadaInstance(operatorClient, karmada)
71+
gomega.Expect(err).Should(gomega.HaveOccurred())
72+
gomega.Expect(err.Error()).Should(gomega.ContainSubstring("minAvailable and maxUnavailable are mutually exclusive"))
73+
})
74+
})
75+
76+
ginkgo.Context("PodDisruptionBudget functionality testing", func() {
77+
ginkgo.It("should handle PDB correctly when podDisruptionBudgetConfig is specified in CommonSettings", func() {
78+
var cmPDBName string
79+
var cmPDB *policyv1.PodDisruptionBudget
80+
var etcdPDBName string
81+
var etcdPDB *policyv1.PodDisruptionBudget
82+
83+
ginkgo.By("deploy a Karmada instance with valid PDB configuration", func() {
84+
minAvailable = intstr.FromInt32(1)
85+
pdbconfig := &operatorv1alpha1.PodDisruptionBudgetConfig{
86+
MinAvailable: &minAvailable,
87+
}
88+
karmada.Spec.Components.Etcd.Local = &operatorv1alpha1.LocalEtcd{
89+
CommonSettings: operatorv1alpha1.CommonSettings{
90+
PodDisruptionBudgetConfig: pdbconfig,
91+
},
92+
}
93+
karmada.Spec.Components.KarmadaAPIServer.CommonSettings.PodDisruptionBudgetConfig = pdbconfig
94+
karmada.Spec.Components.KarmadaControllerManager.CommonSettings.PodDisruptionBudgetConfig = pdbconfig
95+
karmada.Spec.Components.KarmadaAggregatedAPIServer.CommonSettings.PodDisruptionBudgetConfig = pdbconfig
96+
karmada.Spec.Components.KarmadaScheduler.CommonSettings.PodDisruptionBudgetConfig = pdbconfig
97+
karmada.Spec.Components.KarmadaWebhook.CommonSettings.PodDisruptionBudgetConfig = pdbconfig
98+
karmada.Spec.Components.KarmadaMetricsAdapter.CommonSettings.PodDisruptionBudgetConfig = pdbconfig
99+
100+
err = operatorresource.CreateKarmadaInstance(operatorClient, karmada)
101+
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
102+
103+
operatorresource.WaitKarmadaReady(operatorClient, testNamespace, karmadaName, time.Now())
104+
})
105+
106+
ginkgo.By("check if PDB of component etcd is created successfully", func() {
107+
etcdPDBName = util.KarmadaEtcdName(karmadaName)
108+
etcdPDB, err = hostClient.PolicyV1().PodDisruptionBudgets(testNamespace).Get(context.TODO(), etcdPDBName, metav1.GetOptions{})
109+
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
110+
gomega.Expect(etcdPDB.Spec.MinAvailable.IntValue()).Should(gomega.Equal(1))
111+
gomega.Expect(etcdPDB.Spec.MaxUnavailable == nil).Should(gomega.BeTrue())
112+
113+
var etcd *appsv1.StatefulSet
114+
etcd, err = hostClient.AppsV1().StatefulSets(testNamespace).Get(context.TODO(), etcdPDBName, metav1.GetOptions{})
115+
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
116+
gomega.Expect(etcdPDB.Labels).Should(gomega.Equal(etcd.Spec.Template.Labels))
117+
gomega.Expect(etcdPDB.Spec.Selector.MatchLabels).Should(gomega.Equal(etcd.Spec.Template.Labels))
118+
gomega.Expect(len(etcdPDB.ObjectMeta.OwnerReferences)).Should(gomega.Equal(1))
119+
// object etcd has no gvk info, so cannot use etcd.GroupVersionKind() to get StatefulSet gvk.
120+
expectedOwnerReferences := *metav1.NewControllerRef(etcd, statefulSetGVK)
121+
expectedOwnerReferences.Controller = ptr.To(true)
122+
expectedOwnerReferences.BlockOwnerDeletion = ptr.To(true)
123+
gomega.Expect(etcdPDB.ObjectMeta.OwnerReferences[0]).Should(gomega.Equal(expectedOwnerReferences))
124+
})
125+
126+
ginkgo.By("check if PDB of component KarmadaControllerManager is created successfully", func() {
127+
cmPDBName = util.KarmadaControllerManagerName(karmadaName)
128+
cmPDB, err = hostClient.PolicyV1().PodDisruptionBudgets(testNamespace).Get(context.TODO(), cmPDBName, metav1.GetOptions{})
129+
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
130+
gomega.Expect(cmPDB.Spec.MinAvailable.IntValue()).Should(gomega.Equal(1))
131+
gomega.Expect(cmPDB.Spec.MaxUnavailable == nil).Should(gomega.BeTrue())
132+
133+
var karmadaControllerManager *appsv1.Deployment
134+
karmadaControllerManager, err = hostClient.AppsV1().Deployments(testNamespace).Get(context.TODO(), cmPDBName, metav1.GetOptions{})
135+
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
136+
gomega.Expect(cmPDB.Labels).Should(gomega.Equal(karmadaControllerManager.Spec.Template.Labels))
137+
gomega.Expect(cmPDB.Spec.Selector.MatchLabels).Should(gomega.Equal(karmadaControllerManager.Spec.Template.Labels))
138+
gomega.Expect(len(cmPDB.ObjectMeta.OwnerReferences)).Should(gomega.Equal(1))
139+
// object karmadaControllerManager has no gvk info, so cannot use karmadaControllerManager.GroupVersionKind() to get Deployment gvk.
140+
expectedOwnerReferences := *metav1.NewControllerRef(karmadaControllerManager, deploymentGVK)
141+
expectedOwnerReferences.Controller = ptr.To(true)
142+
expectedOwnerReferences.BlockOwnerDeletion = ptr.To(true)
143+
gomega.Expect(cmPDB.ObjectMeta.OwnerReferences[0]).Should(gomega.Equal(expectedOwnerReferences))
144+
})
145+
146+
ginkgo.By("set PodDisruptionBudgetConfig.MaxUnavailable to 1", func() {
147+
karmada, err = operatorClient.OperatorV1alpha1().Karmadas(testNamespace).Get(context.TODO(), karmadaName, metav1.GetOptions{})
148+
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
149+
150+
maxUnavailable = intstr.FromInt32(1)
151+
karmada.Spec.Components.Etcd.Local.CommonSettings.PodDisruptionBudgetConfig = &operatorv1alpha1.PodDisruptionBudgetConfig{
152+
MaxUnavailable: &maxUnavailable,
153+
}
154+
karmada.Spec.Components.KarmadaControllerManager.CommonSettings.PodDisruptionBudgetConfig = &operatorv1alpha1.PodDisruptionBudgetConfig{
155+
MaxUnavailable: &maxUnavailable,
156+
}
157+
operatorresource.UpdateKarmadaInstanceWithSpec(operatorClient, testNamespace, karmadaName, karmada.Spec)
158+
operatorresource.WaitKarmadaReady(operatorClient, testNamespace, karmadaName, operatorresource.GetLastTransitionTime(karmada, operatorv1alpha1.Ready))
159+
})
160+
161+
ginkgo.By("check if PDB of etcd is updated successfully", func() {
162+
etcdPDB, err = hostClient.PolicyV1().PodDisruptionBudgets(testNamespace).Get(context.TODO(), etcdPDBName, metav1.GetOptions{})
163+
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
164+
gomega.Expect(etcdPDB.Spec.MinAvailable == nil).Should(gomega.BeTrue())
165+
gomega.Expect(etcdPDB.Spec.MaxUnavailable.IntValue()).Should(gomega.Equal(1))
166+
})
167+
168+
ginkgo.By("check if PDB of KarmadaControllerManager is updated successfully", func() {
169+
cmPDB, err = hostClient.PolicyV1().PodDisruptionBudgets(testNamespace).Get(context.TODO(), cmPDBName, metav1.GetOptions{})
170+
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
171+
gomega.Expect(cmPDB.Spec.MinAvailable == nil).Should(gomega.BeTrue())
172+
gomega.Expect(cmPDB.Spec.MaxUnavailable.IntValue()).Should(gomega.Equal(1))
173+
})
174+
175+
ginkgo.By("remove PodDisruptionBudgetConfig", func() {
176+
karmada, err = operatorClient.OperatorV1alpha1().Karmadas(testNamespace).Get(context.TODO(), karmadaName, metav1.GetOptions{})
177+
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
178+
179+
karmada.Spec.Components.Etcd.Local.CommonSettings.PodDisruptionBudgetConfig = nil
180+
karmada.Spec.Components.KarmadaControllerManager.CommonSettings.PodDisruptionBudgetConfig = nil
181+
operatorresource.UpdateKarmadaInstanceWithSpec(operatorClient, testNamespace, karmadaName, karmada.Spec)
182+
operatorresource.WaitKarmadaReady(operatorClient, testNamespace, karmadaName, operatorresource.GetLastTransitionTime(karmada, operatorv1alpha1.Ready))
183+
})
184+
185+
ginkgo.By("check if PDB is deleted", func() {
186+
gomega.Eventually(func() bool {
187+
_, err := hostClient.PolicyV1().PodDisruptionBudgets(testNamespace).Get(context.TODO(), etcdPDBName, metav1.GetOptions{})
188+
if !apierrors.IsNotFound(err) {
189+
return false
190+
}
191+
192+
_, err = hostClient.PolicyV1().PodDisruptionBudgets(testNamespace).Get(context.TODO(), cmPDBName, metav1.GetOptions{})
193+
return apierrors.IsNotFound(err)
194+
}, pollTimeout, pollInterval).Should(gomega.Equal(true))
195+
})
196+
})
197+
})
198+
})

0 commit comments

Comments
 (0)