Skip to content

Commit e8e53f9

Browse files
committed
syncing resources from the same apiexport works
On-behalf-of: @SAP [email protected]
1 parent 7247040 commit e8e53f9

File tree

3 files changed

+67
-23
lines changed

3 files changed

+67
-23
lines changed

internal/controller/apiexport/controller.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,22 @@ func (r *Reconciler) reconcile(ctx context.Context, apiExport *kcpdevv1alpha1.AP
130130
return fmt.Errorf("failed to list PublishedResources: %w", err)
131131
}
132132

133-
// filter out those PRs that have not yet been processed into an ARS or that are invalid
133+
// filter out those PRs that are invalid; we keep those that are not yet converted into ARS,
134+
// just to reduce the amount of re-reconciles when the agent processes a number of PRs in a row
135+
// and would constantly update the APIExport; instead we rely on kcp to handle the eventual
136+
// consistency.
137+
// This allows us to already determine all resources we "own", which helps us in
138+
// bilding the proper permission claims if one of the related resources is using a resource
139+
// type managed via PublishedResource. Otherwise the controller might not see the PR for the
140+
// related resource and temporarily incorrectly assume it needs to add a permission claim.
134141
filteredPubResources := slices.DeleteFunc(pubResources.Items, func(pr syncagentv1alpha1.PublishedResource) bool {
135142
// TODO: Turn this into a webhook or CEL expressions.
136-
if err := validation.ValidatePublishedResource(&pr); err != nil {
143+
err := validation.ValidatePublishedResource(&pr)
144+
if err != nil {
137145
r.log.With("pr", pr.Name, "error", err).Warn("Ignoring invalid PublishedResource.")
138-
return true
139146
}
140147

141-
return pr.Status.ResourceSchemaName == ""
148+
return err != nil
142149
})
143150

144151
// for each PR, we note down the created ARS and also the GVKs of related resources

internal/sync/syncer_related.go

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ func (s *ResourceSyncer) processRelatedResource(ctx context.Context, log *zap.Su
8181
eventObjSide = syncSideSource
8282
}
8383

84+
fmt.Println("processRelatedResource()")
85+
8486
// find the all objects on the origin side that match the given criteria
8587
resolvedObjects, err := resolveRelatedResourceObjects(ctx, origin, dest, relRes)
8688
if err != nil {
@@ -89,6 +91,7 @@ func (s *ResourceSyncer) processRelatedResource(ctx context.Context, log *zap.Su
8991

9092
// no objects were found yet, that's okay
9193
if len(resolvedObjects) == 0 {
94+
fmt.Println("stop: no objects found")
9295
return false, nil
9396
}
9497

@@ -101,12 +104,18 @@ func (s *ResourceSyncer) processRelatedResource(ctx context.Context, log *zap.Su
101104

102105
// Synchronize related objects the same way the parent object was synchronized.
103106
projectedGVR := projection.RelatedResourceProjectedGVR(&relRes)
104-
// projectedAPIVersion, projectedKind := projection.RelatedResourceProjectedGVR(&relRes).ToAPIVersionAndKind()
107+
108+
projectedGVK, err := dest.client.RESTMapper().KindFor(projectedGVR)
109+
if err != nil {
110+
return false, fmt.Errorf("failed to lookup %v: %w", projectedGVR, err)
111+
}
112+
113+
fmt.Printf("projectedGVK: %+v\n", projectedGVK)
105114

106115
for idx, resolved := range resolvedObjects {
107116
destObject := &unstructured.Unstructured{}
108-
destObject.SetAPIVersion(projectedGVR.GroupVersion().String())
109-
destObject.SetKind(projectedGVR.Resource)
117+
destObject.SetAPIVersion(projectedGVK.GroupVersion().String())
118+
destObject.SetKind(projectedGVK.Kind)
110119

111120
if err = dest.client.Get(ctx, resolved.destination, destObject); err != nil {
112121
destObject = nil
@@ -136,8 +145,8 @@ func (s *ResourceSyncer) processRelatedResource(ctx context.Context, log *zap.Su
136145
destCreator: func(source *unstructured.Unstructured) (*unstructured.Unstructured, error) {
137146
fmt.Printf("source: %+v\n", *source)
138147
dest := source.DeepCopy()
139-
dest.SetAPIVersion(projectedGVR.GroupVersion().String())
140-
dest.SetKind(projectedGVR.Resource)
148+
dest.SetAPIVersion(projectedGVK.GroupVersion().String())
149+
dest.SetKind(projectedGVK.Kind)
141150
dest.SetName(resolved.destination.Name)
142151
dest.SetNamespace(resolved.destination.Namespace)
143152

@@ -358,6 +367,8 @@ func mapSlices(a, b []string) map[string]string {
358367
func resolveRelatedResourceObjectsInNamespaces(ctx context.Context, relatedOrigin, relatedDest syncSide, relRes syncagentv1alpha1.RelatedResourceSpec, spec syncagentv1alpha1.RelatedResourceObjectSpec, namespaceMap map[string]string) ([]resolvedObject, error) {
359368
result := []resolvedObject{}
360369

370+
fmt.Printf("resolveRelatedResourceObjectsInNamespaces(%+v)\n", namespaceMap)
371+
361372
for originNamespace, destNamespace := range namespaceMap {
362373
nameMap, err := resolveRelatedResourceObjectsInNamespace(ctx, relatedOrigin, relatedDest, relRes, spec, originNamespace)
363374
if err != nil {
@@ -366,14 +377,19 @@ func resolveRelatedResourceObjectsInNamespaces(ctx context.Context, relatedOrigi
366377

367378
for originName, destName := range nameMap {
368379
originGVR := projection.RelatedResourceGVR(&relRes)
369-
// apiVersion := schema.GroupVersion{Group: relRes.Group, Version: relRes.Version}.String()
380+
381+
originGVK, err := relatedOrigin.client.RESTMapper().KindFor(originGVR)
382+
if err != nil {
383+
return nil, fmt.Errorf("failed to lookup %v: %w", originGVR, err)
384+
}
370385

371386
originObj := &unstructured.Unstructured{}
372-
originObj.SetAPIVersion(originGVR.GroupVersion().String())
373-
originObj.SetKind(originGVR.Resource)
387+
originObj.SetAPIVersion(originGVK.GroupVersion().String())
388+
originObj.SetKind(originGVK.Kind)
374389

375390
err = relatedOrigin.client.Get(ctx, types.NamespacedName{Name: originName, Namespace: originNamespace}, originObj)
376391
if err != nil {
392+
fmt.Printf("err: %v\n", err)
377393
// this should rarely happen, only if an object was deleted in between the .List() call
378394
// above and the .Get() call here.
379395
if apierrors.IsNotFound(err) {

test/e2e/sync/related_test.go

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,6 +1048,10 @@ func TestSyncNonStandardRelatedResources(t *testing.T) {
10481048
name: "turn a related ConfigMap into a Secret",
10491049
workspace: "sync-related-configmap-to-secret",
10501050
mainResource: crds.Crontab{
1051+
TypeMeta: metav1.TypeMeta{
1052+
APIVersion: "kcp.example.com/v1",
1053+
Kind: "CronTab",
1054+
},
10511055
ObjectMeta: metav1.ObjectMeta{
10521056
Name: "my-crontab",
10531057
Namespace: "default",
@@ -1107,6 +1111,10 @@ func TestSyncNonStandardRelatedResources(t *testing.T) {
11071111
name: "use a resource from the same APIExport as a related resource",
11081112
workspace: "sync-related-same-apiexport",
11091113
mainResource: crds.Crontab{
1114+
TypeMeta: metav1.TypeMeta{
1115+
APIVersion: "kcp.example.com/v1",
1116+
Kind: "CronTab",
1117+
},
11101118
ObjectMeta: metav1.ObjectMeta{
11111119
Name: "my-crontab",
11121120
Namespace: "default",
@@ -1117,7 +1125,7 @@ func TestSyncNonStandardRelatedResources(t *testing.T) {
11171125
},
11181126
},
11191127
relatedConfig: syncagentv1alpha1.RelatedResourceSpec{
1120-
Identifier: "credentials",
1128+
Identifier: "the-backup",
11211129
Origin: syncagentv1alpha1.RelatedResourceOriginService,
11221130
Resource: "backups",
11231131
Group: "eksempel.no",
@@ -1135,13 +1143,21 @@ func TestSyncNonStandardRelatedResources(t *testing.T) {
11351143
},
11361144
},
11371145
sourceRelatedObject: &crds.Backup{
1146+
TypeMeta: metav1.TypeMeta{
1147+
APIVersion: "eksempel.no/v1",
1148+
Kind: "Backup",
1149+
},
11381150
ObjectMeta: metav1.ObjectMeta{
11391151
Name: "my-backup",
1140-
Namespace: "default",
1152+
Namespace: "synced-default",
11411153
},
11421154
Spec: crds.BackupSpec{},
11431155
},
11441156
expectedSyncedRelatedObject: &crds.Backup{
1157+
TypeMeta: metav1.TypeMeta{
1158+
APIVersion: "kcp.example.com/v1",
1159+
Kind: "Backup",
1160+
},
11451161
ObjectMeta: metav1.ObjectMeta{
11461162
Name: "my-backup",
11471163
Namespace: "default",
@@ -1669,14 +1685,11 @@ func TestSyncNonStandardRelatedResources(t *testing.T) {
16691685
t.Log("Creating CronTab in kcp…")
16701686

16711687
crontab := utils.ToUnstructured(t, &testcase.mainResource)
1672-
crontab.SetAPIVersion("kcp.example.com/v1")
1673-
crontab.SetKind("CronTab")
1674-
16751688
if err := teamClient.Create(ctx, crontab); err != nil {
16761689
t.Fatalf("Failed to create CronTab in kcp: %v", err)
16771690
}
16781691

1679-
// fake operator: create a credential Secret
1692+
// fake operator: create a related object as a result of processing the main object
16801693
t.Logf("Creating original related object on the %s side…", testcase.relatedConfig.Origin)
16811694

16821695
originClient := envtestClient
@@ -1688,20 +1701,28 @@ func TestSyncNonStandardRelatedResources(t *testing.T) {
16881701

16891702
ensureNamespace(t, ctx, originClient, testcase.sourceRelatedObject.GetNamespace())
16901703

1691-
if err := originClient.Create(ctx, testcase.sourceRelatedObject); err != nil {
1704+
relatedObject := utils.ToUnstructured(t, &testcase.sourceRelatedObject)
1705+
if err := originClient.Create(ctx, relatedObject); err != nil {
16921706
t.Fatalf("Failed to create related object: %v", err)
16931707
}
16941708

16951709
// wait for the agent to do its magic
16961710
t.Log("Wait for related object to be synced…")
16971711
projectedGVR := projection.RelatedResourceProjectedGVR(&testcase.relatedConfig)
1712+
projectedGVK, err := destClient.RESTMapper().KindFor(projectedGVR)
1713+
if err != nil {
1714+
t.Fatalf("Failed to resolve projected GVR %v: %v", projectedGVR, err)
1715+
}
1716+
16981717
copiedRelatedObject := &unstructured.Unstructured{}
1699-
copiedRelatedObject.SetAPIVersion(projectedGVR.GroupVersion().String())
1700-
copiedRelatedObject.SetKind(projectedGVR.Resource)
1718+
copiedRelatedObject.SetAPIVersion(projectedGVK.GroupVersion().String())
1719+
copiedRelatedObject.SetKind(projectedGVK.Kind)
17011720

1702-
err := wait.PollUntilContextTimeout(ctx, 500*time.Millisecond, 30*time.Second, false, func(ctx context.Context) (done bool, err error) {
1721+
err = wait.PollUntilContextTimeout(ctx, 500*time.Millisecond, 30*time.Second, false, func(ctx context.Context) (done bool, err error) {
17031722
copyKey := ctrlruntimeclient.ObjectKeyFromObject(testcase.expectedSyncedRelatedObject)
1704-
return destClient.Get(ctx, copyKey, copiedRelatedObject) == nil, nil
1723+
err = destClient.Get(ctx, copyKey, copiedRelatedObject)
1724+
fmt.Printf("poll err: %v\n", err)
1725+
return err == nil, nil
17051726
})
17061727
if err != nil {
17071728
t.Fatalf("Failed to wait for related object to be synced: %v", err)

0 commit comments

Comments
 (0)