From 1293193331fcaf06ea8d6ad680f984ce204ab90d Mon Sep 17 00:00:00 2001 From: Tomash Sidei <43379202+tomashibm@users.noreply.github.com> Date: Wed, 11 May 2022 10:57:17 +0300 Subject: [PATCH] Allow backends selection from other namespaces (#66) * Implement backends selector from multiple namespaces. An added `namespaces` field allows to specify the namespace(s) where the operator should look for backend pods. By default looks in the same namespace where the VarnishCluster is deployed. Signed-off-by: Tomash Sidei * Minor doc fix -- sneaking into Tomash's PR Co-authored-by: Craig Ingram --- api/v1alpha1/varnishcluster_types.go | 1 + api/v1alpha1/zz_generated.deepcopy.go | 7 +- cmd/varnish-controller/main.go | 1 - .../caching.ibm.com_varnishclusters.yaml | 4 + docs/quick-start.md | 4 +- docs/varnish-cluster-configuration.md | 5 +- .../controller/varnishcluster_clusterrole.go | 15 + .../controller/varnishcluster_role.go | 17 +- .../controller/controller.go | 74 +- pkg/varnishcontroller/controller/endpoints.go | 78 +- .../controller/endpoints_test.go | 285 ++++++ pkg/varnishcontroller/predicates/debug.go | 39 + .../predicates/label_matcher.go | 63 ++ .../predicates/label_matcher_test.go | 116 +++ .../predicates/namespaces_matcher.go | 61 ++ pkg/varnishcontroller/predicates/predicate.go | 166 ---- .../predicates/predicate_test.go | 904 ------------------ .../predicates/varnishcluster.go | 82 ++ tests/main_test.go | 10 +- varnish-operator/crds/varnishcluster.yaml | 4 + 20 files changed, 786 insertions(+), 1150 deletions(-) create mode 100644 pkg/varnishcontroller/controller/endpoints_test.go create mode 100644 pkg/varnishcontroller/predicates/debug.go create mode 100644 pkg/varnishcontroller/predicates/label_matcher.go create mode 100644 pkg/varnishcontroller/predicates/label_matcher_test.go create mode 100644 pkg/varnishcontroller/predicates/namespaces_matcher.go delete mode 100644 pkg/varnishcontroller/predicates/predicate.go delete mode 100644 pkg/varnishcontroller/predicates/predicate_test.go create mode 100644 pkg/varnishcontroller/predicates/varnishcluster.go diff --git a/api/v1alpha1/varnishcluster_types.go b/api/v1alpha1/varnishcluster_types.go index b6d3a6a0..c049a1f8 100644 --- a/api/v1alpha1/varnishcluster_types.go +++ b/api/v1alpha1/varnishcluster_types.go @@ -195,6 +195,7 @@ type VarnishClusterBackend struct { Selector map[string]string `json:"selector,omitempty"` // +kubebuilder:validation:Required Port *intstr.IntOrString `json:"port,omitempty"` + Namespaces []string `json:"namespaces,omitempty"` ZoneBalancing *VarnishClusterBackendZoneBalancing `json:"zoneBalancing,omitempty"` } diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 42eee975..22632825 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -7,7 +7,7 @@ package v1alpha1 import ( appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" + "k8s.io/api/core/v1" "k8s.io/api/policy/v1beta1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" @@ -136,6 +136,11 @@ func (in *VarnishClusterBackend) DeepCopyInto(out *VarnishClusterBackend) { *out = new(intstr.IntOrString) **out = **in } + if in.Namespaces != nil { + in, out := &in.Namespaces, &out.Namespaces + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.ZoneBalancing != nil { in, out := &in.ZoneBalancing, &out.ZoneBalancing *out = new(VarnishClusterBackendZoneBalancing) diff --git a/cmd/varnish-controller/main.go b/cmd/varnish-controller/main.go index 7395a4df..3251d09a 100644 --- a/cmd/varnish-controller/main.go +++ b/cmd/varnish-controller/main.go @@ -73,7 +73,6 @@ func main() { controllerMetrics.Registry.MustRegister(vMetrics.VCLCompilationError) mgr, err := ctrl.NewManager(clientConfig, ctrl.Options{ - Namespace: varnishControllerConfig.Namespace, Scheme: scheme, HealthProbeBindAddress: fmt.Sprintf(":%d", v1alpha1.HealthCheckPort), MetricsBindAddress: fmt.Sprintf(":%d", v1alpha1.VarnishControllerMetricsPort), diff --git a/config/crd/bases/caching.ibm.com_varnishclusters.yaml b/config/crd/bases/caching.ibm.com_varnishclusters.yaml index 0af732a3..64da0455 100644 --- a/config/crd/bases/caching.ibm.com_varnishclusters.yaml +++ b/config/crd/bases/caching.ibm.com_varnishclusters.yaml @@ -864,6 +864,10 @@ spec: type: object backend: properties: + namespaces: + items: + type: string + type: array port: anyOf: - type: integer diff --git a/docs/quick-start.md b/docs/quick-start.md index a1ef7820..acd96167 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -37,7 +37,7 @@ varnish-operator-fd96f48f-gn6mc 1/1 Running 0 40s 1. Create a simple backend that will be cached by Varnish: ```bash - $ kubectl create deployment nginx-backend --image nginx -n varnish-cluster + $ kubectl create deployment nginx-backend --image nginx -n varnish-cluster --port=80 deployment.apps/nginx-backend created $ kubectl get deployment -n varnish-cluster nginx-backend --show-labels #get pod labels, they will be used to identify your backend pods NAME READY UP-TO-DATE AVAILABLE AGE LABELS @@ -85,7 +85,7 @@ You can check if all works by doing `kubectl port-forward` and checking the serv Port forward your service: ```bash -$ kubectl port-forward -n varnish-cluster service/varnishcluster-example 8080:80 +$ kubectl port-forward -n varnish-cluster service/varnishcluster-example 8080:6081 Forwarding from 127.0.0.1:8080 -> 6081 Forwarding from [::1]:8080 -> 6081 ... diff --git a/docs/varnish-cluster-configuration.md b/docs/varnish-cluster-configuration.md index 167e0dba..5e93e692 100644 --- a/docs/varnish-cluster-configuration.md +++ b/docs/varnish-cluster-configuration.md @@ -3,8 +3,9 @@ | Field | Description | Is Required | |-------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------| | `affinity ` | [Affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) settings for the pods. It allows you to configure onto which nodes Varnish pods should prefer being scheduled. | `optional` | -| `backend.port ` | The port of the backend pods being cached by Varnish. Can be port name or port number. | `optional` | -| `backend.selector ` | The selector used to identify the backend Pods. | `optional` | +| `backend.namespaces ` | Namespace(s) to look for backend pods. By default - namespace the VarnishCluster is deployed to. | `required` | +| `backend.port ` | The port of the backend pods being cached by Varnish. Can be port name or port number. | `required` | +| `backend.selector ` | The selector used to identify the backend Pods. | `required` | | `backend.zoneBalancing ` | Controls Varnish backend topology aware routing which can assign weights to backends according to their geographical location. | `optional` | | `backend.zoneBalancing.type ` | Varnish backend zone-balancing type. Accepted values: `disabled`, `auto`, `thresholds` | `optional` | | `backend.zoneBalancing.thresholds ` | Array of thresholds objects to determine condition and respective weights to be assigned to backends: `threshold`, `local` - local backend weight, `remote` - remote backend weight | `optional` | diff --git a/pkg/varnishcluster/controller/varnishcluster_clusterrole.go b/pkg/varnishcluster/controller/varnishcluster_clusterrole.go index 5552fc90..f7698233 100644 --- a/pkg/varnishcluster/controller/varnishcluster_clusterrole.go +++ b/pkg/varnishcluster/controller/varnishcluster_clusterrole.go @@ -33,6 +33,21 @@ func (r *ReconcileVarnishCluster) reconcileClusterRole(ctx context.Context, inst Resources: []string{"nodes"}, Verbs: []string{"list", "watch"}, }, + { + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"list", "watch", "get", "update"}, + }, + { + APIGroups: []string{"caching.ibm.com"}, + Resources: []string{"varnishclusters"}, + Verbs: []string{"list", "watch"}, + }, + { + APIGroups: []string{""}, + Resources: []string{"secrets", "configmaps"}, + Verbs: []string{"list", "get", "watch"}, + }, }, } diff --git a/pkg/varnishcluster/controller/varnishcluster_role.go b/pkg/varnishcluster/controller/varnishcluster_role.go index 35157d2f..ce9c4b12 100644 --- a/pkg/varnishcluster/controller/varnishcluster_role.go +++ b/pkg/varnishcluster/controller/varnishcluster_role.go @@ -28,12 +28,7 @@ func (r *ReconcileVarnishCluster) reconcileRole(ctx context.Context, instance *v Rules: []rbac.PolicyRule{ { APIGroups: []string{""}, - Resources: []string{"endpoints", "configmaps"}, - Verbs: []string{"list", "watch"}, - }, - { - APIGroups: []string{"caching.ibm.com"}, - Resources: []string{"varnishclusters"}, + Resources: []string{"endpoints"}, Verbs: []string{"list", "watch"}, }, { @@ -41,16 +36,6 @@ func (r *ReconcileVarnishCluster) reconcileRole(ctx context.Context, instance *v Resources: []string{"events"}, Verbs: []string{"create", "patch"}, }, - { - APIGroups: []string{""}, - Resources: []string{"pods"}, - Verbs: []string{"list", "get", "watch", "update"}, - }, - { - APIGroups: []string{""}, - Resources: []string{"secrets"}, - Verbs: []string{"get", "watch"}, - }, }, } diff --git a/pkg/varnishcontroller/controller/controller.go b/pkg/varnishcontroller/controller/controller.go index 3cc95529..55f04eac 100644 --- a/pkg/varnishcontroller/controller/controller.go +++ b/pkg/varnishcontroller/controller/controller.go @@ -5,6 +5,8 @@ import ( "strings" "time" + ctrlBuilder "sigs.k8s.io/controller-runtime/pkg/builder" + "github.com/ibm/varnish-operator/api/v1alpha1" "github.com/ibm/varnish-operator/pkg/logger" "github.com/ibm/varnish-operator/pkg/varnishcontroller/config" @@ -42,14 +44,24 @@ type PodInfo struct { // SetupVarnishReconciler creates a new VarnishCluster Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller // and Start it when the Manager is Started. func SetupVarnishReconciler(mgr manager.Manager, cfg *config.Config, varnish varnishadm.VarnishAdministrator, metrics *metrics.VarnishControllerMetrics, logr *logger.Logger) error { + backendsLabels, err := labels.ConvertSelectorToLabelsMap(cfg.EndpointSelectorString) + if err != nil { + return err + } + + backendNamespacePredicate := predicates.NewNamespacesMatcherPredicate([]string{cfg.Namespace}, logr) + backendLabelsPredicate := predicates.NewLabelMatcherPredicate(backendsLabels.AsSelector(), logr) + r := &ReconcileVarnish{ - config: cfg, - logger: logr, - Client: mgr.GetClient(), - scheme: mgr.GetScheme(), - varnish: varnish, - eventHandler: events.NewEventHandler(mgr.GetEventRecorderFor(events.EventRecorderName), cfg.PodName), - metrics: metrics, + config: cfg, + logger: logr, + Client: mgr.GetClient(), + scheme: mgr.GetScheme(), + varnish: varnish, + eventHandler: events.NewEventHandler(mgr.GetEventRecorderFor(events.EventRecorderName), cfg.PodName), + metrics: metrics, + backendsSelectorPredicate: backendLabelsPredicate, + backendsNamespacePredicate: backendNamespacePredicate, } podMapFunc := handler.EnqueueRequestsFromMapFunc( @@ -65,22 +77,31 @@ func SetupVarnishReconciler(mgr manager.Manager, cfg *config.Config, varnish var builder := ctrl.NewControllerManagedBy(mgr) builder.Named("varnish-controller") - builder.For(&v1alpha1.VarnishCluster{}) - builder.Watches(&source.Kind{Type: &v1.Endpoints{}}, podMapFunc) + builder.For(&v1alpha1.VarnishCluster{}, ctrlBuilder.WithPredicates(predicates.NewVarnishClusterPredicate(r.config.VarnishClusterUID, logr))) - backendsLabels, err := labels.ConvertSelectorToLabelsMap(cfg.EndpointSelectorString) - if err != nil { - return err - } + builder.Watches( + &source.Kind{Type: &v1.Pod{}}, + podMapFunc, + ctrlBuilder.WithPredicates( + backendNamespacePredicate, + backendLabelsPredicate, + ), + ) varnishPodsSelector := labels.SelectorFromSet(labels.Set{ v1alpha1.LabelVarnishOwner: cfg.VarnishClusterName, v1alpha1.LabelVarnishComponent: v1alpha1.VarnishComponentCacheService, v1alpha1.LabelVarnishUID: string(cfg.VarnishClusterUID), }) - - endpointsSelectors := []labels.Selector{labels.SelectorFromSet(backendsLabels), varnishPodsSelector} - builder.WithEventFilter(predicates.NewVarnishControllerPredicate(cfg.VarnishClusterUID, endpointsSelectors, nil)) + builder.Watches( + &source.Kind{Type: &v1.Pod{}}, + podMapFunc, + ctrlBuilder.WithPredicates( + predicates.NewNamespacesMatcherPredicate([]string{cfg.Namespace}, logr), + predicates.NewLabelMatcherPredicate(varnishPodsSelector, logr), + ), + ) + //builder.WithEventFilter(predicates.NewDebugPredicate(logr)) return builder.Complete(r) } @@ -89,12 +110,14 @@ var _ reconcile.Reconciler = &ReconcileVarnish{} type ReconcileVarnish struct { client.Client - config *config.Config - logger *logger.Logger - scheme *runtime.Scheme - eventHandler *events.EventHandler - varnish varnishadm.VarnishAdministrator - metrics *metrics.VarnishControllerMetrics + config *config.Config + logger *logger.Logger + scheme *runtime.Scheme + eventHandler *events.EventHandler + varnish varnishadm.VarnishAdministrator + metrics *metrics.VarnishControllerMetrics + backendsNamespacePredicate *predicates.NamespacesMatcherPredicate + backendsSelectorPredicate *predicates.LabelMatcherPredicate } func (r *ReconcileVarnish) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) { @@ -135,6 +158,13 @@ func (r *ReconcileVarnish) reconcileWithContext(ctx context.Context, request rec r.scheme.Default(vc) + if len(vc.Spec.Backend.Namespaces) > 0 { + r.backendsNamespacePredicate.Namespaces = vc.Spec.Backend.Namespaces + } else { + r.backendsNamespacePredicate.Namespaces = []string{r.config.Namespace} + } + r.backendsSelectorPredicate.Selector = labels.SelectorFromSet(vc.Spec.Backend.Selector) + varnishPort := int32(v1alpha1.VarnishPort) entrypointFileName := *vc.Spec.VCL.EntrypointFileName diff --git a/pkg/varnishcontroller/controller/endpoints.go b/pkg/varnishcontroller/controller/endpoints.go index 88af997a..99726951 100644 --- a/pkg/varnishcontroller/controller/endpoints.go +++ b/pkg/varnishcontroller/controller/endpoints.go @@ -15,16 +15,15 @@ import ( ) func (r *ReconcileVarnish) getBackendEndpoints(ctx context.Context, vc *v1alpha1.VarnishCluster) ([]PodInfo, int32, float64, float64, error) { - varnishNodeLabels, err := r.getNodeLabels(ctx, r.config.NodeName) if err != nil { return nil, 0, 0, 0, errors.WithStack(err) } // Check for deprecated topology labels - zoneLabel := "topology.kubernetes.io/zone" - if _, ok := varnishNodeLabels["failure-domain.beta.kubernetes.io/zone"]; ok { - zoneLabel = "failure-domain.beta.kubernetes.io/zone" + zoneLabel := v1.LabelTopologyZone + if _, ok := varnishNodeLabels[v1.LabelFailureDomainBetaZone]; ok { + zoneLabel = v1.LabelFailureDomainBetaZone } currentZone := varnishNodeLabels[zoneLabel] @@ -32,7 +31,12 @@ func (r *ReconcileVarnish) getBackendEndpoints(ctx context.Context, vc *v1alpha1 actualLocalWeight := 1.0 actualRemoteWeight := 1.0 - backendList, portNumber, err := r.getPodsInfo(ctx, r.config.EndpointSelector, *vc.Spec.Backend.Port) + namespaces := []string{r.config.Namespace} + if len(vc.Spec.Backend.Namespaces) > 0 { + namespaces = vc.Spec.Backend.Namespaces + } + + backendList, portNumber, err := r.getPodsInfo(ctx, namespaces, labels.SelectorFromSet(vc.Spec.Backend.Selector), *vc.Spec.Backend.Port) if err != nil { return nil, 0, 0, 0, errors.WithStack(err) } @@ -95,11 +99,10 @@ func (r *ReconcileVarnish) getBackendEndpoints(ctx context.Context, vc *v1alpha1 } func (r *ReconcileVarnish) getVarnishEndpoints(ctx context.Context, vc *v1alpha1.VarnishCluster) ([]PodInfo, error) { - - labels := labels.SelectorFromSet(vclabels.CombinedComponentLabels(vc, v1alpha1.VarnishComponentCacheService)) + varnishLables := labels.SelectorFromSet(vclabels.CombinedComponentLabels(vc, v1alpha1.VarnishComponentVarnish)) varnishPort := intstr.FromString(v1alpha1.VarnishPortName) - varnishEndpoints, _, err := r.getPodsInfo(ctx, labels, varnishPort) + varnishEndpoints, _, err := r.getPodsInfo(ctx, []string{r.config.Namespace}, varnishLables, varnishPort) if err != nil { return nil, errors.WithStack(err) } @@ -107,35 +110,47 @@ func (r *ReconcileVarnish) getVarnishEndpoints(ctx context.Context, vc *v1alpha1 return varnishEndpoints, nil } -func (r *ReconcileVarnish) getPodsInfo(ctx context.Context, labels labels.Selector, validPort intstr.IntOrString) ([]PodInfo, int32, error) { - - found := &v1.EndpointsList{} - err := r.List(ctx, found, client.MatchingLabelsSelector{Selector: labels}, client.InNamespace(r.config.Namespace)) - if err != nil { - return nil, 0, errors.Wrapf(err, "could not retrieve endpoints from namespace %s with labels %s", r.config.Namespace, labels.String()) - } +func (r *ReconcileVarnish) getPodsInfo(ctx context.Context, namespaces []string, labels labels.Selector, validPort intstr.IntOrString) ([]PodInfo, int32, error) { + var pods []v1.Pod + for _, namespace := range namespaces { + listOptions := []client.ListOption{ + client.MatchingLabelsSelector{Selector: labels}, + client.InNamespace(namespace), + } + found := &v1.PodList{} + err := r.List(ctx, found, listOptions...) + if err != nil { + return nil, 0, errors.Wrapf(err, "could not retrieve endpoints from namespace %v with labels %s", namespaces, labels.String()) + } - if len(found.Items) == 0 { - return nil, 0, errors.Errorf("no endpoints from namespace %s matching labels %s", r.config.Namespace, labels.String()) + pods = append(pods, found.Items...) } var portNumber int32 var podInfoList []PodInfo - for _, endpoints := range found.Items { - for _, endpoint := range endpoints.Subsets { - for _, address := range append(endpoint.Addresses, endpoint.NotReadyAddresses...) { - for _, port := range endpoint.Ports { - if port.Port == validPort.IntVal || port.Name == validPort.StrVal { - portNumber = port.Port - var backendWeight float64 = 1.0 - nodeLabels, err := r.getNodeLabels(ctx, *address.NodeName) - if err != nil { - return nil, 0, errors.WithStack(err) - } - b := PodInfo{IP: address.IP, NodeLabels: nodeLabels, PodName: address.TargetRef.Name, Weight: backendWeight} - podInfoList = append(podInfoList, b) - break + + if len(pods) == 0 { + r.logger.Infof("No pods found by labels %v in namespace(s) %v", labels.String(), namespaces) + return podInfoList, 0, nil + } + + for _, pod := range pods { + if len(pod.Status.PodIP) == 0 || len(pod.Spec.NodeName) == 0 { + continue + } + + for _, container := range pod.Spec.Containers { + for _, containerPort := range container.Ports { + if containerPort.ContainerPort == validPort.IntVal || containerPort.Name == validPort.StrVal { + portNumber = containerPort.ContainerPort + var backendWeight = 1.0 + nodeLabels, err := r.getNodeLabels(ctx, pod.Spec.NodeName) + if err != nil { + return nil, 0, errors.WithStack(err) } + b := PodInfo{IP: pod.Status.PodIP, NodeLabels: nodeLabels, PodName: pod.Name, Weight: backendWeight} + podInfoList = append(podInfoList, b) + break } } } @@ -185,7 +200,6 @@ func calculateBackendRatio(backends []PodInfo, currentZone string, zoneLabel str } func checkMultizone(endpoints []PodInfo, zoneLabel string, currentZone string) bool { - for _, b := range endpoints { if _, ok := b.NodeLabels[zoneLabel]; ok { if b.NodeLabels[zoneLabel] != currentZone { diff --git a/pkg/varnishcontroller/controller/endpoints_test.go b/pkg/varnishcontroller/controller/endpoints_test.go new file mode 100644 index 00000000..b8d9414f --- /dev/null +++ b/pkg/varnishcontroller/controller/endpoints_test.go @@ -0,0 +1,285 @@ +package controller + +import ( + "context" + "testing" + + "github.com/ibm/varnish-operator/pkg/logger" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/onsi/gomega" + + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/ibm/varnish-operator/api/v1alpha1" + "github.com/ibm/varnish-operator/pkg/varnishcontroller/config" + "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func TestGetBackendsEndpoint(t *testing.T) { + baseScheme := scheme.Scheme + utilruntime.Must(clientgoscheme.AddToScheme(baseScheme)) + utilruntime.Must(v1alpha1.AddToScheme(baseScheme)) + backendPortNumber := intstr.FromInt(4314) + backendPortName := intstr.FromString("backend") + local1, remote1, threshold1 := 30, 70, 70 + local2, remote2, threshold2 := 10, 40, 30 + + tcs := []struct { + name string + vc *v1alpha1.VarnishCluster + podNamespace string + podNode string + k8sObjects []client.Object + k8sLists []client.ObjectList + expectedPodNumber int32 + expectedPodInfo []PodInfo + expectedErr error + }{ + { + name: "one backend", + vc: &v1alpha1.VarnishCluster{ + Spec: v1alpha1.VarnishClusterSpec{ + Backend: &v1alpha1.VarnishClusterBackend{ + Selector: map[string]string{"app": "backend"}, + Port: &backendPortNumber, + }, + }, + }, + podNamespace: "ns1", + podNode: "node1", + k8sObjects: []client.Object{ + createTestNode("node1", map[string]string{v1.LabelTopologyZone: "zone1"}), + }, + k8sLists: []client.ObjectList{ + &v1.PodList{ + Items: []v1.Pod{ + createTestPod("backend1", "ns1", "10.24.12.2", "node1", + map[string]string{"app": "backend"}, + []v1.ContainerPort{{Name: "backend", ContainerPort: backendPortNumber.IntVal}}, + ), + }, + }, + }, + expectedPodNumber: backendPortNumber.IntVal, + expectedPodInfo: []PodInfo{ + {IP: "10.24.12.2", NodeLabels: map[string]string{v1.LabelTopologyZone: "zone1"}, PodName: "backend1", Weight: 1}, + }, + expectedErr: nil, + }, + { + name: "multiple backends in multiple namespaces", + vc: &v1alpha1.VarnishCluster{ + Spec: v1alpha1.VarnishClusterSpec{ + Backend: &v1alpha1.VarnishClusterBackend{ + Selector: map[string]string{"app": "backend"}, + Port: &backendPortName, + Namespaces: []string{"ns1", "ns2"}, + ZoneBalancing: &v1alpha1.VarnishClusterBackendZoneBalancing{ + Type: v1alpha1.VarnishClusterBackendZoneBalancingTypeAuto, + }, + }, + }, + }, + podNamespace: "ns1", + podNode: "node1", + k8sObjects: []client.Object{ + createTestNode("node1", map[string]string{v1.LabelTopologyZone: "zone1"}), + createTestNode("node2", map[string]string{v1.LabelTopologyZone: "zone2"}), + }, + k8sLists: []client.ObjectList{ + &v1.PodList{ + Items: []v1.Pod{ + createTestPod("backend1", "ns1", "10.24.12.2", "node1", + map[string]string{"app": "backend"}, + []v1.ContainerPort{{Name: "backend", ContainerPort: backendPortNumber.IntVal}}, + ), + createTestPod("backend2", "ns2", "10.24.12.3", "node2", + map[string]string{"app": "backend"}, + []v1.ContainerPort{{Name: "backend", ContainerPort: backendPortNumber.IntVal}}, + ), + }, + }, + }, + expectedPodNumber: backendPortNumber.IntVal, + expectedPodInfo: []PodInfo{ + {IP: "10.24.12.2", NodeLabels: map[string]string{v1.LabelTopologyZone: "zone1"}, PodName: "backend1", Weight: 10}, + {IP: "10.24.12.3", NodeLabels: map[string]string{v1.LabelTopologyZone: "zone2"}, PodName: "backend2", Weight: 1}, + }, + expectedErr: nil, + }, + { + name: "threshold type of zone balancing", + vc: &v1alpha1.VarnishCluster{ + Spec: v1alpha1.VarnishClusterSpec{ + Backend: &v1alpha1.VarnishClusterBackend{ + Selector: map[string]string{"app": "backend"}, + Port: &backendPortName, + Namespaces: []string{"ns1", "ns2"}, + ZoneBalancing: &v1alpha1.VarnishClusterBackendZoneBalancing{ + Type: v1alpha1.VarnishClusterBackendZoneBalancingTypeThresholds, + Thresholds: []v1alpha1.VarnishClusterBackendZoneBalancingThreshold{ + { + Local: &local1, + Remote: &remote1, + Threshold: &threshold1, + }, + { + Local: &local2, + Remote: &remote2, + Threshold: &threshold2, + }, + }, + }, + }, + }, + }, + podNamespace: "ns1", + podNode: "node1", + k8sObjects: []client.Object{ + createTestNode("node1", map[string]string{v1.LabelTopologyZone: "zone1"}), + createTestNode("node2", map[string]string{v1.LabelTopologyZone: "zone2"}), + }, + k8sLists: []client.ObjectList{ + &v1.PodList{ + Items: []v1.Pod{ + createTestPod("backend1", "ns1", "10.24.12.2", "node1", + map[string]string{"app": "backend"}, + []v1.ContainerPort{{Name: "backend", ContainerPort: backendPortNumber.IntVal}}, + ), + createTestPod("backend2", "ns2", "10.24.12.3", "node2", + map[string]string{"app": "backend"}, + []v1.ContainerPort{{Name: "backend", ContainerPort: backendPortNumber.IntVal}}, + ), + createTestPod("backend3", "ns2", "10.24.12.5", "node2", + map[string]string{"app": "backend1"}, //should not be selected + []v1.ContainerPort{{Name: "backend", ContainerPort: backendPortNumber.IntVal}}, + ), + createTestPod("backend4", "ns2", "", "", //not scheduled yet + map[string]string{"app": "backend"}, + []v1.ContainerPort{{Name: "backend", ContainerPort: backendPortNumber.IntVal}}, + ), + }, + }, + }, + expectedPodNumber: backendPortNumber.IntVal, + expectedPodInfo: []PodInfo{ + {IP: "10.24.12.2", NodeLabels: map[string]string{v1.LabelTopologyZone: "zone1"}, PodName: "backend1", Weight: 30}, + {IP: "10.24.12.3", NodeLabels: map[string]string{v1.LabelTopologyZone: "zone2"}, PodName: "backend2", Weight: 70}, + }, + expectedErr: nil, + }, + { + name: "no backends", + vc: &v1alpha1.VarnishCluster{ + Spec: v1alpha1.VarnishClusterSpec{ + Backend: &v1alpha1.VarnishClusterBackend{ + Selector: map[string]string{"app": "backend"}, + Port: &backendPortName, + }, + }, + }, + podNamespace: "ns1", + podNode: "node1", + k8sObjects: []client.Object{ + createTestNode("node1", map[string]string{v1.LabelTopologyZone: "zone1"}), + createTestNode("node2", map[string]string{v1.LabelTopologyZone: "zone2"}), + }, + k8sLists: []client.ObjectList{ + &v1.PodList{ + Items: []v1.Pod{ + createTestPod("backend1", "ns1", "10.24.12.2", "node1", + map[string]string{"app": "backend4"}, + []v1.ContainerPort{{Name: "backend", ContainerPort: backendPortNumber.IntVal}}, + ), + createTestPod("backend2", "ns2", "10.24.12.3", "node2", + map[string]string{"app": "backend3"}, + []v1.ContainerPort{{Name: "backend", ContainerPort: backendPortNumber.IntVal}}, + ), + }, + }, + }, + expectedPodNumber: 0, + expectedPodInfo: nil, + expectedErr: nil, + }, + } + + for _, tc := range tcs { + t.Log(tc.name) + clientBuilder := fake.NewClientBuilder().WithScheme(baseScheme) + clientBuilder.WithLists(tc.k8sLists...) + clientBuilder.WithObjects(tc.k8sObjects...) + tClient := clientBuilder.Build() + + reconciler := &ReconcileVarnish{ + config: &config.Config{ + Namespace: tc.podNamespace, + NodeName: tc.podNode, + }, + Client: tClient, + logger: logger.NewNopLogger(), + } + podInfo, portNumber, _, _, err := reconciler.getBackendEndpoints(context.Background(), tc.vc) + + a := gomega.NewGomegaWithT(t) + if tc.expectedErr == nil { + a.Expect(err).To(gomega.BeNil()) + } else { + a.Expect(err.Error()).To(gomega.Equal(tc.expectedErr.Error())) + } + a.Expect(podInfo).To(gomega.Equal(tc.expectedPodInfo)) + a.Expect(portNumber).To(gomega.Equal(tc.expectedPodNumber)) + + } +} + +func createTestPod(name, namespace, ip, nodeName string, labels map[string]string, ports []v1.ContainerPort) v1.Pod { + return v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: labels, + }, + Spec: v1.PodSpec{ + NodeName: nodeName, + Containers: []v1.Container{ + { + Ports: ports, + }, + }, + }, + Status: v1.PodStatus{ + PodIP: ip, + }, + } +} + +func createTestNode(name string, labels map[string]string) *v1.Node { + node := v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Status: v1.NodeStatus{ + Addresses: []v1.NodeAddress{ + { + Type: v1.NodeInternalIP, + Address: "192.24.51.2", + }, + }, + }, + } + + if len(labels) > 0 { + node.Labels = labels + } + + return &node +} diff --git a/pkg/varnishcontroller/predicates/debug.go b/pkg/varnishcontroller/predicates/debug.go new file mode 100644 index 00000000..438f2bb2 --- /dev/null +++ b/pkg/varnishcontroller/predicates/debug.go @@ -0,0 +1,39 @@ +package predicates + +import ( + "github.com/ibm/varnish-operator/pkg/logger" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +var _ predicate.Predicate = &LabelMatcherPredicate{} + +type debugPredicate struct { + logger *logger.Logger +} + +func NewDebugPredicate(logr *logger.Logger) predicate.Predicate { + return &debugPredicate{ + logger: logr, + } +} + +func (p *debugPredicate) Create(e event.CreateEvent) bool { + p.logger.Debugf("Create event for resource %T: %s/%s", e.Object, e.Object.GetNamespace(), e.Object.GetName()) + return true +} + +func (p *debugPredicate) Delete(e event.DeleteEvent) bool { + p.logger.Debugf("Delete event for resource %T: %s/%s", e.Object, e.Object.GetNamespace(), e.Object.GetName()) + return true +} + +func (p *debugPredicate) Update(e event.UpdateEvent) bool { + p.logger.Debugf("Update event for resource %T: %s/%s", e.ObjectNew, e.ObjectNew.GetNamespace(), e.ObjectNew.GetName()) + return true +} + +func (p *debugPredicate) Generic(e event.GenericEvent) bool { + p.logger.Debugf("Generic event for resource %T: %s/%s", e.Object, e.Object.GetNamespace(), e.Object.GetName()) + return true +} diff --git a/pkg/varnishcontroller/predicates/label_matcher.go b/pkg/varnishcontroller/predicates/label_matcher.go new file mode 100644 index 00000000..b65a33cd --- /dev/null +++ b/pkg/varnishcontroller/predicates/label_matcher.go @@ -0,0 +1,63 @@ +package predicates + +import ( + "github.com/ibm/varnish-operator/pkg/logger" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +var _ predicate.Predicate = &LabelMatcherPredicate{} + +type LabelMatcherPredicate struct { + logger *logger.Logger + Selector labels.Selector +} + +func NewLabelMatcherPredicate(selector labels.Selector, logr *logger.Logger) *LabelMatcherPredicate { + if logr == nil { + logr = logger.NewNopLogger() + } + return &LabelMatcherPredicate{ + logger: logr, + Selector: selector, + } +} + +func (p *LabelMatcherPredicate) Create(e event.CreateEvent) bool { + return p.Selector.Matches(labels.Set(e.Object.GetLabels())) +} + +func (p *LabelMatcherPredicate) Delete(e event.DeleteEvent) bool { + return p.Selector.Matches(labels.Set(e.Object.GetLabels())) +} + +func (p *LabelMatcherPredicate) Update(e event.UpdateEvent) bool { + if !p.Selector.Matches(labels.Set(e.ObjectNew.GetLabels())) { + return false + } + + oldPod, isPod := e.ObjectOld.(*v1.Pod) + if !isPod { + return true + } + newPod, isPod := e.ObjectNew.(*v1.Pod) + if !isPod { + return true + } + + if len(newPod.Status.PodIP) != 0 && oldPod.Status.PodIP != newPod.Status.PodIP { + return true + } + + if len(newPod.Spec.NodeName) != 0 && oldPod.Spec.NodeName != newPod.Spec.NodeName { + return true + } + + return false +} + +func (p *LabelMatcherPredicate) Generic(e event.GenericEvent) bool { + return p.Selector.Matches(labels.Set(e.Object.GetLabels())) +} diff --git a/pkg/varnishcontroller/predicates/label_matcher_test.go b/pkg/varnishcontroller/predicates/label_matcher_test.go new file mode 100644 index 00000000..ba789286 --- /dev/null +++ b/pkg/varnishcontroller/predicates/label_matcher_test.go @@ -0,0 +1,116 @@ +package predicates + +import ( + "testing" + + v1 "k8s.io/api/core/v1" + v12 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/ibm/varnish-operator/pkg/logger" + "k8s.io/apimachinery/pkg/labels" + + "sigs.k8s.io/controller-runtime/pkg/event" +) + +func TestLabelMatcherPredicate_Update(t *testing.T) { + tcs := []struct { + name string + selector map[string]string + updateEvent event.UpdateEvent + shouldTriggerEvent bool + }{ + { + name: "nothing changed", + selector: map[string]string{"app": "backend"}, + updateEvent: event.UpdateEvent{ + ObjectOld: &v1.Pod{ + ObjectMeta: v12.ObjectMeta{Name: "pod1", Labels: map[string]string{"app": "backend"}}, + Spec: v1.PodSpec{NodeName: "node1"}, + Status: v1.PodStatus{PodIP: "19.43.11.32"}, + }, + ObjectNew: &v1.Pod{ + ObjectMeta: v12.ObjectMeta{Name: "pod1", Labels: map[string]string{"app": "backend"}}, + Spec: v1.PodSpec{NodeName: "node1"}, + Status: v1.PodStatus{PodIP: "19.43.11.32"}, + }, + }, + shouldTriggerEvent: false, + }, + { + name: "nothing significant changed", + selector: map[string]string{"app": "backend"}, + updateEvent: event.UpdateEvent{ + ObjectOld: &v1.Pod{ + ObjectMeta: v12.ObjectMeta{Name: "pod1", Labels: map[string]string{"app": "backend", "one": "two"}}, + Spec: v1.PodSpec{NodeName: "node1"}, + Status: v1.PodStatus{PodIP: "19.43.11.32"}, + }, + ObjectNew: &v1.Pod{ + ObjectMeta: v12.ObjectMeta{Name: "pod1", Labels: map[string]string{"app": "backend", "one": "three"}}, + Spec: v1.PodSpec{NodeName: "node1"}, + Status: v1.PodStatus{PodIP: "19.43.11.32"}, + }, + }, + shouldTriggerEvent: false, + }, + { + name: "pod doesn't match selector", + selector: map[string]string{"app": "backend"}, + updateEvent: event.UpdateEvent{ + ObjectOld: &v1.Pod{ + ObjectMeta: v12.ObjectMeta{Name: "pod1", Labels: map[string]string{"app": "backend4"}}, + Spec: v1.PodSpec{NodeName: "node1"}, + Status: v1.PodStatus{PodIP: "19.43.11.32"}, + }, + ObjectNew: &v1.Pod{ + ObjectMeta: v12.ObjectMeta{Name: "pod1", Labels: map[string]string{"app": "backend4"}}, + Spec: v1.PodSpec{NodeName: "node2"}, + Status: v1.PodStatus{PodIP: "19.43.11.33"}, + }, + }, + shouldTriggerEvent: false, + }, + { + name: "ip changed (pod scheduled)", + selector: map[string]string{"app": "backend"}, + updateEvent: event.UpdateEvent{ + ObjectOld: &v1.Pod{ + ObjectMeta: v12.ObjectMeta{Name: "pod1", Labels: map[string]string{"app": "backend"}}, + Spec: v1.PodSpec{NodeName: "node2"}, + Status: v1.PodStatus{PodIP: ""}, + }, + ObjectNew: &v1.Pod{ + ObjectMeta: v12.ObjectMeta{Name: "pod1", Labels: map[string]string{"app": "backend"}}, + Spec: v1.PodSpec{NodeName: "node2"}, + Status: v1.PodStatus{PodIP: "19.43.11.33"}, + }, + }, + shouldTriggerEvent: true, + }, + { + name: "node changed (pod scheduled)", + selector: map[string]string{"app": "backend"}, + updateEvent: event.UpdateEvent{ + ObjectOld: &v1.Pod{ + ObjectMeta: v12.ObjectMeta{Name: "pod1", Labels: map[string]string{"app": "backend"}}, + Spec: v1.PodSpec{NodeName: ""}, + Status: v1.PodStatus{PodIP: "19.43.11.33"}, + }, + ObjectNew: &v1.Pod{ + ObjectMeta: v12.ObjectMeta{Name: "pod1", Labels: map[string]string{"app": "backend"}}, + Spec: v1.PodSpec{NodeName: "node2"}, + Status: v1.PodStatus{PodIP: "19.43.11.33"}, + }, + }, + shouldTriggerEvent: true, + }, + } + + for _, tc := range tcs { + predicate := NewLabelMatcherPredicate(labels.SelectorFromSet(tc.selector), logger.NewNopLogger()) + if predicate.Update(tc.updateEvent) != tc.shouldTriggerEvent { + t.Logf(tc.name+": expected %t got %t", tc.shouldTriggerEvent, !tc.shouldTriggerEvent) + t.Fail() + } + } +} diff --git a/pkg/varnishcontroller/predicates/namespaces_matcher.go b/pkg/varnishcontroller/predicates/namespaces_matcher.go new file mode 100644 index 00000000..ffffe38c --- /dev/null +++ b/pkg/varnishcontroller/predicates/namespaces_matcher.go @@ -0,0 +1,61 @@ +package predicates + +import ( + "github.com/ibm/varnish-operator/pkg/logger" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +var _ predicate.Predicate = &NamespacesMatcherPredicate{} + +type NamespacesMatcherPredicate struct { + logger *logger.Logger + Namespaces []string +} + +func NewNamespacesMatcherPredicate(namespaces []string, logr *logger.Logger) *NamespacesMatcherPredicate { + if logr == nil { + logr = logger.NewNopLogger() + } + return &NamespacesMatcherPredicate{ + logger: logr, + Namespaces: namespaces, + } +} + +func (p *NamespacesMatcherPredicate) Create(e event.CreateEvent) bool { + return p.allow(e.Object.GetNamespace()) +} + +func (p *NamespacesMatcherPredicate) Delete(e event.DeleteEvent) bool { + return p.allow(e.Object.GetNamespace()) +} + +func (p *NamespacesMatcherPredicate) Update(e event.UpdateEvent) bool { + return p.allow(e.ObjectNew.GetNamespace()) +} + +func (p *NamespacesMatcherPredicate) Generic(e event.GenericEvent) bool { + return p.allow(e.Object.GetNamespace()) +} + +func (p *NamespacesMatcherPredicate) allow(namespace string) bool { + if len(p.Namespaces) == 0 { + return true + } + return contains(namespace, p.Namespaces) +} + +func contains(v string, s []string) bool { + if s == nil { + return false + } + + for _, value := range s { + if value == v { + return true + } + } + + return false +} diff --git a/pkg/varnishcontroller/predicates/predicate.go b/pkg/varnishcontroller/predicates/predicate.go deleted file mode 100644 index ef5e9dbb..00000000 --- a/pkg/varnishcontroller/predicates/predicate.go +++ /dev/null @@ -1,166 +0,0 @@ -package predicates - -import ( - "reflect" - "sort" - - "github.com/google/go-cmp/cmp" - "github.com/ibm/varnish-operator/api/v1alpha1" - "github.com/ibm/varnish-operator/pkg/logger" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/predicate" -) - -var _ predicate.Predicate = &varnishControllerPredicate{} - -type varnishControllerPredicate struct { - clusterUID types.UID - logger *logger.Logger - endpointsSelectors []labels.Selector -} - -func NewVarnishControllerPredicate(clusterUID types.UID, endpointsSelectors []labels.Selector, logr *logger.Logger) predicate.Predicate { - if logr == nil { - logr = logger.NewNopLogger() - } - return &varnishControllerPredicate{ - clusterUID: clusterUID, - logger: logr, - endpointsSelectors: endpointsSelectors, - } -} - -func (p *varnishControllerPredicate) Create(e event.CreateEvent) bool { - switch v := e.Object.(type) { - case *v1alpha1.VarnishCluster: - if e.Object.GetUID() != p.clusterUID { - return false - } - case *v1.Endpoints: - if !p.endpointMatchesSelector(v) { - return false - } - } - - p.logger.Debugf("Create event for resource %T: %s/%s", e.Object, e.Object.GetNamespace(), e.Object.GetName()) - return true -} - -func (p *varnishControllerPredicate) Delete(e event.DeleteEvent) bool { - switch e.Object.(type) { - case *v1alpha1.VarnishCluster: - return false - case *v1.Endpoints: - return false - } - - p.logger.Debugf("Delete event for resource %T: %s/%s", e.Object, e.Object.GetNamespace(), e.Object.GetName()) - return true -} - -func (p *varnishControllerPredicate) Update(e event.UpdateEvent) bool { - if reflect.TypeOf(e.ObjectNew) != reflect.TypeOf(e.ObjectOld) { - p.logger.Errorf("New and Old object kinds are different. New: %T, Old: %T", e.ObjectNew, e.ObjectOld) - return false - } - - switch e.ObjectNew.(type) { - case *v1alpha1.VarnishCluster: - if !p.allowVarnishClusterUpdateEvent(e) { - return false - } - case *v1.Endpoints: - if !p.allowEndpointsUpdateEvent(e) { - return false - } - } - - p.logger.Debugf("Update event for resource %T: %s/%s", e.ObjectNew, e.ObjectNew.GetNamespace(), e.ObjectNew.GetName()) - return true -} - -func (p *varnishControllerPredicate) Generic(e event.GenericEvent) bool { - switch endpoint := e.Object.(type) { - case *v1alpha1.VarnishCluster: - if e.Object.GetUID() != p.clusterUID { - return false - } - case *v1.Endpoints: - if !p.endpointMatchesSelector(endpoint) { - return false - } - } - - p.logger.Debugf("Generic event for resource %T: %s/%s", e.Object, e.Object.GetNamespace(), e.Object.GetName()) - return true -} - -func (p *varnishControllerPredicate) allowVarnishClusterUpdateEvent(e event.UpdateEvent) bool { - newCluster := e.ObjectNew.(*v1alpha1.VarnishCluster) - oldCluster := e.ObjectOld.(*v1alpha1.VarnishCluster) - if e.ObjectNew.GetUID() != p.clusterUID || e.ObjectOld.GetUID() != p.clusterUID { - return false - } - - if newCluster.Status.VCL.ConfigMapVersion != oldCluster.Status.VCL.ConfigMapVersion { - return true - } - - if !cmp.Equal(newCluster.Spec.Backend, oldCluster.Spec.Backend) { - return true - } - return false -} - -func (p *varnishControllerPredicate) allowEndpointsUpdateEvent(e event.UpdateEvent) bool { - newEndpoints := e.ObjectNew.(*v1.Endpoints) - oldEndpoints := e.ObjectOld.(*v1.Endpoints) - if !p.endpointMatchesSelector(newEndpoints) { - return false - } - - if cmp.Equal(getIPs(oldEndpoints.Subsets), getIPs(newEndpoints.Subsets)) && - cmp.Equal(getPorts(oldEndpoints.Subsets), getPorts(newEndpoints.Subsets)) { - return false - } - return true -} - -func (p *varnishControllerPredicate) endpointMatchesSelector(endpoint *v1.Endpoints) bool { - for _, selector := range p.endpointsSelectors { - if selector.Matches(labels.Set(endpoint.GetLabels())) { - return true - } - } - - return false -} - -func getIPs(eps []v1.EndpointSubset) []string { - ips := make([]string, 0) - for _, ep := range eps { - for _, addr := range append(ep.Addresses, ep.NotReadyAddresses...) { - ips = append(ips, addr.IP) - } - - } - sort.Strings(ips) - return ips -} - -func getPorts(eps []v1.EndpointSubset) []int32 { - ports := make([]int32, 0) - for _, ep := range eps { - for _, port := range ep.Ports { - ports = append(ports, port.Port) - } - - } - sort.Slice(ports, func(i, j int) bool { - return ports[i] < ports[j] - }) - return ports -} diff --git a/pkg/varnishcontroller/predicates/predicate_test.go b/pkg/varnishcontroller/predicates/predicate_test.go deleted file mode 100644 index c8fd3b1b..00000000 --- a/pkg/varnishcontroller/predicates/predicate_test.go +++ /dev/null @@ -1,904 +0,0 @@ -package predicates - -import ( - "testing" - - "github.com/ibm/varnish-operator/api/v1alpha1" - "github.com/ibm/varnish-operator/pkg/logger" - "k8s.io/apimachinery/pkg/types" - - v1 "k8s.io/api/core/v1" - v12 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "sigs.k8s.io/controller-runtime/pkg/event" -) - -func TestVarnishControllerPredicate_Update(t *testing.T) { - clusterUID := types.UID("varnishcluster-uid") - epSelectors := []labels.Selector{ - labels.Set{ - "app": "nginx", - }.AsSelector(), - labels.Set{ - "varnish-component": "varnish-service", - "varnish-owner": "varnishcluster-example", - "varnish-uid": "some-uid", - }.AsSelector(), - } - - cases := []struct { - name string - event event.UpdateEvent - shouldBeProcessed bool - }{ - { - name: "Nothing has changed nginx endpoints", - event: event.UpdateEvent{ - ObjectOld: &v1.Endpoints{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "epName", - Namespace: "epNamespace", - Labels: map[string]string{ - "app": "nginx", - }, - }, - Subsets: []v1.EndpointSubset{ - { - Addresses: []v1.EndpointAddress{ - { - IP: "192.168.0.1", - Hostname: "host1", - NodeName: func(str string) *string { return &str }("node1"), - }, - { - IP: "192.168.0.2", - Hostname: "host2", - NodeName: func(str string) *string { return &str }("node2"), - }, - }, - NotReadyAddresses: nil, - Ports: []v1.EndpointPort{ - { - Name: "web", - Port: 80, - Protocol: "TCP", - }, - { - Name: "metrics", - Port: 8080, - Protocol: "TCP", - }, - }, - }, - }, - }, - ObjectNew: &v1.Endpoints{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "epName", - Namespace: "epNamespace", - Labels: map[string]string{ - "app": "nginx", - }, - }, - Subsets: []v1.EndpointSubset{ - { - Addresses: []v1.EndpointAddress{ - { - IP: "192.168.0.1", - Hostname: "host1", - NodeName: func(str string) *string { return &str }("node1"), - }, - { - IP: "192.168.0.2", - Hostname: "host2", - NodeName: func(str string) *string { return &str }("node2"), - }, - }, - NotReadyAddresses: nil, - Ports: []v1.EndpointPort{ - { - Name: "web", - Port: 80, - Protocol: "TCP", - }, - { - Name: "metrics", - Port: 8080, - Protocol: "TCP", - }, - }, - }, - }, - }, - }, - shouldBeProcessed: false, - }, - { - name: "Nothing has changed for varnish endpoints", - event: event.UpdateEvent{ - ObjectOld: &v1.Endpoints{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "epName", - Namespace: "epNamespace", - Labels: map[string]string{ - "varnish-component": "varnish-service", - "varnish-owner": "varnishcluster-example", - "varnish-uid": "some-uid", - }, - }, - Subsets: []v1.EndpointSubset{ - { - Addresses: []v1.EndpointAddress{ - { - IP: "192.168.0.4", - Hostname: "host1", - NodeName: func(str string) *string { return &str }("node1"), - }, - { - IP: "192.168.0.5", - Hostname: "host2", - NodeName: func(str string) *string { return &str }("node2"), - }, - }, - NotReadyAddresses: nil, - Ports: []v1.EndpointPort{ - { - Name: "web", - Port: 80, - Protocol: "TCP", - }, - { - Name: "metrics", - Port: 8080, - Protocol: "TCP", - }, - }, - }, - }, - }, - ObjectNew: &v1.Endpoints{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "epName", - Namespace: "epNamespace", - Labels: map[string]string{ - "varnish-component": "varnish-service", - "varnish-owner": "varnishcluster-example", - "varnish-uid": "some-uid", - }, - }, - Subsets: []v1.EndpointSubset{ - { - Addresses: []v1.EndpointAddress{ - { - IP: "192.168.0.4", - Hostname: "host1", - NodeName: func(str string) *string { return &str }("node1"), - }, - { - IP: "192.168.0.5", - Hostname: "host2", - NodeName: func(str string) *string { return &str }("node2"), - }, - }, - NotReadyAddresses: nil, - Ports: []v1.EndpointPort{ - { - Name: "web", - Port: 80, - Protocol: "TCP", - }, - { - Name: "metrics", - Port: 8080, - Protocol: "TCP", - }, - }, - }, - }, - }, - }, - shouldBeProcessed: false, - }, - { - name: "Only the order has changed", - event: event.UpdateEvent{ - ObjectOld: &v1.Endpoints{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "epName", - Namespace: "epNamespace", - Labels: map[string]string{ - "app": "nginx", - }, - }, - Subsets: []v1.EndpointSubset{ - { - Addresses: []v1.EndpointAddress{ - { - IP: "192.168.0.2", - Hostname: "host2", - NodeName: func(str string) *string { return &str }("node2"), - }, - { - IP: "192.168.0.1", - Hostname: "host1", - NodeName: func(str string) *string { return &str }("node1"), - }, - }, - NotReadyAddresses: nil, - Ports: []v1.EndpointPort{ - { - Name: "web", - Port: 80, - Protocol: "TCP", - }, - { - Name: "metrics", - Port: 8080, - Protocol: "TCP", - }, - }, - }, - }, - }, - ObjectNew: &v1.Endpoints{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "epName", - Namespace: "epNamespace", - Labels: map[string]string{ - "app": "nginx", - }, - }, - Subsets: []v1.EndpointSubset{ - { - Addresses: []v1.EndpointAddress{ - { - IP: "192.168.0.1", - Hostname: "host1", - NodeName: func(str string) *string { return &str }("node1"), - }, - { - IP: "192.168.0.2", - Hostname: "host2", - NodeName: func(str string) *string { return &str }("node2"), - }, - }, - NotReadyAddresses: nil, - Ports: []v1.EndpointPort{ - { - Name: "metrics", - Port: 8080, - Protocol: "TCP", - }, - { - Name: "web", - Port: 80, - Protocol: "TCP", - }, - }, - }, - }, - }, - }, - shouldBeProcessed: false, - }, - { - name: "An endpoint has been removed", - event: event.UpdateEvent{ - ObjectOld: &v1.Endpoints{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "epName", - Namespace: "epNamespace", - Labels: map[string]string{ - "app": "nginx", - }, - }, - Subsets: []v1.EndpointSubset{ - { - Addresses: []v1.EndpointAddress{ - { - IP: "192.168.0.1", - Hostname: "host1", - NodeName: func(str string) *string { return &str }("node1"), - }, - { - IP: "192.168.0.2", - Hostname: "host2", - NodeName: func(str string) *string { return &str }("node2"), - }, - }, - NotReadyAddresses: nil, - Ports: []v1.EndpointPort{ - { - Name: "web", - Port: 80, - Protocol: "TCP", - }, - { - Name: "metrics", - Port: 8080, - Protocol: "TCP", - }, - }, - }, - }, - }, - ObjectNew: &v1.Endpoints{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "epName", - Namespace: "epNamespace", - Labels: map[string]string{ - "app": "nginx", - }, - }, - Subsets: []v1.EndpointSubset{ - { - Addresses: []v1.EndpointAddress{ - { - IP: "192.168.0.1", - Hostname: "host1", - NodeName: func(str string) *string { return &str }("node1"), - }, - }, - NotReadyAddresses: nil, - Ports: []v1.EndpointPort{ - { - Name: "metrics", - Port: 8080, - Protocol: "TCP", - }, - { - Name: "web", - Port: 80, - Protocol: "TCP", - }, - }, - }, - }, - }, - }, - shouldBeProcessed: true, - }, - { - name: "An address in endpoints moved to not ready state should not trigger a reconcile", - event: event.UpdateEvent{ - ObjectOld: &v1.Endpoints{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "epName", - Namespace: "epNamespace", - Labels: map[string]string{ - "app": "nginx", - }, - }, - Subsets: []v1.EndpointSubset{ - { - Addresses: []v1.EndpointAddress{ - { - IP: "192.168.0.1", - Hostname: "host1", - NodeName: func(str string) *string { return &str }("node1"), - }, - { - IP: "192.168.0.2", - Hostname: "host2", - NodeName: func(str string) *string { return &str }("node2"), - }, - }, - NotReadyAddresses: nil, - Ports: []v1.EndpointPort{ - { - Name: "web", - Port: 80, - Protocol: "TCP", - }, - { - Name: "metrics", - Port: 8080, - Protocol: "TCP", - }, - }, - }, - }, - }, - ObjectNew: &v1.Endpoints{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "epName", - Namespace: "epNamespace", - Labels: map[string]string{ - "app": "nginx", - }, - }, - Subsets: []v1.EndpointSubset{ - { - Addresses: []v1.EndpointAddress{ - { - IP: "192.168.0.1", - Hostname: "host1", - NodeName: func(str string) *string { return &str }("node1"), - }, - }, - NotReadyAddresses: []v1.EndpointAddress{ - { - IP: "192.168.0.2", - Hostname: "host2", - NodeName: func(str string) *string { return &str }("node2"), - }, - }, - Ports: []v1.EndpointPort{ - { - Name: "web", - Port: 80, - Protocol: "TCP", - }, - { - Name: "metrics", - Port: 8080, - Protocol: "TCP", - }, - }, - }, - }, - }, - }, - shouldBeProcessed: false, - }, - { - name: "A removed port from endpoints should trigger a processing", - event: event.UpdateEvent{ - ObjectOld: &v1.Endpoints{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "epName", - Namespace: "epNamespace", - Labels: map[string]string{ - "app": "nginx", - }, - }, - Subsets: []v1.EndpointSubset{ - { - Addresses: []v1.EndpointAddress{ - { - IP: "192.168.0.1", - Hostname: "host1", - NodeName: func(str string) *string { return &str }("node1"), - }, - { - IP: "192.168.0.2", - Hostname: "host2", - NodeName: func(str string) *string { return &str }("node2"), - }, - }, - NotReadyAddresses: nil, - Ports: []v1.EndpointPort{ - { - Name: "web", - Port: 80, - Protocol: "TCP", - }, - { - Name: "metrics", - Port: 8080, - Protocol: "TCP", - }, - }, - }, - }, - }, - ObjectNew: &v1.Endpoints{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "epName", - Namespace: "epNamespace", - Labels: map[string]string{ - "app": "nginx", - }, - }, - Subsets: []v1.EndpointSubset{ - { - Addresses: []v1.EndpointAddress{ - { - IP: "192.168.0.1", - Hostname: "host1", - NodeName: func(str string) *string { return &str }("node1"), - }, - { - IP: "192.168.0.2", - Hostname: "host2", - NodeName: func(str string) *string { return &str }("node2"), - }, - }, - NotReadyAddresses: nil, - Ports: []v1.EndpointPort{ - { - Name: "web", - Port: 80, - Protocol: "TCP", - }, - }, - }, - }, - }, - }, - shouldBeProcessed: true, - }, - { - name: "Endpoint not matching the selector should not be processed", - event: event.UpdateEvent{ - ObjectNew: &v1.Endpoints{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "epName", - Namespace: "epNamespace", - Labels: map[string]string{ - "app": "not-nginx", - }, - }, - }, - ObjectOld: &v1.Endpoints{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "epName", - Namespace: "epNamespace", - Labels: map[string]string{ - "app": "not-nginx", - }, - }, - }, - }, - shouldBeProcessed: false, - }, - { - name: "An event from correct VarnishCluster with changed backend labels should be processed", - event: event.UpdateEvent{ - ObjectNew: &v1alpha1.VarnishCluster{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "vcName", - Namespace: "vcNamespace", - UID: clusterUID, - Labels: map[string]string{ - "app": "varnish", - }, - }, - Spec: v1alpha1.VarnishClusterSpec{ - Backend: &v1alpha1.VarnishClusterBackend{ - Selector: map[string]string{ - "app": "nginx", - }, - }, - }, - }, - ObjectOld: &v1alpha1.VarnishCluster{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "vcName", - Namespace: "vcNamespace", - UID: clusterUID, - Labels: map[string]string{ - "app": "varnish", - }, - }, - Spec: v1alpha1.VarnishClusterSpec{ - Backend: &v1alpha1.VarnishClusterBackend{ - Selector: map[string]string{ - "app": "not-nginx", - }, - }, - }, - }, - }, - shouldBeProcessed: true, - }, - { - name: "An event from VarnishCluster with changed ConfigMap version should be processed", - event: event.UpdateEvent{ - ObjectNew: &v1alpha1.VarnishCluster{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "vcName", - Namespace: "vcNamespace", - UID: clusterUID, - Labels: map[string]string{ - "app": "varnish", - }, - }, - Spec: v1alpha1.VarnishClusterSpec{ - Backend: &v1alpha1.VarnishClusterBackend{ - Selector: map[string]string{ - "app": "nginx", - }, - }, - }, - Status: v1alpha1.VarnishClusterStatus{ - VCL: v1alpha1.VCLStatus{ - ConfigMapVersion: "version1", - }, - }, - }, - ObjectOld: &v1alpha1.VarnishCluster{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "vcName", - Namespace: "vcNamespace", - UID: clusterUID, - Labels: map[string]string{ - "app": "varnish", - }, - }, - Spec: v1alpha1.VarnishClusterSpec{ - Backend: &v1alpha1.VarnishClusterBackend{ - Selector: map[string]string{ - "app": "nginx", - }, - }, - }, - Status: v1alpha1.VarnishClusterStatus{ - VCL: v1alpha1.VCLStatus{ - ConfigMapVersion: "version2", - }, - }, - }, - }, - shouldBeProcessed: true, - }, - { - name: "An unchanged VarnishCluster event should not be processed", - event: event.UpdateEvent{ - ObjectNew: &v1alpha1.VarnishCluster{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "vcName", - Namespace: "vcNamespace", - UID: clusterUID, - Labels: map[string]string{ - "app": "varnish", - }, - }, - Spec: v1alpha1.VarnishClusterSpec{ - Backend: &v1alpha1.VarnishClusterBackend{ - Selector: map[string]string{ - "app": "nginx", - }, - }, - }, - Status: v1alpha1.VarnishClusterStatus{ - VCL: v1alpha1.VCLStatus{ - ConfigMapVersion: "version1", - }, - }, - }, - ObjectOld: &v1alpha1.VarnishCluster{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "vcName", - Namespace: "vcNamespace", - UID: clusterUID, - Labels: map[string]string{ - "app": "varnish", - }, - }, - Spec: v1alpha1.VarnishClusterSpec{ - Backend: &v1alpha1.VarnishClusterBackend{ - Selector: map[string]string{ - "app": "nginx", - }, - }, - }, - Status: v1alpha1.VarnishClusterStatus{ - VCL: v1alpha1.VCLStatus{ - ConfigMapVersion: "version1", - }, - }, - }, - }, - shouldBeProcessed: false, - }, - { - name: "An event from not corresponding VarnishCluster should not be processed", - event: event.UpdateEvent{ - ObjectNew: &v1alpha1.VarnishCluster{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "vcName", - Namespace: "vcNamespace", - UID: "differentUID", - Labels: map[string]string{ - "app": "not-nginx", - }, - }, - }, - ObjectOld: &v1alpha1.VarnishCluster{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "vcName", - Namespace: "vcNamespace", - UID: "differentUID", - Labels: map[string]string{ - "app": "not-nginx", - }, - }, - }, - }, - shouldBeProcessed: false, - }, - { - name: "An event with wrong object type for event.ObjectOld should not be processed", - event: event.UpdateEvent{ - ObjectOld: &v1.Pod{ - ObjectMeta: v12.ObjectMeta{ - Name: "epName", - Namespace: "epNamespace", - }, - }, - ObjectNew: &v1.Endpoints{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "epName", - Namespace: "epNamespace", - Labels: map[string]string{ - "app": "nginx", - }, - }, - }, - }, - shouldBeProcessed: false, - }, - } - - for _, c := range cases { - epPredicate := NewVarnishControllerPredicate(clusterUID, epSelectors, nil) - if allowToProcess := epPredicate.Update(c.event); allowToProcess != c.shouldBeProcessed { - t.Fatalf("Test %q failed. Allowed to process %t. Should've been allowed to process: %t", c.name, allowToProcess, c.shouldBeProcessed) - } - } -} - -func TestVarnishControllerPredicateCreateGeneric(t *testing.T) { - clusterUID := types.UID("varnishcluster-uid") - epSelectors := []labels.Selector{ - labels.Set{ - "app": "nginx", - }.AsSelector(), - labels.Set{ - "varnish-component": "varnish-service", - "varnish-owner": "varnishcluster-example", - "varnish-uid": "some-uid", - }.AsSelector(), - } - - cases := []struct { - name string - event event.CreateEvent - shouldBeProcessed bool - }{ - { - name: "The VarnishCluster with correct UID should be processed", - event: event.CreateEvent{ - Object: &v1alpha1.VarnishCluster{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "vcName", - Namespace: "vcNamespace", - UID: clusterUID, - Labels: map[string]string{ - "app": "nginx", - }, - }, - }, - }, - shouldBeProcessed: true, - }, - { - name: "The VarnishCluster with wrong UID should not be processed", - event: event.CreateEvent{ - Object: &v1alpha1.VarnishCluster{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "vcName", - Namespace: "vcNamespace", - UID: "wrongUID", - Labels: map[string]string{ - "app": "nginx", - }, - }, - }, - }, - shouldBeProcessed: false, - }, - { - name: "Endpoints with not matching selector should not be processed", - event: event.CreateEvent{ - Object: &v1.Endpoints{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "epName", - Namespace: "epNamespace", - Labels: map[string]string{ - "wrong": "labels", - }, - }, - }, - }, - shouldBeProcessed: false, - }, - { - name: "Endpoints with matching selector should be processed", - event: event.CreateEvent{ - Object: &v1.Endpoints{ - TypeMeta: v12.TypeMeta{}, - ObjectMeta: v12.ObjectMeta{ - Name: "epName", - Namespace: "epNamespace", - Labels: map[string]string{ - "app": "nginx", - }, - }, - }, - }, - shouldBeProcessed: true, - }, - } - - for _, c := range cases { - vcPredicate := NewVarnishControllerPredicate(clusterUID, epSelectors, logger.NewNopLogger()) - if allowToProcess := vcPredicate.Create(c.event); allowToProcess != c.shouldBeProcessed { - t.Fatalf("Test %q failed for Create. Allowed to process %t. Should've been allowed to process: %t", c.name, allowToProcess, c.shouldBeProcessed) - } - // the logic for Generic is the same as for Create so reuse the test cases - if allowToProcess := vcPredicate.Generic(event.GenericEvent{Object: c.event.Object}); allowToProcess != c.shouldBeProcessed { - t.Fatalf("Test %q failed for Generic. Allowed to process %t. Should've been allowed to process: %t", c.name, allowToProcess, c.shouldBeProcessed) - } - } -} - -func TestVarnishControllerPredicate_Delete(t *testing.T) { - cases := []struct { - name string - event event.DeleteEvent - shouldBeProcessed bool - }{ - { - name: "Deleted endpoints shouldn't trigger and event", - event: event.DeleteEvent{ - Object: &v1.Endpoints{}, - }, - shouldBeProcessed: false, - }, - { - name: "Deleted VarnishClusters shouldn't trigger and event", - event: event.DeleteEvent{ - Object: &v1alpha1.VarnishCluster{}, - }, - shouldBeProcessed: false, - }, - { - name: "Other watched resources deletion should trigger an event", - event: event.DeleteEvent{ - Object: &v1.Pod{ - ObjectMeta: v12.ObjectMeta{ - Name: "testPod", - Namespace: "testNamespace", - }, - }, //if a watch for a Pod will be added, it should not be ignored - }, - shouldBeProcessed: true, - }, - } - - for _, c := range cases { - epPredicate := NewVarnishControllerPredicate("someUID", []labels.Selector{}, logger.NewNopLogger()) - - if allowToProcess := epPredicate.Delete(c.event); allowToProcess != c.shouldBeProcessed { - t.Fatalf("Test %q failed for Delete. Allowed to process %t. Should've been allowed to process: %t", c.name, allowToProcess, c.shouldBeProcessed) - } - } -} diff --git a/pkg/varnishcontroller/predicates/varnishcluster.go b/pkg/varnishcontroller/predicates/varnishcluster.go new file mode 100644 index 00000000..b8b78c06 --- /dev/null +++ b/pkg/varnishcontroller/predicates/varnishcluster.go @@ -0,0 +1,82 @@ +package predicates + +import ( + "reflect" + + "github.com/google/go-cmp/cmp" + "github.com/ibm/varnish-operator/api/v1alpha1" + "github.com/ibm/varnish-operator/pkg/logger" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +var _ predicate.Predicate = &LabelMatcherPredicate{} + +type varnishClusterPredicate struct { + uuid types.UID + logger *logger.Logger +} + +func NewVarnishClusterPredicate(uuid types.UID, logr *logger.Logger) predicate.Predicate { + if logr == nil { + logr = logger.NewNopLogger() + } + return &varnishClusterPredicate{ + logger: logr, + uuid: uuid, + } +} + +func (p *varnishClusterPredicate) Create(e event.CreateEvent) bool { + vc, ok := e.Object.(*v1alpha1.VarnishCluster) + if !ok { + return true + } + + if vc.GetUID() != p.uuid { + return false + } + + return true +} + +func (p *varnishClusterPredicate) Delete(e event.DeleteEvent) bool { + _, ok := e.Object.(*v1alpha1.VarnishCluster) + return ok +} + +func (p *varnishClusterPredicate) Update(e event.UpdateEvent) bool { + if reflect.TypeOf(e.ObjectNew) != reflect.TypeOf(e.ObjectOld) { + return false + } + + newCluster := e.ObjectNew.(*v1alpha1.VarnishCluster) + oldCluster := e.ObjectOld.(*v1alpha1.VarnishCluster) + if e.ObjectNew.GetUID() != p.uuid || e.ObjectOld.GetUID() != p.uuid { + return false + } + + if newCluster.Status.VCL.ConfigMapVersion != oldCluster.Status.VCL.ConfigMapVersion { + return true + } + + if !cmp.Equal(newCluster.Spec.Backend, oldCluster.Spec.Backend) { + return true + } + + return true +} + +func (p *varnishClusterPredicate) Generic(e event.GenericEvent) bool { + vc, ok := e.Object.(*v1alpha1.VarnishCluster) + if !ok { + return true + } + + if vc.GetUID() != p.uuid { + return false + } + + return true +} diff --git a/tests/main_test.go b/tests/main_test.go index 841070f8..f11ffc47 100644 --- a/tests/main_test.go +++ b/tests/main_test.go @@ -6,6 +6,8 @@ import ( "os" "testing" + "github.com/onsi/ginkgo/v2/types" + v1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" @@ -67,18 +69,18 @@ var _ = BeforeSuite(func() { }) var _ = JustAfterEach(func() { - if CurrentGinkgoTestDescription().Failed { - fmt.Printf("Test failed! Collecting diags just after failed test in %s\n", CurrentGinkgoTestDescription().TestText) + if CurrentSpecReport().Failed() && CurrentSpecReport().State != types.SpecStateInterrupted { + fmt.Printf("Test failed! Collecting diags just after failed test in %s:%d\n", CurrentSpecReport().FileName(), CurrentSpecReport().LineNumber()) Expect(os.MkdirAll(debugLogsDir, 0777)).To(Succeed()) vcList := &vcapi.VarnishClusterList{} Expect(k8sClient.List(context.Background(), vcList)).To(Succeed()) - fmt.Fprintf(GinkgoWriter, "Gathering log info for Varnish Operator\n") + GinkgoWriter.Println("Gathering log info for Varnish Operator") showPodLogs(operatorPodLabels, "varnish-operator") for _, vc := range vcList.Items { - fmt.Fprintf(GinkgoWriter, "Gathering log info for VarnishCluster %s/%s\n", vc.Namespace, vc.Name) + GinkgoWriter.Printf("Gathering log info for VarnishCluster %s/%s\n", vc.Namespace, vc.Name) podList := &v1.PodList{} Expect(k8sClient.List(context.Background(), podList, client.InNamespace(vc.Namespace))).To(Succeed()) diff --git a/varnish-operator/crds/varnishcluster.yaml b/varnish-operator/crds/varnishcluster.yaml index 4979e128..453b9bbf 100644 --- a/varnish-operator/crds/varnishcluster.yaml +++ b/varnish-operator/crds/varnishcluster.yaml @@ -867,6 +867,10 @@ spec: type: object backend: properties: + namespaces: + items: + type: string + type: array port: anyOf: - type: integer