@@ -27,6 +27,7 @@ import (
2727 corev1 "k8s.io/api/core/v1"
2828 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2929 "k8s.io/apimachinery/pkg/types"
30+ "k8s.io/apimachinery/pkg/util/sets"
3031 v1 "knative.dev/eventing/pkg/apis/duck/v1"
3132 "knative.dev/eventing/pkg/channel"
3233 "knative.dev/eventing/pkg/channel/fanout"
@@ -38,6 +39,7 @@ import (
3839
3940 "knative.dev/eventing-natss/pkg/client/clientset/versioned"
4041 commonerr "knative.dev/eventing-natss/pkg/common/error"
42+ msgingversioned "knative.dev/eventing/pkg/client/clientset/versioned"
4143
4244 "knative.dev/eventing-natss/pkg/apis/messaging/v1alpha1"
4345 jsmreconciler "knative.dev/eventing-natss/pkg/client/injection/reconciler/messaging/v1alpha1/natsjetstreamchannel"
@@ -57,9 +59,11 @@ const (
5759// - Creates a HTTP listener which publishes received events to the Stream
5860// - Creates a consumer for each .spec.subscribers[] and forwards events to the subscriber address
5961type Reconciler struct {
60- clientSet versioned.Interface
61- js nats.JetStreamManager
62- dispatcher * Dispatcher
62+ skipOrphanedSubscriptions bool
63+ msgingClient msgingversioned.Interface
64+ clientSet versioned.Interface
65+ js nats.JetStreamManager
66+ dispatcher * Dispatcher
6367
6468 streamNameFunc StreamNameFunc
6569 consumerNameFunc ConsumerNameFunc
@@ -78,6 +82,11 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, nc *v1alpha1.NatsJetStre
7882 return err
7983 }
8084
85+ if err := r .reconcileOrphanedSubscriptions (ctx , nc ); err != nil {
86+ logger .Errorw ("failed to reconcile orphaned subscriptions" , zap .Error (err ))
87+ return err
88+ }
89+
8190 if err := r .syncChannel (ctx , nc , true ); err != nil {
8291 logger .Errorw ("failed to syncChannel" , zap .Error (err ))
8392 return err
@@ -86,6 +95,86 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, nc *v1alpha1.NatsJetStre
8695 return r .reconcileSubscriberStatuses (ctx , nc )
8796}
8897
98+ func (r * Reconciler ) reconcileOrphanedSubscriptions (ctx context.Context , nc * v1alpha1.NatsJetStreamChannel ) error {
99+ logger := logging .FromContext (ctx )
100+ if r .skipOrphanedSubscriptions {
101+ return nil
102+ }
103+ var allSubsInNs , err = r .msgingClient .MessagingV1 ().Subscriptions (nc .Namespace ).List (ctx , metav1.ListOptions {})
104+ if err != nil {
105+ logger .Errorw ("failed to get subs" , zap .Error (err ))
106+ return err
107+ }
108+
109+ allSubsInNsUids := sets .New [string ]()
110+ for _ , s := range allSubsInNs .Items {
111+ allSubsInNsUids .Insert (string (s .UID ))
112+ }
113+
114+ subsUids := sets .New [string ]()
115+ for _ , s := range nc .Spec .Subscribers {
116+ subsUids .Insert (string (s .UID ))
117+ }
118+
119+ orphanedSubs := subsUids .Difference (allSubsInNsUids )
120+ if len (orphanedSubs ) == 0 {
121+ return nil
122+ }
123+ logger .Warnw ("orpaned subscriptions found" , zap .Any ("orphaned_subs" , orphanedSubs ))
124+
125+ after := nc .DeepCopy ()
126+ after .Status .Subscribers = make ([]v1.SubscriberStatus , len (nc .Status .Subscribers )- len (orphanedSubs ))
127+ after .Spec .Subscribers = make ([]v1.SubscriberSpec , len (nc .Spec .Subscribers )- len (orphanedSubs ))
128+ i := 0
129+ for _ , s := range nc .Spec .Subscribers {
130+ if orphanedSubs .Has (string (s .UID )) {
131+ continue
132+ }
133+
134+ after .Spec .Subscribers [i ] = * s .DeepCopy ()
135+ i = i + 1
136+ }
137+
138+ i = 0
139+ for _ , s := range nc .Status .Subscribers {
140+ if orphanedSubs .Has (string (s .UID )) {
141+ continue
142+ }
143+
144+ after .Status .Subscribers [i ] = * s .DeepCopy ()
145+ i = i + 1
146+ }
147+
148+ logger .Debugw ("reconciling orphaned subscribers" )
149+
150+ jsonPatch , err := duck .CreatePatch (nc , after )
151+ if err != nil {
152+ return fmt .Errorf ("failed to create JSON patch: %w" , err )
153+ }
154+
155+ // If there is nothing to patch, we are good, just return.
156+ // Empty patch is [], hence we check for that.
157+ if len (jsonPatch ) == 0 {
158+ return nil
159+ }
160+
161+ patch , err := jsonPatch .MarshalJSON ()
162+ if err != nil {
163+ return fmt .Errorf ("failed to marshal patch to JSON: %w" , err )
164+ }
165+
166+ patched , err := r .clientSet .MessagingV1alpha1 ().
167+ NatsJetStreamChannels (nc .Namespace ).
168+ Patch (ctx , nc .Name , types .JSONPatchType , patch , metav1.PatchOptions {})
169+ if err != nil {
170+ return fmt .Errorf ("failed to patch subscriber: %w" , err )
171+ }
172+
173+ logger .Debugw ("patched resource" , zap .Any ("patch" , patch ), zap .Any ("patched" , patched ))
174+
175+ return nil
176+ }
177+
89178func (r * Reconciler ) reconcileSubscriberStatuses (ctx context.Context , nc * v1alpha1.NatsJetStreamChannel ) error {
90179 logger := logging .FromContext (ctx )
91180 after := nc .DeepCopy ()
0 commit comments