@@ -1320,282 +1320,6 @@ func TestSyncerProcessingSingleResourceWithStatus(t *testing.T) {
13201320 })
13211321 }
13221322}
1323- func TestSyncerProcessingRelatedResources (t * testing.T ) {
1324- type testcase struct {
1325- name string
1326- remoteAPIGroup string
1327- localCRD * apiextensionsv1.CustomResourceDefinition
1328- pubRes * syncagentv1alpha1.PublishedResource
1329- remoteObject * unstructured.Unstructured
1330- localObject * unstructured.Unstructured
1331- existingState string
1332- performRequeues bool
1333- expectedRemoteObject * unstructured.Unstructured
1334- expectedLocalObject * unstructured.Unstructured
1335- expectedState string
1336- customVerification func (t * testing.T , requeue bool , processErr error , finalRemoteObject * unstructured.Unstructured , finalLocalObject * unstructured.Unstructured , testcase testcase )
1337- }
1338-
1339- clusterName := logicalcluster .Name ("testcluster" )
1340-
1341- remoteThingPR := & syncagentv1alpha1.PublishedResource {
1342- Spec : syncagentv1alpha1.PublishedResourceSpec {
1343- Resource : syncagentv1alpha1.SourceResourceDescriptor {
1344- APIGroup : dummyv1alpha1 .GroupName ,
1345- Version : dummyv1alpha1 .GroupVersion ,
1346- Kind : "Thing" ,
1347- },
1348- Projection : & syncagentv1alpha1.ResourceProjection {
1349- Kind : "RemoteThing" ,
1350- },
1351- // include explicit naming rules to be independent of possible changes to the defaults
1352- Naming : & syncagentv1alpha1.ResourceNaming {
1353- Name : "$remoteClusterName-$remoteName" , // Things are Cluster-scoped
1354- },
1355- Related : []syncagentv1alpha1.RelatedResourceSpec {
1356- {
1357- Identifier : "mandatory-secret" ,
1358- Origin : "service" ,
1359- Kind : "Thing" ,
1360- Reference : syncagentv1alpha1.RelatedResourceReference {
1361- Name : syncagentv1alpha1.ResourceLocator {
1362- Path : "spec.otherTest.name" ,
1363- },
1364- Namespace : & syncagentv1alpha1.ResourceLocator {
1365- Path : "spec.otherTest.namespace" ,
1366- },
1367- },
1368- Optional : false ,
1369- },
1370- {
1371- Identifier : "optional-secret" ,
1372- Origin : "kcp" ,
1373- Kind : "Thing" ,
1374- Reference : syncagentv1alpha1.RelatedResourceReference {
1375- Name : syncagentv1alpha1.ResourceLocator {
1376- Path : "spec.test.name" ,
1377- },
1378- Namespace : & syncagentv1alpha1.ResourceLocator {
1379- Path : "spec.test.namespace" ,
1380- },
1381- },
1382- Optional : true ,
1383- },
1384- },
1385- },
1386- }
1387-
1388- testcases := []testcase {
1389- {
1390- name : "optional related resource does not exist" ,
1391- remoteAPIGroup : "remote.example.corp" ,
1392- localCRD : loadCRD ("things" ),
1393- pubRes : remoteThingPR ,
1394- performRequeues : true ,
1395-
1396- remoteObject : newUnstructured (& dummyv1alpha1.Thing {
1397- ObjectMeta : metav1.ObjectMeta {
1398- Name : "my-test-thing" ,
1399- },
1400- Spec : dummyv1alpha1.ThingSpec {
1401- Username : "Colonel Mustard" ,
1402- },
1403- }, withGroupKind ("remote.example.corp" , "RemoteThing" )),
1404- localObject : nil ,
1405- existingState : "" ,
1406-
1407- expectedRemoteObject : newUnstructured (& dummyv1alpha1.Thing {
1408- ObjectMeta : metav1.ObjectMeta {
1409- Name : "my-test-thing" ,
1410- Finalizers : []string {
1411- deletionFinalizer ,
1412- },
1413- },
1414- Spec : dummyv1alpha1.ThingSpec {
1415- Username : "Colonel Mustard" ,
1416- },
1417- }, withGroupKind ("remote.example.corp" , "RemoteThing" )),
1418- expectedLocalObject : newUnstructured (& dummyv1alpha1.Thing {
1419- ObjectMeta : metav1.ObjectMeta {
1420- Name : "testcluster-my-test-thing" ,
1421- Labels : map [string ]string {
1422- agentNameLabel : "textor-the-doctor" ,
1423- remoteObjectClusterLabel : "testcluster" ,
1424- remoteObjectNameHashLabel : "c346c8ceb5d104cc783d09b95e8ea7032c190948" ,
1425- },
1426- Annotations : map [string ]string {
1427- remoteObjectNameAnnotation : "my-test-thing" ,
1428- },
1429- },
1430- Spec : dummyv1alpha1.ThingSpec {
1431- Username : "Colonel Mustard" ,
1432- },
1433- }),
1434- expectedState : `{"apiVersion":"remote.example.corp/v1alpha1","kind":"RemoteThing","metadata":{"name":"my-test-thing"},"spec":{"username":"Colonel Mustard"}}` ,
1435- },
1436- {
1437- name : "mandatory related resource does not exist" ,
1438- remoteAPIGroup : "remote.example.corp" ,
1439- localCRD : loadCRD ("things" ),
1440- pubRes : remoteThingPR ,
1441- performRequeues : true ,
1442-
1443- remoteObject : newUnstructured (& dummyv1alpha1.Thing {
1444- ObjectMeta : metav1.ObjectMeta {
1445- Name : "my-test-thing" ,
1446- },
1447- Spec : dummyv1alpha1.ThingSpec {
1448- Username : "Colonel Mustard" ,
1449- },
1450- }, withGroupKind ("remote.example.corp" , "RemoteThing" )),
1451- localObject : nil ,
1452- existingState : "" ,
1453-
1454- expectedRemoteObject : newUnstructured (& dummyv1alpha1.Thing {
1455- ObjectMeta : metav1.ObjectMeta {
1456- Name : "my-test-thing" ,
1457- Finalizers : []string {
1458- deletionFinalizer ,
1459- },
1460- },
1461- Spec : dummyv1alpha1.ThingSpec {
1462- Username : "Colonel Mustard" ,
1463- },
1464- }, withGroupKind ("remote.example.corp" , "RemoteThing" )),
1465- expectedLocalObject : newUnstructured (& dummyv1alpha1.Thing {
1466- ObjectMeta : metav1.ObjectMeta {
1467- Name : "testcluster-my-test-thing" ,
1468- Labels : map [string ]string {
1469- agentNameLabel : "textor-the-doctor" ,
1470- remoteObjectClusterLabel : "testcluster" ,
1471- remoteObjectNameHashLabel : "c346c8ceb5d104cc783d09b95e8ea7032c190948" ,
1472- },
1473- Annotations : map [string ]string {
1474- remoteObjectNameAnnotation : "my-test-thing" ,
1475- },
1476- },
1477- Spec : dummyv1alpha1.ThingSpec {
1478- Username : "Colonel Mustard" ,
1479- },
1480- }),
1481- expectedState : `{"apiVersion":"remote.example.corp/v1alpha1","kind":"RemoteThing","metadata":{"name":"my-test-thing"},"spec":{"username":"Colonel Mustard"}}` ,
1482- },
1483- }
1484-
1485- const stateNamespace = "kcp-system"
1486-
1487- for _ , testcase := range testcases {
1488- t .Run (testcase .name , func (t * testing.T ) {
1489- localClient := buildFakeClient (testcase .localObject )
1490- remoteClient := buildFakeClient (testcase .remoteObject )
1491-
1492- syncer , err := NewResourceSyncer (
1493- // zap.Must(zap.NewDevelopment()).Sugar(),
1494- zap .NewNop ().Sugar (),
1495- localClient ,
1496- remoteClient ,
1497- testcase .pubRes ,
1498- testcase .localCRD ,
1499- testcase .remoteAPIGroup ,
1500- nil ,
1501- stateNamespace ,
1502- "textor-the-doctor" ,
1503- )
1504- if err != nil {
1505- t .Fatalf ("Failed to create syncer: %v" , err )
1506- }
1507-
1508- localCtx := context .Background ()
1509- remoteCtx := kontext .WithCluster (localCtx , clusterName )
1510- ctx := NewContext (localCtx , remoteCtx )
1511-
1512- // setup a custom state backend that we can prime
1513- var backend * kubernetesBackend
1514- syncer .newObjectStateStore = func (primaryObject , stateCluster syncSide ) ObjectStateStore {
1515- // .Process() is called multiple times, but we want the state to persist between reconciles.
1516- if backend == nil {
1517- backend = newKubernetesBackend (stateNamespace , primaryObject , stateCluster )
1518- if testcase .existingState != "" {
1519- if err := backend .Put (testcase .remoteObject , clusterName , []byte (testcase .existingState )); err != nil {
1520- t .Fatalf ("Failed to prime state store: %v" , err )
1521- }
1522- }
1523- }
1524-
1525- return & objectStateStore {
1526- backend : backend ,
1527- }
1528- }
1529-
1530- var requeue bool
1531-
1532- if testcase .performRequeues {
1533- target := testcase .remoteObject .DeepCopy ()
1534-
1535- for i := 0 ; true ; i ++ {
1536- if i > 20 {
1537- t .Fatalf ("Detected potential infinite loop, stopping after %d requeues." , i )
1538- }
1539-
1540- requeue , err = syncer .Process (ctx , target )
1541- if err != nil {
1542- break
1543- }
1544-
1545- if ! requeue {
1546- break
1547- }
1548-
1549- if err = remoteClient .Get (remoteCtx , ctrlruntimeclient .ObjectKeyFromObject (target ), target ); err != nil {
1550- // it's possible for the processing to have deleted the remote object,
1551- // so a NotFound is valid here
1552- if apierrors .IsNotFound (err ) {
1553- break
1554- }
1555-
1556- t .Fatalf ("Failed to get updated remote object: %v" , err )
1557- }
1558- }
1559- } else {
1560- requeue , err = syncer .Process (ctx , testcase .remoteObject )
1561- }
1562-
1563- finalRemoteObject , getErr := getFinalObjectVersion (remoteCtx , remoteClient , testcase .remoteObject , testcase .expectedRemoteObject )
1564- if getErr != nil {
1565- t .Fatalf ("Failed to get final remote object: %v" , getErr )
1566- }
1567-
1568- finalLocalObject , getErr := getFinalObjectVersion (localCtx , localClient , testcase .localObject , testcase .expectedLocalObject )
1569- if getErr != nil {
1570- t .Fatalf ("Failed to get final local object: %v" , getErr )
1571- }
1572-
1573- if testcase .customVerification != nil {
1574- testcase .customVerification (t , requeue , err , finalRemoteObject , finalLocalObject , testcase )
1575- } else {
1576- if err != nil {
1577- t .Fatalf ("Processing failed: %v" , err )
1578- }
1579-
1580- assertObjectsEqual (t , "local" , testcase .expectedLocalObject , finalLocalObject )
1581- assertObjectsEqual (t , "remote" , testcase .expectedRemoteObject , finalRemoteObject )
1582-
1583- if testcase .expectedState != "" {
1584- if backend == nil {
1585- t .Fatal ("Cannot check object state, state store was never instantiated." )
1586- }
1587-
1588- finalState , err := backend .Get (testcase .expectedRemoteObject , clusterName )
1589- if err != nil {
1590- t .Fatalf ("Failed to get final state: %v" , err )
1591- } else if ! bytes .Equal (finalState , []byte (testcase .expectedState )) {
1592- t .Fatalf ("States do not match:\n %s" , diff .StringDiff (testcase .expectedState , string (finalState )))
1593- }
1594- }
1595- }
1596- })
1597- }
1598- }
15991323
16001324func assertObjectsEqual (t * testing.T , kind string , expected , actual * unstructured.Unstructured ) {
16011325 if expected == nil {
0 commit comments