Skip to content

Commit d90da29

Browse files
committed
issue events during synchronization
On-behalf-of: @SAP [email protected]
1 parent 3c51b95 commit d90da29

File tree

5 files changed

+46
-2
lines changed

5 files changed

+46
-2
lines changed

internal/controller/sync/controller.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, request mcreconcile.Request)
215215
if r.pubRes.Spec.EnableWorkspacePaths {
216216
lc := &kcpdevcorev1alpha1.LogicalCluster{}
217217
if err := vwClient.Get(ctx, types.NamespacedName{Name: kcpdevcorev1alpha1.LogicalClusterName}, lc); err != nil {
218+
recorder.Event(remoteObj, corev1.EventTypeWarning, "ReconcilingError", "Failed to retrieve workspace path, cannot process object.")
218219
return reconcile.Result{}, fmt.Errorf("failed to retrieve remote logicalcluster: %w", err)
219220
}
220221

@@ -225,11 +226,13 @@ func (r *Reconciler) Reconcile(ctx context.Context, request mcreconcile.Request)
225226
// sync main object
226227
syncer, err := sync.NewResourceSyncer(log, r.localClient, vwClient, r.pubRes, r.localCRD, mutation.NewMutator, r.stateNamespace, r.agentName)
227228
if err != nil {
229+
recorder.Event(remoteObj, corev1.EventTypeWarning, "ReconcilingError", "Failed to process object: a provider-side issue has occurred.")
228230
return reconcile.Result{}, fmt.Errorf("failed to create syncer: %w", err)
229231
}
230232

231233
requeue, err := syncer.Process(ctx, remoteObj)
232234
if err != nil {
235+
recorder.Event(remoteObj, corev1.EventTypeWarning, "ReconcilingError", "Failed to process object: a provider-side issue has occurred.")
233236
return reconcile.Result{}, err
234237
}
235238

internal/sync/context.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ func workspacePathFromContext(ctx context.Context) logicalcluster.Path {
5858
return path
5959
}
6060

61+
// WithEventRecorder adds a event recorder to the context. The recorder must be configured
62+
// for the kcp side of the synchronization as the agent never issues events on the local cluster.
6163
func WithEventRecorder(ctx context.Context, recorder record.EventRecorder) context.Context {
6264
return context.WithValue(ctx, recorderContextKey, recorder)
6365
}

internal/sync/object_syncer.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
corev1 "k8s.io/api/core/v1"
3232
apierrors "k8s.io/apimachinery/pkg/api/errors"
3333
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
34+
"k8s.io/apimachinery/pkg/runtime"
3435
"k8s.io/apimachinery/pkg/types"
3536
ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client"
3637
)
@@ -57,15 +58,39 @@ type objectSyncer struct {
5758
mutator mutation.Mutator
5859
// stateStore is capable of remembering the state of a Kubernetes object
5960
stateStore ObjectStateStore
61+
// eventObjSide is configuring whether the source or destination object will
62+
// receive events. Since these objects might be created during the sync,
63+
// they cannot be specified here directly.
64+
eventObjSide syncSideType
6065
}
6166

67+
type syncSideType int
68+
69+
const (
70+
syncSideSource syncSideType = iota
71+
syncSideDestination
72+
)
73+
6274
type syncSide struct {
6375
clusterName logicalcluster.Name
6476
workspacePath logicalcluster.Path
6577
client ctrlruntimeclient.Client
6678
object *unstructured.Unstructured
6779
}
6880

