Skip to content

Commit c47b06c

Browse files
committed
add service export health check
1 parent f6ce21b commit c47b06c

File tree

3 files changed

+167
-0
lines changed

3 files changed

+167
-0
lines changed

pkg/controllers/workapplier/availability_tracker.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@ import (
1414
policyv1 "k8s.io/api/policy/v1"
1515
apiextensionshelpers "k8s.io/apiextensions-apiserver/pkg/apihelpers"
1616
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
17+
"k8s.io/apimachinery/pkg/api/meta"
18+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1719
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1820
"k8s.io/apimachinery/pkg/runtime"
1921
"k8s.io/apimachinery/pkg/runtime/schema"
2022
"k8s.io/component-helpers/apps/poddisruptionbudget"
2123
"k8s.io/klog/v2"
2224

25+
fleetnetworkingv1alpha1 "go.goms.io/fleet-networking/api/v1alpha1"
26+
"go.goms.io/fleet-networking/pkg/common/objectmeta"
2327
"go.goms.io/fleet/pkg/utils"
2428
"go.goms.io/fleet/pkg/utils/controller"
2529
)
@@ -83,6 +87,8 @@ func trackInMemberClusterObjAvailabilityByGVR(
8387
return trackCRDAvailability(inMemberClusterObj)
8488
case utils.PodDisruptionBudgetGVR:
8589
return trackPDBAvailability(inMemberClusterObj)
90+
case utils.ServiceExportGVR:
91+
return trackServiceExportAvailability(inMemberClusterObj)
8692
default:
8793
if isDataResource(*gvr) {
8894
klog.V(2).InfoS("The object from the member cluster is a data object, consider it to be immediately available",
@@ -247,6 +253,30 @@ func trackPDBAvailability(curObj *unstructured.Unstructured) (ManifestProcessing
247253
return ManifestProcessingAvailabilityResultTypeNotYetAvailable, nil
248254
}
249255

256+
// trackServiceExportAvailability tracks the availability of a service export in the member cluster.
257+
// It is available if it has a weight annotation and the ServiceExportValid condition is True.
258+
func trackServiceExportAvailability(curObj *unstructured.Unstructured) (ManifestProcessingAvailabilityResultType, error) {
259+
var svcExport fleetnetworkingv1alpha1.ServiceExport
260+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(curObj.Object, &svcExport); err != nil {
261+
return ManifestProcessingAvailabilityResultTypeFailed, controller.NewUnexpectedBehaviorError(err)
262+
}
263+
264+
weight, hasAnnotation := svcExport.Annotations[objectmeta.ServiceExportAnnotationWeight]
265+
validCond := meta.FindStatusCondition(svcExport.Status.Conditions, string(fleetnetworkingv1alpha1.ServiceExportValid))
266+
conflictCond := meta.FindStatusCondition(svcExport.Status.Conditions, string(fleetnetworkingv1alpha1.ServiceExportConflict))
267+
268+
isValid := validCond != nil && validCond.Status == metav1.ConditionTrue
269+
hasNoConflict := conflictCond != nil && conflictCond.Status == metav1.ConditionFalse
270+
271+
if hasAnnotation && ((weight == "0" && isValid) || (isValid && hasNoConflict)) {
272+
klog.V(2).InfoS("ServiceExport is available", "serviceExport", klog.KObj(curObj))
273+
return ManifestProcessingAvailabilityResultTypeAvailable, nil
274+
}
275+
276+
klog.V(2).InfoS("Still need to wait for ServiceExport to be available", "serviceExport", klog.KObj(curObj))
277+
return ManifestProcessingAvailabilityResultTypeNotYetAvailable, nil
278+
}
279+
250280
// isDataResource checks if the resource is a data resource; such resources are
251281
// available immediately after creation.
252282
func isDataResource(gvr schema.GroupVersionResource) bool {

pkg/controllers/workapplier/availability_tracker_test.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import (
2626
"k8s.io/klog/v2"
2727
"k8s.io/utils/ptr"
2828

29+
fleetnetworkingv1alpha1 "go.goms.io/fleet-networking/api/v1alpha1"
30+
"go.goms.io/fleet-networking/pkg/common/objectmeta"
2931
fleetv1beta1 "go.goms.io/fleet/apis/placement/v1beta1"
3032
"go.goms.io/fleet/pkg/utils"
3133
"go.goms.io/fleet/pkg/utils/parallelizer"
@@ -157,6 +159,16 @@ var (
157159
MinAvailable: &minAvailable,
158160
},
159161
}
162+
163+
svcExportTemplate = &fleetnetworkingv1alpha1.ServiceExport{
164+
ObjectMeta: metav1.ObjectMeta{
165+
Name: "test-svcExport",
166+
Namespace: nsName,
167+
Annotations: map[string]string{
168+
objectmeta.ServiceExportAnnotationWeight: "0",
169+
},
170+
},
171+
}
160172
)
161173

162174
// TestTrackDeploymentAvailability tests the trackDeploymentAvailability function.
@@ -983,6 +995,125 @@ func TestTrackInMemberClusterObjAvailabilityByGVR(t *testing.T) {
983995
}
984996
}
985997

998+
func TestServiceExportAvailability(t *testing.T) {
999+
availableValidSvcExport := svcExportTemplate.DeepCopy()
1000+
availableValidSvcExport.Status = fleetnetworkingv1alpha1.ServiceExportStatus{
1001+
Conditions: []metav1.Condition{
1002+
{
1003+
Type: string(fleetnetworkingv1alpha1.ServiceExportValid),
1004+
Status: metav1.ConditionTrue,
1005+
Reason: "ServiceIsValid",
1006+
},
1007+
},
1008+
}
1009+
1010+
unavailableInvalidSvcExport := svcExportTemplate.DeepCopy()
1011+
unavailableInvalidSvcExport.Status = fleetnetworkingv1alpha1.ServiceExportStatus{
1012+
Conditions: []metav1.Condition{
1013+
{
1014+
Type: string(fleetnetworkingv1alpha1.ServiceExportValid),
1015+
Status: metav1.ConditionFalse,
1016+
Reason: "ServiceNotFound",
1017+
},
1018+
},
1019+
}
1020+
1021+
availableNoConflictSvcExport := svcExportTemplate.DeepCopy()
1022+
availableNoConflictSvcExport.Annotations["networking.fleet.azure.com/weight"] = "1"
1023+
availableNoConflictSvcExport.Status = fleetnetworkingv1alpha1.ServiceExportStatus{
1024+
Conditions: []metav1.Condition{
1025+
{
1026+
Type: string(fleetnetworkingv1alpha1.ServiceExportValid),
1027+
Status: metav1.ConditionTrue,
1028+
Reason: "ServiceIsValid",
1029+
},
1030+
{
1031+
Type: string(fleetnetworkingv1alpha1.ServiceExportConflict),
1032+
Status: metav1.ConditionFalse,
1033+
Reason: "NoConflictFound",
1034+
},
1035+
},
1036+
}
1037+
1038+
unavailableHasConflictSvcExport := svcExportTemplate.DeepCopy()
1039+
unavailableHasConflictSvcExport.Annotations["networking.fleet.azure.com/weight"] = "1"
1040+
unavailableHasConflictSvcExport.Status = fleetnetworkingv1alpha1.ServiceExportStatus{
1041+
Conditions: []metav1.Condition{
1042+
{
1043+
Type: string(fleetnetworkingv1alpha1.ServiceExportValid),
1044+
Status: metav1.ConditionTrue,
1045+
Reason: "ServiceIsValid",
1046+
},
1047+
{
1048+
Type: string(fleetnetworkingv1alpha1.ServiceExportConflict),
1049+
Status: metav1.ConditionTrue,
1050+
Reason: "ConflictFound",
1051+
},
1052+
},
1053+
}
1054+
1055+
unavailableInvalidNoConflictSvcExport := svcExportTemplate.DeepCopy()
1056+
unavailableInvalidNoConflictSvcExport.Annotations["networking.fleet.azure.com/weight"] = "1"
1057+
unavailableInvalidNoConflictSvcExport.Status = fleetnetworkingv1alpha1.ServiceExportStatus{
1058+
Conditions: []metav1.Condition{
1059+
{
1060+
Type: string(fleetnetworkingv1alpha1.ServiceExportValid),
1061+
Status: metav1.ConditionFalse,
1062+
Reason: "ServiceIneligible",
1063+
},
1064+
{
1065+
Type: string(fleetnetworkingv1alpha1.ServiceExportConflict),
1066+
Status: metav1.ConditionTrue,
1067+
Reason: "ConflictFound",
1068+
},
1069+
},
1070+
}
1071+
1072+
testCases := []struct {
1073+
name string
1074+
svcExport *fleetnetworkingv1alpha1.ServiceExport
1075+
wantManifestProcessingAvailabilityResultType ManifestProcessingAvailabilityResultType
1076+
}{
1077+
{
1078+
name: "available svcExport (annotation weight is 0)",
1079+
svcExport: availableValidSvcExport,
1080+
wantManifestProcessingAvailabilityResultType: ManifestProcessingAvailabilityResultTypeAvailable,
1081+
},
1082+
{
1083+
name: "available svcExport (annotation weight is 1)",
1084+
svcExport: availableNoConflictSvcExport,
1085+
wantManifestProcessingAvailabilityResultType: ManifestProcessingAvailabilityResultTypeAvailable,
1086+
},
1087+
{
1088+
name: "unavailable svcExport (annotation weight is 0)",
1089+
svcExport: unavailableInvalidSvcExport,
1090+
wantManifestProcessingAvailabilityResultType: ManifestProcessingAvailabilityResultTypeNotYetAvailable,
1091+
},
1092+
{
1093+
name: "unavailable svcExport with conflict (annotation weight is 1)",
1094+
svcExport: unavailableHasConflictSvcExport,
1095+
wantManifestProcessingAvailabilityResultType: ManifestProcessingAvailabilityResultTypeNotYetAvailable,
1096+
},
1097+
{
1098+
name: "unavailable invalid svcExport (annotation weight is 1)",
1099+
svcExport: unavailableInvalidNoConflictSvcExport,
1100+
wantManifestProcessingAvailabilityResultType: ManifestProcessingAvailabilityResultTypeNotYetAvailable,
1101+
},
1102+
}
1103+
1104+
for _, tc := range testCases {
1105+
t.Run(tc.name, func(t *testing.T) {
1106+
gotResTyp, err := trackServiceExportAvailability(toUnstructured(t, tc.svcExport))
1107+
if err != nil {
1108+
t.Fatalf("trackServiceExportAvailability() = %v, want no error", err)
1109+
}
1110+
if gotResTyp != tc.wantManifestProcessingAvailabilityResultType {
1111+
t.Errorf("manifestProcessingAvailabilityResultType = %v, want %v", gotResTyp, tc.wantManifestProcessingAvailabilityResultType)
1112+
}
1113+
})
1114+
}
1115+
}
1116+
9861117
// TestTrackInMemberClusterObjAvailability tests the trackInMemberClusterObjAvailability method.
9871118
func TestTrackInMemberClusterObjAvailability(t *testing.T) {
9881119
ctx := context.Background()

pkg/utils/common.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,12 @@ var (
358358
Resource: "serviceaccounts",
359359
}
360360

361+
ServiceExportGVR = schema.GroupVersionResource{
362+
Group: fleetnetworkingv1alpha1.GroupVersion.Group,
363+
Version: fleetnetworkingv1alpha1.GroupVersion.Version,
364+
Resource: "serviceexports",
365+
}
366+
361367
StorageClassGVR = schema.GroupVersionResource{
362368
Group: storagev1.SchemeGroupVersion.Group,
363369
Version: storagev1.SchemeGroupVersion.Version,

0 commit comments

Comments
 (0)