From 9470fb880093522021c1eaeecfb0373d087c39c3 Mon Sep 17 00:00:00 2001
From: Manuel Alejandro de Brito Fontes <aledbf@gmail.com>
Date: Wed, 6 Sep 2023 11:33:04 -0300
Subject: [PATCH] Filter subscriber reconciler events (#18660)

* Filter subscriber reconciler events

* Notify changes in the port visibility

* Switch to reflect.DeepEquals

* Address feedback
---
 .../go/crd/v1/workspace_types.go              | 16 ++++++++++
 .../controllers/subscriber_controller.go      | 29 ++++++++++++++++++-
 2 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/components/ws-manager-api/go/crd/v1/workspace_types.go b/components/ws-manager-api/go/crd/v1/workspace_types.go
index 86c65b662eba27..aa54d684b6e97e 100644
--- a/components/ws-manager-api/go/crd/v1/workspace_types.go
+++ b/components/ws-manager-api/go/crd/v1/workspace_types.go
@@ -150,6 +150,22 @@ type PortSpec struct {
 	Protocol PortProtocol `json:"protocol"`
 }
 
+func (ps PortSpec) Equal(other PortSpec) bool {
+	if ps.Port != other.Port {
+		return false
+	}
+
+	if ps.Visibility != other.Visibility {
+		return false
+	}
+
+	if ps.Protocol != other.Protocol {
+		return false
+	}
+
+	return true
+}
+
 // WorkspaceStatus defines the observed state of Workspace
 type WorkspaceStatus struct {
 	PodStarts  int    `json:"podStarts"`
diff --git a/components/ws-manager-mk2/controllers/subscriber_controller.go b/components/ws-manager-mk2/controllers/subscriber_controller.go
index 5c98242eb14f03..0235da0a4b093b 100644
--- a/components/ws-manager-mk2/controllers/subscriber_controller.go
+++ b/components/ws-manager-mk2/controllers/subscriber_controller.go
@@ -7,18 +7,22 @@ package controllers
 import (
 	"context"
 	"os"
+	"reflect"
 
 	"k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/controller"
+	"sigs.k8s.io/controller-runtime/pkg/event"
 	"sigs.k8s.io/controller-runtime/pkg/handler"
 	"sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/predicate"
 	"sigs.k8s.io/controller-runtime/pkg/source"
 
 	config "github.com/gitpod-io/gitpod/ws-manager/api/config"
 	workspacev1 "github.com/gitpod-io/gitpod/ws-manager/api/crd/v1"
+	"github.com/google/go-cmp/cmp"
 )
 
 func NewSubscriberReconciler(c client.Client, cfg *config.Configuration) (*SubscriberReconciler, error) {
@@ -76,5 +80,28 @@ func (r *SubscriberReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Ma
 		}
 	}()
 
-	return c.Watch(source.Kind(mgr.GetCache(), &workspacev1.Workspace{}), &handler.EnqueueRequestForObject{})
+	// we need several reconciliation loops during a workspace creation until it reaches a stable state.
+	// this introduces the side effect of multiple notifications to the subscribers with partial information.
+	// the filterByUpdate predicate acts as a filter to avoid this
+	filterByUpdate := predicate.Funcs{
+		CreateFunc: func(ce event.CreateEvent) bool {
+			return true
+		},
+		UpdateFunc: func(e event.UpdateEvent) bool {
+			old := e.ObjectOld.(*workspacev1.Workspace)
+			new := e.ObjectNew.(*workspacev1.Workspace)
+
+			if !cmp.Equal(old.Spec.Ports, new.Spec.Ports) {
+				return true
+			}
+
+			// do not notify LastActivity changes
+			old.Status.LastActivity = nil
+			new.Status.LastActivity = nil
+
+			return !reflect.DeepEqual(old.Status, new.Status)
+		},
+	}
+
+	return c.Watch(source.Kind(mgr.GetCache(), &workspacev1.Workspace{}), &handler.EnqueueRequestForObject{}, filterByUpdate)
 }