Skip to content

Commit 08eaa65

Browse files
authored
feat: Add CustomResourceDefinition trackability (#980)
1 parent bfaf5ee commit 08eaa65

File tree

3 files changed

+212
-0
lines changed

3 files changed

+212
-0
lines changed

pkg/controllers/work/apply_controller.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import (
2929
"go.uber.org/atomic"
3030
appv1 "k8s.io/api/apps/v1"
3131
v1 "k8s.io/api/core/v1"
32+
apiextensionshelpers "k8s.io/apiextensions-apiserver/pkg/apihelpers"
33+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
3234
apierrors "k8s.io/apimachinery/pkg/api/errors"
3335
"k8s.io/apimachinery/pkg/api/meta"
3436
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -448,6 +450,9 @@ func trackResourceAvailability(gvr schema.GroupVersionResource, curObj *unstruct
448450
case utils.ServiceGVR:
449451
return trackServiceAvailability(curObj)
450452

453+
case utils.CustomResourceDefinitionGVR:
454+
return trackCRDAvailability(curObj)
455+
451456
default:
452457
if isDataResource(gvr) {
453458
klog.V(2).InfoS("Data resources are available immediately", "gvr", gvr, "resource", klog.KObj(curObj))
@@ -458,6 +463,22 @@ func trackResourceAvailability(gvr schema.GroupVersionResource, curObj *unstruct
458463
}
459464
}
460465

466+
func trackCRDAvailability(curObj *unstructured.Unstructured) (ApplyAction, error) {
467+
var crd apiextensionsv1.CustomResourceDefinition
468+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(curObj.Object, &crd); err != nil {
469+
return errorApplyAction, controller.NewUnexpectedBehaviorError(err)
470+
}
471+
472+
// If both conditions are True, the CRD is available
473+
if apiextensionshelpers.IsCRDConditionTrue(&crd, apiextensionsv1.Established) && apiextensionshelpers.IsCRDConditionTrue(&crd, apiextensionsv1.NamesAccepted) {
474+
klog.V(2).InfoS("CustomResourceDefinition is available", "customResourceDefinition", klog.KObj(curObj))
475+
return manifestAvailableAction, nil
476+
}
477+
478+
klog.V(2).InfoS("Still need to wait for CustomResourceDefinition to be available", "customResourceDefinition", klog.KObj(curObj))
479+
return manifestNotAvailableYetAction, nil
480+
}
481+
461482
func trackDeploymentAvailability(curObj *unstructured.Unstructured) (ApplyAction, error) {
462483
var deployment appv1.Deployment
463484
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(curObj.Object, &deployment); err != nil {

pkg/controllers/work/apply_controller_test.go

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,6 +1235,191 @@ func TestTrackResourceAvailability(t *testing.T) {
12351235
expected: manifestNotTrackableAction,
12361236
err: nil,
12371237
},
1238+
"Test CustomResourceDefinition available": {
1239+
gvr: utils.CustomResourceDefinitionGVR,
1240+
obj: &unstructured.Unstructured{
1241+
Object: map[string]interface{}{
1242+
"apiVersion": "apiextensions.k8s.io/v1",
1243+
"kind": "CustomResourceDefinition",
1244+
"metadata": map[string]interface{}{
1245+
"name": "testresources.example.com",
1246+
},
1247+
"spec": map[string]interface{}{
1248+
"group": "example.com",
1249+
"names": map[string]interface{}{
1250+
"plural": "testresources",
1251+
"singular": "testresource",
1252+
"kind": "TestResource",
1253+
"shortNames": []string{"tr"},
1254+
},
1255+
"scope": "Namespaced",
1256+
"versions": []interface{}{
1257+
map[string]interface{}{
1258+
"name": "v1",
1259+
"served": true,
1260+
"storage": true,
1261+
"schema": map[string]interface{}{
1262+
"openAPIV3Schema": map[string]interface{}{
1263+
"type": "object",
1264+
"properties": map[string]interface{}{
1265+
"spec": map[string]interface{}{
1266+
"type": "object",
1267+
"properties": map[string]interface{}{
1268+
"field": map[string]interface{}{
1269+
"type": "string",
1270+
},
1271+
},
1272+
},
1273+
},
1274+
},
1275+
},
1276+
},
1277+
},
1278+
},
1279+
"status": map[string]interface{}{
1280+
"conditions": []interface{}{
1281+
map[string]interface{}{
1282+
"type": "Established",
1283+
"status": "True",
1284+
"lastTransitionTime": metav1.Now(),
1285+
"reason": "InitialNamesAccepted",
1286+
"message": "the initial names have been accepted",
1287+
},
1288+
map[string]interface{}{
1289+
"type": "NamesAccepted",
1290+
"status": "True",
1291+
"lastTransitionTime": metav1.Now(),
1292+
"reason": "NoConflicts",
1293+
"message": "no conflicts found",
1294+
},
1295+
},
1296+
},
1297+
},
1298+
},
1299+
expected: manifestAvailableAction,
1300+
err: nil,
1301+
},
1302+
"Test CustomResourceDefinition unavailable (not established)": {
1303+
gvr: utils.CustomResourceDefinitionGVR,
1304+
obj: &unstructured.Unstructured{
1305+
Object: map[string]interface{}{
1306+
"apiVersion": "apiextensions.k8s.io/v1",
1307+
"kind": "CustomResourceDefinition",
1308+
"metadata": map[string]interface{}{
1309+
"name": "testresources.example.com",
1310+
},
1311+
"spec": map[string]interface{}{
1312+
"group": "example.com",
1313+
"names": map[string]interface{}{
1314+
"plural": "testresources",
1315+
"singular": "testresource",
1316+
"kind": "TestResource",
1317+
"shortNames": []string{"tr"},
1318+
},
1319+
"scope": "Namespaced",
1320+
"versions": []interface{}{
1321+
map[string]interface{}{
1322+
"name": "v1",
1323+
"served": true,
1324+
"storage": true,
1325+
"schema": map[string]interface{}{
1326+
"openAPIV3Schema": map[string]interface{}{
1327+
"type": "object",
1328+
"properties": map[string]interface{}{
1329+
"spec": map[string]interface{}{
1330+
"type": "object",
1331+
"properties": map[string]interface{}{
1332+
"field": map[string]interface{}{
1333+
"type": "string",
1334+
},
1335+
},
1336+
},
1337+
},
1338+
},
1339+
},
1340+
},
1341+
},
1342+
},
1343+
"status": map[string]interface{}{
1344+
"conditions": []interface{}{
1345+
map[string]interface{}{
1346+
"type": "Established",
1347+
"status": "False",
1348+
"lastTransitionTime": metav1.Now(),
1349+
"reason": "Installing",
1350+
"message": "the initial names have been accepted",
1351+
},
1352+
map[string]interface{}{
1353+
"type": "NamesAccepted",
1354+
"status": "True",
1355+
"lastTransitionTime": metav1.Now(),
1356+
"reason": "NoConflicts",
1357+
"message": "no conflicts found",
1358+
},
1359+
},
1360+
},
1361+
},
1362+
},
1363+
expected: manifestNotAvailableYetAction,
1364+
err: nil,
1365+
},
1366+
"Test CustomResourceDefinition unavailable (name not accepted)": {
1367+
gvr: utils.CustomResourceDefinitionGVR,
1368+
obj: &unstructured.Unstructured{
1369+
Object: map[string]interface{}{
1370+
"apiVersion": "apiextensions.k8s.io/v1",
1371+
"kind": "CustomResourceDefinition",
1372+
"metadata": map[string]interface{}{
1373+
"name": "testresources.example.com",
1374+
},
1375+
"status": map[string]interface{}{
1376+
"conditions": []interface{}{
1377+
map[string]interface{}{
1378+
"type": "NamesAccepted",
1379+
"status": "False",
1380+
"lastTransitionTime": metav1.Now(),
1381+
"reason": "NameConflict",
1382+
"message": "names conflict",
1383+
},
1384+
},
1385+
},
1386+
},
1387+
},
1388+
expected: manifestNotAvailableYetAction,
1389+
err: nil,
1390+
},
1391+
"Test CustomResourceDefinition unavailable (established but name not accepted)": {
1392+
gvr: utils.CustomResourceDefinitionGVR,
1393+
obj: &unstructured.Unstructured{
1394+
Object: map[string]interface{}{
1395+
"apiVersion": "apiextensions.k8s.io/v1",
1396+
"kind": "CustomResourceDefinition",
1397+
"metadata": map[string]interface{}{
1398+
"name": "testresources.example.com",
1399+
},
1400+
"status": map[string]interface{}{
1401+
"conditions": []interface{}{
1402+
map[string]interface{}{
1403+
"type": "Established",
1404+
"status": "True",
1405+
"lastTransitionTime": metav1.Now(),
1406+
"reason": "InitialNamesAccepted",
1407+
"message": "the initial names have been accepted",
1408+
},
1409+
map[string]interface{}{
1410+
"type": "NamesAccepted",
1411+
"status": "False",
1412+
"lastTransitionTime": metav1.Now(),
1413+
"reason": "NotAccepted",
1414+
"message": "not all names are accepted",
1415+
},
1416+
},
1417+
},
1418+
},
1419+
},
1420+
expected: manifestNotAvailableYetAction,
1421+
err: nil,
1422+
},
12381423
}
12391424

12401425
for name, tt := range tests {

pkg/utils/common.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,12 @@ var (
167167
Kind: "CustomResourceDefinition",
168168
}
169169

170+
CustomResourceDefinitionGVR = schema.GroupVersionResource{
171+
Group: apiextensionsv1.SchemeGroupVersion.Group,
172+
Version: apiextensionsv1.SchemeGroupVersion.Version,
173+
Resource: "customresourcedefinitions",
174+
}
175+
170176
EndpointSliceExportMetaGVK = metav1.GroupVersionKind{
171177
Group: fleetnetworkingv1alpha1.GroupVersion.Group,
172178
Version: fleetnetworkingv1alpha1.GroupVersion.Version,

0 commit comments

Comments
 (0)