From 2def550af80c19ba6fe05c6411e9e0e3dd7a4a9d Mon Sep 17 00:00:00 2001
From: KevFan <chfan@redhat.com>
Date: Tue, 24 Sep 2024 11:11:12 +0100
Subject: [PATCH] feat: check dependency for state of the world reconciler

Signed-off-by: KevFan <chfan@redhat.com>
---
 controllers/state_of_the_world.go   | 76 +++++++++++++++++++++++++++--
 controllers/test_common.go          |  8 ++-
 controllers/tlspolicy_controller.go | 12 +++++
 make/integration-tests.mk           | 10 ++--
 pkg/envoygateway/utils.go           | 11 +++++
 pkg/istio/utils.go                  | 11 +++++
 6 files changed, 119 insertions(+), 9 deletions(-)

diff --git a/controllers/state_of_the_world.go b/controllers/state_of_the_world.go
index 563db511d..3189ef545 100644
--- a/controllers/state_of_the_world.go
+++ b/controllers/state_of_the_world.go
@@ -5,10 +5,15 @@ import (
 	"fmt"
 	"strings"
 
+	certmanagerv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
+	egv1alpha1 "github.com/envoyproxy/gateway/api/v1alpha1"
 	"github.com/go-logr/logr"
 	"github.com/google/go-cmp/cmp"
 	"github.com/kuadrant/policy-machinery/controller"
 	"github.com/kuadrant/policy-machinery/machinery"
+	istioclientgoextensionv1alpha1 "istio.io/client-go/pkg/apis/extensions/v1alpha1"
+	istioclientnetworkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3"
+	istioclientgosecurityv1beta1 "istio.io/client-go/pkg/apis/security/v1beta1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime/schema"
@@ -20,6 +25,9 @@ import (
 	kuadrantv1alpha1 "github.com/kuadrant/kuadrant-operator/api/v1alpha1"
 	kuadrantv1beta1 "github.com/kuadrant/kuadrant-operator/api/v1beta1"
 	kuadrantv1beta2 "github.com/kuadrant/kuadrant-operator/api/v1beta2"
+	"github.com/kuadrant/kuadrant-operator/pkg/envoygateway"
+	"github.com/kuadrant/kuadrant-operator/pkg/istio"
+	kuadrantgatewayapi "github.com/kuadrant/kuadrant-operator/pkg/library/gatewayapi"
 	"github.com/kuadrant/kuadrant-operator/pkg/library/kuadrant"
 )
 
@@ -36,9 +44,6 @@ func NewPolicyMachineryController(manager ctrlruntime.Manager, client *dynamic.D
 		controller.WithLogger(logger),
 		controller.WithClient(client),
 		controller.WithRunnable("kuadrant watcher", controller.Watch(&kuadrantv1beta1.Kuadrant{}, kuadrantv1beta1.KuadrantResource, metav1.NamespaceAll)),
-		controller.WithRunnable("gatewayclass watcher", controller.Watch(&gwapiv1.GatewayClass{}, controller.GatewayClassesResource, metav1.NamespaceAll)),
-		controller.WithRunnable("gateway watcher", controller.Watch(&gwapiv1.Gateway{}, controller.GatewaysResource, metav1.NamespaceAll)),
-		controller.WithRunnable("httproute watcher", controller.Watch(&gwapiv1.HTTPRoute{}, controller.HTTPRoutesResource, metav1.NamespaceAll)),
 		controller.WithRunnable("dnspolicy watcher", controller.Watch(&kuadrantv1alpha1.DNSPolicy{}, kuadrantv1alpha1.DNSPoliciesResource, metav1.NamespaceAll)),
 		controller.WithRunnable("tlspolicy watcher", controller.Watch(&kuadrantv1alpha1.TLSPolicy{}, kuadrantv1alpha1.TLSPoliciesResource, metav1.NamespaceAll)),
 		controller.WithRunnable("authpolicy watcher", controller.Watch(&kuadrantv1beta2.AuthPolicy{}, kuadrantv1beta2.AuthPoliciesResource, metav1.NamespaceAll)),
@@ -59,6 +64,71 @@ func NewPolicyMachineryController(manager ctrlruntime.Manager, client *dynamic.D
 		controller.WithReconcile(buildReconciler(client)),
 	}
 
+	ok, err := kuadrantgatewayapi.IsGatewayAPIInstalled(manager.GetRESTMapper())
+	if err != nil || !ok {
+		logger.Info("gateway api is not installed, skipping watches and reconcilers", "err", err)
+	} else {
+		controllerOpts = append(controllerOpts,
+			controller.WithRunnable("gatewayclass watcher", controller.Watch(&gwapiv1.GatewayClass{}, controller.GatewayClassesResource, metav1.NamespaceAll)),
+			controller.WithRunnable("gateway watcher", controller.Watch(&gwapiv1.Gateway{}, controller.GatewaysResource, metav1.NamespaceAll)),
+			controller.WithRunnable("httproute watcher", controller.Watch(&gwapiv1.HTTPRoute{}, controller.HTTPRoutesResource, metav1.NamespaceAll)),
+		)
+	}
+
+	ok, err = envoygateway.IsEnvoyGatewayInstalled(manager.GetRESTMapper())
+	if err != nil || !ok {
+		logger.Info("envoygateway is not installed, skipping related watches and reconcilers", "err", err)
+	} else {
+		controllerOpts = append(controllerOpts,
+			controller.WithRunnable("envoypatchpolicy watcher", controller.Watch(&egv1alpha1.EnvoyPatchPolicy{}, envoygateway.EnvoyPatchPoliciesResource, metav1.NamespaceAll)),
+			controller.WithRunnable("envoyextensionpolicy watcher", controller.Watch(&egv1alpha1.EnvoyExtensionPolicy{}, envoygateway.EnvoyExtensionPoliciesResource, metav1.NamespaceAll)),
+			controller.WithRunnable("envoysecuritypolicy watcher", controller.Watch(&egv1alpha1.SecurityPolicy{}, envoygateway.SecurityPoliciesResource, metav1.NamespaceAll)),
+			controller.WithObjectKinds(
+				envoygateway.EnvoyPatchPolicyGroupKind,
+				envoygateway.EnvoyExtensionPolicyGroupKind,
+				envoygateway.SecurityPolicyGroupKind,
+			),
+			// TODO: add object links
+		)
+		// TODO: add specific tasks to workflow
+	}
+
+	ok, err = istio.IsIstioInstalled(manager.GetRESTMapper())
+	if err != nil || !ok {
+		logger.Info("istio is not installed, skipping related watches and reconcilers", "err", err)
+	} else {
+		controllerOpts = append(controllerOpts,
+			controller.WithRunnable("envoyfilter watcher", controller.Watch(&istioclientnetworkingv1alpha3.EnvoyFilter{}, istio.EnvoyFiltersResource, metav1.NamespaceAll)),
+			controller.WithRunnable("wasmplugin watcher", controller.Watch(&istioclientgoextensionv1alpha1.WasmPlugin{}, istio.WasmPluginsResource, metav1.NamespaceAll)),
+			controller.WithRunnable("authorizationpolicy watcher", controller.Watch(&istioclientgosecurityv1beta1.AuthorizationPolicy{}, istio.AuthorizationPoliciesResource, metav1.NamespaceAll)),
+			controller.WithObjectKinds(
+				istio.EnvoyFilterGroupKind,
+				istio.WasmPluginGroupKind,
+				istio.AuthorizationPolicyGroupKind,
+			),
+			// TODO: add object links
+		)
+		// TODO: add istio specific tasks to workflow
+	}
+
+	ok, err = kuadrantgatewayapi.IsCertManagerInstalled(manager.GetRESTMapper(), logger)
+	if err != nil || !ok {
+		logger.Info("cert manager is not installed, skipping related watches and reconcilers", "err", err)
+	} else {
+		controllerOpts = append(controllerOpts,
+			controller.WithRunnable("certificate watcher", controller.Watch(&certmanagerv1.Certificate{}, CertManagerCertificatesResource, metav1.NamespaceAll)),
+			controller.WithRunnable("issuers watcher", controller.Watch(&certmanagerv1.Issuer{}, CertManagerIssuersResource, metav1.NamespaceAll)),
+			controller.WithRunnable("clusterissuers watcher", controller.Watch(&certmanagerv1.Certificate{}, CertMangerClusterIssuersResource, metav1.NamespaceAll)),
+			controller.WithObjectKinds(
+				CertManagerCertificateKind,
+				CertManagerIssuerKind,
+				CertManagerClusterIssuerKind,
+			),
+			// TODO: add object links
+		)
+		// TODO: add tls policy specific tasks to workflow
+	}
+
 	return controller.NewController(controllerOpts...)
 }
 
diff --git a/controllers/test_common.go b/controllers/test_common.go
index 0819b2067..41bab5e64 100644
--- a/controllers/test_common.go
+++ b/controllers/test_common.go
@@ -34,6 +34,7 @@ import (
 	istiosecurityv1beta1 "istio.io/client-go/pkg/apis/security/v1beta1"
 	istioapis "istio.io/istio/operator/pkg/apis"
 	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/client-go/dynamic"
 	"k8s.io/client-go/kubernetes/scheme"
 	"k8s.io/client-go/rest"
 	istiov1alpha1 "maistra.io/istio-operator/api/v1alpha1"
@@ -256,9 +257,14 @@ func SetupKuadrantOperatorForTest(s *runtime.Scheme, cfg *rest.Config) {
 
 	Expect(err).NotTo(HaveOccurred())
 
+	dClient, err := dynamic.NewForConfig(mgr.GetConfig())
+	Expect(err).NotTo(HaveOccurred())
+
+	stateOfTheWorld := NewPolicyMachineryController(mgr, dClient, log.Log)
+
 	go func() {
 		defer GinkgoRecover()
-		err = mgr.Start(ctrl.SetupSignalHandler())
+		err = stateOfTheWorld.Start(ctrl.SetupSignalHandler())
 		Expect(err).ToNot(HaveOccurred())
 	}()
 }
diff --git a/controllers/tlspolicy_controller.go b/controllers/tlspolicy_controller.go
index 0c6a62076..90ba0bc7d 100644
--- a/controllers/tlspolicy_controller.go
+++ b/controllers/tlspolicy_controller.go
@@ -21,9 +21,11 @@ import (
 	"fmt"
 	"reflect"
 
+	"github.com/cert-manager/cert-manager/pkg/apis/certmanager"
 	certmanagerv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/api/meta"
+	"k8s.io/apimachinery/pkg/runtime/schema"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/builder"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -44,6 +46,16 @@ import (
 
 const TLSPolicyFinalizer = "kuadrant.io/tls-policy"
 
+var (
+	CertManagerCertificatesResource  = certmanagerv1.SchemeGroupVersion.WithResource("certificates")
+	CertManagerIssuersResource       = certmanagerv1.SchemeGroupVersion.WithResource("issuers")
+	CertMangerClusterIssuersResource = certmanagerv1.SchemeGroupVersion.WithResource("clusterissuers")
+
+	CertManagerCertificateKind   = schema.GroupKind{Group: certmanager.GroupName, Kind: certmanagerv1.CertificateKind}
+	CertManagerIssuerKind        = schema.GroupKind{Group: certmanager.GroupName, Kind: certmanagerv1.IssuerKind}
+	CertManagerClusterIssuerKind = schema.GroupKind{Group: certmanager.GroupName, Kind: certmanagerv1.ClusterIssuerKind}
+)
+
 // TLSPolicyReconciler reconciles a TLSPolicy object
 type TLSPolicyReconciler struct {
 	*reconcilers.BaseReconciler
diff --git a/make/integration-tests.mk b/make/integration-tests.mk
index 0fe4db81e..c28050d12 100644
--- a/make/integration-tests.mk
+++ b/make/integration-tests.mk
@@ -9,7 +9,7 @@ INTEGRATION_TEST_NUM_PROCESSES ?= 10
 test-bare-k8s-integration: clean-cov generate fmt vet ginkgo ## Requires only bare kubernetes cluster.
 	mkdir -p $(PROJECT_PATH)/coverage/bare-k8s-integration
 #	Check `ginkgo help run` for command line options. For example to filtering tests.
-	$(GINKGO) \
+	OPERATOR_NAMESPACE=kuadrant-system $(GINKGO) \
 		--coverpkg $(INTEGRATION_COVER_PKGS) \
 		--output-dir $(PROJECT_PATH)/coverage/bare-k8s-integration \
 		--coverprofile cover.out \
@@ -27,7 +27,7 @@ test-bare-k8s-integration: clean-cov generate fmt vet ginkgo ## Requires only ba
 test-gatewayapi-env-integration: clean-cov generate fmt vet ginkgo ## Requires kubernetes cluster with GatewayAPI installed.
 	mkdir -p $(PROJECT_PATH)/coverage/gatewayapi-integration
 #	Check `ginkgo help run` for command line options. For example to filtering tests.
-	$(GINKGO) \
+	OPERATOR_NAMESPACE=kuadrant-system $(GINKGO) \
 		--coverpkg $(INTEGRATION_COVER_PKGS) \
 		--output-dir $(PROJECT_PATH)/coverage/gatewayapi-integration \
 		--coverprofile cover.out \
@@ -45,7 +45,7 @@ test-gatewayapi-env-integration: clean-cov generate fmt vet ginkgo ## Requires k
 test-istio-env-integration: clean-cov generate fmt vet ginkgo ## Requires kubernetes cluster with GatewayAPI and Istio installed.
 	mkdir -p $(PROJECT_PATH)/coverage/istio-integration
 #	Check `ginkgo help run` for command line options. For example to filtering tests.
-	GATEWAYAPI_PROVIDER=istio $(GINKGO) \
+	GATEWAYAPI_PROVIDER=istio OPERATOR_NAMESPACE=kuadrant-system $(GINKGO) \
 		--coverpkg $(INTEGRATION_COVER_PKGS) \
 		--output-dir $(PROJECT_PATH)/coverage/istio-integration \
 		--coverprofile cover.out \
@@ -62,7 +62,7 @@ test-istio-env-integration: clean-cov generate fmt vet ginkgo ## Requires kubern
 test-envoygateway-env-integration: clean-cov generate fmt vet ginkgo ## Requires kubernetes cluster with GatewayAPI and EnvoyGateway installed.
 	mkdir -p $(PROJECT_PATH)/coverage/envoygateway-integration
 #	Check `ginkgo help run` for command line options. For example to filtering tests.
-	GATEWAYAPI_PROVIDER=envoygateway $(GINKGO) \
+	GATEWAYAPI_PROVIDER=envoygateway OPERATOR_NAMESPACE=kuadrant-system $(GINKGO) \
 		--coverpkg $(INTEGRATION_COVER_PKGS) \
 		--output-dir $(PROJECT_PATH)/coverage/envoygateway-integration \
 		--coverprofile cover.out \
@@ -80,7 +80,7 @@ test-envoygateway-env-integration: clean-cov generate fmt vet ginkgo ## Requires
 test-integration: clean-cov generate fmt vet ginkgo ## Requires kubernetes cluster with at least one GatewayAPI provider installed.
 	mkdir -p $(PROJECT_PATH)/coverage/integration
 #	Check `ginkgo help run` for command line options. For example to filtering tests.
-	GATEWAYAPI_PROVIDER=$(GATEWAYAPI_PROVIDER) $(GINKGO) \
+	GATEWAYAPI_PROVIDER=$(GATEWAYAPI_PROVIDER) OPERATOR_NAMESPACE=kuadrant-system $(GINKGO) \
 		--coverpkg $(INTEGRATION_COVER_PKGS) \
 		--output-dir $(PROJECT_PATH)/coverage/integration \
 		--coverprofile cover.out \
diff --git a/pkg/envoygateway/utils.go b/pkg/envoygateway/utils.go
index e06d5fec8..3fb068395 100644
--- a/pkg/envoygateway/utils.go
+++ b/pkg/envoygateway/utils.go
@@ -3,10 +3,21 @@ package envoygateway
 import (
 	egv1alpha1 "github.com/envoyproxy/gateway/api/v1alpha1"
 	"k8s.io/apimachinery/pkg/api/meta"
+	"k8s.io/apimachinery/pkg/runtime/schema"
 
 	kuadrantgatewayapi "github.com/kuadrant/kuadrant-operator/pkg/library/gatewayapi"
 )
 
+var (
+	EnvoyPatchPoliciesResource     = egv1alpha1.SchemeBuilder.GroupVersion.WithResource("envoypatchpolicies")
+	EnvoyExtensionPoliciesResource = egv1alpha1.SchemeBuilder.GroupVersion.WithResource("envoyextensionpolicies")
+	SecurityPoliciesResource       = egv1alpha1.SchemeBuilder.GroupVersion.WithResource("securitypolicies")
+
+	EnvoyPatchPolicyGroupKind     = schema.GroupKind{Group: egv1alpha1.GroupName, Kind: egv1alpha1.KindEnvoyPatchPolicy}
+	EnvoyExtensionPolicyGroupKind = schema.GroupKind{Group: egv1alpha1.GroupName, Kind: egv1alpha1.KindEnvoyExtensionPolicy}
+	SecurityPolicyGroupKind       = schema.GroupKind{Group: egv1alpha1.GroupName, Kind: egv1alpha1.KindSecurityPolicy}
+)
+
 func IsEnvoyPatchPolicyInstalled(restMapper meta.RESTMapper) (bool, error) {
 	return kuadrantgatewayapi.IsCRDInstalled(
 		restMapper,
diff --git a/pkg/istio/utils.go b/pkg/istio/utils.go
index 277b6af98..38fdd7378 100644
--- a/pkg/istio/utils.go
+++ b/pkg/istio/utils.go
@@ -9,12 +9,23 @@ import (
 	istioclientnetworkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3"
 	istioclientgosecurityv1beta1 "istio.io/client-go/pkg/apis/security/v1beta1"
 	"k8s.io/apimachinery/pkg/api/meta"
+	"k8s.io/apimachinery/pkg/runtime/schema"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1"
 
 	kuadrantgatewayapi "github.com/kuadrant/kuadrant-operator/pkg/library/gatewayapi"
 )
 
+var (
+	EnvoyFiltersResource          = istioclientnetworkingv1alpha3.SchemeGroupVersion.WithResource("envoyfilters")
+	WasmPluginsResource           = istioclientgoextensionv1alpha1.SchemeGroupVersion.WithResource("wasmplugins")
+	AuthorizationPoliciesResource = istioclientgosecurityv1beta1.SchemeGroupVersion.WithResource("authorizationpolicies")
+
+	EnvoyFilterGroupKind         = schema.GroupKind{Group: istioclientnetworkingv1alpha3.GroupName, Kind: "EnvoyFilter"}
+	WasmPluginGroupKind          = schema.GroupKind{Group: istioclientgoextensionv1alpha1.GroupName, Kind: "WasmPlugin"}
+	AuthorizationPolicyGroupKind = schema.GroupKind{Group: istioclientgosecurityv1beta1.GroupName, Kind: "AuthorizationPolicy"}
+)
+
 func WorkloadSelectorFromGateway(ctx context.Context, k8sClient client.Client, gateway *gatewayapiv1.Gateway) *istiocommon.WorkloadSelector {
 	logger, _ := logr.FromContext(ctx)
 	gatewayWorkloadSelector, err := kuadrantgatewayapi.GetGatewayWorkloadSelector(ctx, k8sClient, gateway)