diff --git a/bundle/manifests/multicluster-gateway-controller.clusterserviceversion.yaml b/bundle/manifests/multicluster-gateway-controller.clusterserviceversion.yaml index e5a073ed..37cb742a 100644 --- a/bundle/manifests/multicluster-gateway-controller.clusterserviceversion.yaml +++ b/bundle/manifests/multicluster-gateway-controller.clusterserviceversion.yaml @@ -4,7 +4,7 @@ metadata: annotations: alm-examples: '[]' capabilities: Basic Install - createdAt: "2023-12-08T11:41:09Z" + createdAt: "2023-12-21T13:08:31Z" operators.operatorframework.io/builder: operator-sdk-v1.28.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 name: multicluster-gateway-controller.v0.0.0 @@ -349,14 +349,6 @@ spec: - get - list - watch - - apiGroups: - - cluster.open-cluster-management.io - resources: - - managedclusters - verbs: - - get - - list - - watch - apiGroups: - gateway.networking.k8s.io resources: diff --git a/config/policy-controller/rbac/role.yaml b/config/policy-controller/rbac/role.yaml index 81909ce5..6c0ad91f 100644 --- a/config/policy-controller/rbac/role.yaml +++ b/config/policy-controller/rbac/role.yaml @@ -41,14 +41,6 @@ rules: - get - list - watch -- apiGroups: - - cluster.open-cluster-management.io - resources: - - managedclusters - verbs: - - get - - list - - watch - apiGroups: - gateway.networking.k8s.io resources: diff --git a/pkg/controllers/dnspolicy/dnspolicy_controller.go b/pkg/controllers/dnspolicy/dnspolicy_controller.go index bcba1eb3..ebdf574f 100644 --- a/pkg/controllers/dnspolicy/dnspolicy_controller.go +++ b/pkg/controllers/dnspolicy/dnspolicy_controller.go @@ -64,7 +64,6 @@ type DNSPolicyReconciler struct { //+kubebuilder:rbac:groups=kuadrant.io,resources=dnspolicies,verbs=get;list;watch;update;patch;delete //+kubebuilder:rbac:groups=kuadrant.io,resources=dnspolicies/status,verbs=get;update;patch //+kubebuilder:rbac:groups=kuadrant.io,resources=dnspolicies/finalizers,verbs=update -//+kubebuilder:rbac:groups=cluster.open-cluster-management.io,resources=managedclusters,verbs=get;list;watch //+kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=gateways,verbs=get;list;watch;update;patch //+kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=gateways/status,verbs=get;update;patch //+kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=gateways/finalizers,verbs=update diff --git a/pkg/controllers/gateway/gateway_controller.go b/pkg/controllers/gateway/gateway_controller.go index 78acf282..c7686fb6 100644 --- a/pkg/controllers/gateway/gateway_controller.go +++ b/pkg/controllers/gateway/gateway_controller.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "reflect" + "strings" "time" clusterv1 "open-cluster-management.io/api/cluster/v1" @@ -56,10 +57,11 @@ import ( ) const ( - GatewayClusterLabelSelectorAnnotation = "kuadrant.io/gateway-cluster-label-selector" - GatewayClustersAnnotation = "kuadrant.io/gateway-clusters" - GatewayFinalizer = "kuadrant.io/gateway" - ManagedLabel = "kuadrant.io/managed" + LabelPrefix = "kuadrant.io/" + GatewayClusterLabelSelectorAnnotation = LabelPrefix + "gateway-cluster-label-selector" + GatewayClustersAnnotation = LabelPrefix + "gateway-clusters" + GatewayFinalizer = LabelPrefix + "gateway" + ManagedLabel = LabelPrefix + "managed" ) type GatewayPlacer interface { @@ -272,14 +274,24 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct return ctrl.Result{}, reconcileErr } +// reconcileClusterLabels fetches labels from ManagedCluster related to clusters array and adds them to the provided Gateway func (r *GatewayReconciler) reconcileClusterLabels(ctx context.Context, gateway *gatewayapiv1.Gateway, clusters []string) error { - //ToDo Implement me !! - - //Iterate clusters and for each find the ManagedCluster resource, get its labels and convert all "kuadrant.io/" labels to a cluster specific label and add it to the gateway - // (cluster kind-mgc-workload-1 label) kuadrant.io/lb-attribute-custom-weight=AWS = (gateway label) kuadrant.io/kind-mgc-workload-1_lb-attribute-custom-weight=AWS - // (cluster kind-mgc-workload-2 label) kuadrant.io/lb-attribute-geo-code=ES = (gateway label) kuadrant.io/kind-mgc-workload-2_lb-attribute-geo-code=ES - // etc ... + for _, cluster := range clusters { + managedCluster := &clusterv1.ManagedCluster{} + if err := r.Client.Get(ctx, client.ObjectKey{Name: cluster}, managedCluster); client.IgnoreNotFound(err) != nil { + return err + } + for key, value := range managedCluster.Labels { + if strings.Contains(key, LabelPrefix) { + _, attribute, found := strings.Cut(key, "/") + if !found { + continue + } + gateway.Labels[LabelPrefix+cluster+"_"+attribute] = value + } + } + } return nil } diff --git a/test/e2e/gateway_single_spoke_test.go b/test/e2e/gateway_single_spoke_test.go index 9ade9447..fcccb969 100644 --- a/test/e2e/gateway_single_spoke_test.go +++ b/test/e2e/gateway_single_spoke_test.go @@ -373,8 +373,9 @@ var _ = Describe("Gateway single target cluster", func() { if gw.Spec.Listeners == nil { gw.Spec.Listeners = []gatewayapiv1.Listener{} } + patch := client.MergeFrom(gw.DeepCopy()) AddListener("wildcard", testHostnameWildcard, gatewayapiv1.ObjectName(testHostname), gw) - err = tconfig.HubClient().Update(ctx, gw) + err = tconfig.HubClient().Patch(ctx, gw, patch) Expect(err).ToNot(HaveOccurred()) Eventually(func(g Gomega, ctx SpecContext) { checkGateway := &gatewayapiv1.Gateway{} @@ -431,9 +432,10 @@ var _ = Describe("Gateway single target cluster", func() { if gw.Spec.Listeners == nil { gw.Spec.Listeners = []gatewayapiv1.Listener{} } + patch := client.MergeFrom(gw.DeepCopy()) AddListener("other", testHostnameOther, gatewayapiv1.ObjectName(testHostnameOther), gw) Eventually(func(g Gomega, ctx SpecContext) { - err = tconfig.HubClient().Update(ctx, gw) + err = tconfig.HubClient().Patch(ctx, gw, patch) Expect(err).ToNot(HaveOccurred()) checkGateway := &gatewayapiv1.Gateway{} err = tconfig.HubClient().Get(ctx, client.ObjectKey{Name: testID, Namespace: tconfig.HubNamespace()}, checkGateway) @@ -450,6 +452,7 @@ var _ = Describe("Gateway single target cluster", func() { // remove the listener err = tconfig.HubClient().Get(ctx, client.ObjectKey{Name: testID, Namespace: tconfig.HubNamespace()}, gw) Expect(err).ToNot(HaveOccurred()) + patch = client.MergeFrom(gw.DeepCopy()) if gw.Spec.Listeners == nil { gw.Spec.Listeners = []gatewayapiv1.Listener{} @@ -460,7 +463,7 @@ var _ = Describe("Gateway single target cluster", func() { gw.Spec.Listeners = append(gw.Spec.Listeners[:i], gw.Spec.Listeners[i+1:]...) } } - err = tconfig.HubClient().Update(ctx, gw) + err = tconfig.HubClient().Patch(ctx, gw, patch) Expect(err).ToNot(HaveOccurred()) Eventually(func(g Gomega, ctx SpecContext) { secret := &corev1.Secret{} diff --git a/test/policy_integration/dnspolicy_controller_multi_cluster_test.go b/test/policy_integration/dnspolicy_controller_multi_cluster_test.go index 7ecb9b0d..45d633b6 100644 --- a/test/policy_integration/dnspolicy_controller_multi_cluster_test.go +++ b/test/policy_integration/dnspolicy_controller_multi_cluster_test.go @@ -17,6 +17,7 @@ import ( gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" "github.com/Kuadrant/multicluster-gateway-controller/pkg/apis/v1alpha1" + mgcgateway "github.com/Kuadrant/multicluster-gateway-controller/pkg/controllers/gateway" "github.com/Kuadrant/multicluster-gateway-controller/pkg/dns" "github.com/Kuadrant/multicluster-gateway-controller/pkg/utils" testutil "github.com/Kuadrant/multicluster-gateway-controller/test/util" @@ -462,6 +463,194 @@ var _ = Describe("DNSPolicy Multi Cluster", func() { }) + Context("custom geo+weighted", func() { + + BeforeEach(func() { + dnsPolicyBuilder. + WithLoadBalancingWeightedFor(120, []*v1alpha1.CustomWeight{ + { + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "kuadrant.io/lb-attribute-custom-weight": "CAD", + }, + }, + Weight: 100, + }, + { + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "kuadrant.io/lb-attribute-custom-weight": "ES", + }, + }, + Weight: 160, + }, + }). + WithLoadBalancingGeoFor("IE") + dnsPolicy = dnsPolicyBuilder.DNSPolicy + Expect(k8sClient.Create(ctx, dnsPolicy)).To(Succeed()) + + Eventually(func() error { + gateway.Labels = map[string]string{} + gateway.Labels[mgcgateway.LabelPrefix+TestClusterNameOne+"_lb-attribute-custom-weight"] = "CAD" + gateway.Labels[mgcgateway.LabelPrefix+TestClusterNameTwo+"_lb-attribute-custom-weight"] = "ES" + gateway.Labels[mgcgateway.LabelPrefix+TestClusterNameOne+"_lb-attribute-geo-code"] = "CAD" + gateway.Labels[mgcgateway.LabelPrefix+TestClusterNameTwo+"_lb-attribute-geo-code"] = "ES" + return k8sClient.Update(ctx, gateway) + }, TestTimeoutMedium, TestRetryIntervalMedium).ShouldNot(HaveOccurred()) + }) + + It("should create dns records", func() { + Eventually(func(g Gomega, ctx context.Context) { + recordList := &v1alpha1.DNSRecordList{} + err := k8sClient.List(ctx, recordList, &client.ListOptions{Namespace: testNamespace}) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(recordList.Items).To(HaveLen(2)) + g.Expect(recordList.Items).To( + ContainElements( + MatchFields(IgnoreExtras, Fields{ + "ObjectMeta": HaveField("Name", recordName), + "Spec": MatchFields(IgnoreExtras, Fields{ + "ManagedZoneRef": HaveField("Name", "mz-example-com"), + "Endpoints": ConsistOf( + PointTo(MatchFields(IgnoreExtras, Fields{ + "DNSName": Equal("2w705o.lb-" + lbHash + ".test.example.com"), + "Targets": ConsistOf(TestIPAddressTwo), + "RecordType": Equal("A"), + "SetIdentifier": Equal(""), + "RecordTTL": Equal(v1alpha1.TTL(60)), + })), + PointTo(MatchFields(IgnoreExtras, Fields{ + "DNSName": Equal("es.lb-" + lbHash + ".test.example.com"), + "Targets": ConsistOf("2w705o.lb-" + lbHash + ".test.example.com"), + "RecordType": Equal("CNAME"), + "SetIdentifier": Equal("2w705o.lb-" + lbHash + ".test.example.com"), + "RecordTTL": Equal(v1alpha1.TTL(60)), + "ProviderSpecific": Equal(v1alpha1.ProviderSpecific{{Name: "weight", Value: "160"}}), + })), + PointTo(MatchFields(IgnoreExtras, Fields{ + "DNSName": Equal("cad.lb-" + lbHash + ".test.example.com"), + "Targets": ConsistOf("s07c46.lb-" + lbHash + ".test.example.com"), + "RecordType": Equal("CNAME"), + "SetIdentifier": Equal("s07c46.lb-" + lbHash + ".test.example.com"), + "RecordTTL": Equal(v1alpha1.TTL(60)), + "ProviderSpecific": Equal(v1alpha1.ProviderSpecific{{Name: "weight", Value: "100"}}), + })), + PointTo(MatchFields(IgnoreExtras, Fields{ + "DNSName": Equal("s07c46.lb-" + lbHash + ".test.example.com"), + "Targets": ConsistOf(TestIPAddressOne), + "RecordType": Equal("A"), + "SetIdentifier": Equal(""), + "RecordTTL": Equal(v1alpha1.TTL(60)), + })), + PointTo(MatchFields(IgnoreExtras, Fields{ + "DNSName": Equal("lb-" + lbHash + ".test.example.com"), + "Targets": ConsistOf("es.lb-" + lbHash + ".test.example.com"), + "RecordType": Equal("CNAME"), + "SetIdentifier": Equal("ES"), + "RecordTTL": Equal(v1alpha1.TTL(300)), + "ProviderSpecific": Equal(v1alpha1.ProviderSpecific{{Name: "geo-code", Value: "ES"}}), + })), + PointTo(MatchFields(IgnoreExtras, Fields{ + "DNSName": Equal("lb-" + lbHash + ".test.example.com"), + "Targets": ConsistOf("cad.lb-" + lbHash + ".test.example.com"), + "RecordType": Equal("CNAME"), + "SetIdentifier": Equal("CAD"), + "RecordTTL": Equal(v1alpha1.TTL(300)), + "ProviderSpecific": Equal(v1alpha1.ProviderSpecific{{Name: "geo-code", Value: "CAD"}}), + })), + PointTo(MatchFields(IgnoreExtras, Fields{ + "DNSName": Equal("lb-" + lbHash + ".test.example.com"), + "Targets": ConsistOf("cad.lb-" + lbHash + ".test.example.com"), + "RecordType": Equal("CNAME"), + "SetIdentifier": Equal("default"), + "RecordTTL": Equal(v1alpha1.TTL(300)), + "ProviderSpecific": Equal(v1alpha1.ProviderSpecific{{Name: "geo-code", Value: "*"}}), + })), + PointTo(MatchFields(IgnoreExtras, Fields{ + "DNSName": Equal(TestHostOne), + "Targets": ConsistOf("lb-" + lbHash + ".test.example.com"), + "RecordType": Equal("CNAME"), + "SetIdentifier": Equal(""), + "RecordTTL": Equal(v1alpha1.TTL(300)), + })), + ), + }), + }), + MatchFields(IgnoreExtras, Fields{ + "ObjectMeta": HaveField("Name", wildcardRecordName), + "Spec": MatchFields(IgnoreExtras, Fields{ + "ManagedZoneRef": HaveField("Name", "mz-example-com"), + "Endpoints": ConsistOf( + PointTo(MatchFields(IgnoreExtras, Fields{ + "DNSName": Equal("2w705o.lb-" + lbHash + ".example.com"), + "Targets": ConsistOf(TestIPAddressTwo), + "RecordType": Equal("A"), + "SetIdentifier": Equal(""), + "RecordTTL": Equal(v1alpha1.TTL(60)), + })), + PointTo(MatchFields(IgnoreExtras, Fields{ + "DNSName": Equal("es.lb-" + lbHash + ".example.com"), + "Targets": ConsistOf("2w705o.lb-" + lbHash + ".example.com"), + "RecordType": Equal("CNAME"), + "SetIdentifier": Equal("2w705o.lb-" + lbHash + ".example.com"), + "RecordTTL": Equal(v1alpha1.TTL(60)), + "ProviderSpecific": Equal(v1alpha1.ProviderSpecific{{Name: "weight", Value: "160"}}), + })), + PointTo(MatchFields(IgnoreExtras, Fields{ + "DNSName": Equal("cad.lb-" + lbHash + ".example.com"), + "Targets": ConsistOf("s07c46.lb-" + lbHash + ".example.com"), + "RecordType": Equal("CNAME"), + "SetIdentifier": Equal("s07c46.lb-" + lbHash + ".example.com"), + "RecordTTL": Equal(v1alpha1.TTL(60)), + "ProviderSpecific": Equal(v1alpha1.ProviderSpecific{{Name: "weight", Value: "100"}}), + })), + PointTo(MatchFields(IgnoreExtras, Fields{ + "DNSName": Equal("s07c46.lb-" + lbHash + ".example.com"), + "Targets": ConsistOf(TestIPAddressOne), + "RecordType": Equal("A"), + "SetIdentifier": Equal(""), + "RecordTTL": Equal(v1alpha1.TTL(60)), + })), + PointTo(MatchFields(IgnoreExtras, Fields{ + "DNSName": Equal("lb-" + lbHash + ".example.com"), + "Targets": ConsistOf("cad.lb-" + lbHash + ".example.com"), + "RecordType": Equal("CNAME"), + "SetIdentifier": Equal("CAD"), + "RecordTTL": Equal(v1alpha1.TTL(300)), + "ProviderSpecific": Equal(v1alpha1.ProviderSpecific{{Name: "geo-code", Value: "CAD"}}), + })), + PointTo(MatchFields(IgnoreExtras, Fields{ + "DNSName": Equal("lb-" + lbHash + ".example.com"), + "Targets": ConsistOf("es.lb-" + lbHash + ".example.com"), + "RecordType": Equal("CNAME"), + "SetIdentifier": Equal("ES"), + "RecordTTL": Equal(v1alpha1.TTL(300)), + "ProviderSpecific": Equal(v1alpha1.ProviderSpecific{{Name: "geo-code", Value: "ES"}}), + })), + PointTo(MatchFields(IgnoreExtras, Fields{ + "DNSName": Equal("lb-" + lbHash + ".example.com"), + "Targets": ConsistOf("cad.lb-" + lbHash + ".example.com"), + "RecordType": Equal("CNAME"), + "SetIdentifier": Equal("default"), + "RecordTTL": Equal(v1alpha1.TTL(300)), + "ProviderSpecific": Equal(v1alpha1.ProviderSpecific{{Name: "geo-code", Value: "*"}}), + })), + PointTo(MatchFields(IgnoreExtras, Fields{ + "DNSName": Equal(TestHostWildcard), + "Targets": ConsistOf("lb-" + lbHash + ".example.com"), + "RecordType": Equal("CNAME"), + "SetIdentifier": Equal(""), + "RecordTTL": Equal(v1alpha1.TTL(300)), + })), + ), + }), + }), + )) + }, TestTimeoutMedium, TestRetryIntervalMedium, ctx).Should(Succeed()) + }) + + }) + }) })