81+
func (s *objectSyncer) recordEvent(ctx context.Context, source, dest syncSide, eventtype, reason, msg string, args ...any) {
82+
recorder := recorderFromContext(ctx)
83+
84+
var obj runtime.Object
85+
if s.eventObjSide == syncSideDestination {
86+
obj = dest.object
87+
} else {
88+
obj = source.object
89+
}
90+
91+
recorder.Eventf(obj, eventtype, reason, msg, args...)
92+
}
93+
6994
func (s *objectSyncer) Sync(ctx context.Context, log *zap.SugaredLogger, source, dest syncSide) (requeue bool, err error) {
7095
// handle deletion: if source object is in deletion, delete the destination object (the clone)
7196
if source.object.GetDeletionTimestamp() != nil {
@@ -81,6 +106,7 @@ func (s *objectSyncer) Sync(ctx context.Context, log *zap.SugaredLogger, source,
81106

82107
// the patch above would trigger a new reconciliation anyway
83108
if updated {
109+
s.recordEvent(ctx, source, dest, corev1.EventTypeNormal, "ObjectAccepted", "Object has been seen by the service provider.")
84110
return true, nil
85111
}
86112
}
@@ -103,6 +129,7 @@ func (s *objectSyncer) Sync(ctx context.Context, log *zap.SugaredLogger, source,
103129

104130
// The function above either created a new destination object or patched-in the missing labels,
105131
// in both cases do we want to requeue.
132+
s.recordEvent(ctx, source, dest, corev1.EventTypeNormal, "ObjectPlaced", "Object has been placed.")
106133
return true, nil
107134
}
108135

@@ -254,6 +281,8 @@ func (s *objectSyncer) syncObjectSpec(ctx context.Context, log *zap.SugaredLogge
254281
}
255282

256283
if requeue {
284+
s.recordEvent(ctx, source, dest, corev1.EventTypeNormal, "ObjectSynced", "The current desired state of the object has been synchronized.")
285+
257286
// remember this object state for the next reconciliation (this will strip any syncer-related
258287
// metadata the 3-way diff may have added above)
259288
if err := s.stateStore.Put(ctx, sourceObjCopy, source.clusterName, s.subresources); err != nil {
@@ -282,6 +311,8 @@ func (s *objectSyncer) syncObjectStatus(ctx context.Context, log *zap.SugaredLog
282311
if err := source.client.Status().Update(ctx, source.object); err != nil {
283312
return false, fmt.Errorf("failed to update source object status: %w", err)
284313
}
314+
315+
s.recordEvent(ctx, source, dest, corev1.EventTypeNormal, "ObjectStatusSynced", "The current object status has been updated.")
285316
}
286317

287318
// always return false; there is no need to requeue the source object when we changed its status
@@ -406,6 +437,7 @@ func (s *objectSyncer) handleDeletion(ctx context.Context, log *zap.SugaredLogge
406437
if dest.object != nil {
407438
if dest.object.GetDeletionTimestamp() == nil {
408439
log.Debugw("Deleting destination object…", "dest-object", newObjectKey(dest.object, dest.clusterName, logicalcluster.None))
440+
s.recordEvent(ctx, source, dest, corev1.EventTypeNormal, "ObjectCleanup", "Object deletion has been started and will progress in the background.")
409441
if err := dest.client.Delete(ctx, dest.object); err != nil {
410442
return false, fmt.Errorf("failed to delete destination object: %w", err)
411443
}
@@ -422,6 +454,7 @@ func (s *objectSyncer) handleDeletion(ctx context.Context, log *zap.SugaredLogge
422454

423455
// if we just removed the finalizer, we can requeue the source object
424456
if updated {
457+
s.recordEvent(ctx, source, dest, corev1.EventTypeNormal, "ObjectDeleted", "Object deletion has been completed, finalizer has been removed.")
425458
return true, nil
426459
}
427460

internal/sync/syncer.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ func (s *ResourceSyncer) Process(ctx context.Context, remoteObj *unstructured.Un
194194
// (i.e. on the service cluster), so that the original and copy are linked
195195
// together and can be found.
196196
metadataOnDestination: true,
197+
eventObjSide: syncSideSource,
197198
}
198199

199200
requeue, err = syncer.Sync(ctx, log, sourceSide, destSide)

internal/sync/syncer_related.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,19 @@ type relatedObjectAnnotation struct {
6464
func (s *ResourceSyncer) processRelatedResource(ctx context.Context, log *zap.SugaredLogger, stateStore ObjectStateStore, remote, local syncSide, relRes syncagentv1alpha1.RelatedResourceSpec) (requeue bool, err error) {
6565
// decide what direction to sync (local->remote vs. remote->local)
6666
var (
67-
origin syncSide
68-
dest syncSide
67+
origin syncSide
68+
dest syncSide
69+
eventObjSide syncSideType
6970
)
7071

7172
if relRes.Origin == syncagentv1alpha1.RelatedResourceOriginService {
7273
origin = local
7374
dest = remote
75+
eventObjSide = syncSideDestination
7476
} else {
7577
origin = remote
7678
dest = local
79+
eventObjSide = syncSideSource
7780
}
7881

7982
// find the all objects on the origin side that match the given criteria
@@ -143,6 +146,8 @@ func (s *ResourceSyncer) processRelatedResource(ctx context.Context, log *zap.Su
143146
mutator: s.relatedMutators[relRes.Identifier],
144147
// we never want to store sync-related metadata inside kcp
145148
metadataOnDestination: false,
149+
// events are always created on the kcp side
150+
eventObjSide: eventObjSide,
146151
}
147152

148153
req, err := syncer.Sync(ctx, log, sourceSide, destSide)

0 commit comments

Comments
 (0)