@@ -24,6 +24,7 @@ import (
24
24
"slices"
25
25
"strings"
26
26
27
+ "github.com/kcp-dev/logicalcluster/v3"
27
28
"github.com/tidwall/gjson"
28
29
"go.uber.org/zap"
29
30
@@ -68,7 +69,7 @@ func (s *ResourceSyncer) processRelatedResource(log *zap.SugaredLogger, stateSto
68
69
dest syncSide
69
70
)
70
71
71
- if relRes .Origin == "service" {
72
+ if relRes .Origin == syncagentv1alpha1 . RelatedResourceOriginService {
72
73
origin = local
73
74
dest = remote
74
75
} else {
@@ -159,7 +160,7 @@ func (s *ResourceSyncer) processRelatedResource(log *zap.SugaredLogger, stateSto
159
160
160
161
// now that the related object was successfully synced, we can remember its details on the
161
162
// main object
162
- if relRes .Origin == "service" {
163
+ if relRes .Origin == syncagentv1alpha1 . RelatedResourceOriginService {
163
164
// TODO: Improve this logic, the added index is just a hack until we find a better solution
164
165
// to let the user know about the related object (this annotation is not relevant for the
165
166
// syncing logic, it's purely for the end-user).
@@ -211,14 +212,15 @@ func resolveRelatedResourceObjects(relatedOrigin, relatedDest syncSide, relRes s
211
212
// resolving the originNamespace first allows us to scope down any .List() calls later
212
213
originNamespace := relatedOrigin .object .GetNamespace ()
213
214
destNamespace := relatedDest .object .GetNamespace ()
215
+ origin := relRes .Origin
214
216
215
217
namespaceMap := map [string ]string {
216
218
originNamespace : destNamespace ,
217
219
}
218
220
219
221
if nsSpec := relRes .Object .Namespace ; nsSpec != nil {
220
222
var err error
221
- namespaceMap , err = resolveRelatedResourceOriginNamespaces (relatedOrigin , relatedDest , * nsSpec )
223
+ namespaceMap , err = resolveRelatedResourceOriginNamespaces (relatedOrigin , relatedDest , origin , * nsSpec )
222
224
if err != nil {
223
225
return nil , fmt .Errorf ("failed to resolve namespace: %w" , err )
224
226
}
@@ -248,7 +250,7 @@ func resolveRelatedResourceObjects(relatedOrigin, relatedDest syncSide, relRes s
248
250
return objects , nil
249
251
}
250
252
251
- func resolveRelatedResourceOriginNamespaces (relatedOrigin , relatedDest syncSide , spec syncagentv1alpha1.RelatedResourceObjectSpec ) (map [string ]string , error ) {
253
+ func resolveRelatedResourceOriginNamespaces (relatedOrigin , relatedDest syncSide , origin syncagentv1alpha1. RelatedResourceOrigin , spec syncagentv1alpha1.RelatedResourceObjectSpec ) (map [string ]string , error ) {
252
254
switch {
253
255
//nolint:staticcheck
254
256
case spec .Reference != nil :
@@ -294,7 +296,7 @@ func resolveRelatedResourceOriginNamespaces(relatedOrigin, relatedDest syncSide,
294
296
for _ , namespace := range namespaces .Items {
295
297
name := namespace .Name
296
298
297
- destinationName , err := applyRewrites (relatedOrigin , relatedDest , name , spec .Selector .Rewrite )
299
+ destinationName , err := applySelectorRewrites (relatedOrigin , relatedDest , name , spec .Selector .Rewrite )
298
300
if err != nil {
299
301
return nil , fmt .Errorf ("failed to rewrite origin namespace: %w" , err )
300
302
}
@@ -305,7 +307,7 @@ func resolveRelatedResourceOriginNamespaces(relatedOrigin, relatedDest syncSide,
305
307
return namespaceMap , nil
306
308
307
309
case spec .Template != nil :
308
- originValue , destValue , err := applyTemplateBothSides (relatedOrigin , relatedDest , * spec .Template )
310
+ originValue , destValue , err := applyTemplateBothSides (relatedOrigin , relatedDest , origin , * spec .Template )
309
311
if err != nil {
310
312
return nil , fmt .Errorf ("failed to apply template: %w" , err )
311
313
}
@@ -391,7 +393,12 @@ func resolveRelatedResourceObjectsInNamespace(relatedOrigin, relatedDest syncSid
391
393
originObjects .SetAPIVersion ("v1" ) // we only support ConfigMaps and Secrets, both are in core/v1
392
394
originObjects .SetKind (relRes .Kind )
393
395
394
- selector , err := metav1 .LabelSelectorAsSelector (& spec .Selector .LabelSelector )
396
+ labelSelector , err := templateLabelSelector (relatedOrigin , relatedDest , relRes .Origin , & spec .Selector .LabelSelector )
397
+ if err != nil {
398
+ return nil , fmt .Errorf ("failed to apply templates to label selector: %w" , err )
399
+ }
400
+
401
+ selector , err := metav1 .LabelSelectorAsSelector (labelSelector )
395
402
if err != nil {
396
403
return nil , fmt .Errorf ("invalid selector configured: %w" , err )
397
404
}
@@ -409,7 +416,7 @@ func resolveRelatedResourceObjectsInNamespace(relatedOrigin, relatedDest syncSid
409
416
for _ , originObject := range originObjects .Items {
410
417
name := originObject .GetName ()
411
418
412
- destinationName , err := applyRewrites (relatedOrigin , relatedDest , name , spec .Selector .Rewrite )
419
+ destinationName , err := applySelectorRewrites (relatedOrigin , relatedDest , name , spec .Selector .Rewrite )
413
420
if err != nil {
414
421
return nil , fmt .Errorf ("failed to rewrite origin name: %w" , err )
415
422
}
@@ -420,7 +427,7 @@ func resolveRelatedResourceObjectsInNamespace(relatedOrigin, relatedDest syncSid
420
427
return nameMap , nil
421
428
422
429
case spec .Template != nil :
423
- originValue , destValue , err := applyTemplateBothSides (relatedOrigin , relatedDest , * spec .Template )
430
+ originValue , destValue , err := applyTemplateBothSides (relatedOrigin , relatedDest , relRes . Origin , * spec .Template )
424
431
if err != nil {
425
432
return nil , fmt .Errorf ("failed to apply template: %w" , err )
426
433
}
@@ -468,7 +475,7 @@ func resolveReference(jsonData []byte, ref syncagentv1alpha1.RelatedResourceObje
468
475
return strVal , nil
469
476
}
470
477
471
- func applyRewrites (relatedOrigin , relatedDest syncSide , value string , rewrite syncagentv1alpha1.RelatedResourceSelectorRewrite ) (string , error ) {
478
+ func applySelectorRewrites (relatedOrigin , relatedDest syncSide , value string , rewrite syncagentv1alpha1.RelatedResourceSelectorRewrite ) (string , error ) {
472
479
switch {
473
480
case rewrite .Regex != nil :
474
481
return applyRegularExpression (value , * rewrite .Regex )
@@ -496,14 +503,8 @@ func applyTemplate(relatedOrigin, relatedDest syncSide, tpl syncagentv1alpha1.Te
496
503
return "" , errors .New ("not yet implemented" )
497
504
}
498
505
499
- func applyTemplateBothSides (relatedOrigin , relatedDest syncSide , tpl syncagentv1alpha1.TemplateExpression ) (originValue , destValue string , err error ) {
500
- // clusterName and workspacePath are only set on the kcp side of the sync.
501
- clusterName := relatedDest .clusterName
502
- workspacePath := relatedDest .workspacePath
503
- if clusterName == "" {
504
- clusterName = relatedOrigin .clusterName
505
- workspacePath = relatedOrigin .workspacePath
506
- }
506
+ func applyTemplateBothSides (relatedOrigin , relatedDest syncSide , origin syncagentv1alpha1.RelatedResourceOrigin , tpl syncagentv1alpha1.TemplateExpression ) (originValue , destValue string , err error ) {
507
+ clusterName , workspacePath := clusterIdent (relatedOrigin , relatedDest , origin )
507
508
508
509
// evaluate the template for the origin object side
509
510
ctx := templating .NewRelatedObjectContext (relatedOrigin .object , clusterName , workspacePath )
@@ -521,3 +522,53 @@ func applyTemplateBothSides(relatedOrigin, relatedDest syncSide, tpl syncagentv1
521
522
522
523
return originValue , destValue , nil
523
524
}
525
+
526
+ // templateLabelSelector applies Go templating logic to all keys and values in the MatchLabels of
527
+ // a label selector.
528
+ func templateLabelSelector (relatedOrigin , relatedDest syncSide , origin syncagentv1alpha1.RelatedResourceOrigin , selector * metav1.LabelSelector ) (* metav1.LabelSelector , error ) {
529
+ clusterName , workspacePath := clusterIdent (relatedOrigin , relatedDest , origin )
530
+
531
+ localObject := relatedOrigin .object
532
+ remoteObject := relatedDest .object
533
+ if origin == syncagentv1alpha1 .RelatedResourceOriginKcp {
534
+ localObject = relatedDest .object
535
+ remoteObject = relatedOrigin .object
536
+ }
537
+
538
+ ctx := templating .NewRelatedObjectLabelContext (localObject , remoteObject , clusterName , workspacePath )
539
+
540
+ newMatchLabels := map [string ]string {}
541
+ for key , value := range selector .MatchLabels {
542
+ if strings .Contains (key , "{{" ) {
543
+ rendered , err := templating .Render (key , ctx )
544
+ if err != nil {
545
+ return nil , fmt .Errorf ("failed to evaluate key as template: %w" , err )
546
+ }
547
+
548
+ key = rendered
549
+ }
550
+
551
+ if strings .Contains (value , "{{" ) {
552
+ rendered , err := templating .Render (value , ctx )
553
+ if err != nil {
554
+ return nil , fmt .Errorf ("failed to evaluate value as template: %w" , err )
555
+ }
556
+
557
+ value = rendered
558
+ }
559
+
560
+ newMatchLabels [key ] = value
561
+ }
562
+
563
+ selector .MatchLabels = newMatchLabels
564
+
565
+ return selector , nil
566
+ }
567
+
568
+ func clusterIdent (relatedOrigin , relatedDest syncSide , origin syncagentv1alpha1.RelatedResourceOrigin ) (logicalcluster.Name , logicalcluster.Path ) {
569
+ if origin == syncagentv1alpha1 .RelatedResourceOriginKcp {
570
+ return relatedOrigin .clusterName , relatedOrigin .workspacePath
571
+ }
572
+
573
+ return relatedDest .clusterName , relatedDest .workspacePath
574
+ }
0 commit comments