Skip to content

Commit 72fb230

Browse files
committed
failed approach :/
1 parent 525e44a commit 72fb230

File tree

6 files changed

+150
-23
lines changed

6 files changed

+150
-23
lines changed

deploy/crd/kcp.io/syncagent.kcp.io_publishedresources.yaml

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ spec:
318318
type: object
319319
related:
320320
items:
321+
description: RelatedResourceSpec describes a related resource that should be synchronized
321322
properties:
322323
identifier:
323324
description: |-
@@ -329,6 +330,50 @@ spec:
329330
kind:
330331
description: ConfigMap or Secret
331332
type: string
333+
labelSelector:
334+
description: LabelSelector is used to filter the related resource in the service cluster.
335+
properties:
336+
matchExpressions:
337+
description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
338+
items:
339+
description: |-
340+
A label selector requirement is a selector that contains values, a key, and an operator that
341+
relates the key and values.
342+
properties:
343+
key:
344+
description: key is the label key that the selector applies to.
345+
type: string
346+
operator:
347+
description: |-
348+
operator represents a key's relationship to a set of values.
349+
Valid operators are In, NotIn, Exists and DoesNotExist.
350+
type: string
351+
values:
352+
description: |-
353+
values is an array of string values. If the operator is In or NotIn,
354+
the values array must be non-empty. If the operator is Exists or DoesNotExist,
355+
the values array must be empty. This array is replaced during a strategic
356+
merge patch.
357+
items:
358+
type: string
359+
type: array
360+
x-kubernetes-list-type: atomic
361+
required:
362+
- key
363+
- operator
364+
type: object
365+
type: array
366+
x-kubernetes-list-type: atomic
367+
matchLabels:
368+
additionalProperties:
369+
type: string
370+
description: |-
371+
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
372+
map is equivalent to an element of matchExpressions, whose key field is "key", the
373+
operator is "In", and the values array contains only "value". The requirements are ANDed.
374+
type: object
375+
type: object
376+
x-kubernetes-map-type: atomic
332377
mutation:
333378
description: |-
334379
Mutation configures optional transformation rules for the related resource.
@@ -411,6 +456,7 @@ spec:
411456
description: '"service" or "kcp"'
412457
type: string
413458
reference:
459+
description: Reference to the related resource in the service cluster.
414460
properties:
415461
name:
416462
properties:
@@ -453,8 +499,10 @@ spec:
453499
- identifier
454500
- kind
455501
- origin
456-
- reference
457502
type: object
503+
x-kubernetes-validations:
504+
- message: must specify exactly one of reference or labelSelector
505+
rule: (has(self.reference) && !has(self.labelSelector)) || (!has(self.reference) && has(self.labelSelector))
458506
type: array
459507
resource:
460508
description: |-

internal/projection/naming.go

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,7 @@ func GenerateLocalObjectName(pr *syncagentv1alpha1.PublishedResource, object met
4141
naming = &syncagentv1alpha1.ResourceNaming{}
4242
}
4343

44-
replacer := strings.NewReplacer(
45-
// order of elements is important here, "$fooHash" needs to be defined before "$foo"
46-
syncagentv1alpha1.PlaceholderRemoteClusterName, clusterName.String(),
47-
syncagentv1alpha1.PlaceholderRemoteNamespaceHash, shortSha1Hash(object.GetNamespace()),
48-
syncagentv1alpha1.PlaceholderRemoteNamespace, object.GetNamespace(),
49-
syncagentv1alpha1.PlaceholderRemoteNameHash, shortSha1Hash(object.GetName()),
50-
syncagentv1alpha1.PlaceholderRemoteName, object.GetName(),
51-
)
44+
replacer := getReplacer(object, clusterName)
5245

5346
result := types.NamespacedName{}
5447

@@ -69,6 +62,33 @@ func GenerateLocalObjectName(pr *syncagentv1alpha1.PublishedResource, object met
6962
return result
7063
}
7164

