Skip to content

Commit a7f9b82

Browse files
authored
feat: add PodDisruptionBudget trackability (#999)
add PDB trackability and unit test
1 parent 4884796 commit a7f9b82

File tree

4 files changed

+132
-0
lines changed

4 files changed

+132
-0
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ require (
3030
k8s.io/apimachinery v0.30.3
3131
k8s.io/client-go v0.30.3
3232
k8s.io/component-base v0.30.2
33+
k8s.io/component-helpers v0.28.3
3334
k8s.io/klog/v2 v2.130.1
3435
k8s.io/metrics v0.25.2
3536
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8

pkg/controllers/workapplier/availability_tracker.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ import (
1111

1212
appv1 "k8s.io/api/apps/v1"
1313
corev1 "k8s.io/api/core/v1"
14+
policyv1 "k8s.io/api/policy/v1"
1415
apiextensionshelpers "k8s.io/apiextensions-apiserver/pkg/apihelpers"
1516
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
1617
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1718
"k8s.io/apimachinery/pkg/runtime"
1819
"k8s.io/apimachinery/pkg/runtime/schema"
20+
"k8s.io/component-helpers/apps/poddisruptionbudget"
1921
"k8s.io/klog/v2"
2022

2123
"go.goms.io/fleet/pkg/utils"
@@ -79,6 +81,8 @@ func trackInMemberClusterObjAvailabilityByGVR(
7981
return trackServiceAvailability(inMemberClusterObj)
8082
case utils.CustomResourceDefinitionGVR:
8183
return trackCRDAvailability(inMemberClusterObj)
84+
case utils.PodDisruptionBudgetGVR:
85+
return trackPDBAvailability(inMemberClusterObj)
8286
default:
8387
if isDataResource(*gvr) {
8488
klog.V(2).InfoS("The object from the member cluster is a data object, consider it to be immediately available",
@@ -218,6 +222,21 @@ func trackCRDAvailability(inMemberClusterObj *unstructured.Unstructured) (Manife
218222
return ManifestProcessingAvailabilityResultTypeNotYetAvailable, nil
219223
}
220224

225+
// trackPDBAvailability tracks the availability of a pod disruption budget in the member cluster
226+
func trackPDBAvailability(curObj *unstructured.Unstructured) (ManifestProcessingAvailabilityResultType, error) {
227+
var pdb policyv1.PodDisruptionBudget
228+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(curObj.Object, &pdb); err != nil {
229+
return ManifestProcessingAvailabilityResultTypeFailed, controller.NewUnexpectedBehaviorError(err)
230+
}
231+
// Check if conditions are up-to-date
232+
if poddisruptionbudget.ConditionsAreUpToDate(&pdb) {
233+
klog.V(2).InfoS("PodDisruptionBudget is available", "pdb", klog.KObj(curObj))
234+
return ManifestProcessingAvailabilityResultTypeAvailable, nil
235+
}
236+
klog.V(2).InfoS("Still need to wait for PodDisruptionBudget to be available", "pdb", klog.KObj(curObj))
237+
return ManifestProcessingAvailabilityResultTypeNotYetAvailable, nil
238+
}
239+
221240
// isDataResource checks if the resource is a data resource; such resources are
222241
// available immediately after creation.
223242
func isDataResource(gvr schema.GroupVersionResource) bool {

pkg/controllers/workapplier/availability_tracker_test.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ import (
1414
appsv1 "k8s.io/api/apps/v1"
1515
batchv1 "k8s.io/api/batch/v1"
1616
corev1 "k8s.io/api/core/v1"
17+
policyv1 "k8s.io/api/policy/v1"
1718
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
1819
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1920
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2021
"k8s.io/apimachinery/pkg/runtime/schema"
22+
"k8s.io/apimachinery/pkg/util/intstr"
2123
"k8s.io/klog/v2"
2224
"k8s.io/utils/ptr"
2325

@@ -136,6 +138,22 @@ var (
136138
},
137139
},
138140
}
141+
minAvailable = intstr.FromInt32(1)
142+
143+
pdbTemplate = &policyv1.PodDisruptionBudget{
144+
TypeMeta: metav1.TypeMeta{
145+
APIVersion: "policy/v1",
146+
Kind: "PodDisruptionBudget",
147+
},
148+
ObjectMeta: metav1.ObjectMeta{
149+
Name: "test-pdb",
150+
Namespace: nsName,
151+
Generation: 2,
152+
},
153+
Spec: policyv1.PodDisruptionBudgetSpec{
154+
MinAvailable: &minAvailable,
155+
},
156+
}
139157
)
140158

141159
// TestTrackDeploymentAvailability tests the trackDeploymentAvailability function.
@@ -667,6 +685,93 @@ func TestTrackCRDAvailability(t *testing.T) {
667685
}
668686
}
669687

688+
// TestTrackPDBAvailability tests the trackPDBAvailability function.
689+
func TestTrackPDBAvailability(t *testing.T) {
690+
availablePDB := pdbTemplate.DeepCopy()
691+
availablePDB.Status = policyv1.PodDisruptionBudgetStatus{
692+
DisruptionsAllowed: 1,
693+
CurrentHealthy: 2,
694+
ObservedGeneration: 2,
695+
DesiredHealthy: 2,
696+
ExpectedPods: 1,
697+
Conditions: []metav1.Condition{
698+
{
699+
Type: policyv1.DisruptionAllowedCondition,
700+
Status: metav1.ConditionTrue,
701+
Reason: policyv1.SufficientPodsReason,
702+
ObservedGeneration: 2,
703+
},
704+
},
705+
}
706+
unavailablePDBInsufficientPods := pdbTemplate.DeepCopy()
707+
unavailablePDBInsufficientPods.Status = policyv1.PodDisruptionBudgetStatus{
708+
DisruptionsAllowed: 0,
709+
CurrentHealthy: 1,
710+
ObservedGeneration: 2,
711+
DesiredHealthy: 2,
712+
ExpectedPods: 1,
713+
Conditions: []metav1.Condition{
714+
{
715+
Type: policyv1.DisruptionAllowedCondition,
716+
Status: metav1.ConditionTrue,
717+
Reason: policyv1.SufficientPodsReason,
718+
ObservedGeneration: 2,
719+
},
720+
},
721+
}
722+
723+
unavailablePDBStaleCondition := pdbTemplate.DeepCopy()
724+
unavailablePDBStaleCondition.Status = policyv1.PodDisruptionBudgetStatus{
725+
DisruptionsAllowed: 1,
726+
CurrentHealthy: 2,
727+
ObservedGeneration: 1,
728+
DesiredHealthy: 2,
729+
ExpectedPods: 1,
730+
Conditions: []metav1.Condition{
731+
{
732+
Type: policyv1.DisruptionAllowedCondition,
733+
Status: metav1.ConditionTrue,
734+
Reason: policyv1.SufficientPodsReason,
735+
ObservedGeneration: 1,
736+
},
737+
},
738+
}
739+
740+
testCases := []struct {
741+
name string
742+
pdb *policyv1.PodDisruptionBudget
743+
wantManifestProcessingAvailabilityResultType ManifestProcessingAvailabilityResultType
744+
}{
745+
{
746+
name: "available PDB",
747+
pdb: availablePDB,
748+
wantManifestProcessingAvailabilityResultType: ManifestProcessingAvailabilityResultTypeAvailable,
749+
},
750+
{
751+
name: "unavailable PDB (insufficient pods)",
752+
pdb: unavailablePDBInsufficientPods,
753+
wantManifestProcessingAvailabilityResultType: ManifestProcessingAvailabilityResultTypeNotYetAvailable,
754+
},
755+
{
756+
name: "unavailable PDB (stale condition)",
757+
pdb: unavailablePDBStaleCondition,
758+
wantManifestProcessingAvailabilityResultType: ManifestProcessingAvailabilityResultTypeNotYetAvailable,
759+
},
760+
}
761+
762+
for _, tc := range testCases {
763+
t.Run(tc.name, func(t *testing.T) {
764+
gotResTyp, err := trackPDBAvailability(toUnstructured(t, tc.pdb))
765+
if err != nil {
766+
t.Fatalf("trackPDBAvailability() = %v, want no error", err)
767+
}
768+
if gotResTyp != tc.wantManifestProcessingAvailabilityResultType {
769+
t.Errorf("manifestProcessingAvailabilityResultType = %v, want %v", gotResTyp, tc.wantManifestProcessingAvailabilityResultType)
770+
}
771+
})
772+
}
773+
}
774+
670775
// TestTrackInMemberClusterObjAvailabilityByGVR tests the trackInMemberClusterObjAvailabilityByGVR function.
671776
func TestTrackInMemberClusterObjAvailabilityByGVR(t *testing.T) {
672777
availableDeploy := deploy.DeepCopy()

pkg/utils/common.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
batchv1 "k8s.io/api/batch/v1"
1818
corev1 "k8s.io/api/core/v1"
1919
discoveryv1 "k8s.io/api/discovery/v1"
20+
policyv1 "k8s.io/api/policy/v1"
2021
rbacv1 "k8s.io/api/rbac/v1"
2122
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
2223
"k8s.io/apimachinery/pkg/api/equality"
@@ -263,6 +264,12 @@ var (
263264
Kind: "Pod",
264265
}
265266

267+
PodDisruptionBudgetGVR = schema.GroupVersionResource{
268+
Group: policyv1.GroupName,
269+
Version: policyv1.SchemeGroupVersion.Version,
270+
Resource: "poddisruptionbudgets",
271+
}
272+
266273
RoleMetaGVK = metav1.GroupVersionKind{
267274
Group: rbacv1.SchemeGroupVersion.Group,
268275
Version: rbacv1.SchemeGroupVersion.Version,

0 commit comments

Comments
 (0)