Skip to content

Commit 9736d94

Browse files
committed
use typed variables for name/paths, ensure the path is handed through the callstack
On-behalf-of: @SAP [email protected]
1 parent 511599c commit 9736d94

File tree

10 files changed

+76
-49
lines changed

10 files changed

+76
-49
lines changed

internal/controller/sync/controller.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ import (
2121
"fmt"
2222
"time"
2323

24-
kcpcore "github.com/kcp-dev/kcp/sdk/apis/core"
25-
kcpdevcorev1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1"
2624
"github.com/kcp-dev/logicalcluster/v3"
2725
"go.uber.org/zap"
2826

@@ -32,6 +30,9 @@ import (
3230
"github.com/kcp-dev/api-syncagent/internal/sync"
3331
syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1"
3432

33+
kcpcore "github.com/kcp-dev/kcp/sdk/apis/core"
34+
kcpdevcorev1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1"
35+
3536
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3637
"k8s.io/apimachinery/pkg/types"
3738
"k8s.io/utils/ptr"

internal/controller/syncmanager/lifecycle/cluster.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ import (
2424
"regexp"
2525
"strings"
2626

27-
kcpdevcorev1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1"
2827
"github.com/kcp-dev/logicalcluster/v3"
2928
"go.uber.org/zap"
3029

30+
kcpdevcorev1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1"
31+
3132
"k8s.io/apimachinery/pkg/api/meta"
3233
"k8s.io/apimachinery/pkg/runtime"
3334
"k8s.io/client-go/rest"

internal/sync/context.go

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121

2222
"github.com/kcp-dev/logicalcluster/v3"
23+
2324
"sigs.k8s.io/controller-runtime/pkg/kontext"
2425
)
2526

internal/sync/meta.go

+24-15
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package sync
1919
import (
2020
"context"
2121

22+
"github.com/kcp-dev/logicalcluster/v3"
2223
"go.uber.org/zap"
2324

2425
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -65,7 +66,7 @@ func ensureFinalizer(ctx context.Context, log *zap.SugaredLogger, client ctrlrun
6566
finalizers.Insert(deletionFinalizer)
6667
obj.SetFinalizers(sets.List(finalizers))
6768

68-
log.Debugw("Adding finalizer…", "on", newObjectKey(obj, ""), "finalizer", finalizer)
69+
log.Debugw("Adding finalizer…", "on", newObjectKey(obj, "", logicalcluster.None), "finalizer", finalizer)
6970
if err := client.Patch(ctx, obj, ctrlruntimeclient.MergeFrom(original)); err != nil {
7071
return false, err
7172
}
@@ -84,7 +85,7 @@ func removeFinalizer(ctx context.Context, log *zap.SugaredLogger, client ctrlrun
8485
finalizers.Delete(deletionFinalizer)
8586
obj.SetFinalizers(sets.List(finalizers))
8687

87-
log.Debugw("Removing finalizer…", "on", newObjectKey(obj, ""), "finalizer", finalizer)
88+
log.Debugw("Removing finalizer…", "on", newObjectKey(obj, "", logicalcluster.None), "finalizer", finalizer)
8889
if err := client.Patch(ctx, obj, ctrlruntimeclient.MergeFrom(original)); err != nil {
8990
return false, err
9091
}
@@ -93,16 +94,18 @@ func removeFinalizer(ctx context.Context, log *zap.SugaredLogger, client ctrlrun
9394
}
9495

9596
type objectKey struct {
96-
Cluster string
97-
Namespace string
98-
Name string
97+
ClusterName logicalcluster.Name
98+
ClusterPath logicalcluster.Path
99+
Namespace string
100+
Name string
99101
}
100102

101-
func newObjectKey(obj metav1.Object, clusterName string) objectKey {
103+
func newObjectKey(obj metav1.Object, clusterName logicalcluster.Name, clusterPath logicalcluster.Path) objectKey {
102104
return objectKey{
103-
Cluster: clusterName,
104-
Namespace: obj.GetNamespace(),
105-
Name: obj.GetName(),
105+
ClusterName: clusterName,
106+
ClusterPath: clusterPath,
107+
Namespace: obj.GetNamespace(),
108+
Name: obj.GetName(),
106109
}
107110
}
108111

@@ -111,8 +114,8 @@ func (k objectKey) String() string {
111114
if k.Namespace != "" {
112115
result = k.Namespace + "/" + result
113116
}
114-
if k.Cluster != "" {
115-
result = k.Cluster + "|" + result
117+
if k.ClusterName != "" {
118+
result = string(k.ClusterName) + "|" + result
116119
}
117120

118121
return result
@@ -123,17 +126,23 @@ func (k objectKey) Key() string {
123126
if k.Namespace != "" {
124127
result = k.Namespace + "_" + result
125128
}
126-
if k.Cluster != "" {
127-
result = k.Cluster + "_" + result
129+
if k.ClusterName != "" {
130+
result = string(k.ClusterName) + "_" + result
128131
}
129132

130133
return result
131134
}
132135

133136
func (k objectKey) Labels() labels.Set {
134-
return labels.Set{
135-
remoteObjectClusterLabel: k.Cluster,
137+
s := labels.Set{
138+
remoteObjectClusterLabel: string(k.ClusterName),
136139
remoteObjectNamespaceLabel: k.Namespace,
137140
remoteObjectNameLabel: k.Name,
138141
}
142+
143+
if !k.ClusterPath.Empty() {
144+
s[remoteObjectClusterPathLabel] = k.ClusterPath.String()
145+
}
146+
147+
return s
139148
}

internal/sync/meta_test.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ package sync
1919
import (
2020
"testing"
2121

22+
"github.com/kcp-dev/logicalcluster/v3"
23+
2224
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2325
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2426
)
@@ -34,7 +36,8 @@ func createNewObject(name, namespace string) metav1.Object {
3436
func TestObjectKey(t *testing.T) {
3537
testcases := []struct {
3638
object metav1.Object
37-
clusterName string
39+
clusterName logicalcluster.Name
40+
clusterPath logicalcluster.Path
3841
expected string
3942
}{
4043
{
@@ -57,11 +60,17 @@ func TestObjectKey(t *testing.T) {
5760
clusterName: "abc123",
5861
expected: "abc123|namespace/test",
5962
},
63+
{
64+
object: createNewObject("test", "namespace"),
65+
clusterName: "abc123",
66+
clusterPath: logicalcluster.NewPath("this:should:not:appear:in:the:key"),
67+
expected: "abc123|namespace/test",
68+
},
6069
}
6170

6271
for _, testcase := range testcases {
6372
t.Run("", func(t *testing.T) {
64-
key := newObjectKey(testcase.object, testcase.clusterName)
73+
key := newObjectKey(testcase.object, testcase.clusterName, testcase.clusterPath)
6574

6675
if stringified := key.String(); stringified != testcase.expected {
6776
t.Fatalf("Expected %q but got %q.", testcase.expected, stringified)

internal/sync/object_syncer.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"slices"
2323

2424
jsonpatch "github.com/evanphx/json-patch/v5"
25+
"github.com/kcp-dev/logicalcluster/v3"
2526
"go.uber.org/zap"
2627
"k8c.io/reconciler/pkg/equality"
2728

@@ -54,7 +55,8 @@ type objectSyncer struct {
5455

5556
type syncSide struct {
5657
ctx context.Context
57-
clusterName string
58+
clusterName logicalcluster.Name
59+
clusterPath logicalcluster.Path
5860
client ctrlruntimeclient.Client
5961
object *unstructured.Unstructured
6062
}
@@ -104,7 +106,7 @@ func (s *objectSyncer) Sync(log *zap.SugaredLogger, source, dest syncSide) (requ
104106
// do not try to update a destination object that is in deletion
105107
// (this should only happen if a service admin manually deletes something on the service cluster)
106108
if dest.object.GetDeletionTimestamp() != nil {
107-
log.Debugw("Destination object is in deletion, skipping any further synchronization", "dest-object", newObjectKey(dest.object, dest.clusterName))
109+
log.Debugw("Destination object is in deletion, skipping any further synchronization", "dest-object", newObjectKey(dest.object, dest.clusterName, logicalcluster.None))
108110
return false, nil
109111
}
110112

@@ -173,7 +175,7 @@ func (s *objectSyncer) syncObjectSpec(log *zap.SugaredLogger, source, dest syncS
173175
sourceObjCopy := source.object.DeepCopy()
174176
stripMetadata(sourceObjCopy)
175177

176-
log = log.With("dest-object", newObjectKey(dest.object, dest.clusterName))
178+
log = log.With("dest-object", newObjectKey(dest.object, dest.clusterName, logicalcluster.None))
177179

178180
// calculate the patch to go from the last known state to the current source object's state
179181
if lastKnownSourceState != nil {
@@ -271,11 +273,11 @@ func (s *objectSyncer) ensureDestinationObject(log *zap.SugaredLogger, source, d
271273
stripMetadata(destObj)
272274

273275
// remember the connection between the source and destination object
274-
sourceObjKey := newObjectKey(source.object, source.clusterName)
276+
sourceObjKey := newObjectKey(source.object, source.clusterName, source.clusterPath)
275277
ensureLabels(destObj, sourceObjKey.Labels())
276278

277279
// finally, we can create the destination object
278-
objectLog := log.With("dest-object", newObjectKey(destObj, dest.clusterName))
280+
objectLog := log.With("dest-object", newObjectKey(destObj, dest.clusterName, logicalcluster.None))
279281
objectLog.Debugw("Creating destination object…")
280282

281283
if err := dest.client.Create(dest.ctx, destObj); err != nil {
@@ -356,7 +358,7 @@ func (s *objectSyncer) handleDeletion(log *zap.SugaredLogger, source, dest syncS
356358
// if the destination object still exists, delete it and wait for it to be cleaned up
357359
if dest.object != nil {
358360
if dest.object.GetDeletionTimestamp() == nil {
359-
log.Debugw("Deleting destination object…", "dest-object", newObjectKey(dest.object, dest.clusterName))
361+
log.Debugw("Deleting destination object…", "dest-object", newObjectKey(dest.object, dest.clusterName, logicalcluster.None))
360362
if err := dest.client.Delete(dest.ctx, dest.object); err != nil {
361363
return false, fmt.Errorf("failed to delete destination object: %w", err)
362364
}

internal/sync/state_store.go

+11-9
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import (
2323
"fmt"
2424
"strings"
2525

26+
"github.com/kcp-dev/logicalcluster/v3"
27+
2628
corev1 "k8s.io/api/core/v1"
2729
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2830
"k8s.io/apimachinery/pkg/labels"
@@ -32,7 +34,7 @@ import (
3234

3335
type ObjectStateStore interface {
3436
Get(source syncSide) (*unstructured.Unstructured, error)
35-
Put(obj *unstructured.Unstructured, clusterName string, subresources []string) error
37+
Put(obj *unstructured.Unstructured, clusterName logicalcluster.Name, subresources []string) error
3638
}
3739

3840
// objectStateStore is capable of creating/updating a target Kubernetes object
@@ -71,7 +73,7 @@ func (op *objectStateStore) Get(source syncSide) (*unstructured.Unstructured, er
7173
return lastKnown, nil
7274
}
7375

74-
func (op *objectStateStore) Put(obj *unstructured.Unstructured, clusterName string, subresources []string) error {
76+
func (op *objectStateStore) Put(obj *unstructured.Unstructured, clusterName logicalcluster.Name, subresources []string) error {
7577
encoded, err := op.snapshotObject(obj, subresources)
7678
if err != nil {
7779
return err
@@ -99,8 +101,8 @@ func (op *objectStateStore) snapshotObject(obj *unstructured.Unstructured, subre
99101
}
100102

101103
type backend interface {
102-
Get(obj *unstructured.Unstructured, clusterName string) ([]byte, error)
103-
Put(obj *unstructured.Unstructured, clusterName string, data []byte) error
104+
Get(obj *unstructured.Unstructured, clusterName logicalcluster.Name) ([]byte, error)
105+
Put(obj *unstructured.Unstructured, clusterName logicalcluster.Name, data []byte) error
104106
}
105107

106108
type kubernetesBackend struct {
@@ -131,7 +133,7 @@ func hashObject(obj *unstructured.Unstructured) string {
131133
func newKubernetesBackend(namespace string, primaryObject, stateCluster syncSide) *kubernetesBackend {
132134
keyHash := hashObject(primaryObject.object)
133135

134-
secretLabels := newObjectKey(primaryObject.object, primaryObject.clusterName).Labels()
136+
secretLabels := newObjectKey(primaryObject.object, primaryObject.clusterName, primaryObject.clusterPath).Labels()
135137
secretLabels[objectStateLabelName] = objectStateLabelValue
136138

137139
return &kubernetesBackend{
@@ -145,13 +147,13 @@ func newKubernetesBackend(namespace string, primaryObject, stateCluster syncSide
145147
}
146148
}
147149

148-
func (b *kubernetesBackend) Get(obj *unstructured.Unstructured, clusterName string) ([]byte, error) {
150+
func (b *kubernetesBackend) Get(obj *unstructured.Unstructured, clusterName logicalcluster.Name) ([]byte, error) {
149151
secret := corev1.Secret{}
150152
if err := b.stateCluster.client.Get(b.stateCluster.ctx, b.secretName, &secret); ctrlruntimeclient.IgnoreNotFound(err) != nil {
151153
return nil, err
152154
}
153155

154-
sourceKey := newObjectKey(obj, clusterName).Key()
156+
sourceKey := newObjectKey(obj, clusterName, logicalcluster.None).Key()
155157
data, ok := secret.Data[sourceKey]
156158
if !ok {
157159
return nil, nil
@@ -160,7 +162,7 @@ func (b *kubernetesBackend) Get(obj *unstructured.Unstructured, clusterName stri
160162
return data, nil
161163
}
162164

163-
func (b *kubernetesBackend) Put(obj *unstructured.Unstructured, clusterName string, data []byte) error {
165+
func (b *kubernetesBackend) Put(obj *unstructured.Unstructured, clusterName logicalcluster.Name, data []byte) error {
164166
secret := corev1.Secret{}
165167
if err := b.stateCluster.client.Get(b.stateCluster.ctx, b.secretName, &secret); ctrlruntimeclient.IgnoreNotFound(err) != nil {
166168
return err
@@ -170,7 +172,7 @@ func (b *kubernetesBackend) Put(obj *unstructured.Unstructured, clusterName stri
170172
secret.Data = map[string][]byte{}
171173
}
172174

173-
sourceKey := newObjectKey(obj, clusterName).Key()
175+
sourceKey := newObjectKey(obj, clusterName, logicalcluster.None).Key()
174176
secret.Data[sourceKey] = data
175177
secret.Labels = b.labels
176178

internal/sync/syncer.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package sync
1919
import (
2020
"fmt"
2121

22-
"github.com/kcp-dev/logicalcluster/v3"
2322
"go.uber.org/zap"
2423

2524
"github.com/kcp-dev/api-syncagent/internal/mutation"
@@ -113,7 +112,7 @@ func NewResourceSyncer(
113112
// case, the caller should re-fetch the remote object and call Process() again (most likely in the
114113
// next reconciliation). Only when (false, nil) is returned is the entire process finished.
115114
func (s *ResourceSyncer) Process(ctx Context, remoteObj *unstructured.Unstructured) (requeue bool, err error) {
116-
log := s.log.With("source-object", newObjectKey(remoteObj, ctx.clusterName))
115+
log := s.log.With("source-object", newObjectKey(remoteObj, ctx.clusterName, ctx.clusterPath))
117116

118117
// find the local equivalent object in the local service cluster
119118
localObj, err := s.findLocalObject(ctx, remoteObj)
@@ -129,6 +128,7 @@ func (s *ResourceSyncer) Process(ctx Context, remoteObj *unstructured.Unstructur
129128
sourceSide := syncSide{
130129
ctx: ctx.remote,
131130
clusterName: ctx.clusterName,
131+
clusterPath: ctx.clusterPath,
132132
client: s.remoteClient,
133133
object: remoteObj,
134134
}
@@ -182,7 +182,7 @@ func (s *ResourceSyncer) Process(ctx Context, remoteObj *unstructured.Unstructur
182182
}
183183

184184
func (s *ResourceSyncer) findLocalObject(ctx Context, remoteObj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
185-
localSelector := labels.SelectorFromSet(newObjectKey(remoteObj, ctx.clusterName).Labels())
185+
localSelector := labels.SelectorFromSet(newObjectKey(remoteObj, ctx.clusterName, ctx.clusterPath).Labels())
186186

187187
localObjects := &unstructured.UnstructuredList{}
188188
localObjects.SetAPIVersion(s.destDummy.GetAPIVersion())
@@ -215,7 +215,7 @@ func (s *ResourceSyncer) createLocalObjectCreator(ctx Context) objectCreatorFunc
215215
destScope := syncagentv1alpha1.ResourceScope(s.localCRD.Spec.Scope)
216216

217217
// map namespace/name
218-
mappedName := projection.GenerateLocalObjectName(s.pubRes, remoteObj, logicalcluster.Name(ctx.clusterName))
218+
mappedName := projection.GenerateLocalObjectName(s.pubRes, remoteObj, ctx.clusterName)
219219

220220
switch destScope {
221221
case syncagentv1alpha1.ClusterScoped:

internal/sync/syncer_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,7 @@ func TestSyncerProcessingSingleResourceWithoutStatus(t *testing.T) {
825825
if backend == nil {
826826
backend = newKubernetesBackend(stateNamespace, primaryObject, stateCluster)
827827
if testcase.existingState != "" {
828-
if err := backend.Put(testcase.remoteObject, clusterName.String(), []byte(testcase.existingState)); err != nil {
828+
if err := backend.Put(testcase.remoteObject, clusterName, []byte(testcase.existingState)); err != nil {
829829
t.Fatalf("Failed to prime state store: %v", err)
830830
}
831831
}
@@ -894,7 +894,7 @@ func TestSyncerProcessingSingleResourceWithoutStatus(t *testing.T) {
894894
t.Fatal("Cannot check object state, state store was never instantiated.")
895895
}
896896

897-
finalState, err := backend.Get(testcase.expectedRemoteObject, clusterName.String())
897+
finalState, err := backend.Get(testcase.expectedRemoteObject, clusterName)
898898
if err != nil {
899899
t.Fatalf("Failed to get final state: %v", err)
900900
} else if !bytes.Equal(finalState, []byte(testcase.expectedState)) {
@@ -1122,7 +1122,7 @@ func TestSyncerProcessingSingleResourceWithStatus(t *testing.T) {
11221122
if backend == nil {
11231123
backend = newKubernetesBackend(stateNamespace, primaryObject, stateCluster)
11241124
if testcase.existingState != "" {
1125-
if err := backend.Put(testcase.remoteObject, clusterName.String(), []byte(testcase.existingState)); err != nil {
1125+
if err := backend.Put(testcase.remoteObject, clusterName, []byte(testcase.existingState)); err != nil {
11261126
t.Fatalf("Failed to prime state store: %v", err)
11271127
}
11281128
}
@@ -1191,7 +1191,7 @@ func TestSyncerProcessingSingleResourceWithStatus(t *testing.T) {
11911191
t.Fatal("Cannot check object state, state store was never instantiated.")
11921192
}
11931193

1194-
finalState, err := backend.Get(testcase.expectedRemoteObject, clusterName.String())
1194+
finalState, err := backend.Get(testcase.expectedRemoteObject, clusterName)
11951195
if err != nil {
11961196
t.Fatalf("Failed to get final state: %v", err)
11971197
} else if !bytes.Equal(finalState, []byte(testcase.expectedState)) {

0 commit comments

Comments
 (0)