65+
func GenerateLocalLabelSelector(pr *syncagentv1alpha1.RelatedResourceSpec, object metav1.Object, clusterName logicalcluster.Name) *metav1.LabelSelector {
66+
replacer := getReplacer(object, clusterName)
67+
68+
result := metav1.LabelSelector{}
69+
70+
result.MatchLabels = map[string]string{}
71+
72+
for key, value := range pr.LabelSelector.MatchLabels {
73+
result.MatchLabels[replacer.Replace(key)] = replacer.Replace(value)
74+
}
75+
76+
// TODO: MatchExpressions are not yet supported with the current naming scheme.
77+
result.MatchExpressions = pr.LabelSelector.MatchExpressions
78+
return &result
79+
}
80+
81+
func getReplacer(object metav1.Object, clusterName logicalcluster.Name) *strings.Replacer {
82+
return strings.NewReplacer(
83+
// order of elements is important here, "$fooHash" needs to be defined before "$foo"
84+
syncagentv1alpha1.PlaceholderRemoteClusterName, clusterName.String(),
85+
syncagentv1alpha1.PlaceholderRemoteNamespaceHash, shortSha1Hash(object.GetNamespace()),
86+
syncagentv1alpha1.PlaceholderRemoteNamespace, object.GetNamespace(),
87+
syncagentv1alpha1.PlaceholderRemoteNameHash, shortSha1Hash(object.GetName()),
88+
syncagentv1alpha1.PlaceholderRemoteName, object.GetName(),
89+
)
90+
}
91+
7292
func shortSha1Hash(value string) string {
7393
hash := sha1.New()
7494
if _, err := hash.Write([]byte(value)); err != nil {

internal/sync/syncer_related.go

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,32 @@ func (s *ResourceSyncer) processRelatedResource(log *zap.SugaredLogger, stateSto
7070
dest = local
7171
}
7272

73-
// to find the source related object, we first need to determine its name/namespace
74-
sourceKey, err := resolveResourceReference(source.object, relRes.Reference)
75-
if err != nil {
76-
return false, fmt.Errorf("failed to determine related object's source key: %w", err)
73+
var sourceKey *ctrlruntimeclient.ObjectKey
74+
switch {
75+
case relRes.Reference != nil:
76+
// to find the source related object, we first need to determine its name/namespace
77+
sourceKey, err = resolveResourceReference(source.object, *relRes.Reference)
78+
if err != nil {
79+
return false, fmt.Errorf("failed to determine related object's source key: %w", err)
80+
}
81+
case relRes.LabelSelector != nil:
82+
// if no reference is given, we can use a label selector to find the source object
83+
sourceObjs := &unstructured.UnstructuredList{}
84+
sourceObjs.SetAPIVersion("v1")
85+
sourceObjs.SetKind(relRes.Kind)
86+
87+
// TODO: would need to handle replacer here as well to select right object.
88+
89+
if err := source.client.List(source.ctx, sourceObjs, ctrlruntimeclient.MatchingLabels(relRes.LabelSelector.MatchLabels)); err != nil {
90+
return false, fmt.Errorf("failed to list related objects: %w", err)
91+
}
92+
if len(sourceObjs.Items) == 0 || len(sourceObjs.Items) > 1 {
93+
return false, fmt.Errorf("expected exactly one related object, got %d", len(sourceObjs.Items))
94+
}
95+
sourceKey = &ctrlruntimeclient.ObjectKey{
96+
Namespace: sourceObjs.Items[0].GetNamespace(),
97+
Name: sourceObjs.Items[0].GetName(),
98+
}
7799
}
78100

79101
// find the source related object
@@ -92,9 +114,18 @@ func (s *ResourceSyncer) processRelatedResource(log *zap.SugaredLogger, stateSto
92114
}
93115

94116
// do the same to find the destination object
95-
destKey, err := resolveResourceReference(dest.object, relRes.Reference)
96-
if err != nil {
97-
return false, fmt.Errorf("failed to determine related object's destination key: %w", err)
117+
var destKey *ctrlruntimeclient.ObjectKey
118+
switch {
119+
case relRes.Reference != nil:
120+
destKey, err = resolveResourceReference(dest.object, *relRes.Reference)
121+
if err != nil {
122+
return false, fmt.Errorf("failed to determine related object's destination key: %w", err)
123+
}
124+
case relRes.LabelSelector != nil:
125+
destKey = &ctrlruntimeclient.ObjectKey{
126+
Namespace: sourceObj.GetNamespace(),
127+
Name: sourceObj.GetName(),
128+
}
98129
}
99130

100131
destObj := &unstructured.Unstructured{}

sdk/apis/syncagent/v1alpha1/published_resource.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ type ResourceTemplateMutation struct {
158158
Template string `json:"template"`
159159
}
160160

161+
// +kubebuilder:validation:XValidation:rule="(has(self.reference) && !has(self.labelSelector)) || (!has(self.reference) && has(self.labelSelector))",message="must specify exactly one of reference or labelSelector"
162+
// RelatedResourceSpec describes a related resource that should be synchronized
161163
type RelatedResourceSpec struct {
162164
// Identifier is a unique name for this related resource. The name must be unique within one
163165
// PublishedResource and is the key by which consumers (end users) can identify and consume the
@@ -171,7 +173,11 @@ type RelatedResourceSpec struct {
171173
// ConfigMap or Secret
172174
Kind string `json:"kind"`
173175

174-
Reference RelatedResourceReference `json:"reference"`
176+
// Reference to the related resource in the service cluster.
177+
Reference *RelatedResourceReference `json:"reference,omitempty"`
178+
179+
// LabelSelector is used to filter the related resource in the service cluster.
180+
LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"`
175181

176182
// Mutation configures optional transformation rules for the related resource.
177183
// Status mutations are only performed when the related resource originates in kcp.

sdk/apis/syncagent/v1alpha1/zz_generated.deepcopy.go

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/applyconfiguration/syncagent/v1alpha1/relatedresourcespec.go

Lines changed: 18 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)