From 5c9ae37e652134cf70d74cbf03118538a8329c1f Mon Sep 17 00:00:00 2001 From: Britania Rodriguez Reyes Date: Mon, 2 Dec 2024 13:24:46 -0600 Subject: [PATCH] Add cutsomResourceDefinition trackability --- pkg/controllers/work/apply_controller.go | 27 ++++ pkg/controllers/work/apply_controller_test.go | 128 ++++++++++++++++++ pkg/utils/common.go | 6 + 3 files changed, 161 insertions(+) diff --git a/pkg/controllers/work/apply_controller.go b/pkg/controllers/work/apply_controller.go index 0b49e96a9..d151503c5 100644 --- a/pkg/controllers/work/apply_controller.go +++ b/pkg/controllers/work/apply_controller.go @@ -29,6 +29,7 @@ import ( "go.uber.org/atomic" appv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -448,6 +449,9 @@ func trackResourceAvailability(gvr schema.GroupVersionResource, curObj *unstruct case utils.ServiceGVR: return trackServiceAvailability(curObj) + case utils.CustomResourceDefinitionGVR: + return trackCRDAvailability(curObj) + default: if isDataResource(gvr) { klog.V(2).InfoS("Data resources are available immediately", "gvr", gvr, "resource", klog.KObj(curObj)) @@ -458,6 +462,29 @@ func trackResourceAvailability(gvr schema.GroupVersionResource, curObj *unstruct } } +func trackCRDAvailability(curObj *unstructured.Unstructured) (ApplyAction, error) { + var crd apiextensionsv1.CustomResourceDefinition + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(curObj.Object, &crd); err != nil { + return errorApplyAction, controller.NewUnexpectedBehaviorError(err) + } + // Check if there is a condition of type "Established" + var establishedCondition *apiextensionsv1.CustomResourceDefinitionCondition + for _, condition := range crd.Status.Conditions { + if condition.Type == apiextensionsv1.Established { + establishedCondition = &condition + break + } + } + + // If the condition is found, and it is True, the CRD is available + if establishedCondition != nil && establishedCondition.Status == apiextensionsv1.ConditionTrue { + klog.V(2).InfoS("CustomResourceDefinition is available", "customResourceDefinition", klog.KObj(curObj)) + return manifestAvailableAction, nil + } + klog.V(2).InfoS("Still need to wait for CustomResourceDefinition to be available", "customResourceDefinition", klog.KObj(curObj)) + return manifestNotAvailableYetAction, nil +} + func trackDeploymentAvailability(curObj *unstructured.Unstructured) (ApplyAction, error) { var deployment appv1.Deployment if err := runtime.DefaultUnstructuredConverter.FromUnstructured(curObj.Object, &deployment); err != nil { diff --git a/pkg/controllers/work/apply_controller_test.go b/pkg/controllers/work/apply_controller_test.go index 5cd2715d4..71247e276 100644 --- a/pkg/controllers/work/apply_controller_test.go +++ b/pkg/controllers/work/apply_controller_test.go @@ -1235,6 +1235,134 @@ func TestTrackResourceAvailability(t *testing.T) { expected: manifestNotTrackableAction, err: nil, }, + "Test CustomResourceDefinition available": { + gvr: utils.CustomResourceDefinitionGVR, + obj: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apiextensions.k8s.io/v1", + "kind": "CustomResourceDefinition", + "metadata": map[string]interface{}{ + "name": "testresources.example.com", + }, + "spec": map[string]interface{}{ + "group": "example.com", + "names": map[string]interface{}{ + "plural": "testresources", + "singular": "testresource", + "kind": "TestResource", + "shortNames": []string{"tr"}, + }, + "scope": "Namespaced", + "versions": []interface{}{ + map[string]interface{}{ + "name": "v1", + "served": true, + "storage": true, + "schema": map[string]interface{}{ + "openAPIV3Schema": map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "spec": map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "field": map[string]interface{}{ + "type": "string", + }, + }, + }, + }, + }, + }, + }, + }, + }, + "status": map[string]interface{}{ + "conditions": []interface{}{ + map[string]interface{}{ + "type": "Established", + "status": "True", + "lastTransitionTime": metav1.Now(), + "reason": "CRDEstablished", + "message": "The CustomResourceDefinition has been established.", + }, + map[string]interface{}{ + "type": "NamesAccepted", + "status": "True", + "lastTransitionTime": metav1.Now(), + "reason": "NoConflicts", + "message": "The CustomResourceDefinition name was accepted.", + }, + }, + }, + }, + }, + expected: manifestAvailableAction, + err: nil, + }, + "Test CustomResourceDefinition unavailable": { + gvr: utils.CustomResourceDefinitionGVR, + obj: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apiextensions.k8s.io/v1", + "kind": "CustomResourceDefinition", + "metadata": map[string]interface{}{ + "name": "testresources.example.com", + }, + "spec": map[string]interface{}{ + "group": "example.com", + "names": map[string]interface{}{ + "plural": "testresources", + "singular": "testresource", + "kind": "TestResource", + "shortNames": []string{"tr"}, + }, + "scope": "Namespaced", + "versions": []interface{}{ + map[string]interface{}{ + "name": "v1", + "served": true, + "storage": true, + "schema": map[string]interface{}{ + "openAPIV3Schema": map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "spec": map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "field": map[string]interface{}{ + "type": "string", + }, + }, + }, + }, + }, + }, + }, + }, + }, + "status": map[string]interface{}{ + "conditions": []interface{}{ + map[string]interface{}{ + "type": "Established", + "status": "False", + "lastTransitionTime": metav1.Now(), + "reason": "CRDEstablished", + "message": "The CustomResourceDefinition has been established.", + }, + map[string]interface{}{ + "type": "NamesAccepted", + "status": "True", + "lastTransitionTime": metav1.Now(), + "reason": "NoConflicts", + "message": "The CustomResourceDefinition name was accepted.", + }, + }, + }, + }, + }, + expected: manifestNotAvailableYetAction, + err: nil, + }, } for name, tt := range tests { diff --git a/pkg/utils/common.go b/pkg/utils/common.go index bf8be3b60..788f495fc 100644 --- a/pkg/utils/common.go +++ b/pkg/utils/common.go @@ -167,6 +167,12 @@ var ( Kind: "CustomResourceDefinition", } + CustomResourceDefinitionGVR = schema.GroupVersionResource{ + Group: apiextensionsv1.SchemeGroupVersion.Group, + Version: apiextensionsv1.SchemeGroupVersion.Version, + Resource: "customresourcedefinitions", + } + EndpointSliceExportMetaGVK = metav1.GroupVersionKind{ Group: fleetnetworkingv1alpha1.GroupVersion.Group, Version: fleetnetworkingv1alpha1.GroupVersion.Version,